Elasticsearch系列---shard內部原理

概要

本篇咱們來看看shard內部的一些操做原理,瞭解一下人家是怎麼玩的。java

倒排索引

倒排索引的結構,是很是適合用來作搜索的,Elasticsearch會爲索引的每一個index爲analyzed的字段創建倒排索引。數據庫

基本結構

倒排索引包含如下幾個部分:緩存

  • 某個關鍵詞的doc list
  • 某個關鍵詞的全部doc的數量IDF(inverse document frequency)
  • 某個關鍵詞在每一個doc中出現的次數:TF(term frequency)
  • 某個關鍵詞在這個doc中的次序
  • 每一個doc的長度:length norm
  • 某個關鍵詞的全部doc的平均長度

記錄這些信息,就是爲了方便搜索的效率和_score分值的計算。安全

不可變性

倒排索引寫入磁盤後就是不可變的,這樣有幾個好處:服務器

  1. 不須要鎖,若是不更新索引,不用擔憂鎖的問題,能夠支持較高的併發能力
  2. 若是cache內存足夠,不更新索引的話,索引能夠一直保存在os cache中,能夠提高IO性能。
  3. 若是數據不變,filter cache會一直駐留在內存。
  4. 索引數據能夠壓縮,節省cpu和io開銷。

doc底層原理

前面提到倒排索引是基於不可變模式設計的,但實際Elasticsearch源源不斷地有新數據進來,那光是創建、刪除倒排索引,豈不是很是忙?微信

若是真是不停地創建,刪除倒排索引,那ES壓力也太大了,確定不是這麼實現的。ES經過增長新的補充索引來接收新的文檔和修改的文檔,而不是直接用刪除重建的方式重寫整個索引。架構

doc寫入

整個寫入過程以下圖所示:併發

  1. 新文檔先寫入內存索引緩存
  2. 當間隔必定時間(1秒),將緩存的數據進行提交,這個過程會建立一個Commit Point,Commit Point包含index segment的信息。
  3. 緩存的數據寫入新的index segment。
  4. index segment的數據先寫入os-cache中
  5. 等待操做系統將os-cache的數據強制刷新到磁盤中
  6. 寫入磁盤完成後,新的index segment被打開,此時segment內的文檔能夠被搜索到。
  7. 同時buffer的數據被清空,等待下一次新的文檔寫入。

index segment翻譯過來叫"段",每秒會建立一個,ES把這個1秒內收到的、須要處理的文檔都放在這個段裏,能夠把段認爲是倒排索引的一個子集。異步

索引、分片、段的關係以下:
索引包含多個分片,每一個分片是一個Lucene索引實例,一個分片下面有多個段。若是把分片看做是一個獨立的倒排索引結構,那麼這個倒排索引是由多個段文件的集合。
三者之間是包含關係:索引包含多個分片,分片包含多個段。async

doc刪除和更新

當文檔被刪除時,Commit Point會把信息記錄在.del文件中,在.del文件中會標識哪些文檔是有deleted標記的,但該文檔仍是存在於原先的index segment文件裏,一樣可以被檢索到,只是在最終結果處理時,標記爲deleted的文檔被會過濾掉。

更新也是相似的操做,更新會把舊版本的文檔標記爲deleted,新的文檔會存儲在新的index segment中。

近實時搜索

上面的流程細節的童鞋能夠會發現,每次都須要fsync磁盤,數據纔是可搜索的,那IO壓力將特別大,耗費時間比較長,而且執行週期由操做系統控制,從一個新文檔寫入到能夠被搜索,超過1分鐘那是常有的事。

因此Elasticsearch對此作了一個改進:
index segment信息寫入到os-cache中,即完成上面的第4步,該segment內的文檔信息就能夠被搜索到了。fsync操做就不當即執行了,

os-cache的寫入代價比較低,最耗時的fsync操做交由操做系統調度執行。

上述的index segment寫入到os-cache,並打開搜索的過程,叫作refresh,默認是每隔1秒refresh一次因此,es是近實時的,數據寫入到能夠被搜索,默認是1秒。

refresh的時間也能夠設置,好比咱們一些日誌系統,數據量特別大,但實時性要求不高,咱們爲了優化資源分配,就能夠把refresh設置得大一些:

PUT /music
{
  "settings": {
    "refresh_interval": "30s" 
  }
}

