TTL(Time To Live)は、データが有効と見なされる最大時間を決定するパラメータです。TTLが期限切れになると、レコードは古い(stale)とマークされ、削除または更新される必要があります。Mozilla Developer Network(2026)によると、TTLメカニズムはCache-Control: max-ageヘッダーによるHTTPキャッシングの基盤であり、ネットワークリクエストを最適化するためにすべての最新ブラウザとモバイルアプリケーションで使用されています。
重要ポイント
TTL(Time To Live)は、データが無効と見なされるタイムスタンプまたは間隔です。キャッシングの文脈では、TTLはレコードがソースから再取得される必要がある前にキャッシュに保存できる時間を決定します。ネットワークプロトコルでは、TTLはパケットの有効期間を制限し、無限ルーティングを防ぎます。
TTL値は常にミリ秒、秒、分、時間の単位で表されます。設定された時間が経過すると、レコードはキャッシュから削除されるか、古いとしてマークされます。古いレコードへの次のリクエストでは、システムは後続の更新(stale-while-revalidate)とともに古いデータを返すか、新しいデータが取得されるまでリクエストをブロックできます。
TTLの選択は常にデータの新鮮さとパフォーマンスのトレードオフです。短すぎるTTL(1〜5秒)はアプリケーションに頻繁なネットワークリクエストを強制し、キャッシングの利点を無効にします。長すぎるTTL(数時間〜数日)はユーザーに古い情報を表示するリスクを高めます。最適な値はデータの種類によって異なります:為替レート—秒、天気—分、APIバージョン—時間。
TTLは受動的な無効化です:データは一定期間後に自動的に削除されます。代替案は能動的な無効化で、データソースが変更をキャッシュに通知します(WebSocketメッセージやプッシュ通知など)。TTLによる受動的な無効化は実装が簡単ですが、即時の新鮮さを保証しません。能動的な無効化はより複雑ですが、TTLに固有の遅延なしにデータを最新に保つことができます。
TTLメカニズムは2つの方法で実装できます:絶対有効期限(absolute expiration)と相対有効期限(relative expiration)です。絶対有効期限では、レコードは無効になる特定の時間を保存します。相対有効期限では、レコードの作成時間とTTLが間隔として記録され、creationTime + TTL > currentTimeを計算してチェックが行われます。
キャッシュへの各リクエストで、システムは各レコードのTTLをチェックします。TTLが期限切れの場合、データは削除されるか古いとしてマークされ、リクエストはソースに転送されます。TTLチェックを最適化するために、スケジュールされたクリーンアップ(期限切れレコードの定期的な削除)や遅延クリーンアップ(レコードにアクセスしたときのみ削除)を使用できます。遅延クリーンアップは、キャッシュ全体をスキャンするためのバックグラウンドスレッドを必要としないため、メモリ効率が向上します。
分散システムでは、TTLは自動的な競合解決にも使用されます。たとえば、2つのサーバーが同じキーに異なる値を同時に書き込んだ場合、後のTTLを持つレコードが優先されることがあります。Amazon DynamoDBはテーブル内の古いレコードを自動削除するためにTTLを使用します—これは手動管理を必要としない組み込み機能です。
TTLが期限切れになったときのパフォーマンスを向上させるために、古いデータの読み取り戦略が使用されます。Stale-while-revalidate—すぐにクライアントに古いデータを返し、同時にバックグラウンド更新を開始します。Stale-if-error—ソースが一時的に利用できない場合、古いデータを返します。Cache-Aside(Lazy Loading)—キャッシュミス時、ソースからデータを読み込み、新しいTTLでキャッシュに保存してからクライアントに返します。各戦略はデータの一貫性要件に基づいて選択されます。
モバイルアプリケーションでは、TTLはキャッシュ管理の主要なメカニズムです。TTLがアプリケーションの動作とユーザーエクスペリエンスを決定する主なシナリオを見てみましょう。
HTTPプロトコルはCache-Controlヘッダーを通じて組み込みのTTLメカニズムを提供します。max-ageディレクティブはTTLを秒単位で設定します:Cache-Control: public, max-age=3600は応答を1時間キャッシュできることを意味します。追加のディレクティブs-maxage(共有キャッシュ、例:CDN用)とstale-while-revalidateはより細かい制御を提供します。TTLがexpiresヘッダーと一致する場合、より近代的なHTTP/1.1標準としてmax-ageが優先されます。
| データの種類 | 推奨TTL | 根拠 |
|---|---|---|
| 天気 | 10〜30分 | 予報は頻繁に更新されない |
| 為替レート | 15〜60秒 | 高い変動性 |
| ニュースフィード | 2〜5分 | 新鮮さとパフォーマンスのバランス |
| ユーザープロフィール | 5〜30分 | セッション中にほとんど変更されない |
| 商品リスト | 10〜60分 | 価格は毎秒変わらない |
| 静的リソース | 1〜24時間 | URLまたはETagでバージョン管理 |
画像の場合、コンテンツがほとんど変更されないため、TTLは数日に達することがあります。ただし、モバイルアプリケーションはハイブリッドアプローチをよく使用します:プレビューには短いTTL(30分—フレームの新鮮さ)、フルサイズ画像には長いTTL(7日間)。HTTPヘッダーCache-Control: immutableを持つ画像はTTLが期限切れになるまで再リクエストされるべきではありません—これはRFC 8246で提案された静的リソースの最適化です。そのような画像は、アプリケーションの関与なしにOSレベル(URLCache、OkHttp Cache)でキャッシュされます。
ネットワークでは、TTLはキャッシングではなくパケットの有効期間を制限するために使用されます。各IPパケットにはTTLフィールド(8ビット)が含まれ、各ルーターによって1ずつ減少します。TTLが0に達すると、パケットは破棄され、送信者はICMP Time Exceededメッセージを受け取ります。これにより、ネットワークループ中の無限ルーティングが防止されます。
DNSレコードには、リゾルバ(ISP DNSキャッシュなど)が権威サーバーに問い合わせずにレコードを保存できる時間を決定するTTLがあります。一般的な値:頻繁に変更されるレコードには300秒(5分)、安定したドメインには86400秒(24時間)。CDNサービスは障害時の迅速なトラフィック再ルーティングのために低いTTL(60〜300秒)を設定することが多く、静的ドメインは最大7日間のTTLを持つことができます。サーバー移行時には、最初にTTLを60秒に下げて(移行の48時間前)、変更が迅速に伝播するようにすることをお勧めします。
モバイルアプリケーションでは、TTLはセッションとアクセストークンの管理に使用されます。JWTトークン(JSON Web Tokens)にはexp(有効期限)フィールドが含まれ、これは絶対的なUnix有効期限時間です。期限切れ後、更新トークンを使用して再認証なしで新しいアクセストークンを取得します。アクセストークンのTTLは通常1〜24時間、更新トークンのTTLは7〜30日です。これはセキュリティ(短いTTLは漏洩リスクを低減)とUX(長いTTLは再ログインの頻度を低減)のバランスです。
TTLの選択は、データの種類、鮮度SLA、再リクエストのコストに依存するエンジニアリング上の決定です。主な戦略を見てみましょう。
最も単純なアプローチ—すべてのレコードが同じTTLを持ちます。たとえば、すべてのAPI応答を5分間キャッシュします。利点:実装の単純さと予測可能な動作。欠点:異なるデータ種類の異なる変更頻度を考慮しません。固定TTLは、すべてのレコードが同じ「新鮮さ」を持つ均一なデータに適しています—たとえば、1つの取引所での暗号通貨レート。
TTLはデータの動作に基づいて動的に変化します。たとえば、レコードがサーバー上でほとんど更新されない場合、TTLは増加し、頻繁に更新される場合は減少します。実装はHTTP応答ヘッダーを使用できます:Ageヘッダー(応答がキャッシュにあった秒数)とDateヘッダーにより、残りの有効期間を計算できます。適応型TTLはヒット率を向上させますが、クライアントに追加のロジックが必要です。
Probabilistic Early Expiration(PEE)—指定された範囲内でTTLをランダムに選択する技法です。これにより、多数のリクエストが同時に期限切れになり、すべてのクライアントが同時にソースにアクセスする「スランディングハード」(thundering herd)効果を防ぎます。PEEは特にCDNと高負荷キャッシュに有用です:300秒の単一TTLの代わりに、240〜360秒のランダムな値を使用して、ソースへの負荷を均等に分散します。
絶対有効期限を使用してKotlinでTTL付きキャッシュの実装を見てみましょう。各レコードは作成時間を保存し、読み取り時にTTLが期限切れかどうかがチェックされます。
class TtlCache<K, V>(
private val defaultTtlMs: Long = 300000L
) {
private data class Entry<V>(
val value: V,
val createdAt: Long = System.currentTimeMillis()
)
private val map = ConcurrentHashMap<K, Entry<V>>()
fun get(key: K): V? {
val entry = map[key] ?: return null
if (isExpired(entry)) {
map.remove(key)
return null
}
return entry.value
}
fun put(key: K, value: V, ttlMs: Long = defaultTtlMs) {
map[key] = Entry(value, createdAt = System.currentTimeMillis() + ttlMs)
}
private fun isExpired(entry: Entry<*>): Boolean {
return System.currentTimeMillis() > entry.createdAt
}
fun cleanup() {
map.entries.removeIf { isExpired(it.value) }
}
}
Entryクラスは値と作成時間+TTL(絶対有効期限)を保存します。getメソッドは各アクセスで有効期限をチェックします(遅延クリーンアップ)—期限切れのレコードはアクセスが試みられたときのみ削除されます。cleanupメソッドはバックグラウンドスレッドから定期的に呼び出して、すべての古いレコードを一括削除できます。ConcurrentHashMapはキャッシュ全体をロックせずにスレッドセーフを提供します。
iOSでは、TTL付きキャッシングにmemoryCapacityとdiskCapacityの設定でURLCacheを使用すると便利です。ただし、URLCacheはリクエストごとに個別のTTLをサポートしていません。TTLサポート付きのカスタムNSCacheラッパーを考えてみましょう。
final class ApiResponseCache {
private var cache = NSCache<NSString, CacheEntry>()
func getResponse(for url: URL) -> Data? {
guard let entry = cache.object(forKey: url.absoluteString as NSString)
else { return nil }
guard entry.expirationDate > Date() else {
cache.removeObject(forKey: url.absoluteString as NSString)
return nil
}
return entry.data
}
func storeResponse(data: Data, for url: URL, ttl: TimeInterval) {
let entry = CacheEntry(data: data, expirationDate: Date().addingTimeInterval(ttl))
cache.setObject(entry, forKey: url.absoluteString as NSString)
}
}
final class CacheEntry: NSObject {
let data: Data
let expirationDate: Date
}
この実装では、NSCacheがスレッドセーフなストレージとして使用されます。CacheEntryにはDataとexpirationDateが含まれます。getが呼び出されると、時間が期限切れかどうかがチェックされ、期限切れの場合はレコードが削除されてnilが返されます。TTLはTimeIntervalを介して秒単位で設定され、URLごとに異なる場合があります:API応答の一般的な値は、動的コンテンツで120秒、静的データで3600です。
よくある質問
技術的には、TTLと有効期限は同じものです:データが無効と見なされる時間間隔。違いは文脈にあります:TTLという用語はIT(キャッシング、ネットワーキング、DNS)で使用され、「有効期限」はビジネスロジック(プロモコード、サブスクリプション)でより頻繁に適用されます。実装では、両方のメカニズムは同じです—現在時刻と有効期限の比較。
最適なTTLは経験的に選択されます。方法:控えめな値(30〜60秒)から始め、古いデータに関する苦情が出るまで徐々に増やします。キャッシュヒット率を監視します:70%未満の場合はTTLが短すぎます。SLAを考慮します:金融データのTTLは1秒、ニュースは5分、プロフィールは30分です。
max-ageが期限切れになると、ブラウザまたはモバイルアプリケーションは応答を古いと見なします。同じURLへの次のリクエストで、クライアントはIf-None-Match(ETag)またはIf-Modified-Sinceヘッダーとともにリクエストを送信します。データが変更されていない場合、サーバーは応答本文なしで304 Not Modifiedを返し、TTLが更新されます。変更された場合、サーバーは新しいデータと新しいCache-Controlとともに200を返します。
技術的には、TTLは非常に大きくできます(max-age=31536000 — 1年)が、これが正当化されることはほとんどありません。静的リソースでさえ変更される可能性があり、クライアントはTTLが期限切れになるまでそれを知りません。長いTTLとバージョン管理されたURL(style.css?v=2)を使用することをお勧めします:ファイルが変更されるとURLが変更され、古いキャッシュは自動的に時代遅れになります。
TTLと退出戦略(LRU、FIFO)は異なる問題を解決します。TTLはデータがいつ無関係になるかを決定します—これは時間的基準です。LRUとFIFOはキャッシュがいっぱいになったときにどのデータを削除するかを決定します—これは空間的基準です。これらは組み合わせることができます:TTLが期限切れになった場合、またはキャッシュがいっぱいになった場合(LRU/FIFOによる)、レコードは削除されます。本番システムでは、両方のメカニズムが連携して動作します。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。