from:http://www.infoq.com/cn/articles/analysis-of-elasticsearch-cluster-part01
剖析Elasticsearch集群系列涵蓋了當今最流行的分布式搜索引擎Elasticsearch的底層架構和原型實例。
本文是這個系列的第一篇,在本文中,我們將討論的Elasticsearch的底層存儲模型及CRUD(創建、讀取、更新和刪除)操作的工作原理。
本系列已經得到原文著者Ronak Nathani的授權
Elasticsearch是當今最流行的分布式搜索引擎,GitHub、 SalesforceIQ、Netflix等公司將其用于全文檢索和分析應用。在Insight,我們用到了Elasticsearch的諸多不同功能,比如:
- 全文檢索
- 比如找到與搜索詞項(term)最相關的維基百科文章。
- 聚合
- 地理空間API
正是因為Elasticsearch如此流行并且就在我們身邊,我決定深入研究一下。本文,我將分享Elasticsearch的存儲模型和CRUD操作的工作原理。
當我在思考分布式系統是如何工作時,我腦海里的圖案是這樣的:

水面以上的是API,以下的才是真正的引擎,一切魔幻般的事件都發生在水下。本文所關注的就是水下的部分,我們將關注:
- Elasticsearch是主從架構還是無主架構
- Elasticsearch的存儲模型是什么樣的
- Elasticsearch是怎么執行寫操作的
- Elasticsearch是怎么執行讀操作的
- 如何定義搜索結果的相關性
在我們深入這些概念之前,讓我們熟悉下相關的術語。
1 辨析Elasticsearch的索引與Lucene的索引
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世界的術語,接下來讓我們看一下節點有哪些不同的角色。
2 節點類型
一個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使用標準分析器,它使用了:
- 標準分詞器以單詞為界來切詞
- 小寫詞條(token)過濾器來轉換單詞
還有很多可用的分析器在此不列舉,請參考相關文檔。
為了實現查詢時能得到對應的結果,查詢時應使用與索引時一致的分析器,對文檔進行分析。
注意:標準分析器包含了停用詞過濾器,但默認情況下沒有啟用。
現在,倒排索引的概念已經清楚,讓我們開始CRUD操作的研究吧。我們從寫操作開始。
剖析寫操作
創建((C)reate)
當我們發送索引一個新文檔的請求到協調節點后,將發生如下一組操作:
下圖展示了寫請求及其數據流。
(點擊放大圖像)

更新((U)pdate)和刪除((D)elete)
刪除和更新也都是寫操作。但是Elasticsearch中的文檔是不可變的,因此不能被刪除或者改動以展示其變更。那么,該如何刪除和更新文檔呢?
磁盤上的每個段都有一個相應的.del文件。當刪除請求發送后,文檔并沒有真的被刪除,而是在.del文件中被標記為刪除。該文檔依然能匹配查詢,但是會在結果中被過濾掉。當段合并(我們將在本系列接下來的文章中講到)時,在.del文件中被標記為刪除的文檔將不會被寫入新段。
接下來我們看更新是如何工作的。在新的文檔被創建時,Elasticsearch會為該文檔指定一個版本號。當執行更新時,舊版本的文檔在.del文件中被標記為刪除,新版本的文檔被索引到一個新段。舊版本的文檔依然能匹配查詢,但是會在結果中被過濾掉。
文檔被索引或者更新后,我們就可以執行查詢操作了。讓我們看看在Elasticsearch中是如何處理查詢請求的。
剖析讀操作((R)ead)
讀操作包含2部分內容:
我們來看下每個階段是如何工作的。
查詢階段
在這個階段,協調節點會將查詢請求路由到索引的全部分片(主分片或者其副本)上。每個分片獨立執行查詢,并為查詢結果創建一個優先隊列,以相關性得分排序(我們將在本系列的后續文章中講到)。全部分片都將匹配文檔的ID及其相關性得分返回給協調節點。協調節點創建一個優先隊列并對結果進行全局排序。會有很多文檔匹配結果,但是,默認情況下,每個分片只發送前10個結果給協調節點,協調節點為全部分片上的這些結果創建優先隊列并返回前10個作為hit。
提取階段
當協調節點在生成的全局有序的文檔列表中,為全部結果排好序后,它將向包含原始文檔的分片發起請求。全部分片填充文檔信息并將其返回給協調節點。
下圖展示了讀請求及其數據流。
(點擊放大圖像)

如上所述,查詢結果是按相關性排序的。接下來,讓我們看看相關性是如何定義的。
搜索相關性
相關性是由搜索結果中Elasticsearch打給每個文檔的得分決定的。默認使用的排序算法是tf/idf(詞頻/逆文檔頻率)。詞頻衡量了一個詞項在文檔中出現的次數 (頻率越高 == 相關性越高),逆文檔頻率衡量了詞項在全部索引中出現的頻率,是一個索引中文檔總數的百分比(頻率越高 == 相關性越低)。最后的得分是tf-idf得分與其他因子比如(短語查詢中的)詞項接近度、(模糊查詢中的)詞項相似度等的組合。
接下來有什么?
這些CRUD操作由Elasticsearch內部的一些數據結構所支持,這對于理解Elasticsearch的工作機制非常重要。在接下來的系列文章中,我將帶大家走進類似的那些概念并告訴大家在使用Elasticsearch中有哪些坑。
- Elasticsearch中的腦裂問題及防治措施
- 事務日志
- Lucene的段
- 為什么搜索時使用深層分頁很危險
- 計算搜索相關性中困難及權衡
- 并發控制
- 為什么Elasticsearch是準實時的
- 如何確保讀和寫的一致性
查看原文地址:http://insightdataengineering.com/blog/elasticsearch-crud