剖析Elasticsearch集群系列涵蓋了當(dāng)今最流行的分布式搜索引擎Elasticsearch的底層架構(gòu)和原型實(shí)例。

本文是這個(gè)系列的第一篇,在本文中,我們將討論的Elasticsearch的底層存儲(chǔ)模型及CRUD(創(chuàng)建、讀取、更新和刪除)操作的工作原理。

Elasticsearch是當(dāng)今最流行的分布式搜索引擎,GitHub、 SalesforceIQ、Netflix等公司將其用于全文檢索和分析應(yīng)用。在Insight,我們用到了Elasticsearch的諸多不同功能,比如:

  • 全文檢索

    • 比如找到與搜索詞項(xiàng)(term)最相關(guān)的維基百科文章。

  • 聚合

    • 比如在廣告網(wǎng)絡(luò)中,可視化的搜索詞項(xiàng)的競價(jià)直方圖。

  • 地理空間API

    • 比如在順風(fēng)車平臺(tái),匹配最近的司機(jī)和乘客。

正是因?yàn)镋lasticsearch如此流行并且就在我們身邊,我決定深入研究一下。本文,我將分享Elasticsearch的存儲(chǔ)模型和CRUD操作的工作原理。

 

當(dāng)我在思考分布式系統(tǒng)是如何工作時(shí),我腦海里的圖案是這樣的:

水面以上的是API,以下的才是真正的引擎,一切魔幻般的事件都發(fā)生在水下。本文所關(guān)注的就是水下的部分,我們將關(guān)注:

  • Elasticsearch是主從架構(gòu)還是無主架構(gòu)

  • Elasticsearch的存儲(chǔ)模型是什么樣的

  • Elasticsearch是怎么執(zhí)行寫操作的

  • Elasticsearch是怎么執(zhí)行讀操作的

  • 如何定義搜索結(jié)果的相關(guān)性

在我們深入這些概念之前,讓我們熟悉下相關(guān)的術(shù)語。

1 辨析Elasticsearch的索引與Lucene的索引

Elasticsearch中的索引是組織數(shù)據(jù)的邏輯空間(就好比數(shù)據(jù)庫)。1個(gè)Elasticsearch的索引有1個(gè)或者多個(gè)分片(默認(rèn)是5個(gè))。分片對(duì)應(yīng)實(shí)際存儲(chǔ)數(shù)據(jù)的Lucene的索引,分片自身就是一個(gè)搜索引擎。每個(gè)分片有0或者多個(gè)副本(默認(rèn)是1個(gè))。Elasticsearch的索引還包含"type"(就像數(shù)據(jù)庫中的表),用于邏輯上隔離索引中的數(shù)據(jù)。在Elasticsearch的索引中,給定一個(gè)type,它的所有文檔會(huì)擁有相同的屬性(就像表的schema)。

(點(diǎn)擊放大圖像)

圖a展示了一個(gè)包含3個(gè)分片的Elasticsearch索引,每個(gè)分片擁有1個(gè)副本。這些分片組成了一個(gè)Elasticsearch索引,每個(gè)分片自身是一個(gè)Lucene索引。圖b展示了Elasticsearch索引、分片、Lucene索引和文檔之間的邏輯關(guān)系。

對(duì)應(yīng)于關(guān)系數(shù)據(jù)庫術(shù)語

Elasticsearch Index == Database 
Types == Tables 
Properties == Schema

現(xiàn)在我們熟悉了Elasticsearch世界的術(shù)語,接下來讓我們看一下節(jié)點(diǎn)有哪些不同的角色。

2 節(jié)點(diǎn)類型

