TTL

データやキャッシュの有効期限を設定し、自動的に削除 / 更新する仕組み

キャッシュデータベース

TTL とは

TTL (Time to Live) は、データに有効期限を設定し、期限切れ後に自動的に削除・無効化する仕組みである。キャッシュDNS、データベース、セッション管理など、ソフトウェアのあらゆる層で使われる基本概念だ。

同じ名前でも、IP パケットの TTL だけは時間として働かない。RFC 791 は秒単位の最大生存時間として定義したが、datagram を処理したモジュールは 1 秒未満で転送しても必ず 1 以上減らすと決めたため、実際にはホップ数の上限になる。IPv6 ではこの経緯を踏まえてフィールド名が Hop Limit へ改められ、転送するノードごとに 1 減って 0 になった時点で破棄される。以降で扱うのは時間としての TTL である。

TTL がなければ、古いデータが残り続ける。キャッシュは際限なく肥大化し、DNS の変更は伝播せず、セッションは失効しない。ただし TTL が決めるのは鮮度の上限だけで、期限内に元データが変われば、その期限が切れるまで古い値が返る。鮮度を厳密に合わせたい面では、TTL に加えて更新側からの無効化 (キャッシュのパージ、キー名の付け替え) が必要になる。

使われる場面と設定値の目安

設定値を決める軸はどの層でも同じで、変更をどれだけ早く行き渡らせたいかと、オリジンやデータベースへの問い合わせをどれだけ通したいかの綱引きである。以下は各層で実際に使われている範囲の目安である。

場面典型的な TTL設定場所
ブラウザキャッシュ1 時間〜1 年Cache-Control: max-age=3600
CDN キャッシュ1 分〜24 時間CloudFront のキャッシュポリシー
DNS レコード300 秒〜86400 秒Route 53 のレコード設定
DynamoDB アイテム1 時間〜90 日アイテムの TTL 属性
Redis キャッシュ5 分〜1 時間SET key value EX 300
JWT トークン15 分〜1 時間exp クレーム
セッション30 分〜24 時間CookieMax-Age

DynamoDB の TTL

DynamoDB の TTL は、アイテムに Unix エポック秒のタイムスタンプ属性を設定し、期限切れアイテムを自動削除する機能だ。削除は非同期で行われ、書き込みスループットを消費しない (削除分の書き込み容量を請求されない)。属性は数値型でなければならず、文字列として入れた値は TTL の処理から黙って無視される。テーブル側で TTL を有効化し、どの属性を見るかを指定して初めて働く点も忘れやすい。

// TTL 属性付きでアイテムを保存
const ttl = Math.floor(Date.now() / 1000) + 60 * 60 * 24; // 24時間後

await ddb.send(new PutCommand({
  TableName: 'Sessions',
  Item: {
    sessionId: 'sess-abc123',
    userId: 'user-456',
    expiresAt: ttl,  // TTL 属性 (Unix エポック秒)
  },
}));

注意点: DynamoDB の TTL 削除は期限の到来と同時ではない。公式ドキュメントの表現は「期限切れから数日以内」であり (2026 年 8 月時点)、しかも未削除の期限切れアイテムは Scan や Query の結果に普通に現れる。フィルター式で除外するのは公式に推奨される使い方であって、省略してよい最適化ではない。

この猶予は逆に使うこともできる。削除される前に TTL 属性を未来の時刻へ更新すれば、そのアイテムは期限切れ扱いから外れて削除されない。削除が実行されると DynamoDB Streams にはユーザー操作ではなくサービスによる削除として流れ、LSI と GSI からも通常の削除と同じように消える。

// TTL 切れアイテムを除外するクエリ
const now = Math.floor(Date.now() / 1000);
await ddb.send(new QueryCommand({
  TableName: 'Sessions',
  KeyConditionExpression: 'sessionId = :sid',
  FilterExpression: 'expiresAt > :now',
  ExpressionAttributeValues: { ':sid': sessionId, ':now': now },
}));

CloudFront の TTL

CloudFront のキャッシュ TTL は、オリジンへのリクエスト頻度とコンテンツの鮮度のトレードオフだ。オリジンが返す max-age がそのまま採用されるわけではなく、キャッシュポリシーの Minimum TTL と Maximum TTL で上下から挟まれる。キャッシュポリシーを使わない場合の既定は 24 時間である。

  • 静的アセット (JS, CSS, 画像): 長い TTL (1 年) + ファイル名にハッシュを含める
  • HTML ページ: 短い TTL (数分〜数時間) またはキャッシュ無効化
  • API レスポンス: TTL 0 (キャッシュしない) または短い TTL
Cache-Control: public, max-age=31536000, immutable  ← 静的アセット
Cache-Control: public, max-age=300                   ← HTML ページ
Cache-Control: no-store                              ← API レスポンス

落とし穴は最後の行にある。Minimum TTL が 0 より大きいキャッシュ動作では、オリジンが no-cacheno-store を返しても CloudFront は Minimum TTL の分だけキャッシュする。キャッシュさせたくない経路は、レスポンスヘッダーに頼らずキャッシュポリシー側 (Minimum TTL と Maximum TTL を 0 にする) で止める。

DNS の TTL

DNS レコードの TTL は、リゾルバがレコードをキャッシュする秒数だ。TTL が長いとキャッシュヒット率が上がるが、レコード変更の反映が遅くなる。

  • 通常運用: 300〜3600 秒
  • 切り替え前: TTL を短く (60 秒) に変更し、反映を待ってから切り替え
  • 切り替え後: 問題なければ TTL を元に戻す

「反映を待つ」の中身は、短縮前の TTL でキャッシュされたレコードが各リゾルバから消えるのを待つ、という意味である。TTL を 60 秒に変更した直後に宛先を切り替えても、旧 TTL が 3600 秒なら最大 1 時間は古い宛先へ届き続ける。短縮は切り替え作業の前日までに済ませておく。

よくある失敗パターン

TTL を設定し忘れる

キャッシュに TTL を設定しないと、古いデータが永遠に返り続ける。「デプロイしたのに変更が反映されない」の原因の大半はキャッシュの TTL 問題だ。

TTL が短すぎる

TTL を 1 秒に設定すると、キャッシュの意味がなくなり、オリジンに負荷が集中する。アクセス頻度とデータの鮮度要件から適切な値を決める。

TTL が長すぎる

TTL を 1 年に設定した HTML ページは、修正をデプロイしてもユーザーに届かない。HTML は短い TTL、静的アセットは長い TTL + ファイル名ハッシュが定石だ。

更新のたびに TTL が消える

Redis の SET は、成功すると対象キーに付いていた TTL を破棄する。有効期限付きで入れたキャッシュ値を後から SET で書き換えると、期限のないキーに変わって消えないまま残る。期限を保ちたいなら KEEPTTL を付けるか、書き換えのたびに EX を付け直す。

全体像を把握するには関連書籍も有用。

この記事は役に立ちましたか?

関連用語

関連する記事