2026年7月6日
2026年7月6日
WordPressのオブジェクトキャッシュエラーを解決する方法
はじめに
WordPressでwp_cache_set()でデータを保存したのに次のリクエストでwp_cache_get()がfalseを返す・オブジェクトキャッシュを有効にしたのにデータベースクエリ数が減らない・wp_cache_delete()を呼んでもキャッシュが削除されない・Memcachedの接続エラーでサイトがダウンするといった問題は、キャッシュのスコープ(リクエスト内のみ/永続化)の誤解・グループ名の不一致・外部キャッシュバックエンドの設定ミスが原因です。
症状・原因
wp_cache_set('key', 'value', 'group', 3600)で保存したが別ページで取得できないwp_cache_flush()を呼んだのに特定のグループのキャッシュだけ残っているWP_Object_Cacheの内部キャッシュ(非永続)とRedis/Memcachedの永続キャッシュの違いを理解していない- Memcachedのキャッシュキーが255バイトを超えてエラーになっている
解決手順
ステップ1:オブジェクトキャッシュの状態を診断する
# WP-CLIでキャッシュの動作を確認
wp eval "
// 基本的なキャッシュテスト
\$key = 'test_' . uniqid();
\$group = 'test_group';
// 書き込み
\$set = wp_cache_set(\$key, 'hello', \$group, 60);
echo 'wp_cache_set: ' . (\$set ? 'success' : 'failed') . PHP_EOL;
// 読み込み
\$val = wp_cache_get(\$key, \$group);
echo 'wp_cache_get: ' . (\$val === 'hello' ? 'PASS' : 'FAIL (got: ' . var_export(\$val, true) . ')') . PHP_EOL;
// 削除
wp_cache_delete(\$key, \$group);
\$val2 = wp_cache_get(\$key, \$group);
echo 'wp_cache_delete: ' . (\$val2 === false ? 'PASS' : 'FAIL') . PHP_EOL;
// 外部キャッシュ使用確認
echo 'External cache: ' . (wp_using_ext_object_cache() ? 'yes (Redis/Memcached)' : 'no (in-memory only)') . PHP_EOL;
// キャッシュオブジェクト情報
global \$wp_object_cache;
echo 'Cache class: ' . get_class(\$wp_object_cache) . PHP_EOL;
"
# データベースクエリ数の確認(外部キャッシュ効果測定)
wp eval "
global \$wpdb;
\$before = \$wpdb->num_queries;
\$posts = get_posts(['posts_per_page' => 10]);
\$after = \$wpdb->num_queries;
echo 'Queries for get_posts: ' . (\$after - \$before) . PHP_EOL;
// 2回目は同じクエリがキャッシュされるか確認
\$before2 = \$wpdb->num_queries;
\$posts2 = get_posts(['posts_per_page' => 10]);
\$after2 = \$wpdb->num_queries;
echo 'Queries (2nd call): ' . (\$after2 - \$before2) . PHP_EOL;
"
ステップ2:wp_cacheの正しい使い方を実装する
// functions.php: オブジェクトキャッシュの正しい使い方
// ① 基本的なキャッシュパターン(Cache-Aside)
function get_featured_posts(int $count = 5): array {
$cache_key = "featured_posts_{$count}";
$cache_group = 'featured';
// キャッシュから取得を試みる
$cached = wp_cache_get($cache_key, $cache_group);
if (false !== $cached) {
return $cached;
}
// キャッシュミス:DBからデータを取得
$posts = get_posts([
'posts_per_page' => $count,
'post_status' => 'publish',
'meta_key' => 'is_featured',
'meta_value' => '1',
]);
// 結果をキャッシュに保存(30分)
wp_cache_set($cache_key, $posts, $cache_group, 30 * MINUTE_IN_SECONDS);
return $posts;
}
// ② 投稿更新時にグループ全体を無効化
add_action('save_post', function(int $post_id): void {
if (wp_is_post_revision($post_id)) {
return;
}
// グループ内の全キャッシュを削除
wp_cache_delete("featured_posts_5", 'featured');
wp_cache_delete("featured_posts_10", 'featured');
// WordPress 6.1+ ではグループごと削除が可能
// wp_cache_flush_group('featured');
});
// ③ インクリメントを使ったキャッシュバスティング
function get_cache_version(string $group): int {
$version = wp_cache_get("{$group}_version", 'cache_versions');
if (false === $version) {
$version = 1;
wp_cache_set("{$group}_version", $version, 'cache_versions');
}
return (int) $version;
}
function invalidate_cache_group(string $group): void {
$version = get_cache_version($group);
wp_cache_set("{$group}_version", $version + 1, 'cache_versions');
}
ステップ3:Memcachedの設定エラーを解決する
# Memcachedの動作確認
memcached -h | head -5 # バージョン確認
telnet localhost 11211
# stats
# quit
# PHP Memcachedエクステンション確認
wp eval "
echo 'Memcached class: ' . (class_exists('Memcached') ? 'available' : 'MISSING') . PHP_EOL;
echo 'Memcache class: ' . (class_exists('Memcache') ? 'available' : 'not installed') . PHP_EOL;
// 接続テスト
if (class_exists('Memcached')) {
\$mc = new Memcached();
\$mc->addServer('127.0.0.1', 11211);
\$mc->set('test_key', 'test_value', 60);
echo 'Memcached test: ' . (\$mc->get('test_key') === 'test_value' ? 'PASS' : 'FAIL') . PHP_EOL;
echo 'Result code: ' . \$mc->getResultCode() . PHP_EOL;
}
"
// wp-config.php: Memcached設定
define('MEMCACHE_SERVERS', serialize([
['127.0.0.1', 11211, 100], // [host, port, weight]
]));
// オプション設定
global $memcached_servers;
$memcached_servers = [
'default' => [
['127.0.0.1', 11211],
],
];
ステップ4:永続キャッシュと非永続キャッシュを正しく使い分ける
// functions.php: 永続・非永続キャッシュの使い分け
// ① 非永続グループを定義(リクエスト内のみ有効)
// object-cache.php または wp-config.php で設定
global $wp_object_cache;
if (method_exists($wp_object_cache, 'add_non_persistent_groups')) {
// このグループはRedis/Memcachedに保存されない
$wp_object_cache->add_non_persistent_groups(['user_sessions', 'request_data']);
}
// ② グローバルグループを定義(マルチサイトで全サイト共有)
if (method_exists($wp_object_cache, 'add_global_groups')) {
$wp_object_cache->add_global_groups(['shared_settings', 'global_options']);
}
// ③ キャッシュキーの長さ制限を回避(Memcachedは250バイト制限)
function safe_cache_key(string $key): string {
if (strlen($key) > 200) {
return md5($key); // 長いキーはMD5ハッシュに変換
}
return $key;
}
// ④ wp_cache_remember() 相当の実装(WordPress 6.1未満向け)
function wp_cache_remember(string $key, callable $callback, string $group = '', int $expiration = 0): mixed {
$cached = wp_cache_get($key, $group);
if (false !== $cached) {
return $cached;
}
$value = $callback();
if (false !== $value) {
wp_cache_set($key, $value, $group, $expiration);
}
return $value;
}
ステップ5:キャッシュのデバッグと最適化を行う
// functions.php: キャッシュデバッグ情報を出力
add_action('wp_footer', function(): void {
if (!current_user_can('manage_options') || !WP_DEBUG) {
return;
}
global $wp_object_cache, $wpdb;
$stats = [
'queries' => $wpdb->num_queries,
'cache_hits' => method_exists($wp_object_cache, 'cache_hits') ? $wp_object_cache->cache_hits : 'N/A',
'cache_misses' => method_exists($wp_object_cache, 'cache_misses') ? $wp_object_cache->cache_misses : 'N/A',
'ext_cache' => wp_using_ext_object_cache() ? 'yes' : 'no',
];
if ($stats['cache_hits'] !== 'N/A' && ($stats['cache_hits'] + $stats['cache_misses']) > 0) {
$hit_rate = round($stats['cache_hits'] / ($stats['cache_hits'] + $stats['cache_misses']) * 100, 1);
$stats['hit_rate'] = $hit_rate . '%';
}
printf('<!-- Object Cache: %s -->', wp_json_encode($stats));
});
# キャッシュ統計を確認
wp eval "
global \$wp_object_cache;
if (method_exists(\$wp_object_cache, 'stats')) {
\$wp_object_cache->stats();
} else {
echo 'Hits: ' . (\$wp_object_cache->cache_hits ?? 'N/A') . PHP_EOL;
echo 'Misses: ' . (\$wp_object_cache->cache_misses ?? 'N/A') . PHP_EOL;
}
"
注意事項
wp_cache_flush()はRedisやMemcachedの全キャッシュをフラッシュします。大量のキャッシュがある場合、フラッシュ直後にキャッシュが再構築されるまでの間、DBへのアクセスが集中してサーバーに負荷がかかります。wp_cache_delete()で特定のキーだけ削除する方が安全です- 内部キャッシュ(外部バックエンドなし)はリクエスト内のみ有効です。ページをまたいでデータを永続化したい場合はRedis/Memcachedが必要です
- WordPress 6.1から
wp_cache_flush_group()が利用可能になりました。それ以前のバージョンではバージョン番号インクリメントパターンでグループ全体の無効化を実装してください
まとめ
WordPressオブジェクトキャッシュエラーの解決は①wp_cache_set/getテスト・wp_using_ext_object_cache()確認・DB呼び出し2回目のクエリ数比較でキャッシュ効果を測定、②Cache-Asideパターン(取得→ミス時DB→保存)・save_postフックでグループの無効化・バージョン番号インクリメントパターンでグループ一括削除、③telnet localhost 11211でMemcached動作確認・PHPエクステンションMemcachedクラスの存在確認・MEMCACHE_SERVERS定数で接続設定、④add_non_persistent_groups()でセッション系を非永続化・add_global_groups()でマルチサイト共有グループを定義・255バイト超のキーはMD5ハッシュ化、⑤wp_footerフックでヒット率をHTMLコメントに出力・cache_hits/missesを監視して80%以上を目標にの手順で解決します。