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%以上を目標にの手順で解決します。

お気軽にご相談ください

お見積りへ お問い合わせへ