2026年6月3日

2026年6月3日

WordPressのトランジェントAPIでキャッシュを実装する方法

はじめに

「重いDBクエリの結果をキャッシュしてページを高速化したい」「外部APIのレスポンスをキャッシュして無駄なリクエストを減らしたい」「WordPress標準のキャッシュ機能を使いたい」——トランジェントAPIはWordPress標準の一時データ保存機構で、Redis/Memcachedとも自動連携します。

症状・原因

WP_Queryによる複雑な検索や外部APIコールを毎ページリクエストで実行するとサーバー負荷が高くなります。トランジェントAPIで結果をDBまたはオブジェクトキャッシュに保存することで、TTL(有効期間)内は高速な取得が可能になります。

解決手順

ステップ1:基本的なトランジェントの使い方

// functions.php: トランジェントの基本操作
// 保存(TTL: 1時間)
set_transient( 'my_cache_key', $data, HOUR_IN_SECONDS );

// 取得(falseならキャッシュなし)
$data = get_transient( 'my_cache_key' );
if ( false === $data ) {
    // キャッシュがない場合は新しく取得
    $data = fetch_expensive_data();
    set_transient( 'my_cache_key', $data, HOUR_IN_SECONDS );
}

// 削除
delete_transient( 'my_cache_key' );

// WordPress定数(TTL指定に使う)
// MINUTE_IN_SECONDS  = 60
// HOUR_IN_SECONDS    = 3600
// DAY_IN_SECONDS     = 86400
// WEEK_IN_SECONDS    = 604800
// MONTH_IN_SECONDS   = 2592000
// YEAR_IN_SECONDS    = 31536000

// TTL = 0 にすると有効期限なし(非推奨・肥大化する)
set_transient( 'my_permanent_cache', $data, 0 );

ステップ2:WP_Queryの結果をキャッシュする

// functions.php: 重いクエリのキャッシュパターン
function get_popular_posts( $count = 5 ) {
    $cache_key = 'popular_posts_' . $count;
    $posts     = get_transient( $cache_key );

    if ( false !== $posts ) {
        return $posts; // キャッシュヒット
    }

    // キャッシュミス:DBから取得
    $posts = get_posts( [
        'posts_per_page'  => $count,
        'meta_key'        => 'post_views_count',
        'orderby'         => 'meta_value_num',
        'order'           => 'DESC',
        'post_status'     => 'publish',
        'no_found_rows'   => true,
        'suppress_filters' => false,
    ] );

    // 結果をキャッシュ(6時間)
    set_transient( $cache_key, $posts, 6 * HOUR_IN_SECONDS );

    return $posts;
}

// 投稿更新時にキャッシュを自動削除
add_action( 'save_post', function( $post_id ) {
    if ( wp_is_post_autosave( $post_id ) || wp_is_post_revision( $post_id ) ) {
        return;
    }
    // 関連するキャッシュを削除
    for ( $i = 1; $i <= 20; $i++ ) {
        delete_transient( 'popular_posts_' . $i );
    }
} );

ステップ3:外部APIレスポンスをキャッシュする

// functions.php: 外部APIキャッシュパターン(エラー時は古いキャッシュを使う)
function get_weather_data( $city = 'Tokyo' ) {
    $cache_key    = 'weather_' . sanitize_key( $city );
    $stale_key    = $cache_key . '_stale'; // 期限切れでも保持する古いデータ
    $cached       = get_transient( $cache_key );

    if ( false !== $cached ) {
        return $cached;
    }

    // APIリクエスト
    $api_key  = get_option( 'weather_api_key' );
    $response = wp_remote_get(
        add_query_arg( [ 'q' => $city, 'appid' => $api_key ],
            'https://api.openweathermap.org/data/2.5/weather' ),
        [ 'timeout' => 10 ]
    );

    if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
        // エラー時は古いキャッシュ(stale)を返す
        $stale = get_transient( $stale_key );
        return $stale ?: [];
    }

    $data = json_decode( wp_remote_retrieve_body( $response ), true );

    // 30分キャッシュ
    set_transient( $cache_key, $data, 30 * MINUTE_IN_SECONDS );
    // staleは24時間保持(エラー時のフォールバック用)
    set_transient( $stale_key, $data, DAY_IN_SECONDS );

    return $data;
}

ステップ4:サイト全体のトランジェントを管理する

// functions.php: グループキャッシュパターン(バージョン番号でまとめて無効化)
function get_cache_version( $group ) {
    $version = get_option( 'cache_version_' . $group, 1 );
    return $version;
}

function bump_cache_version( $group ) {
    $version = get_cache_version( $group );
    update_option( 'cache_version_' . $group, $version + 1, false );
}

function get_versioned_transient( $key, $group ) {
    $versioned_key = $key . '_v' . get_cache_version( $group );
    return get_transient( $versioned_key );
}

function set_versioned_transient( $key, $group, $data, $ttl = HOUR_IN_SECONDS ) {
    $versioned_key = $key . '_v' . get_cache_version( $group );
    set_transient( $versioned_key, $data, $ttl );
}

// グループ全体を無効化(バージョンをインクリメント)
// bump_cache_version('products');
// これ以降は新しいバージョン番号のキーが使われ、古いキャッシュは自然に期限切れになる

// 期限切れトランジェントの一括削除(cronで定期実行)
function delete_expired_transients() {
    global $wpdb;
    $wpdb->query(
        $wpdb->prepare(
            "DELETE a, b FROM {$wpdb->options} a
             INNER JOIN {$wpdb->options} b ON b.option_name = REPLACE(a.option_name, '_transient_timeout_', '_transient_')
             WHERE a.option_name LIKE '_transient_timeout_%%'
               AND a.option_value < %d",
            time()
        )
    );
}
add_action( 'my_daily_cleanup', 'delete_expired_transients' );

ステップ5:Redis・Memcachedでオブジェクトキャッシュに昇格させる

// Redis Object Cacheプラグイン(tillkruss/redis-cache)またはW3 Total Cacheを導入すると
// set_transient() / get_transient() が自動的にRedis/Memcachedを使用する
// コードの変更は不要

// wp-config.phpでRedisの接続設定
define( 'WP_REDIS_HOST', '127.0.0.1' );
define( 'WP_REDIS_PORT', 6379 );
define( 'WP_REDIS_PASSWORD', 'your_password' ); // 認証が必要な場合
define( 'WP_REDIS_DATABASE', 0 );
define( 'WP_REDIS_PREFIX', 'mysite_' ); // 複数サイトでRedisを共有する場合

// オブジェクトキャッシュが有効かチェック
if ( wp_using_ext_object_cache() ) {
    // Redis/Memcachedが使われている
    // トランジェントはDBを使わずキャッシュサーバーに保存される
}

// オブジェクトキャッシュを直接使う(TTLなし・リクエスト内のみ)
wp_cache_set( 'my_key', $data, 'my_group' );
$data = wp_cache_get( 'my_key', 'my_group' );
wp_cache_delete( 'my_key', 'my_group' );

注意事項

  • トランジェントはwp_optionsテーブルに保存されます。大量のトランジェントを作成するとテーブルが肥大化し、autoloadオプションの読み込みが遅くなります。キーの命名規則を統一し、不要になったら削除してください。
  • get_transient()falseを返す場合、キャッシュが存在しないか期限切れのどちらかです。値がfalseのデータをキャッシュしてはいけません。必要な場合はラッパー配列に入れて保存してください(例:['value' => false])。
  • Redisが導入されている環境ではset_transient()TTL=0は動作が異なります。Redisではキーが永続化されますが、DBではwp_optionsに無期限で残ります。TTL=0は避けるか、用途を明確にしてください。

まとめ

トランジェントAPIの実装は「get_transient()でキャッシュヒット確認→falseなら処理実行→set_transient()でTTL付きキャッシュ保存→save_postフックで関連キャッシュを自動削除→Redis Object Cacheプラグイン導入でDBレスのキャッシュに昇格」の流れで整備します。関連記事:WordPressのwp_cronで定期処理を実装する方法WordPressのページ表示速度を改善する方法

お気軽にご相談ください

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