mysql千万级数据怎么做优化
本帖最后由 demofere 于 2015-02-11 13:56:31 编辑
我现在要对字符串做统计,统计每个字符串的数量,就是类似一个map, 这符串:数量,当然,数据库顺便还保存了一下字符中的crc等信息
insert函数接收post过来的strs,这是一个字符串数组,每次post过来的字符串数量平均为1500
现在数据库里的记录1200W条,每一次insert花费的时间平均为20秒(经常出现30秒超时)
请问下各位,该怎么做优化,现在只是统计了很小一部分,预计统计完数据量大概在1-10亿条
------解决思路----------------------
你能确定瓶颈是在数据库做 查询 和 修改 的3条语句上吗?
你说 每次post过来的字符串数量平均为1500
那是指 1500 的单词吗?就算不是(是串长度)那么以平均每个单词20个字符计算,也有75个单词
你的过滤条件是 str in $tmp (str in ('xxx','xxx'....))也就是对表中的每一条记录都要做 1500(75)次字符串比较,而命中率至多是1/1500(1/75)你认为这样做合适吗?
数据库的强项在于记录间的比较,而你却在做弱势的列间比较
你可以用传入的数据构造出一个临时表(每行一个单词),再去与主表关联操作。这样才能发挥数据库的优势
再有,你的连续 2 条 update 语句不能合并在一起吗?
------解决思路----------------------
1、mysql 有内存表,你并未使用
2、将两条 update 合并,应该能减少 10 秒
------解决思路----------------------
mysql_error这种库函数的效率也不高吧而且官方也不推荐,我几乎没有用过这种,我学PHP的时候,直接开始PDO的。
还有,你用了in这个关键字,好像它是不走索引的吧。
------解决思路----------------------
应该是你测试环境的问题吧,联表查询大多数情况下是要比in快的
你测的结果差不多可能是如下原因
1.查询表的数据没有达到一定量
2.两种查询方式查出的结果集不同
3.查询字段未加索引或加的不对
4.被其他索引过多而影响
5.其他原因
我认为比较明显的就是第2个原因
你联表查时没有指定查询字段,联表出的字段肯定要比不连表的要多
还有联表查询会有一对多的情况,那样一条记录就会变成多条的,要加group by
<br>function insert(){<br> global $m;<br> if (!isset($_REQUEST['strs']) || !isset($_REQUEST['type'])<br> || !isset($_REQUEST['hash'])){<br> echo 'param error';<br> return;<br> }<br><br> //strs为所有字符串<br> $poststr = $_REQUEST['strs'];<br> <br> $xstrs = json_decode(stripslashes($poststr), true);<br> <br> $type = $_REQUEST['type'];<br> $hash = $_REQUEST['hash'];<br> <br> if (count($xstrs) <= 0){<br> $msg = 'str error';<br> DsLog::errLog($msg.$poststr);<br> echo $msg;<br> return;<br> }<br><br> if ($type == '0'){<br> $table = 'white';<br> }<br> else if($type == '1'){<br> $table = 'black';<br> }<br> else{<br> $msg = 'type error';<br> DsLog::errLog($msg);<br> echo $msg;<br> return;<br> }<br><br> $strs = array();<br> <br> for($i = 0; $i < count($xstrs); $i++) {<br> $strtmp = $xstrs[$i];<br> $strtmp = trim($strtmp);<br> $strtmp = strtolower($strtmp);<br> $strtmp = addslashes($strtmp);<br> if (strlen($strtmp) > 256){<br> $strtmp = substr($strtmp, 0, 256);<br> }<br> if (strlen($strtmp) >= 7)<br> {<br> array_push($strs, $strtmp);<br> }<br> }<br> <br> <br> //拼接所有字符串<br> $tmp = '("'.implode('","', $strs).'")';<br><br> //获取已存在的字符串<br> $sql = "select * from $table where str in $tmp";<br> $ret = mysql_query($sql, $m);<br> if (!$ret){<br> $msg = 'exec error:'.mysql_error($m).','.$sql;<br> DsLog::errLog($msg);<br> echo $msg;<br> return;<br> }<br><br> $exists = array();<br> $notexists = array();<br> $count = mysql_num_rows($ret);<br> for ($i = 0; $i < $count; $i++)<br> {<br> $item = mysql_fetch_assoc($ret);<br> if (!$item){<br> break;<br> }<br> array_push($exists, $item['str']);<br> }<br> <br> for ($i = 0; $i < count($strs); $i++){<br> if (in_array($strs[$i], $exists)){<br> continue;<br> }<br><br> array_push($notexists, $strs[$i]);<br> }<br><br> for($i = 0; $i < count($exists); $i++) {<br> $exists[$i] = addslashes($exists[$i]);<br> }<br> for($i = 0; $i < count($notexists); $i++) {<br> $notexists[$i] = addslashes($notexists[$i]);<br> }<br> <br> if (count($exists) > 0){<br> //更新已存在字符串的count字段<br> $tmp = '("'.implode('","', $exists).'")';<br> $time = date('YmdHi');<br> $sql = "update $table set count=count+1 where str in $tmp";<br> $ret = mysql_query($sql, $m);<br> if (!$ret){<br> $msg = 'exec error:'.mysql_error($m).','.$sql;<br> DsLog::errLog($msg);<br> echo $msg;<br> return;<br> }<br><br> //更新已存在字符串的upd字段<br> $sql = "update $table set upd='$time' where str in $tmp";<br> $ret = mysql_query($sql, $m);<br> if (!$ret){<br> $msg = 'exec error:'.mysql_error($m).','.$sql;<br> DsLog::errLog($msg);<br> echo $msg;<br> return;<br> }<br> }<br> <br> <br> //插入新信息<br> if (count($notexists) > 0){<br> $time = date('YmdHi');<br> $sql = "insert ignore into $table (str,hash,count, upd) values";<br> for ($i = 0; $i < count($notexists); $i++){<br> $str = $notexists[$i];<br> $crc = sprintf("%u", crc32($str));<br> $sql .= "('$str','$crc','1', '$time'),";<br> }<br><br> $sql = substr($sql, 0, strlen($sql) - 1);<br> $ret = mysql_query($sql, $m);<br> if (!$ret){<br> $msg = 'insert error:'.mysql_error($m).','.$sql;<br> DsLog::errLog($msg);<br> echo $msg;<br> return;<br> }<br> }<br> <br> echo !!$ret;<br> }<br>我现在要对字符串做统计,统计每个字符串的数量,就是类似一个map, 这符串:数量,当然,数据库顺便还保存了一下字符中的crc等信息
insert函数接收post过来的strs,这是一个字符串数组,每次post过来的字符串数量平均为1500
现在数据库里的记录1200W条,每一次insert花费的时间平均为20秒(经常出现30秒超时)
请问下各位,该怎么做优化,现在只是统计了很小一部分,预计统计完数据量大概在1-10亿条
------解决思路----------------------
你能确定瓶颈是在数据库做 查询 和 修改 的3条语句上吗?
你说 每次post过来的字符串数量平均为1500
那是指 1500 的单词吗?就算不是(是串长度)那么以平均每个单词20个字符计算,也有75个单词
你的过滤条件是 str in $tmp (str in ('xxx','xxx'....))也就是对表中的每一条记录都要做 1500(75)次字符串比较,而命中率至多是1/1500(1/75)你认为这样做合适吗?
数据库的强项在于记录间的比较,而你却在做弱势的列间比较
你可以用传入的数据构造出一个临时表(每行一个单词),再去与主表关联操作。这样才能发挥数据库的优势
再有,你的连续 2 条 update 语句不能合并在一起吗?
------解决思路----------------------
1、mysql 有内存表,你并未使用
2、将两条 update 合并,应该能减少 10 秒
------解决思路----------------------
mysql_error这种库函数的效率也不高吧而且官方也不推荐,我几乎没有用过这种,我学PHP的时候,直接开始PDO的。
还有,你用了in这个关键字,好像它是不走索引的吧。
------解决思路----------------------
应该是你测试环境的问题吧,联表查询大多数情况下是要比in快的
你测的结果差不多可能是如下原因
1.查询表的数据没有达到一定量
2.两种查询方式查出的结果集不同
3.查询字段未加索引或加的不对
4.被其他索引过多而影响
5.其他原因
我认为比较明显的就是第2个原因
你联表查时没有指定查询字段,联表出的字段肯定要比不连表的要多
还有联表查询会有一对多的情况,那样一条记录就会变成多条的,要加group by









