自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

掌握它才說(shuō)明你真正懂Elasticsearch

原創(chuàng)
開(kāi)發(fā) 架構(gòu) 開(kāi)發(fā)工具
Elasticsearch 基于 Lucene,隱藏其復(fù)雜性,并提供簡(jiǎn)單易用的 Restful API接口、Java API 接口。所以理解 ES 的關(guān)鍵在于理解 Lucene 的基本原理。

【51CTO.com原創(chuàng)稿件】Elasticsearch 基于 Lucene,隱藏其復(fù)雜性,并提供簡(jiǎn)單易用的 Restful API接口、Java API 接口。所以理解 ES 的關(guān)鍵在于理解 Lucene 的基本原理。

Lucene 簡(jiǎn)介

Lucene 是一種高性能、可伸縮的信息搜索(IR)庫(kù),在 2000 年開(kāi)源,最初由鼎鼎大名的 Doug Cutting 開(kāi)發(fā),是基于 Java 實(shí)現(xiàn)的高性能的開(kāi)源項(xiàng)目。

Lucene 采用了基于倒排表的設(shè)計(jì)原理,可以非常高效地實(shí)現(xiàn)文本查找,在底層采用了分段的存儲(chǔ)模式,使它在讀寫(xiě)時(shí)幾乎完全避免了鎖的出現(xiàn),大大提升了讀寫(xiě)性能。

核心模塊

Lucene 的寫(xiě)流程和讀流程如下圖所示:

 

圖 1:Lucene 的寫(xiě)流程和讀流程

其中,虛線箭頭(a、b、c、d)表示寫(xiě)索引的主要過(guò)程,實(shí)線箭頭(1-9)表示查詢的主要過(guò)程。

Lucene 中的主要模塊及模塊說(shuō)明如下:

  • analysis:主要負(fù)責(zé)詞法分析及語(yǔ)言處理,也就是我們常說(shuō)的分詞,通過(guò)該模塊可最終形成存儲(chǔ)或者搜索的最小單元 Term。
  • index 模塊:主要負(fù)責(zé)索引的創(chuàng)建工作。
  • store 模塊:主要負(fù)責(zé)索引的讀寫(xiě),主要是對(duì)文件的一些操作,其主要目的是抽象出和平臺(tái)文件系統(tǒng)無(wú)關(guān)的存儲(chǔ)。
  • queryParser 模塊:主要負(fù)責(zé)語(yǔ)法分析,把我們的查詢語(yǔ)句生成 Lucene 底層可以識(shí)別的條件。
  • search 模塊:主要負(fù)責(zé)對(duì)索引的搜索工作。
  • similarity 模塊:主要負(fù)責(zé)相關(guān)性打分和排序的實(shí)現(xiàn)。

核心術(shù)語(yǔ)

下面介紹 Lucene 中的核心術(shù)語(yǔ):

  • Term:是索引里最小的存儲(chǔ)和查詢單元,對(duì)于英文來(lái)說(shuō)一般是指一個(gè)單詞,對(duì)于中文來(lái)說(shuō)一般是指一個(gè)分詞后的詞。
  • 詞典(Term Dictionary,也叫作字典):是 Term 的集合。詞典的數(shù)據(jù)結(jié)構(gòu)可以有很多種,每種都有自己的優(yōu)缺點(diǎn)。

比如:排序數(shù)組通過(guò)二分查找來(lái)檢索數(shù)據(jù):HashMap(哈希表)比排序數(shù)組的檢索速度更快,但是會(huì)浪費(fèi)存儲(chǔ)空間。

FST(finite-state transducer)有更高的數(shù)據(jù)壓縮率和查詢效率,因?yàn)樵~典是常駐內(nèi)存的,而 FST 有很好的壓縮率,所以 FST 在 Lucene 當(dāng)前版本中有非常多的使用場(chǎng)景,也是默認(rèn)的詞典數(shù)據(jù)結(jié)構(gòu)。

  • 倒排序(Posting List):一篇文章通常由多個(gè)詞組成,倒排表記錄的是某個(gè)詞在哪些文章中出現(xiàn)過(guò)。
  • 正向信息:原始的文檔信息,可以用來(lái)做排序、聚合、展示等。
  • 段(Segment):索引中最小的獨(dú)立存儲(chǔ)單元。一個(gè)索引文件由一個(gè)或者多個(gè)段組成。在 Luence 中的段有不變性,也就是說(shuō)段一旦生成,在其上只能有讀操作,不能有寫(xiě)操作。

Lucene 的底層存儲(chǔ)格式如下圖所示,由詞典和倒排序兩部分組成,其中的詞典就是 Term 的集合:

 

圖 2:Lucene 的底層存儲(chǔ)格式

詞典中的 Term 指向的文檔鏈表的集合,叫做倒排表。詞典和倒排表是 Lucene 中很重要的兩種數(shù)據(jù)結(jié)構(gòu),是實(shí)現(xiàn)快速檢索的重要基石。

詞典和倒排表是分兩部分存儲(chǔ)的,在倒排序中不但存儲(chǔ)了文檔編號(hào),還存儲(chǔ)了詞頻等信息。

在上圖所示的詞典部分包含三個(gè)詞條(Term):Elasticsearch、Lucene 和 Solr。詞典數(shù)據(jù)是查詢的入口,所以這部分?jǐn)?shù)據(jù)是以 FST 的形式存儲(chǔ)在內(nèi)存中的。

在倒排表中,“Lucene”指向有序鏈表 3,7,15,30,35,67,表示字符串“Lucene”在文檔編號(hào)為3、7、15、30、35、67的文章中出現(xiàn)過(guò),Elasticsearch 和 Solr 同理。

檢索方式

在 Lucene 的查詢過(guò)程中的主要檢索方式有以下四種:

①單個(gè)詞查詢

指對(duì)一個(gè) Term 進(jìn)行查詢。比如,若要查找包含字符串“Lucene”的文檔,則只需在詞典中找到 Term“Lucene”,再獲得在倒排表中對(duì)應(yīng)的文檔鏈表即可。

②AND

指對(duì)多個(gè)集合求交集。比如,若要查找既包含字符串“Lucene”又包含字符串“Solr”的文檔,則查找步驟如下:

  • 在詞典中找到 Term “Lucene”,得到“Lucene”對(duì)應(yīng)的文檔鏈表。
  • 在詞典中找到 Term “Solr”,得到“Solr”對(duì)應(yīng)的文檔鏈表。
  • 合并鏈表,對(duì)兩個(gè)文檔鏈表做交集運(yùn)算,合并后的結(jié)果既包含“Lucene”也包含“Solr”。

③OR

指多個(gè)集合求并集。比如,若要查找包含字符串“Luence”或者包含字符串“Solr”的文檔,則查找步驟如下:

  • 在詞典中找到 Term “Lucene”,得到“Lucene”對(duì)應(yīng)的文檔鏈表。
  • 在詞典中找到 Term “Solr”,得到“Solr”對(duì)應(yīng)的文檔鏈表。
  • 合并鏈表,對(duì)兩個(gè)文檔鏈表做并集運(yùn)算,合并后的結(jié)果包含“Lucene”或者包含“Solr”。

④NOT

指對(duì)多個(gè)集合求差集。比如,若要查找包含字符串“Solr”但不包含字符串“Lucene”的文檔,則查找步驟如下:

  • 在詞典中找到 Term “Lucene”,得到“Lucene”對(duì)應(yīng)的文檔鏈表。
  • 在詞典中找到 Term “Solr”,得到“Solr”對(duì)應(yīng)的文檔鏈表。
  • 合并鏈表,對(duì)兩個(gè)文檔鏈表做差集運(yùn)算,用包含“Solr”的文檔集減去包含“Lucene”的文檔集,運(yùn)算后的結(jié)果就是包含“Solr”但不包含“Lucene”。

