续上文,通过WordPress基准页面数据库查询次数统计,让我们对WP运行各页面所需最少查询数有个基本了解。分析这些基准页面所需的数据库查询次数的目的当然是想减低它们,以提高WP运行效率了。本文将就如何不借助外部方法,仅利用Wordpress自带技术来减少Wordpress数据库查询次数和提高运行效率,谈谈我的个人做法和思路。欢迎拍砖。本文假设阅读者应了解WP的基本知识,并能操作简单的PHP代码。
要想减少数据库的查询次数,首先要对模板进行分析,了解到模板中的哪个部分功能需要调用数据库,以及哪些些插件需要读写数据库。然后在此基础上,再来分析下减少的可能性。此文主要以模板为例,插件部分暂且不谈。以ThinkAgain现用模板为例,来分析下页面中的哪几个部分需要读取数据库。如在首页的最新留言,最新存档,目录树和标签云等这些都需要从数据库中读取相应的数据,至于日历,WP已经内置了缓存。进一步考虑下,这些东西在未有新事件前,(如新留言或新文章发布等),每次加载页面读取数据库并生成的html代码的内容都是一样的,既然如此有必要在每次加载页面时,重复这些事情,如果能将这部分内容缓存起来岂不就可以减少数据库读取次数以及提高WP运行效率了吗?OK,这个就是我对精简数据库查询次数的主要思路之一。当然,接下来要解决如何实现缓存问题以及自动更新的问题。
先来看一组数据,让数字来说话。
上图是DH提供的数据库使用情况统计数字,可以看出,从19日到21日,每次数据库链接所消耗的查询次数均值大概在29.1~30.6,22日下午开始换上更改后的代码,启用缓存,22日平均查询次数降低到25.2,23日的平均值就更低了约为20次。大约降低了33%左右。效果还是很显著的吧。
接下来,来谈谈具体如何缓存以及如何解决即时更新的问题。
缓存数据的方法虽然很多,其实按性质划分起来无非就只有2种,缓存到数据库或者缓存文件。后者,记得水煮鱼写过一篇怎样缓存sidebar的文章,简单地说,其思路为利用output control function将侧边栏内容缓存到sidebar.html文件中,然后定时进行更新。此法可以解决缓存问题以及减少数据库查询,唯一不足的就是在指定间隔时间内容,新留言的无法自动更新。p.s,这问题实际上利用WP运行机制是可以解决的。
而我的方法是将数据缓存到数据库中,然后在使用时仍然从数据库中读取。如此做法不是还要读取数据库么?怎么会减少数据库查询次数呢?先别急,且听我慢慢道来。
默认WP有10个数据表,wp_posts和comments主要存储文章内容和评论,其它的几个包括term等存储了目录和标签等等。这里不细谈。wp_options用来存储Wordpress以及插件运行时所涉及的配置等。且WP会在运行时自动读取该表的内容。换句话说,因为WP已经预读这部分内容,所以直接调用wp_options内的数据是不会产生数据库查询的。到此为止,谜底已经揭开了一大半了。高手们不用往下看,也都知道怎么做了吧。呵呵。
2. 即时更新问题
有了新留言或发布了新文章,自然想马上在页面中显示出来,这就需要即时更新已缓存在wp_options里面的数据。要解决这个问题,自然还是要从Wordpress的运行机制方面下手。
此文中,我曾简单地谈过WP是个好东西,提供了一个框架平台,也开放了很多预定义的接口给用户自定义。从系统分析的角度来看,WP也是个离散系统。从用户点击网址,WP解析url开始等,发表评论或文章等等,这些都可以视为一个离散事件。而WP提供了很完备的Hook用于用户对这些事件进行控制。
缓存的内容涉及最新留言,或文章数的显示等,这些无非都需要在新留言或新文章发布这些事件发生后激活更新缓存。理解了这点,也就很容解决问题了。发布新留言和新文章的Hook接口为:
add_action(‘comment_post’, ‘Your_Function’); //comment发布Hook
2个问题都清楚,画个流程图也很简单了。
行文至此,我已经将这个问题的Know-How解释清楚了。老鸟么,可以到此止步。小鸟们继续往下看我给的一个例子。
下面我将给出一个自己写的将最新留言缓存到wp_options表中,以及读取最新留言的2个函数(共3个函数)。有兴趣可以Copy下来放在自己模板的Function.php中,就可以激活使用了。然后在侧边栏的显示最新留言位置,将原来代码替换成
<?php cache_recent_comments();?>
简单介绍下,cache_recent_comments()函数用来显示缓存后的最新留言,cache_get_recent_comments()用来获取最新留言并缓存到wp_options表中。my_utf8_trim(),这个函数是中文工具箱的,其作用在于避免留言被截断后尾部出现乱码。在这里放上来,免得有些网友没有装中文工具箱造成不便。
1: //显示缓存后的最新留言
2: function cache_recent_comments(){
3: $cached = get_option('multicolor_cache_recent_comments');//从options表中获取已缓存的最新留言
4: if($cached){ //如果最新留言已缓存,直接显示,否则获取最新留言并缓存到options表中。
5: echo $cached;
6: }else{
7: $cached = cache_get_recent_comments();
8: echo "Cache Updated";
9: echo $cached;
10: }
11: }
12: //获取最新留言并缓存到options表中
13: function cache_get_recent_comments() {
14: $commentnumber = 6; //显示最新留言数目
15: $before = '<li> '; //留言前后html标签
16: $after = '</li>';
17: $length = 150; //截取留言的长度
18: global $wpdb;
19: $sql = "SELECT ID, comment_ID, comment_author_url, comment_content, comment_author FROM $wpdb->posts, $wpdb->comments WHERE $wpdb->posts.ID=$wpdb->comments.comment_post_ID AND ($wpdb->posts.post_status = 'publish')";
20: $sql .= "AND comment_approved = '1' ORDER BY $wpdb->comments.comment_date DESC LIMIT $commentnumber";
21: $comments = $wpdb->get_results($sql);
22: $output = '';
23: foreach ($comments as $comment) {
24: $comment_author = stripslashes($comment->comment_author);
25: $comment_author_url = stripslashes($comment->comment_author_url);
26: $comment_content = strip_tags($comment->comment_content);
27: $comment_content = stripslashes($comment_content);
28: $comment_excerpt =substr($comment_content,0,$length);
29: $comment_excerpt = convert_smilies($comment_excerpt);
30: $comment_excerpt = my_utf8_trim($comment_excerpt);
31: $permalink = get_permalink($comment->ID)."#comment-".$comment->comment_ID;
32: if (!empty($comment_author_url)){
33: $comment_author_link = '<a style="font-style: italic;color:#444;border-bottom:1px dashed #888" title=" '. $comment_author_url.'" target="_blank" href="'. $comment_author_url . '">'. $comment_author . '</a>';
34: }else{
35: $comment_author_link = '<a style="font-style: italic;color:#444;border-bottom:1px dashed #888" title=" View the entire comment by ' . $comment_author . '" target="_blank" href="'. $permalink . '">'. $comment_author . '</a>';
36: }
37: $output .= $before . $comment_author_link . ': ' . '<a target="_blank" href="' . $permalink . '" title="View the entire comment by ' . $comment_author . '">' . $comment_excerpt . '...' . '</a>' . $after;
38: }
39: update_option('multicolor_cache_recent_comments', $output); //更新缓存在options表中的最新留言
40: return $output;
41: }
42: //中文工具箱的函数,用于砍掉汉字截断尾巴可能存在的乱码。
43: function my_utf8_trim($str) {
44: $len = strlen($str);
45: for ($i=strlen($str)-1; $i>=0; $i-=1){
46: $hex .= ' '.ord($str[$i]);
47: $ch = ord($str[$i]);
48: if (($ch & 128)==0) return(substr($str,0,$i));
49: if (($ch & 192)==192) return(substr($str,0,$i));
50: }
51: return($str.$hex);
52: }
53: //通过此添加此Hook以保证最新留言能够被缓存到options表中。
54: add_action('comment_post', 'cache_get_recent_comments');
55: add_action('edit_comment', 'cache_get_recent_comments');
上述代码只是个例子,仅仅提供了缓存最新留言的功能。这里将代码放上来,目的在于参考。拿来主义固然可以解决目前问题,自己动手方能巩固所学。Do It Yourself才是真正的乐趣。否则的话,玩转WP也就失去意义了。
或许有些网友可能会说,做这些事情干什么?直接静态化不就可以了。通过直接生成实际的html文件固然有其好处,完全不需要php解析和数据库查询等,然后实际应用中,还需要解决一些问题,如一些互动信息,最新留言文章等,一旦静态化后,这部分内容就被定格了。再则,静态化插件很容易和一些下载插件存在兼容性问题,在点击这些下载插件生成的url时,很容易出现返回404错误。等等。
通过分析模板内容,我写了几个简单的函数将各页面中的通用部分缓存到数据库中,可以在一定程度上减少数据库查询次数。如原来这个模板首页需要36次查询,现在为25次。文章页为41次查询,现在为32次。当然,此法并非能解决所有问题,如文章页中的推荐阅读,因为推荐阅读涉及2个方面,一是和该post相关,也就是说要传递post id,在wp_options中给每个post id分个键值,显然是不可取的,二是推荐阅读的内容能自动和新发布文章挂钩。
实际上,我现在模板的数据库查询还是非常高的,主要是当时制作时根本就没有考虑这方面的影响。如果能在模板制作时,将数据库查询问题考虑进去,这样就能最大程度地减低查询数了。
以上的东西能否制作成插件?答案是可以的。制作一个插件提供缓存的留言,文章,标签等功能的显示,乃至可以提供现有wp内置widget提供功能的缓存。然而这并非是我写此文的目的了。分享和探讨,这才是我的初衷。当然如果需要多了,写个插件也未尝不可。
p.s,关于插件制作,上次和牛人Askie瞎聊过这方面的事情。也没有整理,直接放上来。至于为何称此君为牛人呢,您说连Blog地址都是pkphp,能和php PK的人,能不牛乎?
10:34 PM 写一些普通作用的插件也没什么意思的。关键是idea。
我挺欣赏你的那个加水印的插件
看见你的夸奖了
10:35 PM me: 要写就弄些有点新意的东西,要么就不要再去折腾普通的插件。
10:36 PM 人家有的就不去重复了
me: 弄普通的插件,这么浪费你这人才么,呵呵
me: 人家有的,再去弄,除非方法或效率方面有提高,否则有什么意思。你说呢。
imaskie: 是呀
imaskie: 恩
看见你的博客里的内容了
很有技术
不错
10:38 PM 要不瞎弄,怎么玩wp。呵呵
其实坛子上很多朋友的问题,只需要他们自己动手测试下,大部分都可以自己解决的。
OK,到此结束,欢迎拍砖。
转载文章请注明转载自:ThinkAgain - Let's Blog!




























