wordpress支撑百万文章解决方案

  • A+
所属分类:WordPress技巧

作为一个博客系统,wordpress在易用性和可扩展性上都非常出色。后题用户体验是非友好,插件众多。然而由于定位的问题,wordpress无法支撑大量文章。当文章数量达到上万的时候,有些主题的前台可能会非常卡。当文章数量达到数十万的时候,wordpress后台可能会特别卡。更何况大部分插件并没有在性能上下功夫,插件越多,wordpress越卡。那么有没有什么方案能让wordpress支撑大量文章?十万,百万,甚至更多?

支撑百万数据并不是存入一百万文章就可以了。实际上百万文章对mysql来说毫无压力。在mysql中,百万文章仅仅是百万条记录而已。导致缓慢的是mysql的查询。对于百万条记录的数据库,一次全表扫描的代价是十分昂贵的!很不幸,wordpress的经常会干一些扫描全表的事情!

整体优化

需要去掉所有查询中带有的SQL_CALC_FOUND_ROWS,这会扫描表,速度十分慢!对于大量的数据,查询的数目应该是缓存过后,或者是事先计算好的!

需要去掉所有的COUNT操作,这也会导致扫描条件中的所有行,如果条件没有索引,将会导致扫描全表!

由于wordpress查询复杂,针对每一种查询设置一个计数器不太现实,可以考虑使用查询结果缓存的方式来减少扫描全表的次数。缺点就是计数会有延迟。

首页优化

一般来说,达到百万文章级别的站点,首页应该是一个展示很多分类文章的页面。但是由于wp在初始化的时候就会根据url中的查询去数据库查询一些文章,首页是根据时间倒序查询,因此当不需要最新文章的时候,可以利用钩子去掉这部分查询。

文章页优化

看看文章页面,似乎没有什么费时间的查询,基本上就是id查询内容,用到了索引,但是其中有一个翻页的问题,其中有一个翻页的功能是可以筛选出相同分类的上下篇文章,这种查询的sql语句如下

mysql> SELECT p.ID FROM wp_posts AS p
  
INNER JOIN wp_term_relationships AS tr ON p.ID = tr.object_id INNER JOIN wp_term_taxonomy tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE p.post_date > '2017-07-04 09:57:50' AND p.post_type = 'post' AND tt.taxonomy = 'category' AND tt.term_id IN (4) AND p.post_status = 'publish' ORDER BY p.post_date ASC LIMIT 1;

使用explain看看

mysql> explain SELECT p.ID FROM wp_posts AS p INNER JOIN wp_term_relationships AS tr ON p.ID = tr.object_id INNER JOIN wp_term_taxonomy tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE p.post_date > '2017-07-04 09:57:50' AND p.post_type = 'post' AND tt.taxonomy = 'category' AND tt.term_id IN (4) AND p.post_status = 'publish' ORDER BY p.post_date ASC LIMIT 1\G
 *************************** 1. row ***************************
 id: 1
 select_type: SIMPLE
 table: tt
 type: const
 possible_keys: PRIMARY,term_id_taxonomy,taxonomy
 key: term_id_taxonomy
 key_len: 106
 ref: const,const
 rows: 1
 Extra: Using temporary; Using filesort
 *************************** 2. row ***************************
 id: 1
 select_type: SIMPLE
 table: tr
 type: ref
 possible_keys: PRIMARY,term_taxonomy_id
 key: term_taxonomy_id
 key_len: 8
 ref: const
 rows: 5576
 Extra:
 *************************** 3. row ***************************
 id: 1
 select_type: SIMPLE
 table: p
 type: eq_ref
 possible_keys: PRIMARY,type_status_date
 key: PRIMARY
 key_len: 8
 ref: aikanwen.tr.object_id
 rows: 1
 Extra: Using where
 3 rows in set (0.19 sec)

看起来就是个灾难现场,这种查询会随着分类的增加而变得奇慢无比,因为有时候分类和标签可能会达到几万几十万个!

分类优化

同首页,只查询分类的最新一些文章,甚至是提前生成的静态页面。

