全部產品
Search
文件中心

ApsaraDB for MongoDB:MongoDB TTL 索引使用最佳實務

更新時間:Sep 17, 2026

TTL(Time-To-Live)索引是 MongoDB 提供的一種特殊單欄位索引,可在文檔到期後自動刪除。本文介紹 TTL 索引的工作原理、建立與管理方法、使用限制、常見問題與解決方案,以及在ApsaraDB for MongoDB上的最佳實務建議。

概述

TTL(Time-To-Live)索引是 MongoDB 提供的一種特殊單欄位索引,能夠在文檔達到指定到期時間後自動將其刪除。它適合管理具有明確生命週期的資料,例如日誌記錄、會話資訊、臨時緩衝、驗證碼等情境。通過合理使用 TTL 索引,可以有效控制集合的資料量,避免儲存空間無限增長。

在生產環境中,TTL 索引的不當使用可能引發多種問題,包括 CPU 週期性飆高、到期資料未被及時刪除、磁碟空間持續增長等。本文將從 TTL 索引的工作原理、使用方法、常見問題及最佳實務等方面進行全面介紹,協助您在ApsaraDB for MongoDB上正確使用 TTL 索引。

TTL 索引的工作原理

基本機制

每個 mongod 進程啟動時,會建立一個名為 TTLMonitor 的後台線程,該線程預設每隔 60 秒發起一輪 TTL 清理操作。每輪操作的流程如下:

  1. 搜集當前資料庫中所有的 TTL 索引。

  2. 依次對每個 TTL 索引產生執行計畫並執行資料清理。

  3. 刪除索引欄位值加上 expireAfterSeconds 小於目前時間的文檔。

到期閾值的計算方式為:索引欄位的日期值 + expireAfterSeconds 的秒數。如果該結果小於目前時間,則文檔被認為已到期。

複本集中的行為

在複本集架構中,TTL 後台線程僅在 Primary 節點上執行刪除操作。Secondary 節點的 TTL 線程處於空閑狀態,通過複製 Primary 的 oplog 來同步刪除操作。這意味著 TTL 刪除會產生額外的 oplog,可能影響複本集的同步延遲。

版本差異:大量刪除最佳化(MongoDB 7.0+)

從 MongoDB 7.0 開始(對應開發版本 6.1 引入的特性),TTL 刪除操作引入了"公平刪除"機制,通過類似"時間片"的方式為每個 TTL 索引分配刪除時間,避免某些集合的到期資料長期得不到清理。主要改進包括:

  • ttlMonitorBatchDeletes:啟用大量刪除模式,預設開啟。開啟後 TTL 刪除會更加公平地分配到各個集合。

  • ttlIndexDeleteTargetTimeMS:每個 TTL 索引每輪刪除的時間上限,預設 1000 ms。

  • ttlIndexDeleteTargetDocs:每個 TTL 索引每輪刪除的文檔數上限,預設 50000。

  • ttlMonitorSubPassTargetSecs:子輪次遍曆 TTL 索引的時間上限,預設 60 秒。

這些參數在執行計畫中體現為 BATCHED_DELETE 階段,可通過 explain 命令查看。

建立和管理 TTL 索引

建立 TTL 索引

使用 createIndex 方法,在日期類型欄位上建立 TTL 索引。

樣本 1:建立 TTL 索引,文檔在 lastModifiedDate 欄位值的 3600 秒後到期。

db.eventlog.createIndex(
  { "lastModifiedDate": 1 },
  { expireAfterSeconds: 3600 }
)

樣本 2:指定時刻到期。將 expireAfterSeconds 設為 0,由欄位值直接決定到期時間。

db.sessions.createIndex(
  { "expireAt": 1 },
  { expireAfterSeconds: 0 }
)

修改 TTL 索引的到期時間

使用 collMod 命令修改已有 TTL 索引的 expireAfterSeconds 值,無需重建索引。

db.runCommand({
  "collMod": "log_events",
  "index": {
    "keyPattern": { "createdAt": 1 },
    "expireAfterSeconds": 600
  }
})

將普通索引轉換為 TTL 索引(MongoDB 6.0+)

從 MongoDB 6.0 開始,可以通過 collMod 命令將已存在的普通單欄位索引轉換為 TTL 索引,而不需要先刪除再重建。

db.runCommand({
  "collMod": "tickets",
  "index": {
    "keyPattern": { "lastModifiedDate": 1 },
    "expireAfterSeconds": 100
  }
})

監控 TTL 運行狀態

您可以通過以下命令監控 TTL 的運行情況。

// 查看 TTL 刪除的文檔總數
db.serverStatus().metrics.ttl.deletedDocuments

// 查看 TTL 線程的運行輪次
db.serverStatus().metrics.ttl.passes

// 查看 TTL 線程的掃描周期(預設 60 秒)
db.runCommand({ getParameter: 1, ttlMonitorSleepSecs: 1 })

// 查看當前正在執行的 TTL 刪除操作
db.currentOp()

TTL 索引的使用限制