通過(guò)上述四種查詢方式,我們不難發(fā)現(xiàn),由于 Lucene 是以倒排表的形式存儲(chǔ)的。

所以在 Lucene 的查找過(guò)程中只需在詞典中找到這些 Term,根據(jù) Term 獲得文檔鏈表,然后根據(jù)具體的查詢條件對(duì)鏈表進(jìn)行交、并、差等操作,就可以準(zhǔn)確地查到我們想要的結(jié)果。

相對(duì)于在關(guān)系型數(shù)據(jù)庫(kù)中的“Like”查找要做全表掃描來(lái)說(shuō),這種思路是非常高效的。

雖然在索引創(chuàng)建時(shí)要做很多工作,但這種一次生成、多次使用的思路也是非常高明的。

分段存儲(chǔ)

在早期的全文檢索中為整個(gè)文檔集合建立了一個(gè)很大的倒排索引,并將其寫(xiě)入磁盤(pán)中,如果索引有更新,就需要重新全量創(chuàng)建一個(gè)索引來(lái)替換原來(lái)的索引。

這種方式在數(shù)據(jù)量很大時(shí)效率很低,并且由于創(chuàng)建一次索引的成本很高,所以對(duì)數(shù)據(jù)的更新不能過(guò)于頻繁,也就不能保證實(shí)效性。

現(xiàn)在,在搜索中引入了段的概念(將一個(gè)索引文件拆分為多個(gè)子文件,則每個(gè)子文件叫做段),每個(gè)段都是一個(gè)獨(dú)立的可被搜索的數(shù)據(jù)集,并且段具有不變性,一旦索引的數(shù)據(jù)被寫(xiě)入硬盤(pán),就不可修改。

在分段的思想下,對(duì)數(shù)據(jù)寫(xiě)操作的過(guò)程如下:

  • 新增:當(dāng)有新的數(shù)據(jù)需要?jiǎng)?chuàng)建索引時(shí),由于段段不變性,所以選擇新建一個(gè)段來(lái)存儲(chǔ)新增的數(shù)據(jù)。
  • 刪除:當(dāng)需要?jiǎng)h除數(shù)據(jù)時(shí),由于數(shù)據(jù)所在的段只可讀,不可寫(xiě),所以 Lucene 在索引文件新增一個(gè) .del 的文件,用來(lái)專門(mén)存儲(chǔ)被刪除的數(shù)據(jù) id。

當(dāng)查詢時(shí),被刪除的數(shù)據(jù)還是可以被查到的,只是在進(jìn)行文檔鏈表合并時(shí),才把已經(jīng)刪除的數(shù)據(jù)過(guò)濾掉。被刪除的數(shù)據(jù)在進(jìn)行段合并時(shí)才會(huì)被真正被移除。

  • 更新:更新的操作其實(shí)就是刪除和新增的組合,先在.del文件中記錄舊數(shù)據(jù),再在新段中添加一條更新后的數(shù)據(jù)。

段不可變性的優(yōu)點(diǎn)如下:

  • 不需要鎖:因?yàn)閿?shù)據(jù)不會(huì)更新,所以不用考慮多線程下的讀寫(xiě)不一致情況。
  • 可以常駐內(nèi)存:段在被加載到內(nèi)存后,由于具有不變性,所以只要內(nèi)存的空間足夠大,就可以長(zhǎng)時(shí)間駐存,大部分查詢請(qǐng)求會(huì)直接訪問(wèn)內(nèi)存,而不需要訪問(wèn)磁盤(pán),使得查詢的性能有很大的提升。
  • 緩存友好:在段的聲明周期內(nèi)始終有效,不需要在每次數(shù)據(jù)更新時(shí)被重建。
  • 增量創(chuàng)建:分段可以做到增量創(chuàng)建索引,可以輕量級(jí)地對(duì)數(shù)據(jù)進(jìn)行更新,由于每次創(chuàng)建的成本很低,所以可以頻繁地更新數(shù)據(jù),使系統(tǒng)接近實(shí)時(shí)更新。

段不可變性的缺點(diǎn)如下:

  • 刪除:當(dāng)對(duì)數(shù)據(jù)進(jìn)行刪除時(shí),舊數(shù)據(jù)不會(huì)被馬上刪除,而是在 .del 文件中被標(biāo)記為刪除。而舊數(shù)據(jù)只能等到段更新時(shí)才能真正地被移除,這樣會(huì)有大量的空間浪費(fèi)。
  • 更新:更新數(shù)據(jù)由刪除和新增這兩個(gè)動(dòng)作組成。若有一條數(shù)據(jù)頻繁更新,則會(huì)有大量的空間浪費(fèi)。
  • 新增:由于索引具有不變性,所以每次新增數(shù)據(jù)時(shí),都需要新增一個(gè)段來(lái)存儲(chǔ)數(shù)據(jù)。當(dāng)段段數(shù)量太多時(shí),對(duì)服務(wù)器的資源(如文件句柄)的消耗會(huì)非常大,查詢的性能也會(huì)受到影響。
  • 過(guò)濾:在查詢后需要對(duì)已經(jīng)刪除的舊數(shù)據(jù)進(jìn)行過(guò)濾,這增加了查詢的負(fù)擔(dān)。

為了提升寫(xiě)的性能,Lucene 并沒(méi)有每新增一條數(shù)據(jù)就增加一個(gè)段,而是采用延遲寫(xiě)的策略,每當(dāng)有新增的數(shù)據(jù)時(shí),就將其先寫(xiě)入內(nèi)存中,然后批量寫(xiě)入磁盤(pán)中。

若有一個(gè)段被寫(xiě)到硬盤(pán),就會(huì)生成一個(gè)提交點(diǎn),提交點(diǎn)就是一個(gè)用來(lái)記錄所有提交后的段信息的文件。

一個(gè)段一旦擁有了提交點(diǎn),就說(shuō)明這個(gè)段只有讀的權(quán)限,失去了寫(xiě)的權(quán)限;相反,當(dāng)段在內(nèi)存中時(shí),就只有寫(xiě)數(shù)據(jù)的權(quán)限,而不具備讀數(shù)據(jù)的權(quán)限,所以也就不能被檢索了。

從嚴(yán)格意義上來(lái)說(shuō),Lucene 或者 Elasticsearch 并不能被稱為實(shí)時(shí)的搜索引擎,只能被稱為準(zhǔn)實(shí)時(shí)的搜索引擎。

寫(xiě)索引的流程如下:

  • 新數(shù)據(jù)被寫(xiě)入時(shí),并沒(méi)有被直接寫(xiě)到硬盤(pán)中,而是被暫時(shí)寫(xiě)到內(nèi)存中。Lucene 默認(rèn)是一秒鐘,或者當(dāng)內(nèi)存中數(shù)據(jù)量達(dá)到一定階段時(shí),再批量提交到磁盤(pán)中。

當(dāng)然,默認(rèn)的時(shí)間和數(shù)據(jù)量的大小是可以通過(guò)參數(shù)控制的。通過(guò)延時(shí)寫(xiě)的策略,可以減少數(shù)據(jù)往磁盤(pán)上寫(xiě)的次數(shù),從而提升整體的寫(xiě)入性能,如圖 3。

  • 在達(dá)到出觸發(fā)條件以后,會(huì)將內(nèi)存中緩存的數(shù)據(jù)一次性寫(xiě)入磁盤(pán)中,并生成提交點(diǎn)。
  • 清空內(nèi)存,等待新的數(shù)據(jù)寫(xiě)入,如下圖所示。

 