插件优化

wordpress最常用的插件可能是wp_postviews了,但是对于百万文章的wordpress来说,这个插件可能是个灾难(如果你用到了它的排行榜的话)。

这个排行榜会使用一个如下的查询

SELECT wp_posts.ID FROM wp_posts INNER JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id ) WHERE 1=1 AND ( 
 wp_postmeta.meta_key = 'views'
) AND wp_posts.post_type IN ('post', 'page', 'attachment') AND (wp_posts.post_status = 'publish' OR wp_posts.post_author = 1 AND wp_posts.post_status = 'private') GROUP BY wp_posts.ID ORDER BY wp_postmeta.meta_value+0 DESC LIMIT 0, 10;

这种查询也是一个灾难,对于wp来说,wp_post_meta表中根本没有meta_key 和 meta_value的索引,也是根本无法使用索引做排序,所以只能使用filesort,需要全表扫描。可以看看explain。

mysql> explain SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts INNER JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id ) WHERE 1=1 AND ( wp_postmeta.meta_key = 'views' ) AND wp_posts.post_type IN ('post', 'page', 'attachment') AND (wp_posts.post_status = 'publish' OR wp_posts.post_author = 1 AND wp_posts.post_status = 'private') GROUP BY wp_posts.ID ORDER BY wp_postmeta.meta_value+0 DESC LIMIT 0, 10 \G;
 *************************** 1. row ***************************
 id: 1
 select_type: SIMPLE
 table: wp_postmeta
 type: ALL
 possible_keys: post_id,meta_key
 key: NULL
 key_len: NULL
 ref: NULL
 rows: 67053
 Extra: Using where; Using temporary; Using filesort
 *************************** 2. row ***************************
 id: 1
 select_type: SIMPLE
 table: wp_posts
 type: eq_ref
 possible_keys: PRIMARY,type_status_date,post_author
 key: PRIMARY
 key_len: 8
 ref: aikanwen.wp_postmeta.post_id
 rows: 1
 Extra: Using where
 2 rows in set (0.17 sec)

这种排序一次,基本就是个灾难。别说百万,十万文章蜘蛛都能搞挂了。

事实上,在文章达到百万的时候,涉及到数据库读写的插件最好都不要乱用,一不小心就是个灾难。

后台优化

后台页面在打开的时候会查询当前作者的所有文章以及各个状态的文章数量,这会扫描全表,别说百万文章了,就算二十万文章也会卡死。

最好的办法就是去掉这些查询,然而去掉这些查询会让后台展示不正常,因此只能利用缓存将这些数量存起来,这样后台的速度就很快了。虽然会有展示不及时的问题,但是到了这个文章量,这已经不是主要的问题了。

全站缓存

这是最重要的一步了,再多的优化也只能保证你的百万文章站点能正常打开,但是如果访问的人一多,站点依然会卡死,甚至服务器宕机。这个时候就必须上全站缓存。看了一下市面上所有的缓存插件,也只有一个满足要求了——imwpcache。

imwpcache支持永不过期的缓存,并且支持使用sqlite作为缓存,这比使用文件缓存更好。文件缓存太分散,几万文章还好,当达到了百万时候,清理这些缓存简直是一个灾难。

总结

经过这些优化之后,支撑百万文章基本不是问题。对于普通用户来说,做到这些可能会比较困难,尤其是主题。现在市面上主题基本不会考虑这么多的文章量,文章量一上来,网站基本就瘫痪了,所以目前最佳策略还是用缓存插件,当然如果你有动手能力,也可以尝试一下去掉这些耗资源的查询,虽然可能会损失一些功能,但是能支撑这么多文章足以弥补这些损失。

你可能还喜欢下面这些文章

Redis主从模式下从库过期的key仍然能够被读到的解决方案

js保存用户自定义的样式重新载入会闪烁的解决方案

赞赏

  • 我的微信
  • 这是我的微信扫一扫
  • weinxin
  • 我的微信公众号
  • 我的微信公众号扫一扫
  • weinxin