偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

掌握它才說明你真正懂Elasticsearch

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

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

Lucene 簡介

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

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

核心模塊

Lucene 的寫流程和讀流程如下圖所示:

 

圖 1:Lucene 的寫流程和讀流程

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

Lucene 中的主要模塊及模塊說明如下:

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

核心術(shù)語

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

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

比如:排序數(shù)組通過二分查找來檢索數(shù)據(jù):HashMap(哈希表)比排序數(shù)組的檢索速度更快,但是會浪費存儲空間。

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

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

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

 

圖 2:Lucene 的底層存儲格式

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

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

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

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

檢索方式

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

①單個詞查詢

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

②AND

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

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

③OR

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

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

④NOT

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

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

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

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

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

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

分段存儲

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

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

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

在分段的思想下,對數(shù)據(jù)寫操作的過程如下:

  • 新增:當有新的數(shù)據(jù)需要創(chuàng)建索引時,由于段段不變性,所以選擇新建一個段來存儲新增的數(shù)據(jù)。
  • 刪除:當需要刪除數(shù)據(jù)時,由于數(shù)據(jù)所在的段只可讀,不可寫,所以 Lucene 在索引文件新增一個 .del 的文件,用來專門存儲被刪除的數(shù)據(jù) id。

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

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

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

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

段不可變性的缺點如下:

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

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

若有一個段被寫到硬盤,就會生成一個提交點,提交點就是一個用來記錄所有提交后的段信息的文件。

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

從嚴格意義上來說,Lucene 或者 Elasticsearch 并不能被稱為實時的搜索引擎,只能被稱為準實時的搜索引擎。

寫索引的流程如下:

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

當然,默認的時間和數(shù)據(jù)量的大小是可以通過參數(shù)控制的。通過延時寫的策略,可以減少數(shù)據(jù)往磁盤上寫的次數(shù),從而提升整體的寫入性能,如圖 3。

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

 

圖 3:Elasticsearch 寫索引

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

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

段合并策略

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

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

因為索引檢索的過程是:查詢所有段中滿足查詢條件的數(shù)據(jù),然后對每個段里查詢的結(jié)果集進行合并,所以為了控制索引里段的數(shù)量,我們必須定期進行段合并操作。

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

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

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

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

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

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

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

  • 對索引中的段進行分組,把大小相近的段分到一組,主要由 LogMergePolicy1 類來處理。
  • 將屬于同一分組的段合并成一個更大的段。

在段合并前對段的大小進行了標準化處理,通過 logMergeFactorSegmentSize 計算得出。

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

段分組的步驟如下:

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

 

圖 4:Lucene 段排序

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

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

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

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

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

 

圖 5:Lucene 段分組

這樣做的好處如下:

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

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

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

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

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

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

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

 

圖 6:Lucene 段分組二

⑦在找到滿足條件的 mergeFactor 個段時,就需要開始合并了。但是在滿足合并條件的段大于 mergeFactor 時,就需要進行多次合并。

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

⑧通過上述幾步,如果找到了滿足合并要求的段,則將會進行段的合并操作。

因為索引里面包含了正向信息和反向信息,所以段合并的操作分為兩部分:

  • 一個是正向信息合并,例如存儲域、詞向量、標準化因子等。
  • 一個是反向信息的合并,例如詞典、倒排表等。

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

Lucene 相似度打分

我們在前面了解到,Lucene 的查詢過程是:首先在詞典中查找每個 Term,根據(jù) Term 獲得每個 Term 所在的文檔鏈表;然后根據(jù)查詢條件對鏈表做交、并、差等操作,鏈表合并后的結(jié)果集就是我們要查找的數(shù)據(jù)。

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

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

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

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

Elasticsearch 簡介

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

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

核心概念

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

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

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

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

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

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

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

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

比如默認的標準 Analyzer 包含一個標準的 Tokenizer 和三個 Filter:Standard Token Filter、Lower Case Token Filter、Stop Token Filter。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

因為 Tribe Node 要在 Elasticsearch 7.0 以后移除,所以不建議使用。

⑤協(xié)調(diào)節(jié)點(Coordinating Node):協(xié)調(diào)節(jié)點,是一種角色,而不是真實的 Elasticsearch 的節(jié)點,我們沒有辦法通過配置項來配置哪個節(jié)點為協(xié)調(diào)節(jié)點。集群中的任何節(jié)點都可以充當協(xié)調(diào)節(jié)點的角色。

當一個節(jié)點 A 收到用戶的查詢請求后,會把查詢語句分發(fā)到其他的節(jié)點,然后合并各個節(jié)點返回的查詢結(jié)果,返回一個完整的數(shù)據(jù)集給用戶。

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

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

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

這時雖然集群的查詢操作還可以進行,但是也只能返回部分數(shù)據(jù)(其他正常分片的數(shù)據(jù)可以返回),而分配到這個分片上的寫入請求將會報錯,最終會導(dǎo)致數(shù)據(jù)的丟失。