圖 3:Elasticsearch 寫(xiě)索引

從上述流程可以看出,數(shù)據(jù)先被暫時(shí)緩存在內(nèi)存中,在達(dá)到一定的條件再被一次性寫(xiě)入硬盤(pán)中,這種做法可以大大提升數(shù)據(jù)寫(xiě)入的書(shū)單。

但是數(shù)據(jù)先被暫時(shí)存放在內(nèi)存中,并沒(méi)有真正持久化到磁盤(pán)中,所以如果這時(shí)出現(xiàn)斷電等不可控的情況,就會(huì)丟失數(shù)據(jù),為此,Elasticsearch 添加了事務(wù)日志,來(lái)保證數(shù)據(jù)的安全。

段合并策略

雖然分段比每次都全量創(chuàng)建索引有更高的效率,但是由于在每次新增數(shù)據(jù)時(shí)都會(huì)新增一個(gè)段,所以經(jīng)過(guò)長(zhǎng)時(shí)間的的積累,會(huì)導(dǎo)致在索引中存在大量的段。

當(dāng)索引中段的數(shù)量太多時(shí),不僅會(huì)嚴(yán)重消耗服務(wù)器的資源,還會(huì)影響檢索的性能。

因?yàn)樗饕龣z索的過(guò)程是:查詢所有段中滿足查詢條件的數(shù)據(jù),然后對(duì)每個(gè)段里查詢的結(jié)果集進(jìn)行合并,所以為了控制索引里段的數(shù)量,我們必須定期進(jìn)行段合并操作。

但是如果每次合并全部的段,則會(huì)造成很大的資源浪費(fèi),特別是“大段”的合并。

所以 Lucene 現(xiàn)在的段合并思路是:根據(jù)段的大小將段進(jìn)行分組,再將屬于同一組的段進(jìn)行合并。

但是由于對(duì)于超級(jí)大的段的合并需要消耗更多的資源,所以 Lucene 會(huì)在段的大小達(dá)到一定規(guī)模,或者段里面的數(shù)據(jù)量達(dá)到一定條數(shù)時(shí),不會(huì)再進(jìn)行合并。

所以 Lucene 的段合并主要集中在對(duì)中小段的合并上,這樣既可以避免對(duì)大段進(jìn)行合并時(shí)消耗過(guò)多的服務(wù)器資源,也可以很好地控制索引中段的數(shù)量。

段合并的主要參數(shù)如下:

  • mergeFactor:每次合并時(shí)參與合并的最少數(shù)量,當(dāng)同一組的段的數(shù)量達(dá)到此值時(shí)開(kāi)始合并,如果小于此值則不合并,這樣做可以減少段合并的頻率,其默認(rèn)值為 10。
  • SegmentSize:指段的實(shí)際大小,單位為字節(jié)。
  • minMergeSize:小于這個(gè)值的段會(huì)被分到一組,這樣可以加速小片段的合并。
  • maxMergeSize:若有一段的文本數(shù)量大于此值,就不再參與合并,因?yàn)榇蠖魏喜?huì)消耗更多的資源。

段合并相關(guān)的動(dòng)作主要有以下兩個(gè):

  • 對(duì)索引中的段進(jìn)行分組,把大小相近的段分到一組,主要由 LogMergePolicy1 類來(lái)處理。
  • 將屬于同一分組的段合并成一個(gè)更大的段。

在段合并前對(duì)段的大小進(jìn)行了標(biāo)準(zhǔn)化處理,通過(guò) logMergeFactorSegmentSize 計(jì)算得出。

其中 MergeFactor 表示一次合并的段的數(shù)量,Lucene 默認(rèn)該數(shù)量為 10;SegmentSize 表示段的實(shí)際大小。通過(guò)上面的公式計(jì)算后,段的大小更加緊湊,對(duì)后續(xù)的分組更加友好。

段分組的步驟如下:

①根據(jù)段生成的時(shí)間對(duì)段進(jìn)行排序,然后根據(jù)上述標(biāo)準(zhǔn)化公式計(jì)算每個(gè)段的大小并且存放到段信息中,后面用到的描述段大小的值都是標(biāo)準(zhǔn)化后的值,如圖 4 所示:

 

圖 4:Lucene 段排序

②在數(shù)組中找到段,然后生成一個(gè)由段的標(biāo)準(zhǔn)化值作為上限,減去 LEVEL_LOG_SPAN(默認(rèn)值為 0.75)后的值作為下限的區(qū)間,小于等于上限并且大于下限的段,都被認(rèn)為是屬于同一組的段,可以合并。

③在確定一個(gè)分組的上下限值后,就需要查找屬于這個(gè)分組的段了,具體過(guò)程是:創(chuàng)建兩個(gè)指針(在這里使用指針的概念是為了更好地理解)start 和 end。

start 指向數(shù)組的第 1 個(gè)段,end 指向第 start+MergeFactor 個(gè)段,然后從 end 逐個(gè)向前查找落在區(qū)間的段。

當(dāng)找到第 1 個(gè)滿足條件的段時(shí),則停止,并把當(dāng)前段到 start 之間的段統(tǒng)一分到一個(gè)組,無(wú)論段的大小是否滿足當(dāng)前分組的條件。

如圖 5 所示,第 2 個(gè)段明顯小于該分組的下限,但還是被分到了這一組。

 

圖 5:Lucene 段分組

這樣做的好處如下:

  • 增加段合并的概率,避免由于段的大小參差不齊導(dǎo)致段難以合并。
  • 簡(jiǎn)化了查找的邏輯,使代碼的運(yùn)行效率更高。

④在分組找到后,需要排除不參加合并的“超大”段,然后判斷剩余的段是否滿足合并的條件。

如圖 5 所示,mergeFactor=5,而找到的滿足合并條件的段的個(gè)數(shù)為 4,所以不滿足合并的條件,暫時(shí)不進(jìn)行合并,繼續(xù)找尋下一個(gè)組的上下限。

⑤由于在第 4 步并沒(méi)有找到滿足段合并的段的數(shù)量,所以這一分組的段不滿足合并的條件,繼續(xù)進(jìn)行下一分組段的查找。

具體過(guò)程是:將 start 指向 end,在剩下的段中尋找大的段,在找到大的值后再減去 LEVEL_LOG_SPAN 的值,再生成一下分組的區(qū)間值。

然后把 end 指向數(shù)組的第 start+MergeFactor 個(gè)段,逐個(gè)向前查找第 1 個(gè)滿足條件的段:重復(fù)第 3 步和第 4 步。

⑥如果一直沒(méi)有找到滿足合并條件的段,則一直重復(fù)第 5 步,直到遍歷完整個(gè)數(shù)組,如圖 6 所示:

 

圖 6:Lucene 段分組二

⑦在找到滿足條件的 mergeFactor 個(gè)段時(shí),就需要開(kāi)始合并了。但是在滿足合并條件的段大于 mergeFactor 時(shí),就需要進(jìn)行多次合并。

也就是說(shuō)每次依然選擇 mergeFactor 個(gè)段進(jìn)行合并,直到該分組的所有段合并完成,再進(jìn)行下一分組的查找合并操作。

⑧通過(guò)上述幾步,如果找到了滿足合并要求的段,則將會(huì)進(jìn)行段的合并操作。

