ElasticSearch底層原理淺析


基本概念

索引(Index)

ES將數據存儲於一個或多個索引中,索引是具有類似特性的文檔的集合。類比傳統的關系型數據庫領域來說,索引相當於SQL中的一個數據庫,或者一個數據存儲方案(schema)。索引由其名稱(必須為全小寫字符)進行標識,並通過引用此名稱完成文檔的創建、搜索、更新及刪除操作。一個ES集群中可以按需創建任意數目的索引。

類型(Type)

類型是索引內部的邏輯分區(category/partition),然而其意義完全取決於用戶需求。因此,一個索引內部可定義一個或多個類型(type)。一般來說,類型就是為那些擁有相同的域的文檔做的預定義。例如,在索引中,可以定義一個用於存儲用戶數據的類型,一個存儲日志數據的類型,以及一個存儲評論數據的類型。類比傳統的關系型數據庫領域來說,類型相當於“表”。

文檔(Document)

文檔是索引和搜索的原子單位,它是包含了一個或多個域(Field)的容器,基於JSON格式進行表示。文檔由一個或多個域組成,每個域擁有一個名字及一個或多個值,有多個值的域通常稱為“多值域”。每個文檔可以存儲不同的域集,但同一類型下的文檔至應該有某種程度上的相似之處。

倒排索引(Inverted Index)

每一個文檔都對應一個ID。倒排索引會按照指定語法對每一個文檔進行分詞,然后維護一張表,列舉所有文檔中出現的terms以及它們出現的文檔ID和出現頻率。搜索時同樣會對關鍵詞進行同樣的分詞分析,然后查表得到結果。

                                               圖1、倒排索引示例

這里所述倒排索引是針對非結構化的文檔構造的,而在ES中存儲的文檔是基於JSON格式的,因此索引結構會更為復雜。簡單來說,ES對於JSON文檔中的每一個field都會構建一個對應的倒排索引。參考官方文檔:ES權威指南

節點(Node)

一個運行中的 Elasticsearch 實例稱為一個節點,而集群是由一個或者多個擁有相同cluster.name配置的節點組成, 它們共同承擔數據和負載的壓力。

ES集群中的節點有三種不同的類型:

  • 主節點:負責管理集群范圍內的所有變更,例如增加、刪除索引,或者增加、刪除節點等。 主節點並不需要涉及到文檔級別的變更和搜索等操作。可以通過屬性node.master進行設置。
  • 數據節點:存儲數據和其對應的倒排索引。默認每一個節點都是數據節點(包括主節點),可以通過node.data屬性進行設置。
  • 協調節點:如果node.master和node.data屬性均為false,則此節點稱為協調節點,用來響應客戶請求,均衡每個節點的負載。

分片(Shard)

一個索引中的數據保存在多個分片中,相當於水平分表。一個分片便是一個Lucene 的實例,它本身就是一個完整的搜索引擎。我們的文檔被存儲和索引到分片內,但是應用程序是直接與索引而不是與分片進行交互。

ES實際上就是利用分片來實現分布式。分片是數據的容器,文檔保存在分片內,分片又被分配到集群內的各個節點里。 當你的集群規模擴大或者縮小時, ES會自動的在各節點中遷移分片,使得數據仍然均勻分布在集群里。

一個分片可以是主分片或者副本分片。 索引內任意一個文檔都歸屬於一個主分片,所以主分片的數目決定着索引能夠保存的最大數據量。一個副本分片只是一個主分片的拷貝。 副本分片作為硬件故障時保護數據不丟失的冗余備份,並為搜索和返回文檔等讀操作提供服務。

在索引建立的時候就已經確定了主分片數,但是副本分片數可以隨時修改。默認情況下,一個索引會有5個主分片,而其副本可以有任意數量。

主分片和副本分片的狀態決定了集群的健康狀態。每一個節點上都只會保存主分片或者其對應的一個副本分片,相同的副本分片不會存在於同一個節點中。如果集群中只有一個節點,則副本分片將不會被分配,此時集群健康狀態為yellow,存在丟失數據的風險。

擁有三個節點的集群

 圖2、 3個節點,3個主分片,1份副本

擁有2份副本分片3個節點的集群

圖3、增加一份副本

關閉了一個節點后的集群

 圖4、其中一個節點出現故障

實際上,每一個分片還會進一步拆分為分段(Segment)。這是ES寫入文檔所采用的機制造成的結果。

寫操作(Write):針對文檔的CRUD操作

索引新文檔(Create)

當用戶向一個節點提交了一個索引新文檔的請求,節點會計算新文檔應該加入到哪個分片(shard)中。每個節點都存儲有每個分片存儲在哪個節點的信息,因此協調節點會將請求發送給對應的節點。注意這個請求會發送給主分片,等主分片完成索引,會並行將請求發送到其所有副本分片,保證每個分片都持有最新數據。

每次寫入新文檔時,都會先寫入內存中,並將這一操作寫入一個translog文件(transaction log)中,此時如果執行搜索操作,這個新文檔還不能被索引到。

New documents are added to the in-memory buffer and appended to the transaction log

圖5、新文檔被寫入內存,操作被寫入translog

