Elasticsearch Document寫入、刪除、查詢以及搜索原理

Elasticsearch Document寫入、刪除、查詢以及搜索原理

序號 內容 鏈接地址
1 SpringBoot整合Elasticsearch7.6.1 https://blog.csdn.net/miaomiao19971215/article/details/105106783
2 Elasticsearch Filter執行原理 https://blog.csdn.net/miaomiao19971215/article/details/105487446
3 Elasticsearch 倒排索引與重建索引 https://blog.csdn.net/miaomiao19971215/article/details/105487532
4 Elasticsearch Document寫入原理 https://blog.csdn.net/miaomiao19971215/article/details/105487574
5 Elasticsearch 相關度評分算法 https://blog.csdn.net/miaomiao19971215/article/details/105487656
6 Elasticsearch Doc values https://blog.csdn.net/miaomiao19971215/article/details/105487676
7 Elasticsearch 搜索技術深入 https://blog.csdn.net/miaomiao19971215/article/details/105487711
8 Elasticsearch 聚合搜索技術深入 https://blog.csdn.net/miaomiao19971215/article/details/105487885
9 Elasticsearch 內存使用 https://blog.csdn.net/miaomiao19971215/article/details/105605379
10 Elasticsearch ES-Document數據建模詳解 https://blog.csdn.net/miaomiao19971215/article/details/105720737

一. 寫入原理

ES爲了實現進實時搜索,在寫入Document時利用了Buffer(內存),OS Cache(系統緩存,屬於系統內存的一部分),Disk(磁盤)三種存儲方式,儘可能的提升搜索的能力。ES的底層lucene實現的,在 luncene中一個index會被分爲若干個數據段segment,每一個segment都會存放index的部分document。從流程上講,ES會先把一個index中的document分散存儲在若干個shard(指的是主分片)上,在shard中,使用若干個segment來存儲具體的數據。
ES寫入數據的流程大致如下:
在這裏插入圖片描述

  1. 客戶端發起請求(增、刪、改)到ES中。
  2. ES將本次請求要操作的document寫入到buffer中。ES爲了保證搜索的近實時(Near Real Time 簡稱 NRT),默認每秒刷新一次buffer,這個刷新時間間隔可以手動修改,也可以通過命令觸發buffer的刷新。建議刷新時間間隔設置在1秒左右,好處使在服務器宕機後,只會丟失1秒左右的數據。當然了,如果Buffer中沒有任何數據,則不會執行refresh操作(總不能創建空文件吧)
POST /index_name/_refresh

PUT index_name
{
  "settings": {
    "number_of_shards": 5,
    "number_of_replicas": 1,
    "refresh_interval": "1s"
  }
}
  1. ES在將document寫入到緩存的同時,也會將本次操作的具體內容寫入到translog文件中,這份文件存在的意義在於即便ES宕機了,也能儘可能的減少丟失的數據(簡單來說,就是把translog中的記錄重新執行一遍),當然translog也不能保證數據絕對不丟失,其原因在第6點詳細的講出了。由於translog存儲在磁盤Disk中,因此爲了提高訪問效率,ES與translog文件之間會建立並保持一個長連接(不然每次訪問都要獲取和釋放文件流)。 此時,寫入的數據不能對外提供搜索
  2. 步驟2中提到過ES每隔一段時間就會刷新buffer,這個刷新的動作會在內存中創建一個全新的index segment,並將buffer中的document數據全部到這個新的index segment中。值得注意的是,index segment同樣是文件,只不過我們目前訪問的是內存中的File(ES底層使用java開發,new File()後文件會被讀取到內存當中)。
    segment中存儲的是buffer指定時間間隔內接收到的document寫操作數據(因爲Disk與內存有速度差,爲了讓數據持久化落盤的速度適應數據寫入內存的速度,我們使用了buffer,index segment,比如數據寫入了10秒,默認每秒刷新一次buffer,則產生10個index segment,而來得及寫入Disk的index segment可能只有2個)。此外 ,index segment已經是新增數據被處理成倒排索引後的數據結果了。
  3. 在index segment被創建並寫入了來自buffer的數據後,ES會立刻將index segment對應的File寫入到系統緩存OS Cache中並打開(也就是說,數據寫入磁盤文件之前,會先寫入OS Cache緩存一下),這樣就可以立刻爲客戶端提供最新數據的請求服務,而不必等待index semeng寫入到磁盤後,再打開index segment。畢竟IO操作是一個重量級的操作,非常費時,一定會影響ES的近實時搜索能力。此時,寫入的數據可以對外提供搜索。 這裏再次體現了ES的NRT特性,寫入到ES的數據,1秒後才能對外提供搜索服務。
  4. 前面說了,translog中記錄的是ES操作的過程,萬一遇到系統宕機,在系統重啓後,ES會重新讀取磁盤Disk中保存的數據(一份份的 index segment文件)至系統緩存,接着讀取translog中的操作日誌,並逐條執行,以此來達到恢復數據的目的。ES默認每隔5秒執行一次translog文件的持久化。translog最初只是寫到了OS Cache,只有在持久化時,纔會真正強制刷新到本次磁盤上。如果在持久化的過程中恰好ES在做document寫操作,那麼本次持久化操作將暫停,直到寫操作徹底完成後,才繼續執行。translog的持久化方式默認爲同步(先寫數據到Buffer,寫完了再寫translog日誌到OS Cache),如果修改成異步(邊寫入數據,邊持久化日誌),那麼在translog持久化的過程中新執行的寫操作對應的日誌就會丟失,假如恰好此時ES所在的服務器宕機,那麼這段時間還沒來得及持久化到Disk,僅位於內存中index segment或OS Cache緩存中的數據便無法恢復,永久丟失。還有一種情況,translog日誌目前僅停留在OS Cache,此時系統宕機,那麼translog仍然會永久丟失。這裏體現了ES可能會丟失數據(5秒內translog中的數據),如果真的不想丟失任何數據,我們可以將index.translog.durability的值設置成"request",也就是說,每次請求後立刻寫入並同步translog至磁盤。當然,這樣做的代價是至少讓寫入的效率下降一個數量級。
PUT index_name
{
  "settings": {
    "number_of_shards": 5,
    "number_of_replicas": 1,
    "index.translog.durability" : "async",
    "index.translog.sync_interval" : "5s"
  }
}
  1. 隨着時間的推移,translog文件會不斷的增大,在內存中積壓的數量衆多的index segment file的文件流也在不斷的增大,OS Cache中積壓的數據也越來越大,當translog文件大到一定程度或默認30分鐘執行一次,ES會自動觸發commit操作(又叫flush操作)。commit操作的具體內容有:
    1. 將buffer中的數據刷新到一個新的index segment中;
    2. 將index segment寫入到OS Cache並打開index segment爲搜索提供服務;
    3. 執行一個commit point操作,將OS Cache中所有的index segment標識記錄在這個commit point中,並持久化到系統磁盤Disk;
    4. commit point操作會觸發fsync操作(file sync),將內存中已經寫入數據的index segment落盤(強制刷新)到Disk磁盤上,持久化成文件。
    5. 清空本次持久化的index segment對應在translog中的日誌。
  2. 按照上述的流程來看,每1秒會生成一個index segment文件,每30分鐘會將index segment文件流持久化到磁盤,照這樣來看,磁盤中的index segment文件會非常多,從而需要處於開啓狀態的index segment也非常多,在執行搜索操作時,找到數據對應的index segment就會比較費時了。但不用擔心,因爲ES會定期進行Merge操作,Merge在接下來的刪除原理中詳細的說明了。

二. 刪除原理

ES爲了保證數據的近實時搜索能力,不會直接在物理磁盤中刪除目標數據所在的segment文件,而是先把待刪除的數據放入一個.del文件中,在執行segment merge操作時,通過參考.del文件,忽略掉已被刪除的數據,最終把大量的segment file合併成一個或幾個segment file。

