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