十張圖帶大家看懂 ES 原理 !明白爲什麼說:ES 是準實時的!


   前言

"   說到 Elasticsearch ,其中最明顯的一個特點就是 near real-time 準實時 —— 當文檔存儲在Elasticsearch中時,將在1秒內以幾乎實時的方式對其進行索引和完全搜索。那爲什麼說 ES 是準實時的呢?   "


  1  


Lucene 和 ES

  Lucene


Lucene 是 Elasticsearch所基於的 Java 庫,它引入了按段搜索的概念。

Segment:也叫段,類似於倒排索引,相當於一個數據集。

Commit point:提交點,記錄着所有已知的段。

Lucene index:“a collection of segments plus a commit point”。由一堆 Segment 的集合加上一個提交點組成。

對於一個 Lucene index 的組成,如下圖所示。


  Lucene


一個 Elasticsearch Index 由一個或者多個 shard (分片) 組成。



而 Lucene 中的 Lucene index 相當於 ES 的一個 shard。




  2  


寫入過程


  寫入過程 1.0 (不完善)


  1. 不斷將 Document 寫入到 In-memory buffer (內存緩衝區)。
  2. 當滿足一定條件後內存緩衝區中的 Documents 刷新到磁盤。
  3. 生成新的 segment 以及一個 Commit point 提交點。
  4. 這個 segment 就可以像其他 segment 一樣被讀取了。

畫圖如下:



將文件刷新到磁盤是非常耗費資源的,而且在內存緩衝區和磁盤中間存在一個高速緩存(cache),一旦文件進入到 cache 就可以像磁盤上的 segment 一樣被讀取了。


  寫入過程 2.0 


  1. 不斷將 Document 寫入到 In-memory buffer (內存緩衝區)。
  2. 當滿足一定條件後內存緩衝區中的 Documents 刷新到 高速緩存( cache)。
  3. 生成新的 segment ,這個 segment 還在 cache 中。
  4. 這時候還沒有 commit ,但是已經可以被讀取了。

畫圖如下:


數據從 buffer 到 cache 的過程是定期每秒刷新一次。所以新寫入的 Document 最慢 1 秒就可以在 cache 中被搜索到。


而 Document 從 buffer 到 cache 的過程叫做 ?refresh 。一般是 1 秒刷新一次,不需要進行額外修改。當然,如果有修改的需要,可以參考文末的相關資料。這也就是爲什麼說 Elasticsearch 是準實時的。


使文檔立即可見:


PUT /test/_doc/1?refresh
{"test""test"}

// 或者
PUT /test/_doc/2?refresh=true
{"test""test"}

  Translog 事務日誌


此處可以聯想 Mysql 的 binlog, ES 中也存在一個 translog 用來失敗恢復。

  1. Document 不斷寫入到 In-memory buffer,此時也會追加 translog。
  2. 當 buffer 中的數據每秒 refresh 到 cache 中時,translog 並沒有進入到刷新到磁盤,是持續追加的。
  3. translog 每隔 5s 會 fsync 到磁盤。
  4. translog 會繼續累加變得越來越大,當 translog 大到一定程度或者每隔一段時間,會執行 flush。


flush 操作會分爲以下幾步執行:

  1. buffer 被清空。
  2. 記錄 commit point。
  3. cache 內的 segment 被 fsync 刷新到磁盤。
  4. translog 被刪除。


值得注意的是:

  1. translog 每 5s 刷新一次磁盤,所以故障重啓,可能會丟失 5s 的數據。
  2. translog 執行 flush 操作,默認 30 分鐘一次,或者 translog 太大 也會執行。

手動執行flush:

POST /my-index-000001/_flush


  3  


刪除和更新


segment 不可改變,所以 docment 並不能從之前的 segment 中移除或更新。


所以每次 commit, 生成 commit point 時,會有一個 .del 文件,裏面會列出被刪除的 document(邏輯刪除)。


而查詢時,獲取到的結果在返回前會經過 .del 過濾。


更新時,也會標記舊的 docment 被刪除,寫入到 .del 文件,同時會寫入一個新的文件。此時查詢會查詢到兩個版本的數據,但在返回前會被移除掉一個。




  4  


segment 合併


每 1s 執行一次 refresh 都會將內存中的數據創建一個 segment。


segment 數目太多會帶來較大的麻煩。每一個 segment 都會消耗文件句柄、內存和cpu運行週期。更重要的是,每個搜索請求都必須輪流檢查每個 segment ;所以 segment 越多,搜索也就越慢。


在 ES 後臺會有一個線程進行 segment 合併:


  1. refresh操作會創建新的 segment 並打開以供搜索使用。
  2. 合併進程選擇一小部分大小相似的 segment,並且在後臺將它們合併到更大的 segment 中。這並不會中斷索引和搜索。
  3. 當合並結束,老的 segment 被刪除 說明合併完成時的活動:
    1. 新的 segment 被刷新(flush)到了磁盤。 寫入一個包含新 segment 且排除舊的和較小的 segment的新 commit point。
    2. 新的 segment 被打開用來搜索。
    3. 老的 segment 被刪除。



物理刪除:


在 segment merge 這塊,那些被邏輯刪除的 document 纔會被真正的物理刪除。



  5  


總結


主要介紹了內部寫入和刪除的過程,需要了解 refresh、fsync、flush、.del、segment merge 等名詞的具體含義。


完整畫圖如下:



以上就是個人分享的 ES 相關的內容,主要目的是組內技術分享,進行掃盲。不對之處,希望大家留言指正。


  相關資料


[1]   準實時搜索:

https://www.elastic.co/guide/en/elasticsearch/reference/7.9/near-real-time.html

[2]   Refresh API:

https://www.elastic.co/guide/en/elasticsearch/reference/7.9/indices-refresh.html

[3]  Flush API:

https://www.elastic.co/guide/en/elasticsearch/reference/7.9/indices-flush.html



- <End /> -



◆  ◆  ◆  ◆  ◆  



一鍵三連


本文分享自微信公衆號 - 匠心Java(code-the-world)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章