merge的大致流程如下:
1. ES會選取一些大小相近的segment文件流,合併成一個大的segment文件流(注意: segment可能是尚未持久化到磁盤的segment file,也可能是已經持久化到磁盤的segment file,不管是哪種狀態的segment file,此時它們都在OS Cache中)。
2. 執行commit操作,在Disk中記錄commit point,這個commit point不僅包含新增的segment,還包含合併後,需要被刪除的segment源文件的標識,剛纔曾提到過,有些segment file中的數據會被刪除(合併時被忽略),如果一個segment file內的數據都被刪除(被合併或忽略了),則這些segment就會標記"被刪除"。
3. commit操作結束後,ES會將merge後的segment文件重新打開,爲搜索提供服務,而那些舊的需要被刪除的segment文件則進行關閉並物理刪除。

此外,ES在執行search搜索時,目標數據可能位於不同的index segment上,因此ES會掃描所有已經開打的index segment文件並找到目標數據。文件打開指的是將數據讀到了OS Cache系統緩存。

在buffer數據寫入到segment的同時,會生成一個.del文件專門記錄哪一個index segment中哪一條document是deleted狀態(在merge後,這個.del文件會被更新)。因此ES搜索時,如果在多個index segment中查到了不同版本(version)的相同id值的document時,會根據.del文件中的記錄來繼續過濾,保證搜索結果的唯一性和正確性(比如segment1中包含一條document version=1,對應新增狀態;而segment2中包含相同id的document version=2,對應更新狀態。由於後者的版本號更新,因此在.del中,version1被視作舊document,會被標記成deleted狀態,從而在搜索時就會得到segment2中包含的version=2的數據了)。

三. 查詢原理

  1. 假設客戶端請求查詢_id=10的數據。
  2. 請求發送到ES集羣中的任意節點,此時該節點成爲本次請求的協調節點。協調節點默認根據數據的_id作爲routing(可以手動指定,只需要在查詢時增加_routing參數即可)進行hash算法,Hash(routing) % number_of_shards,假設計算出目標數據存放的shard的下標是3。接着,協調節點請求master節點,獲取下標爲3的shard所在節點的訪問路徑、端口等信息,並將查詢請求轉發至目標節點中。(注意:顯然下標爲3的shard不一定只有一個,有可能存在一個primary shard和多個replica shard的場景,至於到底把請求發送到哪一個shard上,取決於隨機輪尋算法round-robin)
  3. 目標節點在目標分片內根據_id輕鬆的查詢到數據,並將數據回傳給協調節點。
  4. 協調節點將數據返回給客戶端。

四. 搜索原理

  1. 假設客戶端請求查詢某一個field的值爲"hello java"。
  2. 請求發送到ES集羣中的任意節點,此時該節點成爲本次請求的協調節點。此時,協調節點不知道目標數據到底存放在哪個節點的那個分片上,因此協調節點會把請求轉發到ES集羣當中的每一個節點中。
  3. 其它節點接收到請求後,會在自己節點的分片中(Q: 此處不確定是否是當前節點的所有分片)搜索目標結果的_id,並返回給協調節點。(當然了,協調節點自身也會搜索自己擁有的分片)
  4. 協調節點收集、整理各個節點返回的目標數據的_id,再通過查詢原理那套流程從目標shard中獲取document,最後將document整合並返回給客戶端。

五. 注意

buffer和尚未寫入系統緩存的index segment(就是一段倒排索引)存儲在堆內存,受jvm參數控制。

系統緩存 OScache在這裏可以被看做是"文件系統緩存",用於緩存打開後的segment file(段文件),存儲在非堆內存,受操作系統控制。

非堆內存越大,能夠打開並緩存的segment file(段文件)就越多,搜索和聚合時,能夠直接從內存中獲取的熱數據也就越多(不需要通過IO,在磁盤中找到尚未打開的segment file,讀取文件內容)。

搜索數據時,首先在OS Cache中進行搜索,如果找不到數據,則在磁盤中找到對應的index segment文件並打開,讀取數據至堆內存中,接着,在堆內存中對數據進行聚合、排序等操作,最後把數據返回給協調節點,最終交給調用方。此外,新讀取到堆內存的segment file會被Lucense緩存至非堆內存中。

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