1 開篇
上一篇文章對(duì)elasticsearch進(jìn)行了整體的介紹简识,初步了解了下elasticsearch的發(fā)展溜歪、應(yīng)用場(chǎng)景赌髓、整體功能架構(gòu)和一些基本的概念祝钢,并且實(shí)際上手搭建了一個(gè)集群做了下文檔的操作府框;elasticsearch是如何進(jìn)行數(shù)據(jù)分片的呢吱窝,集群的容錯(cuò)性又是如何保證的呢。為什么elasticsearch是近實(shí)時(shí)搜索而不是實(shí)時(shí)搜索的呢迫靖?帶著這些問題院峡,我們將從分片容錯(cuò)、選主系宜、索引生命周期照激、倒排索引等核心概念來深入了解elasticsearch。
2 分片與容錯(cuò)
2.1 分片
我們知道通過主分片盹牧,可以將數(shù)據(jù)分布在所有的節(jié)點(diǎn)上实抡,分片提供了集群水平擴(kuò)展的能力欠母,而副本提升了系統(tǒng)的容錯(cuò)性,也在一定程度上提升了讀的吞吐量吆寨,但是降低了索引的速率赏淌。需要根據(jù)實(shí)際的應(yīng)用場(chǎng)景選擇合適的副本數(shù)量。
2.2 故障轉(zhuǎn)移原理
如上圖所示啄清,是一個(gè)由3個(gè)節(jié)點(diǎn)構(gòu)成的集群六水,上面的一個(gè)索引有3個(gè)主分片,每個(gè)分片一個(gè)副本±弊洌現(xiàn)演示一個(gè)節(jié)點(diǎn)宕機(jī)的情況:
將node1節(jié)點(diǎn)宕掉掷贾,elasticsearch會(huì)重新選一個(gè)主節(jié)點(diǎn)。
Node1的p0主分片丟失荣茫,node3上的R0提升為主分片想帅,集群健康狀態(tài)變?yōu)辄S色
-
R0和R1再分配如圖所示,集群變綠
3 選主與腦裂
elasticsearch可以配置多個(gè)master eligible節(jié)點(diǎn)啡莉,每個(gè)節(jié)點(diǎn)啟動(dòng)默認(rèn)都是一個(gè)master eligible節(jié)點(diǎn)港准,這種類型的節(jié)點(diǎn)參與選主流程,然后其中一個(gè)成為master節(jié)點(diǎn)咧欣。
3.1 主節(jié)點(diǎn)作用
維護(hù)了所有的節(jié)點(diǎn)信息
維護(hù)了所有的索引浅缸、mapping、setting信息
維護(hù)了分片信息(所以一個(gè)elasticsearch的分片和索引并不能無限擴(kuò)大魄咕,主節(jié)點(diǎn)有限)
其它節(jié)點(diǎn)同步保存了集群狀態(tài)信息衩椒,只有主節(jié)點(diǎn)才能修改狀態(tài)信息,然后同步給其它節(jié)點(diǎn)哮兰,這樣才能保證一致性
3.2 選主流程
互相ping對(duì)方毛萌,node id低的會(huì)成為被選舉的節(jié)點(diǎn)
新節(jié)點(diǎn)加入,不會(huì)重新選主喝滞,發(fā)現(xiàn)主節(jié)點(diǎn)丟失朝聋,重新選主
這里會(huì)存在一些問題,Master職責(zé)負(fù)載過重囤躁,可能無法即時(shí)對(duì)組內(nèi)成員作出響應(yīng),這是一種假死荔睹。但是其它節(jié)點(diǎn)開始選主狸演,選主成功了,原來的Master恢復(fù)了僻他,因?yàn)樵瓉鞰aster節(jié)點(diǎn)的id優(yōu)先級(jí)最高宵距,又開始一輪選主,重新把原來Master又成為主吨拗,后來它又負(fù)載過重了满哪,這樣便重復(fù)選主婿斥,不可用時(shí)間很長。
3.3 腦裂
當(dāng)出現(xiàn)網(wǎng)絡(luò)分區(qū)問題時(shí)哨鸭,可能導(dǎo)致集群分成兩部分民宿,每一部分都有一個(gè)主
在7.0版本以前:
需要配置一個(gè)參數(shù),必須要大于某個(gè)值才能選舉像鸡,來避免腦裂 比如有三個(gè)master eligible節(jié)點(diǎn)活鹰,這個(gè)參數(shù)discovery.zen.minimum_master_nodes設(shè)置為2,必須兩個(gè)節(jié)點(diǎn)才能選舉只估。計(jì)算方法為Quorum = (master 節(jié)點(diǎn)總數(shù) /2)+ 1
在7.0版本以后
采用基于raft算法志群,無需配置,elasticsearch自身可以做判斷了蛔钙。Raft算法有三個(gè)角色锌云,跟隨者、候選人吁脱、領(lǐng)導(dǎo)者桑涎。標(biāo)準(zhǔn)raft的大致的流程為:節(jié)點(diǎn)發(fā)現(xiàn)沒有主了,發(fā)起選舉豫喧,任期增加石洗,接收的節(jié)點(diǎn)根據(jù)先到先得、任期最大紧显、日志最完整作為條件來確認(rèn)是否投票讲衫。elasticsearch與標(biāo)準(zhǔn)的raft有所改動(dòng)忆绰,比如初始都為候選人启涯、不限制每一個(gè)角色直投一票等等,可以參考博客(https://www.pianshen.com/article/16271108957/)
4 倒排索引
4.1 正排索引
和我們通常理解的索引是一樣的草冈,如一本書的目錄一樣篙程,想查找“spring的結(jié)構(gòu)組成”的章節(jié)枷畏,可以直接定位到22頁
4.2 什么是倒排索引?
如果我們想查找“aop”字樣出現(xiàn)的位置虱饿,那么使用上面的目錄就無法做到了拥诡,只能全文掃描才知道。如何來為這個(gè)建立索引呢氮发?可以使用倒排索引渴肉,這么建立索引:
Aop:23,34爽冕,45仇祭,65
Aspect:24,56颈畸,344
Configuration:245
乌奇。没讲。。礁苗。爬凑。。寂屏。
如上面的索引贰谣,我們就可以輕松地找到“aop”所在的位置了。
Elasticsearch的倒排索引就是文檔的單詞和文檔的id關(guān)聯(lián)
4.3 倒排索引的組成
-
單詞詞典
文檔的所有的單詞迁霎,這個(gè)量可能很多
-
倒排列表
單詞出現(xiàn)的詞頻和單詞所在的位置
倒排索引會(huì)占用很大的空間吱抚,如果不需要檢索,可以不使用倒排索引
4.4 相關(guān)度打分
Elasticsearch5之前考廉,默認(rèn)的相關(guān)性算分為TF-IDF秘豹,現(xiàn)在采?用 BM 25算法作為默認(rèn)打分。
- TF(Term Frequency)表示詞在一篇文檔出現(xiàn)的頻率昌粤。
- DF表示詞在所有?文檔中出現(xiàn)的頻率既绕。
如果只靠TF在文檔出現(xiàn)的頻率計(jì)算相關(guān)度打分可能不準(zhǔn)確,比如“的”字頻率很大涮坐,但是貢獻(xiàn)度不高凄贩,所以還需DF進(jìn)行加權(quán),IDF是DF的反向值袱讹,即IDF越大說明這個(gè)詞的貢獻(xiàn)度更有價(jià)值疲扎。1980年,Robertson用信息論相關(guān)理論證明和優(yōu)化了使用于搜索引擎的TF-IDF公式:
后來人們發(fā)現(xiàn)TF-IDF會(huì)隨TF增大而無限增大捷雕,對(duì)搜索的正確性有所影響椒丧,后續(xù)采用了BM25算法了。具體算法可查看相關(guān)資料救巷。 綜合下來壶熏,可以看出文檔的個(gè)數(shù)、詞頻浦译、文檔的長度都可能影響相關(guān)度得分棒假。
4.5 分詞器
分詞是將文本轉(zhuǎn)化為一系列單詞的過程。轉(zhuǎn)化為單詞后就可以形成倒排索引 分詞是由分詞器實(shí)現(xiàn)的精盅。分詞器主要由三部分組成:
-
character filters
原始文本去除一些特殊符號(hào)帽哑,比如《》之類的
-
Tokenizer
按照規(guī)則切分單詞
-
Token filter
對(duì)切分的單詞進(jìn)行加工,比如轉(zhuǎn)小寫渤弛,刪除一些特殊的單詞
Elasticsearch內(nèi)置了很多分詞器,比如standard分詞器甚带,切分單詞和小寫處理她肯,它是默認(rèn)的分詞器佳头。我們使用rest api可以測(cè)試分詞效果:
GET /_analyze
{
"analyzer": "standard",
"text": "Monitor & Metric"
}
{
"tokens" : [
{
"token" : "monitor",
"start_offset" : 0,
"end_offset" : 7,
"type" : "<ALPHANUM>",
"position" : 0
},
{
"token" : "metric",
"start_offset" : 10,
"end_offset" : 16,
"type" : "<ALPHANUM>",
"position" : 1
}
]
}
如果語句中有中文,則分詞效果不好晴氨,需要使用專門的中文分詞器康嘉,如對(duì)unicode很好支持的icu、ik籽前、thulac analyzer亭珍,需要在elasticserach上先安裝再使用
5 基本流程
5.1 索引
如圖所示,當(dāng)一個(gè)索引請(qǐng)求到了node3節(jié)點(diǎn)上枝哄,產(chǎn)生的流程為:
客戶端向 Node 3 發(fā)送新建肄梨、刪除請(qǐng)求。
node3發(fā)現(xiàn)該文檔屬于p1分片挠锥,轉(zhuǎn)發(fā)請(qǐng)求到node1
Node1完成p1分片的索引众羡,轉(zhuǎn)發(fā)node2、node3請(qǐng)求副本分片
所有的副本分片完成后node3返回結(jié)果蓖租。
5.2 查詢
查詢流程
如圖所示粱侣,當(dāng)一個(gè)查詢請(qǐng)求,請(qǐng)求到node1上蓖宦,它的查詢流程如下:
node1為協(xié)調(diào)節(jié)點(diǎn)齐婴,它隨機(jī)從這6個(gè)主副分片選擇3個(gè)分片發(fā)送查詢請(qǐng)求。
每個(gè)分片查詢返回from size排序好了的id和排序值給協(xié)調(diào)節(jié)點(diǎn)稠茂。
協(xié)調(diào)節(jié)點(diǎn)進(jìn)入query階段柠偶,將每個(gè)分片獲取的id和排序值匯總重新排序,選取from size個(gè)id主慰。
并發(fā)攜帶id向?qū)?yīng)的分片獲取詳細(xì)信息嚣州,然后返回?cái)?shù)據(jù)給客戶端。
存在問題
-
性能問題
每個(gè)分片需要查詢的數(shù)量很大(from+size)共螺,深度分頁的時(shí)候性能消耗很嚴(yán)重该肴。解決:攜帶上一次文檔的id(從業(yè)務(wù)的角度解決)
-
算分不準(zhǔn)
由于每個(gè)分片它的相關(guān)度計(jì)算是根據(jù)自身的分片的指標(biāo)計(jì)算的,和多個(gè)分片數(shù)據(jù)匯總起來的真實(shí)的相關(guān)度排名會(huì)有差異藐不。會(huì)存在整體相關(guān)度不準(zhǔn)的問題匀哄。
解決:
數(shù)據(jù)量少的時(shí)候,使用一個(gè)分片雏蛮,數(shù)據(jù)量大的時(shí)候涎嚼,從統(tǒng)計(jì)學(xué)來看分片的數(shù)據(jù)分布的會(huì)比較均勻,結(jié)果會(huì)趨近于準(zhǔn)確挑秉。
6 分片的生命周期
分片寫入數(shù)據(jù)的時(shí)候法梯,底層采用分段的存儲(chǔ)模式。單個(gè)的倒排索引文件被稱為一個(gè)段。多個(gè)段匯總在一起成為一個(gè)lucene索引立哑,也是一個(gè)elasticsearch索引的一個(gè)分片夜惭。
如上圖所示,為一個(gè)節(jié)點(diǎn)索引文檔的流程:
elasticsearch節(jié)點(diǎn)接收index請(qǐng)求铛绰,存入index buffer诈茧,同步存入磁盤transationlog后返回索引結(jié)果
Refresh定時(shí)將index buffer數(shù)據(jù)生成segment,存入到操作系統(tǒng)緩存捂掰,此時(shí)沒有fsync敢会,清空indexbuffer,此時(shí)就可以被elasticsearch查詢了这嚣,如果index buffer占滿時(shí)鸥昏,也會(huì)觸發(fā)refresh,默認(rèn)為jvm的10%疤苹。
Flush定時(shí)將緩存中的segments寫入到磁盤互广,刪除transactionlog。如果transactionlog滿時(shí)(512m)卧土,也會(huì)觸發(fā)flush惫皱。
如果數(shù)據(jù)很多,segment的也很多尤莺,同時(shí)也可能由刪除的文檔旅敷,elasticsearch會(huì)定期將它們合并。
7 lucene索引初識(shí)
Mysql數(shù)據(jù)庫存儲(chǔ)數(shù)據(jù)使用b+樹颤霎,redis內(nèi)部使用了多種數(shù)據(jù)結(jié)構(gòu)存儲(chǔ)比如跳躍表媳谁。Elasticsearch是一款搜索引擎,核心能力之一就是全文檢索友酱,所以采用了倒排索引晴音。
倒排索引的基本結(jié)構(gòu)上文中已經(jīng)介紹。這里介紹下生成一個(gè)lucene段的結(jié)構(gòu)
我們知道缔杉,倒排索引是由字典和倒排表組成锤躁,在lucnen的字典可以由多種數(shù)據(jù)結(jié)構(gòu)構(gòu)成。比如排序列表或详、hash表系羞、樹、跳躍表等等霸琴。正如上面所說椒振,當(dāng)詞的量很大時(shí),檢索這些詞也是非常費(fèi)勁梧乘,首先可以想到將詞排序澎迎,然后二分法查找,但是詞是海量的話,隨機(jī)io依然很多夹供,為了提升效率辑莫,可以使用term index來查找詞。elasticsearch使用trie(單詞查找)樹罩引,如圖所示
這個(gè)單詞前綴的樹結(jié)構(gòu)在內(nèi)存之中,不像mysql的b+樹需要和磁盤多次io操作枝笨。所以在磁盤中就可以根據(jù)索引樹查找到最終數(shù)據(jù)存在哪一個(gè)磁盤塊中袁铐,然后直接到那塊磁盤中查找對(duì)應(yīng)的倒排列表。上面只是介紹了簡單的trie樹横浑,實(shí)際上lucene采用了FST變種的trie樹剔桨,既能前綴匹配也能后綴匹配,而且大大節(jié)省了空間徙融,后續(xù)有時(shí)間再探討下lucene的FST結(jié)構(gòu)洒缀。如果多字段聯(lián)合檢索,獲取到了多個(gè)倒排列表欺冀,需要對(duì)它們的文檔id取交集树绩,lucene使用Skip List或者bitset的方式計(jì)算交集,有興趣的朋友可以去查閱相關(guān)資料了解具體的過程隐轩。
Lucene 為了提升索引和搜索的效率饺饭,從上層到底層,使用了各種巧妙的數(shù)據(jù)結(jié)構(gòu)和設(shè)計(jì)职车,來支持文檔的倒排檢索瘫俊。
8 總結(jié)
通過本文,我們了解了elasticsearch的核心原理悴灵,知道了elasticsearch底層是基于lucenen類庫來實(shí)現(xiàn)倒排索引的扛芽,通過lucene提供了豐富強(qiáng)大的文檔檢索能力。通過索引先刷入內(nèi)存积瞒、然后定時(shí)刷入磁盤來增強(qiáng)寫入性能川尖,這也是它近實(shí)時(shí)搜索的原因。一個(gè)索引是單機(jī)的赡鲜,elasticsearch通過分片副本機(jī)制來保證了容錯(cuò)性空厌,但是過多的副本會(huì)導(dǎo)致寫入性能下降,需要結(jié)合實(shí)際來設(shè)置副本數(shù)量银酬。6.x及以前的版本需要設(shè)置最小的選主節(jié)點(diǎn)數(shù)來預(yù)防腦裂嘲更,7.x以后通過新的選舉機(jī)制來規(guī)避了這個(gè)問題。篇幅有限揩瞪,后面會(huì)介紹一些elasticsearch有關(guān)的內(nèi)容赋朦。
關(guān)注IT巔峰技術(shù),私信作者,獲取以下2021全球架構(gòu)師峰會(huì)PDF資料宠哄。