WordPress Transients API 完全ガイド - キャッシュ・落とし穴・オブジェクトキャッシュ連携まで

WordPress Transients API 完全ガイド - キャッシュ・落とし穴・オブジェクトキャッシュ連携まで

作成日:
更新日:

Transients API とは

Transients API(トランジェント API)は、WordPress 標準で用意されている 「有効期限付きの一時キャッシュ」 の仕組みです。Options API と似ていますが、expiration(秒)を指定できる点が決定的に異なります。

外部 API のレスポンス、複雑な WP_Query の集計結果、サイドバーの人気記事など 「毎回計算したくないが、最新でなくてもよいデータ」 を保存するのに最適です。

本記事は WordPress 6.8 系、2026年5月時点 の公式リファレンス(developer.wordpress.org/apis/transients)に基づきます。コードは PHP 8.2 を想定しています。

3つの基本関数

関数役割
set_transient( $key, $value, $expiration )キャッシュをセット/更新
get_transient( $key )キャッシュを取得(期限切れ・未設定なら false
delete_transient( $key )キャッシュを明示的に削除
// 30分間キャッシュ
set_transient( 'my_popular_posts', $posts, 30 * MINUTE_IN_SECONDS );
 
// 取得(フォールバック必須)
$posts = get_transient( 'my_popular_posts' );
if ( false === $posts ) {
    $posts = expensive_query(); // 重い処理
    set_transient( 'my_popular_posts', $posts, 30 * MINUTE_IN_SECONDS );
}
 
// 明示削除
delete_transient( 'my_popular_posts' );

WordPress には MINUTE_IN_SECONDS / HOUR_IN_SECONDS / DAY_IN_SECONDS / WEEK_IN_SECONDS / MONTH_IN_SECONDS / YEAR_IN_SECONDS といった定数が定義されているので、こちらを使うとマジックナンバーを避けられて可読性が上がります。

どこに保存されるのか

Transients の挙動は オブジェクトキャッシュの有無 で大きく変わります。

オブジェクトキャッシュなし(標準環境)

wp_options テーブルに 2行ペア で保存されます。

option_name内容
_transient_my_popular_postsキャッシュ本体
_transient_timeout_my_popular_postsUNIX タイムスタンプ(有効期限)

get_transient() 呼び出し時に _transient_timeout_* を見て、現在時刻と比較。期限切れであれば両行を削除し false を返します。

オブジェクトキャッシュあり(Redis / Memcached)

wp_using_ext_object_cache()true の場合、Transients は wp_options に書かれず、wp_cache_set() 経由でメモリストアに保存 されます。これは公式ドキュメントにも明記されている重要な挙動です(Transients - Common APIs Handbook)。

// Redis Object Cache 等が有効な環境では、内部的に下記とほぼ等価になる
wp_cache_set( $key, $value, 'transient', $expiration );

これが Transients が 「キャッシュプラグインを入れるだけで自動的に高速化される」 理由です。Options API(update_option)はこの恩恵を受けません。

site_transient との違い

マルチサイト環境では、サイト単体のキャッシュネットワーク全体のキャッシュ かを使い分けます。

関数スコープ保存先(マルチサイト)
set_transient()各サブサイト各サイトの wp_options
set_site_transient()ネットワーク全体wp_sitemeta

シングルサイトであっても、WordPress 本体のアップデート情報(update_core トランジェント等)は site_transient で保存されます。プラグイン作者なら覚えておきたい違いです。

キーの命名と長さ制限

公式リファレンスに 明示されている上限 があります。

関数キーの最大長
set_transient()172 文字
set_site_transient()167 文字

_transient_ プレフィックスや _transient_timeout_ プレフィックスを足したときに、wp_options.option_nameVARCHAR(191) に収まるための制約です。

実用的な命名パターン

  • プラグイン/テーマ名をプレフィックスに付けるmytheme_popular_posts
  • 入力値の md5() ハッシュ で動的キーを安全に短縮:mytheme_api_ . md5( $endpoint . $user_id )
  • バージョン番号を入れるmytheme_v2_popular_posts(破壊的変更時にいっせいに無効化できる)

実用パターン集

1. 外部 API のレスポンスをキャッシュ

おそらく Transients でもっとも一般的なユースケースです。

function mytheme_get_github_repos( $user ) {
    $key = 'mytheme_gh_' . md5( $user );
    $cached = get_transient( $key );
    if ( false !== $cached ) {
        return $cached;
    }
 
    $response = wp_remote_get( "https://api.github.com/users/{$user}/repos" );
    if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
        return array();
    }
 
    $repos = json_decode( wp_remote_retrieve_body( $response ), true );
    set_transient( $key, $repos, HOUR_IN_SECONDS );
    return $repos;
}

2. 重い WP_Query の集計結果をキャッシュ

function mytheme_popular_posts( int $limit = 5 ): array {
    $key = 'mytheme_popular_' . $limit;
    $cached = get_transient( $key );
    if ( false !== $cached ) {
        return $cached;
    }
 
    $query = new WP_Query( array(
        'posts_per_page' => $limit,
        'meta_key'       => 'post_views',
        'orderby'        => 'meta_value_num',
        'order'          => 'DESC',
        'no_found_rows'  => true,
    ) );
 
    $posts = array_map( fn( $p ) => array(
        'id'    => $p->ID,
        'title' => $p->post_title,
        'url'   => get_permalink( $p ),
    ), $query->posts );
 
    set_transient( $key, $posts, 6 * HOUR_IN_SECONDS );
    return $posts;
}

3. 投稿の更新時にキャッシュをパージ

「データが変わったタイミングでキャッシュも消す」をフックで自動化します。

add_action( 'save_post', 'mytheme_purge_popular_cache' );
add_action( 'deleted_post', 'mytheme_purge_popular_cache' );
 
function mytheme_purge_popular_cache(): void {
    foreach ( array( 5, 10, 20 ) as $limit ) {
        delete_transient( 'mytheme_popular_' . $limit );
    }
}

4. 「ストリームライン」パターン(推奨ヘルパー)

毎回フォールバックを書くのは煩雑なので、汎用ヘルパー関数 を一つ用意しておくとプロジェクト全体で楽になります。

/**
 * キャッシュからの取得を試み、なければコールバックで再計算してキャッシュする
 *
 * @template T
 * @param string   $key
 * @param int      $ttl_seconds
 * @param callable():T $producer
 * @return T
 */
function mytheme_remember( string $key, int $ttl_seconds, callable $producer ) {
    $cached = get_transient( $key );
    if ( false !== $cached ) {
        return $cached;
    }
    $value = $producer();
    set_transient( $key, $value, $ttl_seconds );
    return $value;
}
 
// 使い方
$repos = mytheme_remember( 'mytheme_gh_' . md5( $user ), HOUR_IN_SECONDS, function () use ( $user ) {
    return fetch_github_repos( $user );
} );

Laravel の Cache::remember() を WordPress に持ち込んだイメージです。

落とし穴 - 本番で踏みやすいワナ

ワナ 1:「期限切れまでは存在する」と仮定する

公式ドキュメントが何度も警告している通り、Transients は最大期限であって最小期限ではありません

Transients can disappear anytime after being set—they might expire in one second or persist for hours, but will never persist past the expiration time.

LRU 退避のあるオブジェクトキャッシュ環境 や、object-cache.php がない環境で Cron 実行が遅延した場合 など、いつでも false が返ってくる可能性があります。フォールバック処理は必須 です。

ワナ 2:遅延ガベージコレクション(最大の罠)

WordPress は get_transient() が呼ばれた時にしか期限切れトランジェントを削除しませんJasper's blog)。

つまり、次のような 動的キーで毎回違うキャッシュを作る コードは要注意です。

// アンチパターン:ユーザーIDごとに別キーを作っている
$key = 'api_response_' . md5( $endpoint . $user_id );
set_transient( $key, $data, HOUR_IN_SECONDS );

このコードは、同じキーでもう一度 get_transient() が呼ばれない限り、期限切れデータが永遠に wp_options に居座ります。1日に何万人もアクセスがあるサイトでは、wp_options が肥大化して autoload の遅延DB バックアップサイズの異常増加 を引き起こします。

対策:定期掃除を WP-Cron に登録する

add_action( 'init', function () {
    if ( ! wp_next_scheduled( 'mytheme_cleanup_expired_transients' ) ) {
        wp_schedule_event( time(), 'hourly', 'mytheme_cleanup_expired_transients' );
    }
} );
 
