剖析Elasticsearch集羣系列涵蓋了當今最流行的分佈式搜索引擎Elasticsearch的底層架構和原型實例。html
本文是這個系列的第一篇,在本文中,咱們將討論的Elasticsearch的底層存儲模型及CRUD(建立、讀取、更新和刪除)操做的工做原理。node
Elasticsearch是當今最流行的分佈式搜索引擎,GitHub、 SalesforceIQ、Netflix等公司將其用於全文檢索和分析應用。在Insight,咱們用到了Elasticsearch的諸多不一樣功能,好比:linux
正是由於Elasticsearch如此流行而且就在咱們身邊,我決定深刻研究一下。本文,我將分享Elasticsearch的存儲模型和CRUD操做的工做原理。算法
當我在思考分佈式系統是如何工做時,我腦海裏的圖案是這樣的:數據庫
水面以上的是API,如下的纔是真正的引擎,一切魔幻般的事件都發生在水下。本文所關注的就是水下的部分,咱們將關注:apache
在咱們深刻這些概念以前,讓咱們熟悉下相關的術語。緩存
Elasticsearch中的索引是組織數據的邏輯空間(就比如數據庫)。1個Elasticsearch的索引有1個或者多個分片(默認是5個)。分片對應實際存儲數據的Lucene的索引,分片自身就是一個搜索引擎。每一個分片有0或者多個副本(默認是1個)。Elasticsearch的索引還包含"type"(就像數據庫中的表),用於邏輯上隔離索引中的數據。在Elasticsearch的索引中,給定一個type,它的全部文檔會擁有相同的屬性(就像表的schema)。網絡
(點擊放大圖像)數據結構
圖a展現了一個包含3個分片的Elasticsearch索引,每一個分片擁有1個副本。這些分片組成了一個Elasticsearch索引,每一個分片自身是一個Lucene索引。圖b展現了Elasticsearch索引、分片、Lucene索引和文檔之間的邏輯關係。架構
對應於關係數據庫術語
Elasticsearch Index == Database Types == Tables Properties == Schema
如今咱們熟悉了Elasticsearch世界的術語,接下來讓咱們看一下節點有哪些不一樣的角色。
一個Elasticsearch實例是一個節點,一組節點組成了集羣。Elasticsearch集羣中的節點能夠配置爲3種不一樣的角色:
主節點:控制Elasticsearch集羣,負責集羣中的操做,好比建立/刪除一個索引,跟蹤集羣中的節點,分配分片到節點。主節點處理集羣的狀態並廣播到其餘節點,並接收其餘節點的確認響應。
每一個節點均可以經過設定配置文件elasticsearch.yml中的node.master屬性爲true(默認)成爲主節點。
對於大型的生產集羣來講,推薦使用一個專門的主節點來控制集羣,該節點將不處理任何用戶請求。
數據節點:持有數據和倒排索引。默認狀況下,每一個節點均可以經過設定配置文件elasticsearch.yml中的node.data屬性爲true(默認)成爲數據節點。若是咱們要使用一個專門的主節點,應將其node.data屬性設置爲false。
客戶端節點:若是咱們將node.master屬性和node.data屬性都設置爲false,那麼該節點就是一個客戶端節點,扮演一個負載均衡的角色,將到來的請求路由到集羣中的各個節點。
Elasticsearch集羣中做爲客戶端接入的節點叫協調節點。協調節點會將客戶端請求路由到集羣中合適的分片上。對於讀請求來講,協調節點每次會選擇不一樣的分片處理請求,以實現負載均衡。
在咱們開始研究發送給協調節點的CRUD請求是如何在集羣中傳播並被引擎執行以前,讓咱們先來看一下Elasticsearch內部是如何存儲數據,以支持全文檢索結果的低延遲服務的。
Elasticsearch使用了Apache Lucene,後者是Doug Cutting(Apache Hadoop之父)使用Java開發的全文檢索工具庫,其內部使用的是被稱爲倒排索引的數據結構,其設計是爲全文檢索結果的低延遲提供服務。文檔是Elasticsearch的數據單位,對文檔中的詞項進行分詞,並建立去重詞項的有序列表,將詞項與其在文檔中出現的位置列表關聯,便造成了倒排索引。
這和一本書後面的索引很是相似,即書中包含的詞彙與其出現的頁碼列表關聯。當咱們說文檔被索引了,咱們指的是倒排索引。咱們來看下以下2個文檔是如何被倒排索引的:
文檔1(Doc 1): Insight Data Engineering Fellows Program
文檔2(Doc 2): Insight Data Science Fellows Program
若是咱們想找包含詞項"insight"的文檔,咱們能夠掃描這個(單詞有序的)倒排索引,找到"insight"並返回包含改詞的文檔ID,示例中是Doc 1和Doc 2。
爲了提升可檢索性(好比但願大小寫單詞都返回),咱們應當先分析文檔再對其索引。分析包括2個部分:
默認狀況下,Elasticsearch使用標準分析器,它使用了:
還有不少可用的分析器在此不列舉,請參考相關文檔。
爲了實現查詢時能獲得對應的結果,查詢時應使用與索引時一致的分析器,對文檔進行分析。
注意:標準分析器包含了停用詞過濾器,但默認狀況下沒有啓用。
如今,倒排索引的概念已經清楚,讓咱們開始CRUD操做的研究吧。咱們從寫操做開始。
當咱們發送索引一個新文檔的請求到協調節點後,將發生以下一組操做:
Elasticsearch集羣中的每一個節點都包含了改節點上分片的元數據信息。協調節點(默認)使用文檔ID參與計算,以便爲路由提供合適的分片。Elasticsearch使用MurMurHash3函數對文檔ID進行哈希,其結果再對分片數量取模,獲得的結果便是索引文檔的分片。
shard = hash(document_id) % (num_of_primary_shards)
下圖展現了寫請求及其數據流。
(點擊放大圖像)
刪除和更新也都是寫操做。可是Elasticsearch中的文檔是不可變的,所以不能被刪除或者改動以展現其變動。那麼,該如何刪除和更新文檔呢?
磁盤上的每一個段都有一個相應的.del文件。當刪除請求發送後,文檔並無真的被刪除,而是在.del文件中被標記爲刪除。該文檔依然能匹配查詢,可是會在結果中被過濾掉。當段合併(咱們將在本系列接下來的文章中講到)時,在.del文件中被標記爲刪除的文檔將不會被寫入新段。
接下來咱們看更新是如何工做的。在新的文檔被建立時,Elasticsearch會爲該文檔指定一個版本號。當執行更新時,舊版本的文檔在.del文件中被標記爲刪除,新版本的文檔被索引到一個新段。舊版本的文檔依然能匹配查詢,可是會在結果中被過濾掉。
文檔被索引或者更新後,咱們就能夠執行查詢操做了。讓咱們看看在Elasticsearch中是如何處理查詢請求的。
讀操做包含2部份內容:
咱們來看下每一個階段是如何工做的。
在這個階段,協調節點會將查詢請求路由到索引的所有分片(主分片或者其副本)上。每一個分片獨立執行查詢,併爲查詢結果建立一個優先隊列,以相關性得分排序(咱們將在本系列的後續文章中講到)。所有分片都將匹配文檔的ID及其相關性得分返回給協調節點。協調節點建立一個優先隊列並對結果進行全局排序。會有不少文檔匹配結果,可是,默認狀況下,每一個分片只發送前10個結果給協調節點,協調節點爲所有分片上的這些結果建立優先隊列並返回前10個做爲hit。
當協調節點在生成的全局有序的文檔列表中,爲所有結果排好序後,它將向包含原始文檔的分片發起請求。所有分片填充文檔信息並將其返回給協調節點。
下圖展現了讀請求及其數據流。
(點擊放大圖像)
如上所述,查詢結果是按相關性排序的。接下來,讓咱們看看相關性是如何定義的。
相關性是由搜索結果中Elasticsearch打給每一個文檔的得分決定的。默認使用的排序算法是tf/idf(詞頻/逆文檔頻率)。詞頻衡量了一個詞項在文檔中出現的次數 (頻率越高 == 相關性越高),逆文檔頻率衡量了詞項在所有索引中出現的頻率,是一個索引中文檔總數的百分比(頻率越高 == 相關性越低)。最後的得分是tf-idf得分與其餘因子好比(短語查詢中的)詞項接近度、(模糊查詢中的)詞項類似度等的組合。
這些CRUD操做由Elasticsearch內部的一些數據結構所支持,這對於理解Elasticsearch的工做機制很是重要。在接下來的系列文章中,我將帶你們走進相似的那些概念並告訴你們在使用Elasticsearch中有哪些坑。