因?yàn)樗饕锩姘苏蛐畔⒑头聪蛐畔ⅲ远魏喜⒌牟僮鞣譃閮刹糠郑?/p>

  • 一個(gè)是正向信息合并,例如存儲(chǔ)域、詞向量、標(biāo)準(zhǔn)化因子等。
  • 一個(gè)是反向信息的合并,例如詞典、倒排表等。

在段合并時(shí),除了需要對(duì)索引數(shù)據(jù)進(jìn)行合并,還需要移除段中已經(jīng)刪除的數(shù)據(jù)。

Lucene 相似度打分

我們?cè)谇懊媪私獾?,Lucene 的查詢過(guò)程是:首先在詞典中查找每個(gè) Term,根據(jù) Term 獲得每個(gè) Term 所在的文檔鏈表;然后根據(jù)查詢條件對(duì)鏈表做交、并、差等操作,鏈表合并后的結(jié)果集就是我們要查找的數(shù)據(jù)。

這樣做可以完全避免對(duì)關(guān)系型數(shù)據(jù)庫(kù)進(jìn)行全表掃描,可以大大提升查詢效率。

但是,當(dāng)我們一次查詢出很多數(shù)據(jù)時(shí),這些數(shù)據(jù)和我們的查詢條件又有多大關(guān)系呢?其文本相似度是多少?

本節(jié)會(huì)回答這個(gè)問(wèn)題,并介紹 Lucene 最經(jīng)典的兩個(gè)文本相似度算法:基于向量空間模型的算法和基于概率的算法(BM25)。

如果對(duì)此算法不太感興趣,那么只需了解對(duì)文本相似度有影響的因子有哪些,哪些是正向的,哪些是逆向的即可,不需要理解每個(gè)算法的推理過(guò)程。但是這兩個(gè)文本相似度算法有很好的借鑒意義。

Elasticsearch 簡(jiǎn)介

Elasticsearch 是使用 Java 編寫(xiě)的一種開(kāi)源搜索引擎,它在內(nèi)部使用 Luence 做索引與搜索,通過(guò)對(duì) Lucene 的封裝,提供了一套簡(jiǎn)單一致的 RESTful API。

Elasticsearch 也是一種分布式的搜索引擎架構(gòu),可以很簡(jiǎn)單地?cái)U(kuò)展到上百個(gè)服務(wù)節(jié)點(diǎn),并支持 PB 級(jí)別的數(shù)據(jù)查詢,使系統(tǒng)具備高可用和高并發(fā)性。

核心概念

  • Elasticsearch 的核心概念如下:
  • Cluster:集群,由一個(gè)或多個(gè) Elasticsearch 節(jié)點(diǎn)組成。
  • Node:節(jié)點(diǎn),組成 Elasticsearch 集群的服務(wù)單元,同一個(gè)集群內(nèi)節(jié)點(diǎn)的名字不能重復(fù)。通常在一個(gè)節(jié)點(diǎn)上分配一個(gè)或者多個(gè)分片。
  • Shards:分片,當(dāng)索引上的數(shù)據(jù)量太大的時(shí)候,我們通常會(huì)將一個(gè)索引上的數(shù)據(jù)進(jìn)行水平拆分,拆分出來(lái)的每個(gè)數(shù)據(jù)庫(kù)叫作一個(gè)分片。

在一個(gè)多分片的索引中寫(xiě)入數(shù)據(jù)時(shí),通過(guò)路由來(lái)確定具體寫(xiě)入那一個(gè)分片中,所以在創(chuàng)建索引時(shí)需要指定分片的數(shù)量,并且分片的數(shù)量一旦確定就不能更改。

分片后的索引帶來(lái)了規(guī)模上(數(shù)據(jù)水平切分)和性能上(并行執(zhí)行)的提升。每個(gè)分片都是 Luence 中的一個(gè)索引文件,每個(gè)分片必須有一個(gè)主分片和零到多個(gè)副本分片。

  • Replicas:備份也叫作副本,是指對(duì)主分片的備份。主分片和備份分片都可以對(duì)外提供查詢服務(wù),寫(xiě)操作時(shí)先在主分片上完成,然后分發(fā)到備份上。

當(dāng)主分片不可用時(shí),會(huì)在備份的分片中選舉出一個(gè)作為主分片,所以備份不僅可以提升系統(tǒng)的高可用性能,還可以提升搜索時(shí)的并發(fā)性能。但是若副本太多的話,在寫(xiě)操作時(shí)會(huì)增加數(shù)據(jù)同步的負(fù)擔(dān)。

  • Index:索引,由一個(gè)和多個(gè)分片組成,通過(guò)索引的名字在集群內(nèi)進(jìn)行標(biāo)識(shí)。
  • Type:類別,指索引內(nèi)部的邏輯分區(qū),通過(guò) Type 的名字在索引內(nèi)進(jìn)行標(biāo)識(shí)。在查詢時(shí)如果沒(méi)有該值,則表示在整個(gè)索引中查詢。
  • Document:文檔,索引中的每一條數(shù)據(jù)叫作一個(gè)文檔,類似于關(guān)系型數(shù)據(jù)庫(kù)中的一條數(shù)據(jù)通過(guò) _id 在 Type 內(nèi)進(jìn)行標(biāo)識(shí)。
  • Settings:對(duì)集群中索引的定義,比如一個(gè)索引默認(rèn)的分片數(shù)、副本數(shù)等信息。
  • Mapping:類似于關(guān)系型數(shù)據(jù)庫(kù)中的表結(jié)構(gòu)信息,用于定義索引中字段(Field)的存儲(chǔ)類型、分詞方式、是否存儲(chǔ)等信息。Elasticsearch 中的 Mapping 是可以動(dòng)態(tài)識(shí)別的。

如果沒(méi)有特殊需求,則不需要手動(dòng)創(chuàng)建 Mapping,因?yàn)?Elasticsearch 會(huì)自動(dòng)根據(jù)數(shù)據(jù)格式識(shí)別它的類型,但是當(dāng)需要對(duì)某些字段添加特殊屬性(比如:定義使用其他分詞器、是否分詞、是否存儲(chǔ)等)時(shí),就需要手動(dòng)設(shè)置 Mapping 了。一個(gè)索引的 Mapping 一旦創(chuàng)建,若已經(jīng)存儲(chǔ)了數(shù)據(jù),就不可修改了。

  • Analyzer:字段的分詞方式的定義。一個(gè) Analyzer 通常由一個(gè) Tokenizer、零到多個(gè) Filter 組成。

比如默認(rèn)的標(biāo)準(zhǔn) Analyzer 包含一個(gè)標(biāo)準(zhǔn)的 Tokenizer 和三個(gè) Filter:Standard Token Filter、Lower Case Token Filter、Stop Token Filter。

Elasticsearch 的節(jié)點(diǎn)的分類如下:

①主節(jié)點(diǎn)(Master Node):也叫作主節(jié)點(diǎn),主節(jié)點(diǎn)負(fù)責(zé)創(chuàng)建索引、刪除索引、分配分片、追蹤集群中的節(jié)點(diǎn)狀態(tài)等工作。Elasticsearch 中的主節(jié)點(diǎn)的工作量相對(duì)較輕。

用戶的請(qǐng)求可以發(fā)往任何一個(gè)節(jié)點(diǎn),并由該節(jié)點(diǎn)負(fù)責(zé)分發(fā)請(qǐng)求、收集結(jié)果等操作,而并不需要經(jīng)過(guò)主節(jié)點(diǎn)轉(zhuǎn)發(fā)。