add_action( 'mytheme_cleanup_expired_transients', function () {
    global $wpdb;
    $now = time();
 
    // 自分のプレフィックスだけ対象にする(他プラグインに影響を与えない)
    $rows = $wpdb->get_results( $wpdb->prepare(
        "SELECT option_name FROM {$wpdb->options}
         WHERE option_name LIKE %s
           AND option_value < %d",
        $wpdb->esc_like( '_transient_timeout_mytheme_' ) . '%',
        $now
    ) );
 
    foreach ( $rows as $row ) {
        $key = str_replace( '_transient_timeout_', '', $row->option_name );
        delete_transient( $key );
    }
} );

delete_transient() を呼べば、本体(_transient_*)とタイムアウト行(_transient_timeout_*)の両方をきれいに削除してくれます。自プラグイン/自テーマのプレフィックスだけ を対象にすることがマナーです。

ワナ 3:オブジェクトキャッシュ環境では wp_options を覗いても出てこない

「Transients が動いていないかも?」とデバッグで wp_optionsSELECT * WHERE option_name LIKE '_transient_%' しても、Redis Object Cache が有効なら何も返ってこない のが正常です。

確認は wp_using_ext_object_cache() か、WP-CLI の wp transient list を使います。

# WP-CLI で一覧
wp transient list --network=false
 
# 個別の取得・設定・削除
wp transient get my_popular_posts
wp transient set my_popular_posts "value" 3600
wp transient delete my_popular_posts
 
# 期限切れだけ一括削除
wp transient delete --expired
 
# 全部削除(注意!)
wp transient delete --all

ワナ 4:シリアライズできない値を渡す

PHP のリソース(DB 接続ハンドル、Stream など)や クロージャ を渡すと serialize() 時に Fatal が出ます。配列・スカラー・標準クラスのインスタンスに留めるのが安全です。

ワナ 5:「巨大な配列」を1キーに詰める

wp_options のレコードを大きくするほど、autoload との兼ね合いやレプリケーション・バックアップで効いてきます。1MB を超えるようなレスポンスはトランジェントに直接入れず、ファイル/メディアにして URL だけキャッシュする などの工夫を検討してください。

オブジェクトキャッシュとの関係

WordPress でパフォーマンスを真面目にやるなら、Redis Object CacheMemcached を入れるのが定番です。導入後、Transients は自動的にメモリストアに移ります。

項目DB保存(標準)Redis/Memcached
速度〇(1回のSELECT)◎(メモリアクセス)
書き込み負荷DB に書くメモリに書く
永続性〇(DB なので原則残る)△(メモリなので再起動で消える)
GC遅延(get 時のみ)LRU 等が効く
wp_options 肥大化起こりうる起こらない
監視SQL でできるredis-cli / WP-CLI

Transients を本気で使うなら Object Cache を必須化する」は、中規模以上の WordPress 案件では既にデフォルトの考え方です。

いつ使う/いつ使わない

使うべきケース

  • 外部 API の 読み取り結果(GitHub、天気、SNS、為替など)
  • 重い WP_Query読み取り 集計
  • ナビゲーションメニュー・サイドバーなど 更新頻度の低い HTML 断片
  • プラグイン更新チェックや 定期的なバックグラウンドタスクの結果

使わない方がよいケース

  • トランザクションが必要な業務データ(注文、決済、在庫)
  • 永続性が必要な設定値 → Options API へ
  • ユーザー固有のセッション情報 → セッション管理 or Cookie
  • 巨大データ(>1MB)

まとめ

WordPress Transients API は、

  • 3関数だけで使える お手軽な期限付きキャッシュ
  • 標準では wp_options に保存、Redis/Memcached 環境では自動的にメモリへ移行
  • ただし 「最大期限」「遅延 GC」「永続性なし」 の3点を理解しないと事故る
  • 動的キー × 大量アクセスのサイトは、自前 GC をスケジュールする のが必須
  • 業務データには使わず、「再計算可能な読み取りキャッシュ」専用 と割り切る

中規模以上の WordPress を運用しているなら、Redis Object Cache + Transients API + 自前 GC の3点セットがほぼ万能解です。逆に、何も考えずに動的キーで Transients を散らすと wp_options 肥大化のサイレントキラー になりかねません。本番投入前に、必ず本記事のワナ章を一読してから採用してください。

参考リンク

関連記事