1. 程式人生 > >【Elasticsearch 技術分享】—— 十張圖帶大家看懂 ES 原理 !明白為什麼說:ES 是準實時的!

【Elasticsearch 技術分享】—— 十張圖帶大家看懂 ES 原理 !明白為什麼說:ES 是準實時的!

> **前言** > > > 說到 Elasticsearch ,其中最明顯的一個特點就是 *near real-time* 準實時 —— 當文件儲存在Elasticsearch中時,將在1秒內以幾乎實時的方式對其進行索引和完全搜尋。那為什麼說 ES 是準實時的呢? > > > 公眾號:『 劉志航 』,記錄工作學習中的技術、開發及原始碼筆記;時不時分享一些生活中的見聞感悟。歡迎大佬來指導! ### Lucene 和 ES #### Lucene Lucene 是 Elasticsearch所基於的 Java 庫,它引入了按段搜尋的概念。 Segment: 也叫段,類似於倒排索引,相當於一個數據集。 Commit point:提交點,記錄著所有已知的段。 Lucene index: “a collection of segments plus a commit point”。由一堆 Segment 的集合加上一個提交點組成。 對於一個 Lucene index 的組成,如下圖所示。
#### Elasticsearch 一個 Elasticsearch Index 由一個或者多個 shard (分片) 組成。
而 Lucene 中的 Lucene index 相當於 ES 的一個 shard。
### 寫入過程 #### 寫入過程 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 是**準實時**的。 使文件立即可見: ```Json 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: ```json POST /my-index-000001/_flush ``` ### 刪除和更新 segment 不可改變,所以 docment 並不能從之前的 segment 中移除或更新。 所以每次 commit, 生成 commit point 時,會有一個 .del 檔案,裡面會列出被刪除的 document(邏輯刪除)。 而查詢時,獲取到的結果在返回前會經過 .del 過濾。 更新時,也會標記舊的 docment 被刪除,寫入到 .del 檔案,同時會寫入一個新的檔案。此時查詢會查詢到兩個版本的資料,但在返回前會被移除掉一個。
### 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 才會被真正的物理刪除。 ### 總結 主要介紹了內部寫入和刪除的過程,需要了解 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-fl