最近发现有个服务器上的网站打开速度很慢,有时甚至出现完全打不开的情况。查看了一下服务器状态,发现服务器负载很大,最高时候,load average5分钟平均值都到了20(8核心CPU),接着查看了一下服务IO情况,发现Mysql读写非常频繁,如下图
这显然是不正常的情况。接着,本人又查看了mysql的状态,发现出现” Copying to tmp table”的状态,查询语句为“SELECT ID, post_title,guid”。问题应该就出现在这里了,大量临时表的读写显然会导致IO过高,按理说,WordPress官方应该对这些查询做过一些优化,如果一个wordpress会导致出现如此大的问题,它应该也不会如此流行。
百度搜索了一下“SELECT ID, post_title,guid”,发现这个网上有很多获取wordpres随机文章的教程包涵这条语句,整条语句是“SELECT ID, post_title,guid from XXX where post_status = ‘publish’ and post_title != ” AND post_password =” AND post_type = ‘post’ ORDER BY RAND() LIMIT XXX”。这里有个RAND函数,比较显眼。猜想是当查询中出现ORDER BY RAND(),并且记录数量较大时,会出现查询缓慢的情况。查询了一下相关资料:You cannot use a column with RAND() values in an ORDER BY clause, because ORDER BY would evaluate the column multiple times. 在ORDER BY从句里面不能使用RAND()函数,因为这样会导致数据列被多次扫描。所以,IO过高的问题基本就已经找到原因。那么,获取随机文章的功能肯定是需要的,考虑到性能问题,不能用这个查询,有没有其它的办法呢?肯定是有的,这里我把原来的方法和我新做的方法做了一个下对比。下面是具体的函数,实验一,是网上流传的获取随机文章的方法,实验二是新方法,整个数据表的记录是9万条左右,下面是测试代码:
<?php $host="localhost"; $username="******"; $passwd="******"; $dbname="******"; $table="en_posts"; $cono=mysql_pconnect($host,$username,$passwd); if(!$cono){ exit("数据库连接失败"); } mysql_query("set names utf8"); mysql_select_db($dbname); //实验一,原来的查询,开始执行 $begin = microtime(TRUE); $sql="SELECT ID, post_title,guid from $table where post_status = 'publish' and post_title != '' AND post_password ='' AND post_type = 'post' ORDER BY RAND() LIMIT 0,5"; $result=mysql_query($sql,$cono); while($list=mysql_fetch_row($result)){ print_r($list); } //结束执行 $end = microtime(TRUE); echo "\n<br/>实验一,执行时间:".($end-$begin)."秒"; //实验二,新的查询,开始执行 $begin=microtime(TRUE); function getlist($num){ global $table; $arr=array(); $count=mysql_fetch_row(mysql_query("SELECT MIN(ID),MAX(ID) from $table")); $startid=$count[0]; $endid=$count[1]; $querycount=0; for ($i=0;$i<$num;$i++){ //安全设置,保证不出现死循环 if($querycount-$num>5) break; $id=rand($startid,$endid); $sql="SELECT ID, post_title,guid from $table where ID=$id and post_status = 'publish' and post_title != '' AND post_password ='' AND post_type = 'post'"; $result=mysql_query($sql); if(!$result){$i--;continue;} $arr[]=mysql_fetch_row($result); $querycount++; } return $arr; } print_r(getlist(5)); $end=microtime(TRUE); echo "\n<br/>实验二,执行时间:".($end-$begin)."秒"; mysql_close($cono);
两次查询的结果如下:
可以看到,同样是随机后去5条记录,实验一用了35秒多,实验二才用了不到60毫秒,相差不可谓不大。下面是新的wordpress获取随机文章的方法,把函数放到主题的functions.php中,在页面中即可实现调用:
function random_posts($posts_num=5,$before='<li>',$after='</li>'){ global $wpdb; $count= $wpdb->get_results("SELECT MIN(ID) as min,MAX(ID) as max from $wpdb->posts"); $startid=$count[0]->min; $endid=$count[0]->max; $querycount=0; for ($i=0;$i<$posts_num;$i++){ //安全设置,保证不出现死循环 if($querycount-$posts_num>$posts_num) break; $id=rand($startid,$endid); $sql="SELECT ID, post_title,guid from $wpdb->posts where ID=$id and post_status = 'publish' and post_title != '' AND post_password ='' AND post_type = 'post'"; $randposts=$wpdb->get_results($sql); if(! $randposts){$i--;continue;} $output = ''; foreach ($randposts as $randpost) { $post_title = stripslashes($randpost->post_title); $permalink = get_permalink($randpost->ID); $output .= $before.'<a href="' . $permalink . '" rel="bookmark" title="'; $output .= $post_title . '">' . $post_title . '</a>'; $output .= $after; } echo $output; $querycount++; } }
再去测试,发现速度要快很多,IO也降低不少,当然,在文章数量较少时,它可能会出现重复的问题。但是像文章上文描述的,文章数量达到9万的情况,这样的代码是更好的选择。
结论:
1,Mysql执行大查询,内存放不下时,信息会全部copy到硬盘,导致IO飙升,查询时间增大。
2,使用网上的代码一定要慎重。
评论列表: