一 序
本文屬于極客時間Elasticsearch核心技術與實戰學習筆記系列。
二 分片的内部原理
什麼是 ES 的分片
- ES 中最小的工作單元 / 是一個 Lucence 的 Index
一些問題:
- 為什麼 ES 的搜尋時近實時的(1 秒後被搜到)
- ES 如何保證在斷電時資料也不會丢失
- 為甚删除文檔,并不會立刻釋放空間
2.1 反向索引的不可變性
反向索引采用 Immutable Design, 一旦生成,不可更改
不可變性,帶來了的好處如下:
- 不許考慮并發寫檔案的問題,避免了鎖機制帶來的性能問題
- 一旦讀入核心的檔案系統緩存,便留在那裡,隻要檔案系統存有足夠的空間,大部分請求就會直接請求記憶體,不會命中磁盤,提高了很大的性能
- 緩存容易生成和維護 / 資料可以被壓縮
不可變更性,帶來了的挑戰:如果需要讓一個新的文檔可以被搜尋,需要重建整個索引
2.2 Lucence Index
分片是Elasticsearch中的最小工作單元,本質上是一個Lucene Index。在Lucene中,單個反向索引檔案被稱為Segment,Segment是自包含且不可變更的,多個Segment彙總在一起稱為Lucene Index
當有新文檔生成時,會生成新的Segment,查詢時會同時查詢所有Segment,并且對結果彙總,Lucene中有一個檔案用來記錄所有Segments資訊,叫做Commit Point,删除的文檔資訊,儲存在.del檔案中。搜尋的時候,會對删除的文檔做過濾。

2.3 Refresh
- 将 Index buffer 寫入 Segment 的過程叫做 Refresh。Refresh 不執行 fsync 操作
- Refresh 頻率:預設 1 秒發生一次,可通過 index.refresj_interval 配置。Refresh 後,資料就可以被搜尋到了。這也就是為什麼 ES 被稱為近實時搜尋
- 如果系統有大量的資料寫入,那就會産生很多的 Segment
- Index Buffer 被占滿時,會觸發 Refresh, 預設值是 JVM 的 10%
2.4 什麼是Transaction Log
- Segment 寫入磁盤的過程相對耗時,借助檔案系統緩存,Refresh 時,先将 Segment 寫入緩存以開放查詢
- 為了保證資料不會丢失。所有在 Index 文檔時,同時寫 Transaction Log,高版本開始,ra 預設落盤。每個分片都有一個 Transaction Log
- 當 ES Refresh 時,Index Buffer 被清空,Transaction Log 不會清空
當ES産生斷電的時候,因為Transaction Log都做了落盤的處理,是以系統重新開機的時候,會先從Transaction Log 進行recover。不會丢失。
2.5 什麼是Flush
ES Flush & Lucene Commit
調用 Refresh ,Index Buffer 清空并且 Refresh
調用 fsync, 将緩存中的 Segments 寫入磁盤
清空(删除)Transaction Log
預設 30 分鐘調用一次
Transaction Log 滿(預設 512M)
2.6 Merge
-
很多,需要定期被合并Segment
- 減少
/ 删除已經删除的文檔(真正的删除)Segment
- 減少
- ES 和 Lucene 會自動進行 Merge 操作(強制merge)
- POST my_index/_forcemerge
***********************
有優秀的小夥伴Geek_817ea4查了文檔,做了總結,這裡引用下
1)用戶端發起資料寫入請求,對你寫的這條資料根據_routing規則選擇發給哪個Shard。
确認Index Request中是否設定了使用哪個Filed的值作為路由參數,
如果沒有設定,則使用Mapping中的配置,
如果mapping中也沒有配置,則使用_id作為路由參數,然後通過_routing的Hash值選擇出Shard,最後從叢集的Meta中找出出該Shard的Primary節點。
2)寫入請求到達Shard後,先把資料寫入到記憶體(buffer)中,同時會寫入一條日志到translog日志檔案中去。
當寫入請求到shard後,首先是寫Lucene,其實就是建立索引。
索引建立好後并不是馬上生成segment,這個時候索引資料還在緩存中,這裡的緩存是lucene的緩存,并非Elasticsearch緩存,lucene緩存中的資料是不可被查詢的。
3)執行refresh操作:從記憶體buffer中将資料寫入os cache(作業系統的記憶體),産生一個segment file檔案,buffer清空。
寫入os cache的同時,建立反向索引,這時資料就可以供用戶端進行通路了。
預設是每隔1秒refresh一次的,是以es是準實時的,因為寫入的資料1秒之後才能被看到。
buffer記憶體占滿的時候也會執行refresh操作,buffer預設值是JVM記憶體的10%。
通過es的restful api或者java api,手動執行一次refresh操作,就是手動将buffer中的資料刷入os cache中,讓資料立馬就可以被搜尋到。
若要優化索引速度, 而不注重實時性, 可以降低重新整理頻率。
4)translog會每隔5秒或者在一個變更請求完成之後,将translog從緩存刷入磁盤。
translog是存儲在os cache中,每個分片有一個,如果節點當機會有5秒資料丢失,但是性能比較好,最多丢5秒的資料。。
可以将translog設定成每次寫操作必須是直接fsync到磁盤,但是性能會差很多。
可以通過配置增加transLog刷磁盤的頻率來增加資料可靠性,最小可配置100ms,但不建議這麼做,因為這會對性能有非常大的影響。
5)每30分鐘或者當tanslog的大小達到512M時候,就會執行commit操作(flush操作),将os cache中所有的資料全以segment file的形式,持久到磁盤上去。
第一步,就是将buffer中現有資料refresh到os cache中去。
清空buffer 然後強行将os cache中所有的資料全都一個一個的通過segmentfile的形式,持久到磁盤上去。
将commit point這個檔案更新到磁盤中,每個Shard都有一個送出點(commit point), 其中儲存了目前Shard成功寫入磁盤的所有segment。
把translog檔案删掉清空,再開一個空的translog檔案。
flush參數設定:
index.translog.flush_threshold_period:
index.translog.flush_threshold_size:
#控制每收到多少條資料後flush一次
index.translog.flush_threshold_ops:
6)Segment的merge操作:
随着時間,磁盤上的segment越來越多,需要定期進行合并。
Es和Lucene 會自動進行merge操作,合并segment和删除已經删除的文檔。
我們可以手動進行merge:POST index/_forcemerge。一般不需要,這是一個比較消耗資源的操作。
merge一般不需要人為參與,es自動會做。索引從hot遷移到warm,可以人為對索引做一次force merge
這一屆真的知識點很多。