通過(guò)在配置文件中設(shè)置 node.master=true 來(lái)設(shè)置該節(jié)點(diǎn)成為候選主節(jié)點(diǎn)(但該節(jié)點(diǎn)不一定是主節(jié)點(diǎn),主節(jié)點(diǎn)是集群在候選節(jié)點(diǎn)中選舉出來(lái)的),在 Elasticsearch 集群中只有候選節(jié)點(diǎn)才有選舉權(quán)和被選舉權(quán)。其他節(jié)點(diǎn)是不參與選舉工作的。

②數(shù)據(jù)節(jié)點(diǎn)(Data Node):數(shù)據(jù)節(jié)點(diǎn),負(fù)責(zé)數(shù)據(jù)的存儲(chǔ)和相關(guān)具體操作,比如索引數(shù)據(jù)的創(chuàng)建、修改、刪除、搜索、聚合。

所以,數(shù)據(jù)節(jié)點(diǎn)對(duì)機(jī)器配置要求比較高,首先需要有足夠的磁盤(pán)空間來(lái)存儲(chǔ)數(shù)據(jù),其次數(shù)據(jù)操作對(duì)系統(tǒng) CPU、Memory 和 I/O 的性能消耗都很大。

通常隨著集群的擴(kuò)大,需要增加更多的數(shù)據(jù)節(jié)點(diǎn)來(lái)提高可用性。通過(guò)在配置文件中設(shè)置 node.data=true 來(lái)設(shè)置該節(jié)點(diǎn)成為數(shù)據(jù)節(jié)點(diǎn)。

③客戶端節(jié)點(diǎn)(Client Node):就是既不做候選主節(jié)點(diǎn)也不做數(shù)據(jù)節(jié)點(diǎn)的節(jié)點(diǎn),只負(fù)責(zé)請(qǐng)求的分發(fā)、匯總等,也就是下面要說(shuō)到的協(xié)調(diào)節(jié)點(diǎn)的角色。

其實(shí)任何一個(gè)節(jié)點(diǎn)都可以完成這樣的工作,單獨(dú)增加這樣的節(jié)點(diǎn)更多地是為了提高并發(fā)性。

可在配置文件中設(shè)置該節(jié)點(diǎn)成為數(shù)據(jù)節(jié)點(diǎn):

  1. node.master=false 
  2. node.data=false 

④部落節(jié)點(diǎn)(Tribe Node):部落節(jié)點(diǎn)可以跨越多個(gè)集群,它可以接收每個(gè)集群的狀態(tài),然后合并成一個(gè)全局集群的狀態(tài)。

它可以讀寫(xiě)所有集群節(jié)點(diǎn)上的數(shù)據(jù),在配置文件中通過(guò)如下設(shè)置使節(jié)點(diǎn)成為部落節(jié)點(diǎn):

  1. tribe: 
  2.   one: 
  3.     cluster.name: cluster_one 
  4.   two: 
  5.     cluster.name: cluster_two 

因?yàn)?Tribe Node 要在 Elasticsearch 7.0 以后移除,所以不建議使用。

⑤協(xié)調(diào)節(jié)點(diǎn)(Coordinating Node):協(xié)調(diào)節(jié)點(diǎn),是一種角色,而不是真實(shí)的 Elasticsearch 的節(jié)點(diǎn),我們沒(méi)有辦法通過(guò)配置項(xiàng)來(lái)配置哪個(gè)節(jié)點(diǎn)為協(xié)調(diào)節(jié)點(diǎn)。集群中的任何節(jié)點(diǎn)都可以充當(dāng)協(xié)調(diào)節(jié)點(diǎn)的角色。

當(dāng)一個(gè)節(jié)點(diǎn) A 收到用戶的查詢請(qǐng)求后,會(huì)把查詢語(yǔ)句分發(fā)到其他的節(jié)點(diǎn),然后合并各個(gè)節(jié)點(diǎn)返回的查詢結(jié)果,返回一個(gè)完整的數(shù)據(jù)集給用戶。

在這個(gè)過(guò)程中,節(jié)點(diǎn) A 扮演的就是協(xié)調(diào)節(jié)點(diǎn)的角色。由此可見(jiàn),協(xié)調(diào)節(jié)點(diǎn)會(huì)對(duì) CPU、Memory 和 I/O 要求比較高。

集群的狀態(tài)有 Green、Yellow 和 Red 三種,如下所述:

  • Green:綠色,健康。所有的主分片和副本分片都可正常工作,集群 100% 健康。
  • Yellow:預(yù)警。所有的主分片都可以正常工作,但至少有一個(gè)副本分片是不能正常工作的。此時(shí)集群可以正常工作,但是集群的高可用性在某種程度上被弱化。
  • Red:紅色,集群不可正常使用。集群中至少有一個(gè)分片的主分片及它的全部副本分片都不可正常工作。

這時(shí)雖然集群的查詢操作還可以進(jìn)行,但是也只能返回部分?jǐn)?shù)據(jù)(其他正常分片的數(shù)據(jù)可以返回),而分配到這個(gè)分片上的寫(xiě)入請(qǐng)求將會(huì)報(bào)錯(cuò),最終會(huì)導(dǎo)致數(shù)據(jù)的丟失。

3C 和腦裂

①共識(shí)性(Consensus)

共識(shí)性是分布式系統(tǒng)中最基礎(chǔ)也最主要的一個(gè)組件,在分布式系統(tǒng)中的所有節(jié)點(diǎn)必須對(duì)給定的數(shù)據(jù)或者節(jié)點(diǎn)的狀態(tài)達(dá)成共識(shí)。

雖然現(xiàn)在有很成熟的共識(shí)算法如 Raft、Paxos 等,也有比較成熟的開(kāi)源軟件如 Zookeeper。

但是 Elasticsearch 并沒(méi)有使用它們,而是自己實(shí)現(xiàn)共識(shí)系統(tǒng) zen discovery。

Elasticsearch 之父 Shay Banon 解釋了其中主要的原因:“zen discovery是 Elasticsearch 的一個(gè)核心的基礎(chǔ)組件,zen discovery 不僅能夠?qū)崿F(xiàn)共識(shí)系統(tǒng)的選擇工作,還能夠很方便地監(jiān)控集群的讀寫(xiě)狀態(tài)是否健康。當(dāng)然,我們也不保證其后期會(huì)使用 Zookeeper 代替現(xiàn)在的 zen discovery”。

zen discovery 模塊以“八卦傳播”(Gossip)的形式實(shí)現(xiàn)了單播(Unicat):?jiǎn)尾ゲ煌诙嗖?Multicast)和廣播(Broadcast)。節(jié)點(diǎn)間的通信方式是一對(duì)一的。

②并發(fā)(Concurrency)

Elasticsearch 是一個(gè)分布式系統(tǒng)。寫(xiě)請(qǐng)求在發(fā)送到主分片時(shí),同時(shí)會(huì)以并行的形式發(fā)送到備份分片,但是這些請(qǐng)求的送達(dá)時(shí)間可能是無(wú)序的。

在這種情況下,Elasticsearch 用樂(lè)觀并發(fā)控制(Optimistic Concurrency Control)來(lái)保證新版本的數(shù)據(jù)不會(huì)被舊版本的數(shù)據(jù)覆蓋。

樂(lè)觀并發(fā)控制是一種樂(lè)觀鎖,另一種常用的樂(lè)觀鎖即多版本并發(fā)控制(Multi-Version Concurrency Control)。

它們的主要區(qū)別如下:

  • 樂(lè)觀并發(fā)控制(OCC):是一種用來(lái)解決寫(xiě)-寫(xiě)沖突的無(wú)鎖并發(fā)控制,認(rèn)為事務(wù)間的競(jìng)爭(zhēng)不激烈時(shí),就先進(jìn)行修改,在提交事務(wù)前檢查數(shù)據(jù)有沒(méi)有變化,如果沒(méi)有就提交,如果有就放棄并重試。樂(lè)觀并發(fā)控制類似于自選鎖,適用于低數(shù)據(jù)競(jìng)爭(zhēng)且寫(xiě)沖突比較少的環(huán)境。
  • 多版本并發(fā)控制(MVCC):是一種用來(lái)解決讀-寫(xiě)沖突的無(wú)所并發(fā)控制,也就是為事務(wù)分配單向增長(zhǎng)的時(shí)間戳,為每一個(gè)修改保存一個(gè)版本,版本與事務(wù)時(shí)間戳關(guān)聯(lián),讀操作只讀該事務(wù)開(kāi)始前的數(shù)據(jù)庫(kù)的快照。

這樣在讀操作不用阻塞操作且寫(xiě)操作不用阻塞讀操作的同時(shí),避免了臟讀和不可重復(fù)讀。

③一致性(Consistency)

Elasticsearch 集群保證寫(xiě)一致性的方式是在寫(xiě)入前先檢查有多少個(gè)分片可供寫(xiě)入,如果達(dá)到寫(xiě)入條件,則進(jìn)行寫(xiě)操作,否則,Elasticsearch 會(huì)等待更多的分片出現(xiàn),默認(rèn)為一分鐘。

有如下三種設(shè)置來(lái)判斷是否允許寫(xiě)操作:

  • One:只要主分片可用,就可以進(jìn)行寫(xiě)操作。
  • All:只有當(dāng)主分片和所有副本都可用時(shí),才允許寫(xiě)操作。
  • Quorum(k-wu-wo/reng,法定人數(shù)):是 Elasticsearch 的默認(rèn)選項(xiàng)。當(dāng)有大部分的分片可用時(shí)才允許寫(xiě)操作。其中,對(duì)“大部分”的計(jì)算公式為 int((primary+number_of_replicas)/2)+1。

Elasticsearch 集群保證讀寫(xiě)一致性的方式是,為了保證搜索請(qǐng)求的返回結(jié)果是當(dāng)前版本的文檔,備份可以被設(shè)置為 Sync(默認(rèn)值),寫(xiě)操作在主分片和備份分片同時(shí)完成后才會(huì)返回寫(xiě)請(qǐng)求的結(jié)果。

這樣,無(wú)論搜索請(qǐng)求至哪個(gè)分片都會(huì)返回文檔。但是如果我們的應(yīng)用對(duì)寫(xiě)要求很高,就可以通過(guò)設(shè)置 replication=async 來(lái)提升寫(xiě)的效率,如果設(shè)置 replication=async,則只要主分片的寫(xiě)完成,就會(huì)返回寫(xiě)成功。

④腦裂

在 Elasticsearch 集群中主節(jié)點(diǎn)通過(guò) Ping 命令來(lái)檢查集群中的其他節(jié)點(diǎn)是否處于可用狀態(tài),同時(shí)非主節(jié)點(diǎn)也會(huì)通過(guò) Ping 來(lái)檢查主節(jié)點(diǎn)是否處于可用狀態(tài)。

當(dāng)集群網(wǎng)絡(luò)不穩(wěn)定時(shí),有可能會(huì)發(fā)生一個(gè)節(jié)點(diǎn) Ping 不通 Master 節(jié)點(diǎn),則會(huì)認(rèn)為 Master 節(jié)點(diǎn)發(fā)生了故障,然后重新選出一個(gè) Master 節(jié)點(diǎn),這就會(huì)導(dǎo)致在一個(gè)集群內(nèi)出現(xiàn)多個(gè) Master 節(jié)點(diǎn)。

當(dāng)在一個(gè)集群中有多個(gè) Master 節(jié)點(diǎn)時(shí),就有可能會(huì)導(dǎo)致數(shù)據(jù)丟失。我們稱這種現(xiàn)象為腦裂。

事務(wù)日志

我們?cè)谏厦媪私獾剑琇ucene 為了加快寫(xiě)索引的速度,采用了延遲寫(xiě)入的策略。

雖然這種策略提高了寫(xiě)入的效率,但其弊端是,如果數(shù)據(jù)在內(nèi)存中還沒(méi)有持久化到磁盤(pán)上時(shí)發(fā)生了類似斷電等不可控情況,就可能丟失數(shù)據(jù)。

為了避免丟失數(shù)據(jù),Elasticsearch 添加了事務(wù)日志(Translog),事務(wù)日志記錄了所有還沒(méi)有被持久化磁盤(pán)的數(shù)據(jù)。

Elasticsearch 寫(xiě)索引的具體過(guò)程如下:首先,當(dāng)有數(shù)據(jù)寫(xiě)入時(shí),為了提升寫(xiě)入的速度,并沒(méi)有數(shù)據(jù)直接寫(xiě)在磁盤(pán)上,而是先寫(xiě)入到內(nèi)存中,但是為了防止數(shù)據(jù)的丟失,會(huì)追加一份數(shù)據(jù)到事務(wù)日志里。

因?yàn)閮?nèi)存中的數(shù)據(jù)還會(huì)繼續(xù)寫(xiě)入,所以內(nèi)存中的數(shù)據(jù)并不是以段的形式存儲(chǔ)的,是檢索不到的。

總之,Elasticsearch 是一個(gè)準(zhǔn)實(shí)時(shí)的搜索引擎,而不是一個(gè)實(shí)時(shí)的搜索引擎。

此時(shí)的狀態(tài)如圖 7 所示:

 

圖 7:Elasticsearch 寫(xiě)數(shù)據(jù)的過(guò)程

然后,當(dāng)達(dá)到默認(rèn)的時(shí)間(1 秒鐘)或者內(nèi)存的數(shù)據(jù)達(dá)到一定量時(shí),會(huì)觸發(fā)一次刷新(Refresh)。

刷新的主要步驟如下:

  • 將內(nèi)存中的數(shù)據(jù)刷新到一個(gè)新的段中,但是該段并沒(méi)有持久化到硬盤(pán)中,而是緩存在操作系統(tǒng)的文件緩存系統(tǒng)中。雖然數(shù)據(jù)還在內(nèi)存中,但是內(nèi)存里的數(shù)據(jù)和文件緩存系統(tǒng)里的數(shù)據(jù)有以下區(qū)別。

內(nèi)存使用的是 JVM 的內(nèi)存,而文件緩存系統(tǒng)使用的是操作系統(tǒng)的內(nèi)存;內(nèi)存的數(shù)據(jù)不是以段的形式存儲(chǔ)的,并且可以繼續(xù)向內(nèi)存里寫(xiě)數(shù)據(jù)。文件緩存系統(tǒng)中的數(shù)據(jù)是以段的形式存儲(chǔ)的,所以只能讀,不能寫(xiě);內(nèi)存中的數(shù)據(jù)是搜索不到,文件緩存系統(tǒng)中的數(shù)據(jù)是可以搜索的。

  • 打開(kāi)保存在文件緩存系統(tǒng)中的段,使其可被搜索。
  • 清空內(nèi)存,準(zhǔn)備接收新的數(shù)據(jù)。日志不做清空處理。

此時(shí)的狀態(tài)如圖 8 所示:

 