一個(gè)Elasticsearch實(shí)例是一個(gè)節(jié)點(diǎn),一組節(jié)點(diǎn)組成了集群。Elasticsearch集群中的節(jié)點(diǎn)可以配置為3種不同的角色:

  • 主節(jié)點(diǎn):控制Elasticsearch集群,負(fù)責(zé)集群中的操作,比如創(chuàng)建/刪除一個(gè)索引,跟蹤集群中的節(jié)點(diǎn),分配分片到節(jié)點(diǎn)。主節(jié)點(diǎn)處理集群的狀態(tài)并廣播到其他節(jié)點(diǎn),并接收其他節(jié)點(diǎn)的確認(rèn)響應(yīng)。

    每個(gè)節(jié)點(diǎn)都可以通過設(shè)定配置文件elasticsearch.yml中的node.master屬性為true(默認(rèn))成為主節(jié)點(diǎn)。

    對(duì)于大型的生產(chǎn)集群來說,推薦使用一個(gè)專門的主節(jié)點(diǎn)來控制集群,該節(jié)點(diǎn)將不處理任何用戶請(qǐng)求。

  • 數(shù)據(jù)節(jié)點(diǎn):持有數(shù)據(jù)和倒排索引。默認(rèn)情況下,每個(gè)節(jié)點(diǎn)都可以通過設(shè)定配置文件elasticsearch.yml中的node.data屬性為true(默認(rèn))成為數(shù)據(jù)節(jié)點(diǎn)。如果我們要使用一個(gè)專門的主節(jié)點(diǎn),應(yīng)將其node.data屬性設(shè)置為false。

  • 客戶端節(jié)點(diǎn):如果我們將node.master屬性和node.data屬性都設(shè)置為false,那么該節(jié)點(diǎn)就是一個(gè)客戶端節(jié)點(diǎn),扮演一個(gè)負(fù)載均衡的角色,將到來的請(qǐng)求路由到集群中的各個(gè)節(jié)點(diǎn)。

Elasticsearch集群中作為客戶端接入的節(jié)點(diǎn)叫協(xié)調(diào)節(jié)點(diǎn)。協(xié)調(diào)節(jié)點(diǎn)會(huì)將客戶端請(qǐng)求路由到集群中合適的分片上。對(duì)于讀請(qǐng)求來說,協(xié)調(diào)節(jié)點(diǎn)每次會(huì)選擇不同的分片處理請(qǐng)求,以實(shí)現(xiàn)負(fù)載均衡。

在我們開始研究發(fā)送給協(xié)調(diào)節(jié)點(diǎn)的CRUD請(qǐng)求是如何在集群中傳播并被引擎執(zhí)行之前,讓我們先來看一下Elasticsearch內(nèi)部是如何存儲(chǔ)數(shù)據(jù),以支持全文檢索結(jié)果的低延遲服務(wù)的。

存儲(chǔ)模型

Elasticsearch使用了Apache Lucene,后者是Doug Cutting(Apache Hadoop之父)使用Java開發(fā)的全文檢索工具庫,其內(nèi)部使用的是被稱為倒排索引的數(shù)據(jù)結(jié)構(gòu),其設(shè)計(jì)是為全文檢索結(jié)果的低延遲提供服務(wù)。文檔是Elasticsearch的數(shù)據(jù)單位,對(duì)文檔中的詞項(xiàng)進(jìn)行分詞,并創(chuàng)建去重詞項(xiàng)的有序列表,將詞項(xiàng)與其在文檔中出現(xiàn)的位置列表關(guān)聯(lián),便形成了倒排索引。

這和一本書后面的索引非常類似,即書中包含的詞匯與其出現(xiàn)的頁碼列表關(guān)聯(lián)。當(dāng)我們說文檔被索引了,我們指的是倒排索引。我們來看下如下2個(gè)文檔是如何被倒排索引的:

文檔1(Doc 1): Insight Data Engineering Fellows Program
文檔2(Doc 2): Insight Data Science Fellows Program

如果我們想找包含詞項(xiàng)"insight"的文檔,我們可以掃描這個(gè)(單詞有序的)倒排索引,找到"insight"并返回包含改詞的文檔ID,示例中是Doc 1和Doc 2。

為了提高可檢索性(比如希望大小寫單詞都返回),我們應(yīng)當(dāng)先分析文檔再對(duì)其索引。分析包括2個(gè)部分:

  • 將句子詞條化為獨(dú)立的單詞

  • 將單詞規(guī)范化為標(biāo)準(zhǔn)形式

