3.5 索引恢復流程
在ES中,索引有5中恢復的方式,具體方式和用途如下所示。這里我們主要關注的是Peer Recovery,這也是故障恢復使用的方式
類型 | 描述 |
---|---|
EmptyStoreRecoverySource | 從新的副本中恢復 |
ExistingStoreRecoverySource | 從現有的磁盤存儲中恢復 |
PeerRecoverySource | 從主分片中恢復 |
SnapshotRecoverySource | 從快照中恢復 |
LocalShardsRecoverySource | 從同一節點上的其他分片恢復(收縮索引操作) |
ES恢復分片的核心思想:通過Lucene分段和translog來進行恢復
3.5.1 相關概念
1) sequence number
sequence number是用於標記分片中操作的序列號,
2) primary term
primary term是用於標記主分片狀態的數據量。它由master統一進行分配,當副本組的主分片發生變化時(通常是提升新副本為主分片),primary term遞增。
通過primary term和sequence number,我們就可以很好的標識在分片上進行的操作順序。
3) checkpoint
有了primary term 和 sequence number 之后,我們就可以檢測出副本和主分片的差異,並使他們重新對齊
ES通過維護 global checkpoint 和 local checkpoint 來比較主分片和副本之間的差異。
-
global checkpoint
global checkpoint 是所有副本都已經完成操作的序列號。它由主分片來維護。主分片通過跟蹤在副本上完成的操作來實現。當主分片檢測到所有副本均已給出大於當前操作的序列號之后,它會向前推進 global checkpoint
-
local checkpoint
local checkpoint 也是一個序列號,低於 local checkpoint 的操作表示均已在本地副本上執行成功(寫Lucene和translog成功,不一定刷盤)。當副本向主分片ack一個操作之后,它們也會更新 local checkpoint
通過比較 global checkpoint 和 local checkpoint,這樣就只需要比較一小部分的操作,從而提高了檢查效率
4)基於文件恢復和基於操作恢復
由於主分片和副本都是獨立的Lucene索引,它們會各自執行自己的Lucene段合並。即使它們保存相同的文檔和執行相同的操作,它們的文件也不會是一樣的。更何況通過文件恢復還需要通過網絡傳輸大量的數據文件。因此,ES更傾向於通過操作來進行恢復
5)恢復狀態階段
階段 | 描述 |
---|---|
INIT | |
INDEX | 恢復Lucene文件,使用本地文件或從復制一個新的 |
VERIFY_INDEX | 驗證索引 |
TRANSLOG | 啟動engine,重置translog,建立Lucene索引 |
FINALIZE | |
DONE | 完成 |
3.5.2 恢復流程

3.5.3 核心問題
假設在副本恢復期間一直有寫操作,如何保證主分片和副本的數據一致
主要面臨的問題有兩個:translog會隨着flush而刪除,以及重做操作的時序
1)保留translog
在ES早期版本中,副本恢復時,主分片會有如下動作
-
phase1
把主分片的Lucene做快照,發給副本。期間不阻塞寫操作,新增寫操作記錄在translog中
-
phase2
把主分片的translog做快照,發給副本重做操作。期間不阻塞寫操作,新增寫操作同樣記錄在translog中
-
phase3
給主分片加寫鎖,把剩余的translog發送給副本。此時數據量很小,所以阻塞時間很短
在ES2中,重構了translog的文件管理模塊,允許存在多個translog文件。其維護一個引用文件的列表,包括未完成的recovery,以及那些包含尚未提交到Lucene的operations的文件。
同時提出了translog.view的概念,允許recovery獲取一個引用,包括所有當前未提交的translog 文件,以及所有未來新創建的 translog文件,直到view關閉。它們可以使用這個view做operations的遍歷操作。在后續版本中,用TranslogDeletionPolicy來translog.view。核心思想不變。
於是現階段流程如下:
-
phase1
獲取translog保留鎖,從獲取保留鎖開始,會保留translog不受其刷盤清空的影響。然后調用Lucene接口把shard做快照,快照含有shard中已經刷到磁盤的文件引用,把這些shard數據復制到副本節點。在phase1結束前,會向副分片節點發送告知對方啟動Engine,在phase2開始之前,副分片就可以正常處理寫請求了。
-
phase2
對translog做快照,這個快照里包含從phase1開始,到執行translog快照期間的新增索引。將這些translog發送到副分片所在節點進行重放。
2)時序問題
假設在第一階段執行期間,有客戶端索引操作要求將docA的內容寫為1,主分片執行了這個操作,而副分片由於尚未就緒所以沒有執行。第二階段期間客戶端索引操作要求寫 docA 的內容為2,此時副分片已經就緒,先執行將docA寫為2的新增請求,然后又收到了從主分片所在節點發送過來的translog重復寫docA為1的請求該如何處理?
寫操作有三種類型:創建索引、更新、刪除。其中創建索引無需考慮時序問題,那么會造成時序問題的操作就是更新和刪除。