在使用 TTL 索引前,需要瞭解以下重要限制:

  • TTL 索引僅支援單欄位索引。複合索引無法使用 TTL 功能,即使設定了 expireAfterSeconds 也會被忽略。

  • _id 欄位不支援 TTL 索引。

  • 索引欄位必須為 BSON Date 類型。如果欄位值不是 Date 類型(如 UNIX 時間戳記整數、字串等),文檔將不會被自動刪除。

  • 不包含索引欄位的文檔不會到期。

  • 如果欄位是數群組類型,使用數組中最早的日期值來計算到期時間。

  • 不能通過 createIndex 修改已有 TTL 索引,必須使用 collMod 命令。

  • expireAfterSeconds 的取值範圍為 0 到 2,147,483,647,且不得設為 NaN,否則可能導致不可預期的行為和資料丟失。

常見問題與解決方案

問題一:TTL 刪除導致 CPU 週期性飆高

問題現象

執行個體 CPU 使用率出現明顯的周期性峰值,每隔固定時間間隔出現一次。通過 db.currentOp() 可以觀察到 TTL 線程正在執行大量刪除操作。

原因分析

當業務在同一時刻集中插入大量資料,並且這些資料的 TTL 欄位值相同或接近時,它們會在同一時間視窗內集中到期。TTL 線程在單次掃描中需要刪除大量文檔,會導致顯著的 CPU 和 IO 壓力。

解決方案

  • 打散到期時間:在插入資料時,為 TTL 欄位增加隨機位移量,將到期時間分散到一個時間視窗內,避免集中刪除。例如,對於 24 小時到期的資料,可在到期時間上加減數分鐘的隨機位移。

  • 升級到 MongoDB 7.0 及以上版本:利用大量刪除和公平刪除機制,平滑刪除壓力。

  • 選擇合適的執行個體規格:確保執行個體的 CPU 和記憶體資源足以應對 TTL 刪除帶來的額外負載。

樣本:為插入的資料添加隨機到期位移。

// 在 24 小時基礎上增加 0~600 秒的隨機位移
var randomOffset = Math.floor(Math.random() * 600);
var expireTime = new Date(Date.now() + (86400 + randomOffset) * 1000);
db.logs.insertOne({
  data: "log content",
  createdAt: expireTime
})

問題二:資料到期後未被刪除

問題現象

文檔已超過預期的到期時間,但仍然存在於集合中。

排查步驟

  1. 確認欄位類型:檢查 TTL 索引欄位的值是否為 BSON Date 類型。一個常見錯誤是使用 UNIX 時間戳記整數而非 Date 對象。

    // 檢查欄位類型
    db.collection.findOne({}, { createdAt: 1 })
    // 正確樣本:ISODate("2024-01-01T00:00:00Z")
    // 錯誤樣本:1704067200 (整數時間戳記)
  2. 確認欄位是否存在:查看文檔中是否實際包含了 TTL 索引欄位。不包含該欄位的文檔不會被自動刪除。

  3. 檢查索引定義:確認索引確實包含 expireAfterSeconds 屬性。

    db.collection.getIndexes()
  4. 檢查索引方向:確保 TTL 索引是正序(值為 1)。倒序索引可能導致 TTL 功能異常。如發現索引方向不正確,刪除後重建即可。

  5. 檢查 TTL Monitor 是否開啟:確認 ttlMonitorEnabled 參數為 true。

    db.adminCommand({ getParameter: 1, ttlMonitorEnabled: 1 })

問題三:TTL 刪除速度跟不上資料插入速度

問題現象

集合資料量持續增長,磁碟空間不斷膨脹,即使已配置 TTL 索引。

原因分析

TTL 線程是單線程且每 60 秒執行一次。當資料插入的速率遠超過 TTL 清理速率時,到期資料會不斷累積。此外,如果執行個體上存在多個 TTL 索引,它們之間是串列執行的,進一步降低清理效率。

解決方案

  • 調整掃描頻率:可以通過 ttlMonitorSleepSecs 參數縮短掃描間隔,但需注意更高頻率會增加系統負載。

    // 將掃描間隔調整為 10 秒
    db.adminCommand({ setParameter: 1, ttlMonitorSleepSecs: 10 })
  • 業務層面主動清理:對於資料寫入量特別大的情境,建議在業務層實現定時大量刪除邏輯,而不是完全依賴 TTL 索引。

  • 考慮使用時間分區集合:對於極高寫入情境,可以按時間分集合儲存,直接刪除整個到期集合,這比 TTL 索引逐條刪除文檔高效得多。

問題四:刪除後磁碟空間未釋放

問題現象

TTL 刪除了大量文檔,但磁碟使用率並未顯著下降。

解決方案

MongoDB 使用 WiredTiger 儲存引擎,刪除文檔後空間會被標記為可重用,但不會立即返還給作業系統。如果需要立即回收磁碟空間,可以執行 compact 命令或通過初始化同步來重新構建資料檔案。建議在業務低峰期進行 compact 操作。