3C 和腦裂

①共識性(Consensus)

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

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

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

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

zen discovery 模塊以“八卦傳播”(Gossip)的形式實現(xiàn)了單播(Unicat):單播不同于多播(Multicast)和廣播(Broadcast)。節(jié)點間的通信方式是一對一的。

②并發(fā)(Concurrency)

Elasticsearch 是一個分布式系統(tǒng)。寫請求在發(fā)送到主分片時,同時會以并行的形式發(fā)送到備份分片,但是這些請求的送達時間可能是無序的。

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

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

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

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

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

③一致性(Consistency)

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

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

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

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

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

④腦裂

在 Elasticsearch 集群中主節(jié)點通過 Ping 命令來檢查集群中的其他節(jié)點是否處于可用狀態(tài),同時非主節(jié)點也會通過 Ping 來檢查主節(jié)點是否處于可用狀態(tài)。

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

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

事務(wù)日志

我們在上面了解到,Lucene 為了加快寫索引的速度,采用了延遲寫入的策略。

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

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

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

因為內(nèi)存中的數(shù)據(jù)還會繼續(xù)寫入,所以內(nèi)存中的數(shù)據(jù)并不是以段的形式存儲的,是檢索不到的。

總之,Elasticsearch 是一個準實時的搜索引擎,而不是一個實時的搜索引擎。

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

 

圖 7:Elasticsearch 寫數(shù)據(jù)的過程

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

刷新的主要步驟如下:

  • 將內(nèi)存中的數(shù)據(jù)刷新到一個新的段中,但是該段并沒有持久化到硬盤中,而是緩存在操作系統(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ù)不是以段的形式存儲的,并且可以繼續(xù)向內(nèi)存里寫數(shù)據(jù)。文件緩存系統(tǒng)中的數(shù)據(jù)是以段的形式存儲的,所以只能讀,不能寫;內(nèi)存中的數(shù)據(jù)是搜索不到,文件緩存系統(tǒng)中的數(shù)據(jù)是可以搜索的。

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

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

 

圖 8:Elasticsearch 寫數(shù)據(jù)的過程

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

刷新的主要步驟如下:

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

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

 

圖 9:Elasticsearch 寫數(shù)據(jù)的過程

由上面索引創(chuàng)建的過程可知,內(nèi)存里面的數(shù)據(jù)并沒有直接被刷新(Flush)到硬盤中,而是被刷新(Refresh)到了文件緩存系統(tǒng)中,這主要是因為持久化數(shù)據(jù)十分耗費資源,頻繁地調(diào)用會使寫入的性能急劇下降。

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

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

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

 

圖 10:Elasticsearch 寫操作流程

在集群中寫索引

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

 

圖 11:寫索引

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

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

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

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

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

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

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

  1. shard = hash(routing)%number_of_primary_shards 

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

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

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

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

這也就說明了一旦分片數(shù)定下來就不能再改變的原因,因為分片數(shù)改變之后,所有之前的路由值都會變得無效,前期創(chuàng)建的文檔也就找不到了。

由于在 Elasticsearch 集群中每個節(jié)點都知道集群中的文檔的存放位置(通過路由公式定位),所以每個節(jié)點都有處理讀寫請求的能力。

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

 

圖 12:寫索引

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

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

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

集群中的查詢流程

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

 

圖 13

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

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

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

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

當一個搜索請求被發(fā)送到某個節(jié)點時,這個節(jié)點就變成了協(xié)調(diào)節(jié)點(Node 1)。

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

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

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

 

圖 14

取回數(shù)據(jù)等流程如下(見圖 13,圖片來自官網(wǎng)):

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

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

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

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

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

因為深翻時,from 會很大,這時的排序過程可能會變得非常沉重,會占用大量的 CPU、內(nèi)存和帶寬。因為這個原因,所以強烈建議慎重使用深翻。

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

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

作者:錢丁君

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

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

 

責任編輯:武曉燕 來源: 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

2021-07-21 10:10:14

require前端代碼

2021-11-08 10:00:19

require前端模塊

2019-04-01 09:00:05

程序員編程語言HTML

2015-06-09 15:28:14

編程能力編程

2020-04-22 13:41:33

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

2017-05-10 08:39:34

裝機線纜機箱

2023-06-12 08:36:01

static?DOM文檔

2017-06-05 13:53:10

2017-08-23 09:05:09

交互設(shè)計UI界面

2022-12-09 08:19:43

HTTP協(xié)議MIME

2018-08-15 14:18:34

算法AI機器學(xué)習

2017-11-06 11:15:51

智能機器人工智能機器學(xué)習

2022-09-27 19:07:35

工具Hack延遲

2019-09-21 20:57:59

Android安卓開發(fā)

2017-11-12 21:12:34

HPC

2019-05-13 14:17:06

抓包Web安全漏洞

2020-12-11 07:10:03

程序員
點贊
收藏

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