還記得ES的文檔中有version這么一個字段嗎?這個字段我們能拿來做樂觀鎖用。同樣的原理,ES也采用version字段來標記操作的順序
ES在執行寫操作的時候,通過比較操作所帶的版本號和預期的版本號來決定這個操作要不要執行。在恢復過程中,的實現邏輯就是通過比較已有文檔版本號和操作的版本號來判斷這個操作是否過時
3.5.5 如何提高恢復效率
- 配置項
cluster.routing.allocation.node_concurrent_recoveries
決定了單個節點執行恢復時的最大並發數,默認為2 - 配置項
indices.recovery.max_bytes_per_sec
決定節點間復制數據時的限速,可以適當提高此值或取消限速。但只有phase1可以配置限速 - 配置項
cluster.routing.allocation.node_initial_primaries_recoveries
決定了單個節點執行主分片recovery時的最大並發數,默認為4。由於主分片的恢復不涉及在網絡上復制數據,僅在本地磁盤讀寫,所以在節點配置了多個數據磁盤的情況下,可以適當提高此值 - 在重啟集群之前,先暫停寫入,手動執行sync flush。這樣可以有機會跳過phase1
- 合並 Lucene 分段,對於冷索引甚至不再更新的索引執行_forcemerge,較少的Lucene分段可以提升恢復效率,例如,減少對比,降低文件傳輸請求數量。
- 適當地多保留些 translog,配置項
index.translog.retention.size
默認最大保留512MB,index.translog.retention.age
默認為不超過12小時。調整這兩個配置可讓恢復過程有機會跳過phase1。
3.6 索引生命周期管理(ILM)
3.6.1 簡介
ES在6.7中正式引入了索引生命周期管理功能,其根據索引的更新和搜索評率,將索引的生命周期划分為5個階段
- hot —— 索引在非常活躍的更新和查詢
- warm —— 索引幾乎不再更新,但仍有較多的查詢
- cold —— 索引不再更新,並且不經常查詢。 信息仍然可以搜索,但查詢速度較慢
- frozen —— 索引不再更新,並且很少查詢。 信息仍然可以搜索,但查詢速度非常慢
- Delete —— 不再需要這個索引和它的數據了
ILM在索引階段變化時執行如下操作:
- Rollover —— 當索引觸發限制條件后,創建新的索引
- Shrink —— 縮減索引的主分片數量
- Force merge —— 強制合並分片中段的數量
- Freeze —— 凍結索引,使其只讀
- Delete —— 刪除索引
注意
ILM在索引有分片為分配時也會執行更新策略,這可能導致某些不可控的后果。同時,ILM定期運行,檢查索引是否符合策略條件,並執行任何需要的步驟。 為避免競爭條件,ILM可能需要多次運行以執行完成操作所需的所有步驟。這可能導致執行時間會比預期的要長
3.6.2 生命周期策略
實例與說明
PUT _ilm/policy/hot-warm-cold-delete-60days
{
"policy": {
"phases": {
"hot": {
"actions": {
"rollover": {
"max_size":"50gb", // 設置該階段索引的最大大小
"max_age":"30d" // 設置該階段索引的最長持續時間
},
"set_priority": {
"priority":50 // 設置恢復優先級
}
}
},
"warm": {
"min_age":"7d", // 設置進入該階段索引的最小年齡
"actions": {
"forcemerge": { // 段強制合並設置
"max_num_segments":1
},
"shrink": { // 縮減主分片設置
"number_of_shards":1
},
"allocate": { // 再分配節點設置
"require": {
"data": "warm"
},
"number_of_replicas" : 2 // warm階段中的副本數量
},
"set_priority": {
"priority":25
}
}
}
}
}
}