2011年11月28日

WordPressマルチサイトのパフォーマンス問題


WordPress 3 、マルチサイト機能を使うとパフォーマンスが上がらないのです。
問題は、主に wp_includes/functions.php の wp_load_alloptions() と get_options() にある。

wp_load_alloptions() は、wp_optionsテーブルからautoload = ‘yes’ のオプション値を全部とってくる関数だ。
マルチサイトじゃなければ、この関数内では wp_cache_get() が呼ばれてオブジェクトキャッシュが有効になるので、毎回DBにクエリ発行して wp_options のほぼ全件をとってくるなんてことはないのだが、マルチサイトが有効だとサイトごとに異なる wp_options を読まねばならず、オブジェクトキャッシュを使うことができない。(いや、できるんだろうけど、影響が大きすぎるから後回しになってると思われる。)

該当箇所はここらへん。マルチサイトだと $alloptions は false になるので、このあと毎回DBに問い合わせにいくことになる。

[ccln_php width=”600″]
if ( !defined( ‘WP_INSTALLING’ ) || !is_multisite() )
$alloptions = wp_cache_get( ‘alloptions’, ‘options’ );
else
$alloptions = false;
[/ccln_php]

しかも、この関数、get_option() からほぼ無条件に呼び出されるので、 DB へのクエリはHTTPリクエスト毎に1回ではなく、 get_option() が呼ばれる毎に発行されてしまうのである。複雑なパーマリンク構造だったりすると、 wp_options テーブルの rewrite_rule 値が肥大化してたりもするので、この負荷は相当になる。

で、なんで get_option() は「ほぼ無条件に」 wp_load_alloptions() なんていう負荷の高い関数を呼び出すのか、と。これもおそらく、マルチサイトで wp_options テーブルが混乱しないようにするための「とりあえず」な策なのではないか。

該当箇所はこんなんなってます。

[ccln_php width=”600″]
$alloptions = wp_load_alloptions();

$value = false;
if ( isset( $alloptions[$option] ) ) {
$value = $alloptions[$option];
} else {
$value = wp_cache_get( $option, ‘options’ );
[/ccln_php]

ロジックとしては、
「ぜんぶのオプションをとってきて、その中に必要なオプション(1つ)があればそれが値です。なければキャッシュからとってきます。」
ということである。
「あ、wp_cache_get() 使っちゃってる!マルチサイトだと混乱しちゃう!じゃあとりあえず全部取ってきたら、 wp_load_alloptions() ではキャッシュ使わなくしてるから大丈夫だよね。」
ってところか。

たしかにこれで正しく動くのだろうけど、 get_option() なんて実行頻度の高い関数で、wp_options テーブル全件SELECTなんて無茶をしたら、そりゃパフォーマンス上がらないの当たり前である。実際、 query_log には膨大な数の

「SELECT option_name, option_value FROM wp_options WHERE autoload = ‘yes’」

が記録され、DB の CPU 負荷はたいしたことないのに Web サーバはひたすら I/O 待ちをするということになる。

解決策(これも”とりあえず”だけどね・・・)としては、以下のように2行コメントアウト。

[ccln_php width=”600″]
//$alloptions = wp_load_alloptions();

$value = false;
if ( isset( $alloptions[$option] ) ) {
$value = $alloptions[$option];
} else {
//$value = wp_cache_get( $option, ‘options’ );
[/ccln_php]

どうせ、このあとで wp_options から指定されたオプション1行だけを読んでくるクエリを発行しているのである。オブジェクトキャッシュが使えなかったとしても、毎回全件読むよりは毎回1件だけ読んだほうが全然お得。

WordPress 本体を書き換えるのはオススメできないけれど、改善されるまでの窮余の策として。


[社長ブログ]