2008.07.25 Friday 2:05 pm
沙发~~~~~~~~~
WP应该自己建立一个缓存体系,并且有开关控制选择性开启,而不是依靠插件,否则只会越缓存效率越低?
2008.07.25 Friday 2:25 pm
学习中
2008.07.25 Friday 7:16 pm
不错,先顶再看……
2008.07.25 Friday 9:53 pm
还是用缓存?等我仔细看看。
2008.07.26 Saturday 11:35 am
我十几个widget都保存到数据库了,肯定每次都需要查询,效率很低了
2008.07.26 Saturday 3:53 pm
To Dianso:widget多不见得查询数就多哦。要看看该widget是否读写数据库。
2008.07.26 Saturday 9:15 pm
wp 本身是有缓存机制的,但是很多插件 在使用时为图方便 就没有按机制来 所以就造成了 一些问题。
这里我还要纠正一些错误观念。
————
5Dianso
2008.07.26 Saturday 11:35 am
我十几个widget都保存到数据库了,肯定每次都需要查询,效率很低了
———-
就是这种概念,访问数据库 并不会直接造成速度的慢。
mysql 是世界数据库系统中排前5的了,相信设计得当,处理上亿数据级的都是没有问题的。。
整的来说 访问内存 即缓存 速度是最快的 但是内存是有限,接下来是虚拟内存,即硬盘的分页区。但是他们都是有限的而且,使用的还不止你一个程序,然后就是本地文件读取跟数据库了,事实上在大数据的存取时数据库会比本地文件的读取更快。
所以不要盲目迷信 数据库慢,数据库慢的原因,大多是任务多而引起排队的 呵呵
最后我也还没搞清楚WP的缓存机制。呵呵
2008.07.26 Saturday 9:56 pm
To Oyster: 呵呵,这里我也要纠正你的一些错误观点哦。
1.WP本身并不具有缓存机制。但如果你的php支持一些缓存扩展,如memcached,且在wp-config.php中开启wp_cache。wp的缓存机制才会利用这些缓存扩展运行。
2.之所以会被认为wp有缓存机制,是因为wp在打开页面时候,就默认将wp_options里面autoload为true的键值(数据)加载了。这也就是我的方法不需要数据查询的原因之一,因为它们已经默认被加载了。
3.mysql是非常优秀的数据库,其本身查询所消耗的时间,视代码而定,一般都很少,如0.0几秒等,但实际上因为mysql工作载荷如同时查询数等等,你可以看到wp的每个查询都会消耗0点几秒的。这点下篇文章中将会指出。
对否,可以讨论下哦。呵呵。
2008.08.01 Friday 2:35 pm
磁盘缓存载入快还是数据库直接载入快,这个没有弄懂。
2011.04.15 Friday 12:46 pm
“DH提供的数据库使用情况统计数字” 这个DH是什么啊?
2011.04.22 Friday 8:41 am
我知道了,是个空间商的名字
2011.09.13 Tuesday 6:56 pm
这个学习了~~~~ 我的数据库 查询达到112次 亲娘 要命呀 真该急救了