此參數須要在建立索引時使用,要注意一下的是除非有充分的依據,纔會對refresh進行設置,通常使用默認的便可。

translog機制

上述的寫入流程當中,若是fsync到磁盤的操做沒執行完成,服務器斷電宕機了,可能會致使Elasticsearch數據丟失。Elasticsearch也設計了translog機制,跟關係型數據庫的事務日誌機制很是像,整個寫入過程將變成這樣:

  1. 新文檔寫入內存buffer的同時,也寫一份到translog當中。
  2. 內存buffer的數據每隔1秒寫入到index segment,並寫入os-cache,完成refresh操做。
  3. 內存buffer被清空,但translog一直累加。
  4. 每隔5秒translog信息fsync到磁盤上。
  5. 默認每30分鐘或translog累積到512MB時,執行全量commit操做,os-cache中的segment信息和translog信息fsync到磁盤中,持久化完成。
  6. 生成新的translog,舊的translog歸檔(6.x版本translog作歸檔操做,不刪除)。

flush API

這個執行一個提交而且歸檔translog的行爲稱做一次flush。分片每30分鐘被自動刷新(flush),或者在 translog 太大的時候(默認512MB)也會刷新,固然也能夠手動觸發flush的執行,以下請求:

POST /music/_flush

但任其自動flush就夠了。若是重啓節點前擔憂會對索引形成影響,能夠手動flush一下。畢竟節點重啓後須要從translog裏恢復數據,translog越小,恢復就越快。

durability同步和異步

translog寫磁盤行爲主要有兩種,是由index.translog.durability配置項決定的:

  • request:同步寫磁盤,每次寫請求完成以後當即執行(新增、刪除、更新文檔),以及primary shard和replica shard同步都會觸發,數據安全有保障,不丟失,但會帶來一些性能損失。若是是bulk數據導入,每一個文檔平攤下來的損失是比較小的。
  • async:異步寫磁盤,默認5秒fsync一次,若是有宕機事件,可能會丟失幾秒的數據,適用於容許偶爾有數據丟失的場景,如日誌系統。

若是系統不接受數據丟失,用translog同步方式,示例設置:

# 異步方式
PUT /music_new
{
  "settings": {
    "index.translog.durability": "async",
    "index.translog.sync_interval": "5s"
  }
}

# 同步方式
PUT /music_new
{
  "settings": {
    "index.translog.durability": "request"
  }
}

segment合併

Elasticsearch針對活躍的索引,每秒都會生成一個新的index segment,這些segment最終會以文件的形式存儲在磁盤裏,若是不對其進行處理,那麼索引運用一段時間後,會有特別多的文件,零碎的文件太多了,也不是什麼好事情,更耗費更多的文件資源,句柄等,搜索過程也會變慢。

合併過程

Elasticsearch會在後臺對segment進行合併,減小文件的數量,同時,標記爲deleted的文檔在合併時會被丟棄(delete請求只是將文檔標記爲deleted狀態,真正的物理刪除是在段合併的過程當中),合併過程不須要人工干預,讓Elasticsearch自行完成便可。

兩個已經提交的段和一個未提交的段合併成爲一個大的段文件

合併時會挑一些大小接近的段,合併到更大的段中,段合併過程不阻塞索引和搜索。

合併完成後,新的更大的段flush到磁盤中,並完成refresh操做,老的段被刪除掉。

optimize API

optimize命令能夠強制合併API,並指定最終段的數量,以下命令:

POST /music_new/optimize
{
  "max_num_segments": 1
}

指定segment最大數量爲1,表示該索引最終只有一個segment文件。

適用場景
  1. 正常活躍的、常常有更新的索引不建議使用
  2. 日誌類的索引,對老數據進行優化時,能夠將每一個分片的段進行合併
使用建議
  1. 通常不須要人工干預合併過程
  2. optimize操做會消耗大量的IO資源,使用要慎重考慮

小結

本篇主要介紹shard內部的原理,包含寫入、更新刪除,translog機制,segment合併等,瞭解數據庫的童鞋對translog機制應該很是熟悉,原理上大同小異,僅做拋磚引玉,謝謝。

專一Java高併發、分佈式架構,更多技術乾貨分享與心得,請關注公衆號:Java架構社區
能夠掃左邊二維碼添加好友,邀請你加入Java架構社區微信羣共同探討技術
Java架構社區.jpg

相關文章
相關標籤/搜索