圖 8:Elasticsearch 寫(xiě)數(shù)據(jù)的過(guò)程

刷新(Flush)。當(dāng)日志數(shù)據(jù)的大小超過(guò) 512MB 或者時(shí)間超過(guò) 30 分鐘時(shí),需要觸發(fā)一次刷新。

刷新的主要步驟如下:

  • 在文件緩存系統(tǒng)中創(chuàng)建一個(gè)新的段,并把內(nèi)存中的數(shù)據(jù)寫(xiě)入,使其可被搜索。
  • 清空內(nèi)存,準(zhǔn)備接收新的數(shù)據(jù)。
  • 將文件系統(tǒng)緩存中的數(shù)據(jù)通過(guò) Fsync 函數(shù)刷新到硬盤(pán)中。
  • 生成提交點(diǎn)。
  • 刪除舊的日志,創(chuàng)建一個(gè)空的日志。

此時(shí)的狀態(tài)如圖 9 所示:

 

圖 9:Elasticsearch 寫(xiě)數(shù)據(jù)的過(guò)程

由上面索引創(chuàng)建的過(guò)程可知,內(nèi)存里面的數(shù)據(jù)并沒(méi)有直接被刷新(Flush)到硬盤(pán)中,而是被刷新(Refresh)到了文件緩存系統(tǒng)中,這主要是因?yàn)槌志没瘮?shù)據(jù)十分耗費(fèi)資源,頻繁地調(diào)用會(huì)使寫(xiě)入的性能急劇下降。

所以 Elasticsearch,為了提高寫(xiě)入的效率,利用了文件緩存系統(tǒng)和內(nèi)存來(lái)加速寫(xiě)入時(shí)的性能,并使用日志來(lái)防止數(shù)據(jù)的丟失。

在需要重啟時(shí),Elasticsearch 不僅要根據(jù)提交點(diǎn)去加載已經(jīng)持久化過(guò)的段,還需要根據(jù) Translog 里的記錄,把未持久化的數(shù)據(jù)重新持久化到磁盤(pán)上。

根據(jù)上面對(duì) Elasticsearch,寫(xiě)操作流程的介紹,我們可以整理出一個(gè)索引數(shù)據(jù)所要經(jīng)歷的幾個(gè)階段,以及每個(gè)階段的數(shù)據(jù)的存儲(chǔ)方式和作用,如圖 10 所示:

 

圖 10:Elasticsearch 寫(xiě)操作流程

在集群中寫(xiě)索引

假設(shè)我們有如圖 11 所示(圖片來(lái)自官網(wǎng))的一個(gè)集群,該集群由三個(gè)節(jié)點(diǎn)組成(Node 1、Node 2 和 Node 3),包含一個(gè)由兩個(gè)主分片和每個(gè)主分片由兩個(gè)副本分片組成的索引。

 

圖 11:寫(xiě)索引

其中,標(biāo)星號(hào)的 Node 1 是 Master 節(jié)點(diǎn),負(fù)責(zé)管理整個(gè)集群的狀態(tài);p1 和 p2 是主分片;r0 和 r1 是副本分片。為了達(dá)到高可用,Master 節(jié)點(diǎn)避免將主分片和副本放在同一個(gè)節(jié)點(diǎn)。

將數(shù)據(jù)分片是為了提高可處理數(shù)據(jù)的容量和易于進(jìn)行水平擴(kuò)展,為分片做副本是為了提高集群的穩(wěn)定性和提高并發(fā)量。

在主分片掛掉后,會(huì)從副本分片中選舉出一個(gè)升級(jí)為主分片,當(dāng)副本升級(jí)為主分片后,由于少了一個(gè)副本分片,所以集群狀態(tài)會(huì)從 Green 改變?yōu)?Yellow,但是此時(shí)集群仍然可用。

在一個(gè)集群中有一個(gè)分片的主分片和副本分片都掛掉后,集群狀態(tài)會(huì)由 Yellow 改變?yōu)?Red,集群狀態(tài)為 Red 時(shí)集群不可正常使用。

由上面的步驟可知,副本分片越多,集群的可用性就越高,但是由于每個(gè)分片都相當(dāng)于一個(gè) Lucene 的索引文件,會(huì)占用一定的文件句柄、內(nèi)存及 CPU,并且分片間的數(shù)據(jù)同步也會(huì)占用一定的網(wǎng)絡(luò)帶寬,所以,索引的分片數(shù)和副本數(shù)并不是越多越好。

寫(xiě)索引時(shí)只能寫(xiě)在主分片上,然后同步到副本上,那么,一個(gè)數(shù)據(jù)應(yīng)該被寫(xiě)在哪個(gè)分片上呢?

如圖 10 所示,如何知道一個(gè)數(shù)據(jù)應(yīng)該被寫(xiě)在 p0 還是 p1 上呢答案就是路由(routing),路由公式如下:

  1. shard = hash(routing)%number_of_primary_shards 

其中,Routing 是一個(gè)可選擇的值,默認(rèn)是文檔的 _id(文檔的主鍵,文檔在創(chuàng)建時(shí),如果文檔的 _id 已經(jīng)存在,則進(jìn)行更新,如果不存在則創(chuàng)建)。

后面會(huì)介紹如何通過(guò)自定義 Routing 參數(shù)使查詢落在一個(gè)分片中,而不用查詢所有的分片,從而提升查詢的性能。

Routing 通過(guò) Hash 函數(shù)生成一個(gè)數(shù)字,將這個(gè)數(shù)字除以 number_of_primary_shards(分片的數(shù)量)后得到余數(shù)。

這個(gè)分布在 0 到 number_of_primary_shards - 1 之間的余數(shù),就是我們所尋求的文檔所在分片的位置。

這也就說(shuō)明了一旦分片數(shù)定下來(lái)就不能再改變的原因,因?yàn)榉制瑪?shù)改變之后,所有之前的路由值都會(huì)變得無(wú)效,前期創(chuàng)建的文檔也就找不到了。

由于在 Elasticsearch 集群中每個(gè)節(jié)點(diǎn)都知道集群中的文檔的存放位置(通過(guò)路由公式定位),所以每個(gè)節(jié)點(diǎn)都有處理讀寫(xiě)請(qǐng)求的能力。

在一個(gè)寫(xiě)請(qǐng)求被發(fā)送到集群中的一個(gè)節(jié)點(diǎn)后,此時(shí),該節(jié)點(diǎn)被稱為協(xié)調(diào)點(diǎn)(Coordinating Node),協(xié)調(diào)點(diǎn)會(huì)根據(jù)路由公式計(jì)算出需要寫(xiě)到哪個(gè)分片上,再將請(qǐng)求轉(zhuǎn)發(fā)到該分片的主分片節(jié)點(diǎn)上。

 

圖 12:寫(xiě)索引

寫(xiě)操作的流程如下(參考圖 11,圖片來(lái)自官網(wǎng)):

  • 客戶端向 Node 1(協(xié)調(diào)節(jié)點(diǎn))發(fā)送寫(xiě)請(qǐng)求。
  • Node 1 通過(guò)文檔的 _id(默認(rèn)是 _id,但不表示一定是 _id)確定文檔屬于哪個(gè)分片(在本例中是編號(hào)為 0 的分片)。請(qǐng)求會(huì)被轉(zhuǎn)發(fā)到主分片所在的節(jié)點(diǎn) Node 3 上。
  • Node 3 在主分片上執(zhí)行請(qǐng)求,如果成功,則將請(qǐng)求并行轉(zhuǎn)發(fā)到 Node 1 和 Node 2 的副本分片上。

