版本:Elasticsearch 8.x
今天來看下 Elasticsearch 中的寫入流程。
不想看過程可以直接跳轉文章末尾查看總結部分。最后附上個人理解的一個圖。
從我們發出寫入請求,到 Elasticsearch 接收請求,處理請求,保存數據到磁盤,這個過程中經歷了哪些處理呢?Elasticsearch 又做了哪些操作?對于 Elasticsearch 寫入一篇文檔相信大家不陌生,但是Elasticsearch 的底層究竟是如何處理的呢,讓我們一起來一探究竟。
(1) 客戶端發送寫請求時,發送給任意一個節點,這個節點就是所謂的協調節點(coordinating node)。(對應圖中的序號1)
(2) 計算文檔要寫入的分片位置,使用 Hash 取模算法(最新版 Hash 算法)(對應圖中序號2)。
routing_factor = num_routing_shards / num_primary_shardsshard_num = (hash(_routing) % num_routing_shards) / routing_factor
(3) 協調節點進行路由,將請求轉發給對應的 primary sharding 所在的 datanode(對應圖中序號2)。
(4) datanode 節點上的 primary sharding 處理請求,寫入數據到索引庫,并且將數據同步到對應的 replica sharding(對應圖中序號3)。
(5) 等 primary sharding 和 replica sharding 都保存好之后返回響應(對應圖中序號 4,5,6)。
在7.13版本之前,計算方式如下:
shard_num = hash(_routing) % num_primary_shards
從7.13 版本開始,不包括 7.13 ,計算方式就改為了上述步驟2的計算方式。
routing_factor = num_routing_shards / num_primary_shardsshard_num = (hash(_routing) % num_routing_shards) / routing_factor
此處以 Create index API 舉例說明,其中有一個請求參數 wait_for_active_shards。 該參數的作用就是寫入請求發送到ES之后,需要等待多少數量的分片處于激活狀態后再繼續執行后續操作。如果所需要數量的分片副本不足,則寫入操作需等待并重試,直到所有的分片副本都已經啟動或者發生超時。
默認情況下,寫入操作僅等待主分片處于活動狀態后繼續執行(即 wait_for_active_shard=1)。
該設置極大的降低了寫操作未寫入所需數量分片副本的機會,但是并沒有完全避免。
先來一個官網的寫入流程圖(地址在文末獲?。?。
Elasticsearh 寫入流程圖
對于 Elasticsearch 的寫入流程來說,就三部分:
為什么稱為近實時,是因為在寫入到內存緩沖區的時候,我們是還無法進行檢索的,等到寫入到segment之后,就可以進行檢索到了,所以這是近實時的原因。
因為相對于寫到磁盤,打開 segment 寫入文件系統緩存的代價比寫入磁盤的代價低的多。
第一步、寫入文檔到內存緩沖區(此時文檔不可被檢索)。
第二步、緩沖區的內容寫入到 segment,但是還未提交(可被檢索)。
在 Elasticsearch 中,寫入和打開一個新segment的過程稱為 refresh,refresh操作會自上次刷新(refresh)以來執行的所有操作都可用搜索。
refresh觸發的方式有如下三種:
默認情況下,Elasticsearch 每秒定期刷新,但是僅限于在過去的30s內收到的一個或者多個 search請求。這個也就是近實時的一個點,文檔的更改不會立即顯示在下一次的檢索中,需要等待 refresh 操作完成之后才可以檢索出來。
我們可以通過如下方式觸發refresh操作或者調整自動刷新的間隔。
POST /_refresh POST /blogs/_refresh
調整刷新間隔,每 30s 刷新:
PUT /my_logs{ "settings": { "refresh_interval": "30s" }}
關閉自動刷新:
PUT /my_logs/_settings{ "refresh_interval": -1 }
設置為每秒自動刷新:
PUT /my_logs/_settings{ "refresh_interval": "1s"
refresh_interval 需要一個 持續時間 值, 例如 1s (1 秒) 或 2m (2 分鐘)。 一個絕對值 1 表示的是 1毫秒 --無疑會使你的集群陷入癱瘓。
由于 refresh 操作會每秒自動刷新生成一個新的段(segment),這樣的話短時間內,segment會暴增,segment數量太多,每一個都會造成文件句柄、內存、CPU的大量消耗,還有一個更重要的點就是,每個檢索請求也會輪流檢查每一個segment,所以segment越多,檢索也就越慢。
Elasticsearch 通過在后臺自動合并 segment 來解決這個問題的。小的segment被合并到大的segment,然后大的segment在被合并到更大的segment。
segment 合并的時候會自動將已刪除的文檔從文件系統中刪除,已經刪除的文檔或者更新文檔的舊版本不會被合并到新的 segment中。
optimize API 不應該用在經常更新的索引上。
該 optimize API 可以控制分片最大的 segment數量,對于有的索引,例如日志,每天、每周、每月的日志被單獨存在一個索引上,老得索引一般都是只讀的,也不太可能發生變化,所以我們就可以使用這個 optimize API 優化老的索引,將每個分片合并為一個單獨的segment。這樣既可以節省資源,也可以加快檢索速度。
合并索引中的每個分片為一個單獨的段:
POST /logstash-2014-10/_optimize?max_num_segments=1
上述的refresh操作是 Elasticsearch 近實時 的原因,那么數據的持久化就要看fsync操作把數據從文件系統緩沖區flush到磁盤了。所以只有當translog被fsync操作或者是提交時,translog中的數據才會持久化到磁盤。
如果沒有持久化操作,當 Elasticsearch 宕機發生故障的時候,就會發生數據丟失了,所以 Elasticsearch 依賴于translog進行數據恢復。
在 Elasticsearch 進行提交操作的時候,成本是非常高的,所以策略就是在寫入到內存緩沖區的時候,同步寫入一份數據到translog,所有的index與delete操作都會在內部的lucene索引處理后且未確認提交之前寫入teanslog。
如果發生了異常,當分片數據恢復時,已經確認提交但是并沒有被上次lucene提交操作包含在內的最近操作就可以在translog中進行恢復。
Elasticsearch 的 flush操作是執行 Lucene提交并開始生成新的translog的過程,為了確保translog文件不能過大,flush操作在后臺自動執行,否則在恢復的時候也會因為文件過大花費大量的時間。
對于translog有如下設置選項:
對于上述的幾個參數,都可以動態更新:
(1) index.translog.sync_interval:將 translog fsync到磁盤并提交的頻率。默認5s,不允許小于100ms。
(2) index.translog.durability:是否在每次index,delete,update,bulk操作之后提交translog。
(3) index.translog.flush_threshold_size:防止 translog 文件過大的設置,一旦達到設置的該值,就會發生 flush 操作,并生成一個新的 commit point。默認512mb。
(1) 一個文檔被index之后,添加內存緩存區,同時寫入 translog。
(2) refresh 操作完成后,緩存被清空,但是 translog 不會
(3) 更多的文檔被添加到內存緩沖區并追加到 translog。
(4) 每隔一段時間,translog 變得越來越大,索引被刷新(flush),一個新的 translog 被創建,并且一個提交執行。
translog 提供所有還沒有被刷到磁盤的操作的一個持久化記錄。當 Elasticsearch 啟動的時候,它會從磁盤中使用的最后一個提交點(commit point)去恢復已知的 segment ,并且會重放 translog 中所有在最后一次提交后發生的變更操作。
translog 也被用來提供實時的CRUD,當我們通過ID進行查詢、更新、刪除一個文檔、它會嘗試在相應的 segment 中檢索之前,首先檢查 translog 中任何最近的變更操作。也就是說這個是可以實時獲取到文檔的最新版本。
最后送上一個我自己理解的圖,參考了官網的描述,以及網上畫的,如有錯誤歡迎指出。
本文鏈接:http://www.tebozhan.com/showinfo-26-91168-0.html深度解析:Elasticsearch 寫入請求處理流程
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com