默認(rèn)情況下,Elasticsearch使用標(biāo)準(zhǔn)分析器,它使用了:

  • 標(biāo)準(zhǔn)分詞器以單詞為界來切詞

  • 小寫詞條(token)過濾器來轉(zhuǎn)換單詞

還有很多可用的分析器在此不列舉,請(qǐng)參考相關(guān)文檔。

為了實(shí)現(xiàn)查詢時(shí)能得到對(duì)應(yīng)的結(jié)果,查詢時(shí)應(yīng)使用與索引時(shí)一致的分析器,對(duì)文檔進(jìn)行分析。

注意:標(biāo)準(zhǔn)分析器包含了停用詞過濾器,但默認(rèn)情況下沒有啟用。

現(xiàn)在,倒排索引的概念已經(jīng)清楚,讓我們開始CRUD操作的研究吧。我們從寫操作開始。

剖析寫操作

創(chuàng)建((C)reate)

當(dāng)我們發(fā)送索引一個(gè)新文檔的請(qǐng)求到協(xié)調(diào)節(jié)點(diǎn)后,將發(fā)生如下一組操作:

  • Elasticsearch集群中的每個(gè)節(jié)點(diǎn)都包含了改節(jié)點(diǎn)上分片的元數(shù)據(jù)信息。協(xié)調(diào)節(jié)點(diǎn)(默認(rèn))使用文檔ID參與計(jì)算,以便為路由提供合適的分片。Elasticsearch使用MurMurHash3函數(shù)對(duì)文檔ID進(jìn)行哈希,其結(jié)果再對(duì)分片數(shù)量取模,得到的結(jié)果即是索引文檔的分片。

    shard = hash(document_id) % (num_of_primary_shards)
  • 當(dāng)分片所在的節(jié)點(diǎn)接收到來自協(xié)調(diào)節(jié)點(diǎn)的請(qǐng)求后,會(huì)將該請(qǐng)求寫入translog(我們將在本系列接下來的文章中講到),并將文檔加入內(nèi)存緩沖。如果請(qǐng)求在主分片上成功處理,該請(qǐng)求會(huì)并行發(fā)送到該分片的副本上。當(dāng)translog被同步(fsync)到全部的主分片及其副本上后,客戶端才會(huì)收到確認(rèn)通知。

  • 內(nèi)存緩沖會(huì)被周期性刷新(默認(rèn)是1秒),內(nèi)容將被寫到文件系統(tǒng)緩存的一個(gè)新段上。雖然這個(gè)段并沒有被同步(fsync),但它是開放的,內(nèi)容可以被搜索到。

  • 每30分鐘,或者當(dāng)translog很大的時(shí)候,translog會(huì)被清空,文件系統(tǒng)緩存會(huì)被同步。這個(gè)過程在Elasticsearch中稱為沖洗(flush)。在沖洗過程中,內(nèi)存中的緩沖將被清除,內(nèi)容被寫入一個(gè)新段。段的fsync將創(chuàng)建一個(gè)新的提交點(diǎn),并將內(nèi)容刷新到磁盤。舊的translog將被刪除并開始一個(gè)新的translog。

下圖展示了寫請(qǐng)求及其數(shù)據(jù)流。

(點(diǎn)擊放大圖像)

更新((U)pdate)和刪除((D)elete)

刪除和更新也都是寫操作。但是Elasticsearch中的文檔是不可變的,因此不能被刪除或者改動(dòng)以展示其變更。那么,該如何刪除和更新文檔呢?

磁盤上的每個(gè)段都有一個(gè)相應(yīng)的.del文件。當(dāng)刪除請(qǐng)求發(fā)送后,文檔并沒有真的被刪除,而是在.del文件中被標(biāo)記為刪除。該文檔依然能匹配查詢,但是會(huì)在結(jié)果中被過濾掉。當(dāng)段合并(我們將在本系列接下來的文章中講到)時(shí),在.del文件中被標(biāo)記為刪除的文檔將不會(huì)被寫入新段。