ES會每隔1秒時間(這個時間可以修改)進行一次刷新操作(refresh),此時在這1秒時間內寫入內存的新文檔都會被寫入一個文件系統緩存(filesystem cache)中,並構成一個分段(segment)。此時這個segment里的文檔可以被搜索到,但是尚未寫入硬盤,即如果此時發生斷電,則這些文檔可能會丟失。

After a refresh, the buffer is cleared but the transaction log is not

圖6、在執行刷新后清空內存,新文檔寫入文件系統緩存

不斷有新的文檔寫入,則這一過程將不斷重復執行。每隔一秒將生成一個新的segment,而translog文件將越來越大。

The transaction log keeps accumulating documents

圖7、translog不斷加入新文檔記錄

每隔30分鍾或者translog文件變得很大,則執行一次fsync操作。此時所有在文件系統緩存中的segment將被寫入磁盤,而translog將被刪除(此后會生成新的translog)。

After a flush, the segments are fully commited and the transaction log is cleared

圖8、執行fsync后segment寫入磁盤,清空內存和translog

由上面的流程可以看出,在兩次fsync操作之間,存儲在內存和文件系統緩存中的文檔是不安全的,一旦出現斷電這些文檔就會丟失。所以ES引入了translog來記錄兩次fsync之間所有的操作,這樣機器從故障中恢復或者重新啟動,ES便可以根據translog進行還原。

當然,translog本身也是文件,存在於內存當中,如果發生斷電一樣會丟失。因此,ES會在每隔5秒時間或是一次寫入請求完成后將translog寫入磁盤。可以認為一個對文檔的操作一旦寫入磁盤便是安全的可以復原的,因此只有在當前操作記錄被寫入磁盤,ES才會將操作成功的結果返回發送此操作請求的客戶端。

此外,由於每一秒就會生成一個新的segment,很快將會有大量的segment。對於一個分片進行查詢請求,將會輪流查詢分片中的所有segment,這將降低搜索的效率。因此ES會自動啟動合並segment的工作,將一部分相似大小的segment合並成一個新的大segment。合並的過程實際上是創建了一個新的segment,當新segment被寫入磁盤,所有被合並的舊segment被清除。

Two commited segments and one uncommited segment in the process of being merged into a bigger segment

圖9、合並segment

圖10、合並完成后刪除舊segment,新segment可供搜索

更新(Update)和刪除(Delete)文檔

ES的索引是不能修改的,因此更新和刪除操作並不是直接在原索引上直接執行。

每一個磁盤上的segment都會維護一個del文件,用來記錄被刪除的文件。每當用戶提出一個刪除請求,文檔並沒有被真正刪除,索引也沒有發生改變,而是在del文件中標記該文檔已被刪除。因此,被刪除的文檔依然可以被檢索到,只是在返回檢索結果時被過濾掉了。每次在啟動segment合並工作時,那些被標記為刪除的文檔才會被真正刪除。

更新文檔會首先查找原文檔,得到該文檔的版本號。然后將修改后的文檔寫入內存,此過程與寫入一個新文檔相同。同時,舊版本文檔被標記為刪除,同理,該文檔可以被搜索到,只是最終被過濾掉。

 

讀操作(Read):查詢過程

查詢的過程大體上分為查詢(query)和取回(fetch)兩個階段。這個節點的任務是廣播查詢請求到所有相關分片,並將它們的響應整合成全局排序后的結果集合,這個結果集合會返回給客戶端。

 

查詢階段

 

當一個節點接收到一個搜索請求,則這個節點就變成了協調節點。

查詢過程分布式搜索

圖10、查詢過程分布式搜索

第一步是廣播請求到索引中每一個節點的分片拷貝。 查詢請求可以被某個主分片或某個副本分片處理,協調節點將在之后的請求中輪詢所有的分片拷貝來分攤負載。

每個分片將會在本地構建一個優先級隊列。如果客戶端要求返回結果排序中從第from名開始的數量為size的結果集,則每個節點都需要生成一個from+size大小的結果集,因此優先級隊列的大小也是from+size。分片僅會返回一個輕量級的結果給協調節點,包含結果集中的每一個文檔的ID和進行排序所需要的信息。

協調節點會將所有分片的結果匯總,並進行全局排序,得到最終的查詢排序結果。此時查詢階段結束。

取回階段

查詢過程得到的是一個排序結果,標記出哪些文檔是符合搜索要求的,此時仍然需要獲取這些文檔返回客戶端。

協調節點會確定實際需要返回的文檔,並向含有該文檔的分片發送get請求;分片獲取文檔返回給協調節點;協調節點將結果返回給客戶端。

分布式搜索的取回階段

圖11、分布式搜索的取回階段

相關性計算

在搜索過程中對文檔進行排序,需要對每一個文檔進行打分,判別文檔與搜索條件的相關程度。在舊版本的ES中默認采用TF/IDF(term frequency/inverse document frequency)算法對文檔進行打分。

 

數值類型對搜索效率的影響

ES5.x之前用到的Lucene版本,實際上只能夠索引文本類型的數據,表面上被定義為數值類型的字段,在暗地里都被轉換成了字符串,編排成了倒排索引


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM