ちょっと嬉しい発見があったのでメモ。
余分にSQLを発行することでトータルのDBコール回数を劇的に減らせることがある
たとえば次のように10通の投稿へのリンクを作ると、SQLは20回発行される。
// 表示したい投稿ID
$post_ids = array( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 );
// 各投稿へのリンクを作成
foreach ( $post_ids as $post_id ) {
$out[] = sprintf(
'<li><a href="%s">%s</a></li>',
get_permalink( $post_id ),
get_the_title( $post_id ),
);
}
これは get_permalink()
が内部的に get_post()
を発行するため。get_post()
は投稿と関連タームを取得するためにDBを2回コールする。get_the_title()
はオブジェクトキャッシュからデータを取得するのでDBアクセスは発生しない。
このとき次のように get_permalink()
の前にまとめて投稿を取得しておくと、発行される回数は2回で済む。投稿を取得するための1回と、関連タームを取得するための1回。
$post_ids = array( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 );
$q = new WP_Query( array(
'post_status' => 'publish',
'post__in' => $post_ids,
'posts_per_page' => -1,
'update_post_meta_cache' => false,
) );
unset( $q );
foreach ( $post_ids as $post_id ) {
$out[] = sprintf(
'<li><a href="%s">%s</a></li>',
get_permalink( $post_id ),
get_the_title( $post_id ),
);
}
取得してすぐ unset()
しているので無駄なようだが、DBコールの数を大きく削減する効果がある。
これは、WP_Queryが取得した投稿を個別にキャッシュしてくれているため。取得するだけで効果があることを示すために unset()
を入れたが、放置でもOK。
ソースをたどってみると、次のような流れで投稿を個別にキャッシュに格納していた。なんとなく、まとめて取得したデータはまとめてキャッシュされるかと思っていたけれど、当然ながらこのほうがヒット率が高い。
new WP_Query()
→ __construct()
→ get_post()
→ update_post_caches()
→ update_post_cache()
→ 投稿を個別に wp_cache_add()
なお 'update_post_meta_cache' => false
は、ついでに投稿メタを取得しておくためのSQL発行を抑制する。SQL1回だけだが節約にはなる。
'update_post_term_cache' => false
を加えてしまうと逆効果。get_the_category()
のなかでタームを取得しているため。ただしパーマリンクの構造によっては
なんにせよget_permalink()にかぎらず、このテクニックは覚えておきたい。