接下來我們看更新是如何工作的。在新的文檔被創(chuàng)建時(shí),Elasticsearch會(huì)為該文檔指定一個(gè)版本號(hào)。當(dāng)執(zhí)行更新時(shí),舊版本的文檔在.del文件中被標(biāo)記為刪除,新版本的文檔被索引到一個(gè)新段。舊版本的文檔依然能匹配查詢,但是會(huì)在結(jié)果中被過濾掉。

文檔被索引或者更新后,我們就可以執(zhí)行查詢操作了。讓我們看看在Elasticsearch中是如何處理查詢請(qǐng)求的。

剖析讀操作((R)ead)

讀操作包含2部分內(nèi)容:

  • 查詢階段

  • 提取階段

我們來看下每個(gè)階段是如何工作的。

查詢階段

在這個(gè)階段,協(xié)調(diào)節(jié)點(diǎn)會(huì)將查詢請(qǐng)求路由到索引的全部分片(主分片或者其副本)上。每個(gè)分片獨(dú)立執(zhí)行查詢,并為查詢結(jié)果創(chuàng)建一個(gè)優(yōu)先隊(duì)列,以相關(guān)性得分排序(我們將在本系列的后續(xù)文章中講到)。全部分片都將匹配文檔的ID及其相關(guān)性得分返回給協(xié)調(diào)節(jié)點(diǎn)。協(xié)調(diào)節(jié)點(diǎn)創(chuàng)建一個(gè)優(yōu)先隊(duì)列并對(duì)結(jié)果進(jìn)行全局排序。會(huì)有很多文檔匹配結(jié)果,但是,默認(rèn)情況下,每個(gè)分片只發(fā)送前10個(gè)結(jié)果給協(xié)調(diào)節(jié)點(diǎn),協(xié)調(diào)節(jié)點(diǎn)為全部分片上的這些結(jié)果創(chuàng)建優(yōu)先隊(duì)列并返回前10個(gè)作為hit。

提取階段

當(dāng)協(xié)調(diào)節(jié)點(diǎn)在生成的全局有序的文檔列表中,為全部結(jié)果排好序后,它將向包含原始文檔的分片發(fā)起請(qǐng)求。全部分片填充文檔信息并將其返回給協(xié)調(diào)節(jié)點(diǎn)。

下圖展示了讀請(qǐng)求及其數(shù)據(jù)流。

(點(diǎn)擊放大圖像)

如上所述,查詢結(jié)果是按相關(guān)性排序的。接下來,讓我們看看相關(guān)性是如何定義的。

搜索相關(guān)性

相關(guān)性是由搜索結(jié)果中Elasticsearch打給每個(gè)文檔的得分決定的。默認(rèn)使用的排序算法是tf/idf(詞頻/逆文檔頻率)。詞頻衡量了一個(gè)詞項(xiàng)在文檔中出現(xiàn)的次數(shù) (頻率越高 == 相關(guān)性越高),逆文檔頻率衡量了詞項(xiàng)在全部索引中出現(xiàn)的頻率,是一個(gè)索引中文檔總數(shù)的百分比(頻率越高 == 相關(guān)性越低)。最后的得分是tf-idf得分與其他因子比如(短語查詢中的)詞項(xiàng)接近度、(模糊查詢中的)詞項(xiàng)相似度等的組合。

接下來有什么?

這些CRUD操作由Elasticsearch內(nèi)部的一些數(shù)據(jù)結(jié)構(gòu)所支持,這對(duì)于理解Elasticsearch的工作機(jī)制非常重要。在接下來的系列文章中,我將帶大家走進(jìn)類似的那些概念并告訴大家在使用Elasticsearch中有哪些坑。

  • Elasticsearch中的腦裂問題及防治措施

  • 事務(wù)日志

  • Lucene的段

  • 為什么搜索時(shí)使用深層分頁很危險(xiǎn)

  • 計(jì)算搜索相關(guān)性中困難及權(quán)衡

  • 并發(fā)控制

  • 為什么Elasticsearch是準(zhǔn)實(shí)時(shí)的

  • 如何確保讀和寫的一致性

http://www.cnblogs.com/zhangxiaoliu/p/7163761.html