最佳實務建議

一、確保資料類型正確

TTL 索引僅對 BSON Date 類型的欄位生效。建議在應用程式層或通過 MongoDB 的 Schema Validation 功能強制確保 TTL 欄位為正確的日期類型。

db.createCollection("sessions", {
  validator: {
    $jsonSchema: {
      properties: {
        expireAt: { bsonType: "date", description: "TTL 欄位必須為 Date 類型" }
      },
      required: ["expireAt"]
    }
  }
})

二、分散到期時間,避免集中刪除

這是避免 TTL 刪除導致 CPU 飆高的最有效方法。在插入資料時,為到期時間添加隨機位移量,使同一批資料的到期時間分散到一個時間範圍內。這樣 TTL 線程每次掃描時只需要刪除少量文檔,顯著降低對系統效能的影響。

說明

對於保留時間較長的資料(如 24 小時以上),建議在到期時間上添加 0 到 10 分鐘的隨機位移。

三、謹慎調整 expireAfterSeconds

降低已有 TTL 索引的 expireAfterSeconds 值會導致大量已有文檔立即到期,可能觸發大規模刪除操作,嚴重影響執行個體效能。建議的做法是:

  1. 先估算調整後會有多少文檔立即到期。

  2. 在業務低峰期操作。

  3. 如果到期文檔數量巨大,建議先通過業務指令碼分批刪除部分資料,再修改 expireAfterSeconds。

四、建立 TTL 索引時的注意事項

在一個已經包含大量符合到期條件的文檔的集合上建立 TTL 索引,可能導致索引建立完成後立即觸發大規模刪除。建議:

  • 在業務低峰期建立 TTL 索引。

  • 先清理歷史到期資料,再建立 TTL 索引。

  • 使用後台方式建索引,避免影響正常業務。

五、不要長期關閉 TTL Monitor

雖然可以通過 ttlMonitorEnabled 參數臨時關閉 TTL 功能,但不建議在生產環境長期使用。關閉期間累積的大量到期文檔會在重新開啟時集中刪除,可能導致嚴重的效能問題。

警告

臨時關閉 TTL Monitor 後,務必及時重新開啟,避免到期資料大量堆積。

六、合理評估 TTL 對 oplog 的影響

TTL 刪除的每一條文檔都會產生一條 oplog。當刪除量較大時,會顯著增加 oplog 的寫入量,可能影響複本集的同步延遲。建議通過以下方式評估影響:

  • 監控 metrics.ttl.deletedDocuments,瞭解 TTL 刪除量。

  • 監控複本集同步延遲,確保 Secondary 能處理 TTL 產生的額外 oplog。

  • 必要時調大 oplog 大小,以避免因 TTL 刪除導致 oplog 溢出。

七、高寫入情境的替代方案

TTL 索引適用於中等寫入量的情境。對於極高寫入量的情境,建議考慮以下替代方案:

  1. 時間分區集合:按時間維度拆分集合(如按天、按周),直接 drop 到期集合,這是最高效的資料到期方式。

  2. 時間序列集合:MongoDB 5.0+ 支援時間序列集合,內建資料到期功能,按桶大量刪除,效率遠高於普通集合的逐條刪除。

  3. 業務層定時清理:通過定時任務在業務低峰期執行大量刪除,可以更精細地控制刪除速率和時機。

各版本 TTL 特性參考

版本

特性說明

6.0+

支援通過 collMod 將普通單欄位索引轉換為 TTL 索引,無需先刪除再重建。

7.0+

引入大量刪除機制(BATCHED_DELETE),提升刪除效率;實現公平刪除,避免某些集合被"餓死";支援時間序列集合的部分 TTL 索引,支援更靈活的 partialFilterExpression。

TTL 相關參數速查

參數名

說明

預設值

ttlMonitorSleepSecs

TTL 線程掃描間隔。

60 秒

ttlMonitorEnabled

TTL 功能開關。

true

ttlMonitorBatchDeletes

大量刪除模式(7.0+)。

true

ttlIndexDeleteTargetTimeMS

每輪刪除時間上限(7.0+)。

1000 ms

ttlIndexDeleteTargetDocs

每輪刪除文檔數上限(7.0+)。

50000

ttlMonitorSubPassTargetSecs

子輪遍曆時間上限(7.0+)。

60 秒

總結

TTL 索引是 MongoDB 中一個強大且實用的自動資料到期機制,能夠有效地管理具有明確生命週期的資料。在生產環境中,需要結合業務情境合理配置,以避免效能問題。以下是核心要點:

  1. 確保 TTL 欄位類型為 BSON Date,這是 TTL 索引正常工作的前提。

  2. 打散到期時間是避免 CPU 毛刺的最有效手段。

  3. 持續監控 TTL 的運行狀態(刪除量、掃描輪次、複本集同步延遲)。

  4. 對於高寫入情境,考慮使用時間分區集合或時間序列集合等替代方案。

  5. 升級到較新版本(如 7.0+)可獲得更好的 TTL 刪除效能和公平性。