一旦所有的副本分片都報(bào)告成功(默認(rèn)),則 Node 3 將向協(xié)調(diào)節(jié)點(diǎn)報(bào)告成功,協(xié)調(diào)節(jié)點(diǎn)向客戶端報(bào)告成功。

集群中的查詢流程

根據(jù) Routing 字段進(jìn)行的單個(gè)文檔的查詢,在 Elasticsearch 集群中可以在主分片或者副本分片上進(jìn)行。

 

圖 13

查詢字段剛好是 Routing 的分片字段如“_id”的查詢流程如下(見(jiàn)圖 12,圖片來(lái)自官網(wǎng)):

  • 客戶端向集群發(fā)送查詢請(qǐng)求,集群再隨機(jī)選擇一個(gè)節(jié)點(diǎn)作為協(xié)調(diào)點(diǎn)(Node 1),負(fù)責(zé)處理這次查詢。
  • Node 1 使用文檔的 routing id 來(lái)計(jì)算要查詢的文檔在哪個(gè)分片上(在本例中落在了 0 分片上)分片 0 的副本分片存在所有的三個(gè)節(jié)點(diǎn)上。

在這種情況下,協(xié)調(diào)節(jié)點(diǎn)可以把請(qǐng)求轉(zhuǎn)發(fā)到任意節(jié)點(diǎn),本例將請(qǐng)求轉(zhuǎn)發(fā)到 Node 2 上。

  • Node 2 執(zhí)行查找,并將查找結(jié)果返回給協(xié)調(diào)節(jié)點(diǎn) Node 1,Node 1 再將文檔返回給客戶端。

當(dāng)一個(gè)搜索請(qǐng)求被發(fā)送到某個(gè)節(jié)點(diǎn)時(shí),這個(gè)節(jié)點(diǎn)就變成了協(xié)調(diào)節(jié)點(diǎn)(Node 1)。

協(xié)調(diào)節(jié)點(diǎn)的任務(wù)是廣播查詢請(qǐng)求到所有分片(主分片或者副本分片),并將它們的響應(yīng)結(jié)果整合成全局排序后的結(jié)果集合。

由上面步驟 3 所示,默認(rèn)返回給協(xié)調(diào)節(jié)點(diǎn)并不是所有的數(shù)據(jù),而是只有文檔的 id 和得分 score,因?yàn)槲覀冎环祷亟o用戶 size 條數(shù)據(jù),所以這樣做的好處是可以節(jié)省很多帶寬,特別是 from 很大時(shí)。

協(xié)調(diào)節(jié)點(diǎn)對(duì)收集回來(lái)的數(shù)據(jù)進(jìn)行排序后,找到要返回的 size 條數(shù)據(jù)的 id,再根據(jù) id 查詢要返回的數(shù)據(jù),比如 title、content 等。

 

圖 14

取回?cái)?shù)據(jù)等流程如下(見(jiàn)圖 13,圖片來(lái)自官網(wǎng)):

  • Node 3 進(jìn)行二次排序來(lái)找出要返回的文檔 id,并向相關(guān)的分片提交多個(gè)獲得文檔詳情的請(qǐng)求。
  • 每個(gè)分片加載文檔,并將文檔返回給 Node 3。
  • 一旦所有的文檔都取回了,Node 3 就返回結(jié)果給客戶端。

協(xié)調(diào)節(jié)點(diǎn)收集各個(gè)分片查詢出來(lái)的數(shù)據(jù),再進(jìn)行二次排序,然后選擇需要被取回的文檔。

例如,如果我們的查詢指定了{(lán)"from": 20, "size": 10},那么我們需要在每個(gè)分片中查詢出來(lái)得分較高的 20+10 條數(shù)據(jù),協(xié)調(diào)節(jié)點(diǎn)在收集到 30×n(n 為分片數(shù))條數(shù)據(jù)后再進(jìn)行排序。

排序位置在 0-20 的結(jié)果會(huì)被丟棄,只有從第 21 個(gè)開(kāi)始的 10 個(gè)結(jié)果需要被取回。這些文檔可能來(lái)自多個(gè)甚至全部分片。

由上面的搜索策略可以知道,在查詢時(shí)深翻(Deep Pagination)并不是一種好方法。

因?yàn)樯罘瓡r(shí),from 會(huì)很大,這時(shí)的排序過(guò)程可能會(huì)變得非常沉重,會(huì)占用大量的 CPU、內(nèi)存和帶寬。因?yàn)檫@個(gè)原因,所以強(qiáng)烈建議慎重使用深翻。

分片可以減少每個(gè)片上的數(shù)據(jù)量,加快查詢的速度,但是在查詢時(shí),協(xié)調(diào)節(jié)點(diǎn)要在收集數(shù)(from+size)×n 條數(shù)據(jù)后再做一次全局排序。

若這個(gè)數(shù)據(jù)量很大,則也會(huì)占用大量的 CPU、內(nèi)存、帶寬等,并且分片查詢的速度取決于最慢的分片查詢的速度,所以分片數(shù)并不是越多越好。

作者:錢(qián)丁君

簡(jiǎn)介:就職于永輝云創(chuàng),擔(dān)任基礎(chǔ)架構(gòu)開(kāi)發(fā),有多年基礎(chǔ)架構(gòu)經(jīng)驗(yàn),主要從事電商新零售、互聯(lián)網(wǎng)金融行業(yè)。技術(shù)發(fā)燒友,涉獵廣泛。熟悉 Java 微服務(wù)架構(gòu)搭建、推進(jìn)、衍化;多種中間件搭建、封裝和優(yōu)化;自動(dòng)化測(cè)試開(kāi)發(fā)、代碼規(guī)約插件開(kāi)發(fā)、代碼規(guī)范推進(jìn);容器化技術(shù) Docker、容器化編排技術(shù) Kubernetes,有較為豐富的運(yùn)維經(jīng)驗(yàn)。

【51CTO原創(chuàng)稿件,合作站點(diǎn)轉(zhuǎn)載請(qǐng)注明原文作者和出處為51CTO.com】

 

責(zé)任編輯:武曉燕 來(lái)源: 51CTO技術(shù)棧
相關(guān)推薦

2019-05-21 09:40:47

Elasticsear高性能 API

2020-12-04 08:24:34

監(jiān)控多維度立體化監(jiān)控系統(tǒng)

2018-02-02 16:41:01

程序員編程Web

2015-06-09 15:28:14

編程能力編程

2021-07-21 10:10:14

require前端代碼

2021-11-08 10:00:19

require前端模塊

2019-04-01 09:00:05

程序員編程語(yǔ)言HTML

2020-04-22 13:41:33

數(shù)據(jù)結(jié)構(gòu)大數(shù)據(jù)數(shù)據(jù)

2017-05-10 08:39:34

裝機(jī)線纜機(jī)箱

2017-06-05 13:53:10

2023-06-12 08:36:01

static?DOM文檔

2017-08-23 09:05:09

交互設(shè)計(jì)UI界面

2018-08-15 14:18:34

算法AI機(jī)器學(xué)習(xí)

2022-12-09 08:19:43

HTTP協(xié)議MIME

2017-11-06 11:15:51

智能機(jī)器人工智能機(jī)器學(xué)習(xí)

2019-09-21 20:57:59

Android安卓開(kāi)發(fā)

2022-09-27 19:07:35

工具Hack延遲

2017-11-12 21:12:34

HPC

2018-08-28 10:26:54

AI代碼科技

2019-05-13 14:17:06

抓包Web安全漏洞
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)