ES

1 前言

1 大規(guī)模數(shù)據(jù)如何檢索

當系統(tǒng)數(shù)據(jù)量上了10億、100億條的時候姜挺,我們在做系統(tǒng)架構(gòu)的時候通常會從以下角度去考慮:
1)用什么數(shù)據(jù)庫好晰绎?(MySQL、sybase呛谜、Oracle在跳、達夢、神通隐岛、MongoDB猫妙、Hbase…)
2)如何解決單點故障?(lvs聚凹、F5割坠、A10、Zookeeper元践、MQ…)
3)如何保證數(shù)據(jù)安全性?(熱備童谒、冷備单旁、異地多活)
4)如何解決檢索難題?(數(shù)據(jù)庫代理中間件mysql-proxy饥伊、Cobar象浑、MaxScale…)
5)如何解決統(tǒng)計分析問題?(離線琅豆、近實時)

2 傳統(tǒng)數(shù)據(jù)庫的應(yīng)對解決方案

對于關(guān)系型數(shù)據(jù)愉豺,我們通常采用以下或類似架構(gòu)去解決查詢瓶頸和寫入瓶頸:
1)通過主從備份解決數(shù)據(jù)安全性問題;
2)通過數(shù)據(jù)庫代理中間件心跳監(jiān)測茫因,解決單點故障問題蚪拦;
3)通過代理中間件將查詢語句分發(fā)到各個slave節(jié)點進行查詢,并匯總結(jié)果冻押;
4)通過分表分庫解決讀寫效率問題驰贷;

3 非關(guān)系型數(shù)據(jù)庫的解決方案

對于Nosql數(shù)據(jù)庫,以redis為例洛巢,其它原理類似:
1)通過副本備份保證數(shù)據(jù)安全性括袒;
2)通過節(jié)點競選機制解決單點問題;
3)先從配置庫檢索分片信息稿茉,然后將請求分發(fā)到各個節(jié)點锹锰,最后由路由節(jié)點合并匯總結(jié)果芥炭;

4 完全把數(shù)據(jù)放入內(nèi)存怎么樣

完全把數(shù)據(jù)放在內(nèi)存中是不可靠的,實際上也不太現(xiàn)實恃慧,
當我們的數(shù)據(jù)達到PB級別時园蝠,按照每個節(jié)點96G內(nèi)存計算,在內(nèi)存完全裝滿的數(shù)據(jù)情況下糕伐,
我們需要的機器是:1PB=1024T=1048576G 節(jié)點數(shù)=1048576/96=10922個
實際上砰琢,考慮到數(shù)據(jù)備份,節(jié)點數(shù)往往在2.5萬臺左右良瞧。成本巨大決定了其不現(xiàn)實陪汽!
從前面我們了解到,把數(shù)據(jù)放在內(nèi)存也好褥蚯,不放在內(nèi)存也好挚冤,都不能完完全全解決問題。
全部放在內(nèi)存速度問題是解決了赞庶,但成本問題上來了训挡。
為解決以上問題,從源頭著手分析歧强,通常會從以下方式來尋找方法:
1 存儲數(shù)據(jù)時按有序存儲澜薄;
2 將數(shù)據(jù)和索引分離;
3 壓縮數(shù)據(jù)摊册;

2 全文檢索技術(shù)

2.1 什么是全文檢索

Lucene 是一個高效的肤京,基于Java 的全文檢索庫。
什么叫做全文檢索呢茅特?這要從我們生活中的數(shù)據(jù)說起忘分。
我們生活中的數(shù)據(jù)總體分為兩種:結(jié)構(gòu)化數(shù)據(jù)和非結(jié)構(gòu)化數(shù)據(jù)。
結(jié)構(gòu)化數(shù)據(jù):指具有固定格式或有限長度的數(shù)據(jù)白修,如數(shù)據(jù)庫妒峦、元數(shù)據(jù)等。
非結(jié)構(gòu)化數(shù)據(jù):指不定長或無固定格式的數(shù)據(jù)兵睛,如郵件肯骇、word文檔等。
當然有的地方還會提到第三種祖很,半結(jié)構(gòu)化數(shù)據(jù)累盗,如XML,HTML等突琳,當根據(jù)需要可按結(jié)構(gòu)化數(shù)據(jù)來處理若债,也可抽取出純文本按非結(jié)構(gòu)化數(shù)據(jù)來處理。
非結(jié)構(gòu)化數(shù)據(jù)又一種叫法叫全文數(shù)據(jù)拆融。

按照數(shù)據(jù)的分類蠢琳,搜索也分為兩種:
對結(jié)構(gòu)化數(shù)據(jù)的搜索: 如對數(shù)據(jù)庫的搜索啊终,用SQL語句。再如對元數(shù)據(jù)的搜索傲须,如利用windows 搜索對文件名蓝牲,類型,修改時間進行搜索等泰讽。
對非結(jié)構(gòu)化數(shù)據(jù)的搜索: 如利用windows的搜索也可以搜索文件內(nèi)容例衍,Linux下的grep命令,再如用Google和百度可以搜索大量內(nèi)容數(shù)據(jù)已卸。

對非結(jié)構(gòu)化數(shù)據(jù)也即全文數(shù)據(jù)的搜索主要有兩種方法:順序掃描法和全文檢索佛玄。

所謂順序掃描,比如要找內(nèi)容包含某一個字符串的文件累澡,就是一個文檔一個文檔的看梦抢,對于每一個文檔,從頭看到尾愧哟,如果此文檔包含此字符串奥吩,則此文檔為我們要找的文件,接著看下一個文件蕊梧,直到掃描完所有的文件霞赫。如利用windows的搜索也可以搜索文件內(nèi)容,只是相當?shù)穆适浮H绻阌幸粋€80G硬盤端衰,如果想在上面找到一個內(nèi)容包含某字符串的文件,不花他幾個小時橄抹,怕是做不到靴迫。Linux下的grep命令也是這一種方式惕味。大家可能覺得這種方法比較原始楼誓,但對于小數(shù)據(jù)量的文件,這種方法還是最直接名挥,最方便的疟羹。但是對于大量的文件,這種方法就很慢了禀倔。
有人可能會說榄融,對非結(jié)構(gòu)化數(shù)據(jù)順序掃描很慢,對結(jié)構(gòu)化數(shù)據(jù)的搜索卻相對較快(由于結(jié)構(gòu)化數(shù)據(jù)有一定的結(jié)構(gòu)可以采取一定的搜索算法加快速度)救湖,那么把我們的非結(jié)構(gòu)化數(shù)據(jù)想辦法弄得有一定結(jié)構(gòu)不就行了嗎愧杯?這種想法很天然,卻構(gòu)成了全文檢索的基本思路鞋既,也即將非結(jié)構(gòu)化數(shù)據(jù)中的一部分信息提取出來力九,重新組織耍铜,使其變得有一定結(jié)構(gòu),然后對此有一定結(jié)構(gòu)的數(shù)據(jù)進行搜索跌前,從而達到搜索相對較快的目的棕兼。這部分從非結(jié)構(gòu)化數(shù)據(jù)中提取出的然后重新組織的信息,我們稱之索引 抵乓。這種說法比較抽象伴挚,舉幾個例子就很容易明白,比如字典灾炭,字典的拼音表和部首檢字表就相當于字典的索引茎芋,對每一個字的解釋是非結(jié)構(gòu)化的,如果字典沒有音節(jié)表和部首檢字表咆贬,在茫茫辭海中找一個字只能順序掃描败徊。然而字的某些信息可以提取出來進行結(jié)構(gòu)化處理,比如讀音掏缎,就比較結(jié)構(gòu)化皱蹦,分聲母和韻母,分別只有幾種可以一一列舉眷蜈,于是將讀音拿出來按一定的順序排列蜡峰,每一項讀音都指向此字的詳細解釋的頁數(shù)。我們搜索時按結(jié)構(gòu)化的拼音搜到讀音劫拗,然后按其指向的頁數(shù)为居,便可找到我們的非結(jié)構(gòu)化數(shù)據(jù)——也即對字的解釋。這種先建立索引忌怎,再對索引進行搜索的過程就叫全文檢索(Full-text Search) 籍滴。

下面這幅圖來自《Lucene in action》,但卻不僅僅描述了Lucene的檢索過程榴啸,而是描述了全文檢索的一般過程孽惰。
全文檢索大體分兩個過程,索引創(chuàng)建 (Indexing) 和搜索索引 (Search) 鸥印。

01.png

索引創(chuàng)建:將現(xiàn)實世界中所有的結(jié)構(gòu)化和非結(jié)構(gòu)化數(shù)據(jù)提取信息勋功,創(chuàng)建索引的過程。
搜索索引:就是得到用戶的查詢請求库说,搜索創(chuàng)建的索引狂鞋,然后返回結(jié)果的過程。

2.2 索引里面究竟存些什么

索引里面究竟需要存些什么呢潜的?
首先我們來看為什么順序掃描的速度慢骚揍。其實是由于我們想要搜索的信息和非結(jié)構(gòu)化數(shù)據(jù)中所存儲的信息不一致造成的。
非結(jié)構(gòu)化數(shù)據(jù)中所存儲的信息是每個文件包含哪些字符串啰挪,也即已知文件信不,欲求字符串相對容易纤掸,也即是從文件到字符串的映射。而我們想搜索的信息是哪些文件包含此字符串浑塞,也即已知字符串借跪,欲求文件,也即從字符串到文件的映射酌壕。兩者恰恰相反掏愁。于是如果索引總能夠保存從字符串到文件的映射,則會大大提高搜索速度卵牍。由于從字符串到文件的映射是文件到字符串映射的反向過程果港,于是保存這種信息的索引稱為反向索引
反向索引的所保存的信息一般如下:
假設(shè)我的文檔集合里面有100篇文檔糊昙,為了方便表示辛掠,我們?yōu)槲臋n編號從1到100,得到下面的結(jié)構(gòu)

02.png

左邊保存的是一系列字符串释牺,稱為詞典 萝衩。每個字符串都指向包含此字符串的文檔(Document)鏈表,此文檔鏈表稱為倒排表 (Posting List)没咙。有了索引猩谊,便使保存的信息和要搜索的信息一致,可以大大加快搜索的速度祭刚。比如說牌捷,我們要尋找既包含字符串“l(fā)ucene”又包含字符串“solr”的文檔,我們只需要以下幾步:
① 取出包含字符串“l(fā)ucene”的文檔鏈表涡驮。
② 取出包含字符串“solr”的文檔鏈表暗甥。
③ 通過合并鏈表,找出既包含“l(fā)ucene”又包含“solr”的文件捉捅。

03.png

看到這個地方撤防,有人可能會說,全文檢索的確加快了搜索的速度锯梁,但是多了索引的過程即碗,兩者加起來不一定比順序掃描快多少焰情。的確陌凳,加上索引的過程,全文檢索不一定比順序掃描快内舟,尤其是在數(shù)據(jù)量小的時候更是如此合敦,而對一個很大量的數(shù)據(jù)創(chuàng)建索引也是一個很慢的過程,然而兩者還是有區(qū)別的验游。順序掃描是每次都要掃描充岛,而創(chuàng)建索引的過程僅僅需要一次保檐,以后便是一勞永逸的了,每次搜索崔梗,創(chuàng)建索引的過程不必經(jīng)過夜只,僅僅搜索創(chuàng)建好的索引就可以了。這也是全文搜索相對于順序掃描的優(yōu)勢之一:一次索引蒜魄,多次使用扔亥。

2.3 如何創(chuàng)建索引

全文檢索的索引創(chuàng)建過程一般有以下幾步:

第一步:一些要索引的原文檔(Document)。

為了方便說明索引創(chuàng)建過程谈为,這里特意用兩個文件為例:
文件一:Students should be allowed to go out with their friends, but not allowed to drink beer.
文件二:My friend Jerry went to school to see his students but found them drunk which is not allowed.

第二步:將原文檔傳給分詞器(Tokenizer)旅挤。

分詞器(Tokenizer)會做以下幾件事情(此過程稱為Tokenize) :
① 將文檔分成一個一個單獨的單詞。
② 去除標點符號伞鲫。
③ 去除停詞(Stop word) 粘茄。
所謂停詞(Stop word)就是一種語言中最普通的一些單詞,由于沒有特別的意義秕脓,因而大多數(shù)情況下不能成為搜索的關(guān)鍵詞柒瓣,因而創(chuàng)建索引時,這種詞會被去掉而減少索引的大小吠架。英語中停詞如:“the”嘹朗,“a”,“this”等诵肛。對于每一種語言的分詞組件(Tokenizer)屹培,都有一個停詞(stop word)集合。經(jīng)過分詞(Tokenizer) 后得到的結(jié)果稱為詞元(Token) 怔檩。在我們的例子中褪秀,便得到以下詞元(Token):

“Students”,“allowed”薛训,“go”媒吗,“their”,“friends”乙埃,“allowed”闸英,“drink”,“beer”介袜,“My”甫何,“friend”,
“Jerry”遇伞,“went”辙喂,“school”,“see”,“his”巍耗,“students”秋麸,“found”,“them”炬太,“drunk”灸蟆,“allowed”。

第三步:將得到的詞元(Token)傳給語言處理組件(Linguistic Processor)亲族。

語言處理組件(linguistic processor)主要是對得到的詞元(Token)做一些同語言相關(guān)的處理次乓。
對于英語,語言處理組件(Linguistic Processor) 一般做以下幾點:
① 變?yōu)樾?Lowercase) 孽水。
② 將單詞縮減為詞根形式票腰,如“cars ”到“car ”等。這種操作稱為:stemming 女气。
③ 將單詞轉(zhuǎn)變?yōu)樵~根形式杏慰,如“drove ”到“drive ”等。這種操作稱為:lemmatization 炼鞠。

Stemming 和 lemmatization的異同
相同之處:Stemming和lemmatization都要使詞匯成為詞根形式缘滥。
兩者的方式不同
Stemming采用的是“縮減”的方式:“cars”到“car”,“driving”到“drive”谒主。
Lemmatization采用的是“轉(zhuǎn)變”的方式:“drove”到“drove”朝扼,“driving”到“drive”。
兩者的算法不同
Stemming主要是采取某種固定的算法來做這種縮減霎肯,如去除“s”擎颖,去除“ing”加“e”,將“ational”變?yōu)椤癮te”观游,將“tional”變?yōu)椤皌ion”搂捧。
Lemmatization主要是采用保存某種字典的方式做這種轉(zhuǎn)變。比如字典中有“driving”到“drive”懂缕,“drove”到“drive”允跑,“am, is, are”到“be”的映射,做轉(zhuǎn)變時搪柑,只要查字典就可以了聋丝。
Stemming和lemmatization不是互斥關(guān)系,是有交集的工碾,有的詞利用這兩種方式都能達到相同的轉(zhuǎn)換弱睦。
語言處理組件(linguistic processor)的結(jié)果稱為詞(Term)
在我們的例子中倚喂,經(jīng)過語言處理每篷,得到的詞(Term)如下:

“student”,“allow”端圈,“go”焦读,“their”,“friend”舱权,“allow”矗晃,“drink”,“beer”宴倍,“my”张症,“friend”,
“jerry”鸵贬,“go”俗他,“school”,“see”阔逼,“his”兆衅,“student”,“find”嗜浮,“them”羡亩,“drink”,“allow”危融。

也正是因為有語言處理的步驟畏铆,才能使搜索drove,而drive也能被搜索出來吉殃。

第四步:將得到的詞(Term)傳給索引組件(Indexer)辞居。

索引組件(Indexer)主要做以下幾件事情:
① 利用得到的詞(Term)創(chuàng)建一個字典。
在我們的例子中字典如下:

Term Document ID
student 1
allow 1
go 1
their 1
friend 1
allow 1
drink 1
beer 1
my 2
friend 2
jerry 2
go 2
school 2
see 2
his 2
student 2
find 2
them 2
drink 2
allow 2

② 對字典按字母順序進行排序蛋勺。

Term Document ID
allow 1
allow 1
allow 2
beer 1
drink 1
drink 2
find 2
friend 1
friend 2
go 1
go 2
his 2
jerry 2
my 2
school 2
see 2
student 1
student 2
their 1
them 2

③ 合并相同的詞(Term) 成為文檔倒排(Posting List) 鏈表速侈。


04.png

在此表中,有幾個定義:
Document Frequency 即文檔頻次迫卢,表示總共有多少文件包含此詞(Term)倚搬。
Frequency 即詞頻率,表示此文件中包含了幾個此詞(Term)乾蛤。
所以對詞(Term) “allow”來講每界,總共有兩篇文檔包含此詞(Term),從而詞(Term)后面的文檔鏈表總共有兩項家卖,第一項表示包含“allow”的第一篇文檔眨层,即1號文檔,此文檔中上荡,“allow”出現(xiàn)了2次趴樱,第二項表示包含“allow”的第二個文檔馒闷,是2號文檔,此文檔中叁征,“allow”出現(xiàn)了1次纳账。
到此為止,索引已經(jīng)創(chuàng)建好了捺疼,我們可以通過它很快的找到我們想要的文檔疏虫。
而且在此過程中,我們驚喜地發(fā)現(xiàn)啤呼,搜索“drive”卧秘,“driving”,“drove”官扣,“driven”也能夠被搜到翅敌。因為在我們的索引中,“driving”惕蹄,“drove”哼御,“driven”都會經(jīng)過語言處理而變成“drive”,在搜索時焊唬,如果您輸入“driving”恋昼,輸入的查詢語句同樣經(jīng)過我們這里的一到三步,從而變?yōu)椴樵儭癲rive”赶促,從而可以搜索到想要的文檔液肌。

2.4 如何對索引進行搜索

到這里似乎我們可以宣布“我們找到想要的文檔了”。然而事情并沒有結(jié)束鸥滨,找到了僅僅是全文檢索的一個方面嗦哆。不是嗎?如果僅僅只有一個或十個文檔包含我們查詢的字符串婿滓,我們的確找到了老速。然而如果結(jié)果有一千個,甚至成千上萬個呢凸主?哪個又是您最想要的文件呢橘券?
打開Google吧,比如說您想在微軟找份工作卿吐,于是您輸入“Microsoft job”旁舰,您卻發(fā)現(xiàn)總共有22600000個結(jié)果返回。好大的數(shù)字呀嗡官,突然發(fā)現(xiàn)找不到是一個問題箭窜,找到的太多也是一個問題。在如此多的結(jié)果中衍腥,如何將最相關(guān)的放在最前面呢磺樱?


05.png

當然Google做的很不錯纳猫,您一下就找到了jobs at Microsoft。想象一下竹捉,如果前幾個全部是“Microsoft does a good job at software industry…”將是多么可怕的事情呀芜辕。如何像Google一樣,在成千上萬的搜索結(jié)果中活孩,找到和查詢語句最相關(guān)的呢物遇?如何判斷搜索出的文檔和查詢語句的相關(guān)性呢乖仇?這要回到我們第三個問題:如何對索引進行搜索憾儒?
搜索主要分為以下幾步:

第一步:用戶輸入查詢語句。

查詢語句同我們普通的語言一樣乃沙,也是有一定語法的起趾。不同的查詢語句有不同的語法,如SQL語句就有一定的語法警儒。查詢語句的語法根據(jù)全文檢索系統(tǒng)的實現(xiàn)而不同训裆。最基本的有比如:AND, OR, NOT等。舉個例子蜀铲,用戶輸入語句:lucene AND learned NOT hadoop边琉。說明用戶想找一個包含lucene和learned然而不包括hadoop的文檔。

第二步:對查詢語句進行詞法分析记劝、語法分析及語言處理变姨。

由于查詢語句有語法壮锻,因而也要進行詞法分析历涝、語法分析及語言處理。

  1. 詞法分析主要用來識別單詞和關(guān)鍵字聚请。
    如上述例子中怒竿,經(jīng)過詞法分析砍鸠,得到單詞有l(wèi)ucene,learned耕驰,hadoop, 關(guān)鍵字有AND, NOT爷辱。如果在詞法分析中發(fā)現(xiàn)不合法的關(guān)鍵字,則會出現(xiàn)錯誤朦肘。如lucene AMD learned托嚣,其中由于AND拼錯,導(dǎo)致AMD作為一個普通的單詞參與查詢厚骗。

  2. 語法分析主要是根據(jù)查詢語句的語法規(guī)則來形成一棵語法樹示启。
    如果發(fā)現(xiàn)查詢語句不滿足語法規(guī)則,則會報錯领舰。如lucene NOT AND learned夫嗓,則會出錯迟螺。如上述例子,lucene AND learned NOT hadoop形成的語法樹如下:


    06.png
  3. 語言處理同索引過程中的語言處理幾乎相同舍咖。
    如learned變成learn等矩父。經(jīng)過第二步,我們得到一棵經(jīng)過語言處理的語法樹排霉。


    07.png

第三步:搜索索引窍株,得到符合語法樹的文檔。

此步驟又分幾小步攻柠。首先球订,在反向索引表中,分別找出包含lucene瑰钮,learn冒滩,hadoop的文檔鏈表。其次浪谴,對包含lucene开睡,learn的鏈表進行合并操作,得到既包含lucene又包含learn的文檔鏈表苟耻。然后篇恒,將此鏈表與hadoop的文檔鏈表進行差操作,去除包含hadoop的文檔凶杖,從而得到既包含lucene又包含learn而且不包含hadoop的文檔鏈表胁艰。此文檔鏈表就是我們要找的文檔。

第四步:根據(jù)得到的文檔和查詢語句的相關(guān)性官卡,對結(jié)果進行排序蝗茁。

雖然在上一步,我們得到了想要的文檔寻咒,然而對于查詢結(jié)果應(yīng)該按照與查詢語句的相關(guān)性進行排序哮翘,越相關(guān)者越靠前。
如何計算文檔和查詢語句的相關(guān)性呢毛秘?不如我們把查詢語句看作一片短小的文檔饭寺,對文檔與文檔之間的相關(guān)性(relevance)進行打分(scoring),分數(shù)高的相關(guān)性好叫挟,就應(yīng)該排在前面艰匙。
那么又怎么對文檔之間的關(guān)系進行打分呢? 這可不是一件容易的事情抹恳!下面看一下如何判斷文檔之間的關(guān)系员凝。
首先,一個文檔有很多詞(Term)組成 奋献,如search, lucene, full-text, this, a, what等健霹。
其次對于文檔之間的關(guān)系旺上,不同的Term重要性不同 。比如對于本篇文檔糖埋,search, Lucene, full-text就相對重要一些宣吱,this, a , what可能相對不重要一些。所以如果兩篇文檔都包含search, Lucene瞳别,fulltext征候,這兩篇文檔的相關(guān)性好一些,然而就算一篇文檔包含this, a, what祟敛,另一篇文檔不包含this, a, what疤坝,也不能影響兩篇文檔的相關(guān)性。
因而判斷文檔之間的關(guān)系垒棋,首先找出哪些詞(Term)對文檔之間的關(guān)系最重要卒煞,如search, Lucene, fulltext痪宰。然后判斷這些詞(Term)之間的關(guān)系叼架。
找出詞(Term) 對文檔的重要性的過程稱為計算詞的權(quán)重(Term weight) 的過程。計算詞的權(quán)重(term weight)有兩個參數(shù)衣撬,第一個是詞(Term)乖订,第二個是文檔(Document)。詞的權(quán)重(Term weight)表示此詞(Term)在此文檔中的重要程度具练,越重要的詞(Term)有越大的權(quán)重(Term weight)乍构,因而在計算文檔之間的相關(guān)性中將發(fā)揮更大的作用。
判斷詞(Term) 之間的關(guān)系從而得到文檔相關(guān)性的過程應(yīng)用一種叫做向量空間模型的算法(Vector Space Model) 扛点。下面仔細分析一下這兩個過程

1 計算權(quán)重(Term weight)的過程哥遮。
影響一個詞(Term)在一篇文檔中的重要性主要有兩個因素:Term Frequency (tf):即此Term在此文檔中出現(xiàn)了多少次。tf 越大說明越重要陵究。Document Frequency (df):即有多少文檔包含次Term眠饮。df 越大說明越不重要。容易理解嗎铜邮?詞(Term)在文檔中出現(xiàn)的次數(shù)越多仪召,說明此詞(Term)對該文檔越重要,如“搜索”這個詞松蒜,在本文檔中出現(xiàn)的次數(shù)很多扔茅,說明本文檔主要就是講這方面的事的。然而在一篇英語文檔中秸苗,this出現(xiàn)的次數(shù)更多召娜,就說明越重要嗎?不是的惊楼,這是由第二個因素進行調(diào)整玖瘸,第二個因素說明吐句,有越多的文檔包含此詞(Term), 說明此詞(Term)太普通,不足以區(qū)分這些文檔店读,因而重要性越低嗦枢。
這也如我們程序員所學的技術(shù),對于程序員本身來說屯断,這項技術(shù)掌握越深越好(掌握越深說明花時間看的越多文虏,tf越大),找工作時越有競爭力殖演。然而對于所有程序員來說氧秘,這項技術(shù)懂得的人越少越好(懂得的人少df小)趴久,找工作越有競爭力丸相。人的價值在于不可替代性就是這個道理。道理明白了彼棍,我們來看看公式:


08.png

這僅僅只term weight計算公式的簡單典型實現(xiàn)灭忠。實現(xiàn)全文檢索系統(tǒng)的人會有自己的實現(xiàn),Lucene就與此稍有不同座硕。
2 判斷Term之間的關(guān)系從而得到文檔相關(guān)性的過程弛作,也即向量空間模型的算法(VSM)。
我們把文檔看作一系列詞(Term)华匾,每一個詞(Term)都有一個權(quán)重(Term weight)映琳,不同的詞(Term)根據(jù)自己在文檔中的權(quán)重來影響文檔相關(guān)性的打分計算。于是我們把所有此文檔中詞(term)的權(quán)重(term weight) 看作一個向量蜘拉。
Document = {term1, term2, …… ,term N}
Document Vector = {weight1, weight2, …… ,weight N}
同樣我們把查詢語句看作一個簡單的文檔萨西,也用向量來表示。
Query = {term1, term 2, …… , term N}
Query Vector = {weight1, weight2, …… , weight N}
我們把所有搜索出的文檔向量及查詢向量放到一個N維空間中旭旭,每個詞(term)是一維谎脯。
如圖:


09.png

我們認為兩個向量之間的夾角越小,相關(guān)性越大您机。
所以我們計算夾角的余弦值作為相關(guān)性的打分穿肄,夾角越小,余弦值越大际看,打分越高咸产,相關(guān)性越大。
有人可能會問仲闽,查詢語句一般是很短的脑溢,包含的詞(Term)是很少的,因而查詢向量的維數(shù)很小,而文檔很長屑彻,包含詞(Term)很多验庙,文檔向量維數(shù)很大。你的圖中兩者維數(shù)怎么都是N呢社牲?
在這里粪薛,既然要放到相同的向量空間,自然維數(shù)是相同的搏恤,不同時违寿,取二者的并集,如果不含某個詞(Term)時熟空,則權(quán)重(Term Weight)為0藤巢。
相關(guān)性打分公式如下:


10.png

舉個例子,查詢語句有11個Term息罗,共有三篇文檔搜索出來掂咒。其中各自的權(quán)重(Term weight),如下表格迈喉。

t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 t11
D1 0 0 .477 0 .477 .176 0 0 0 .176 0
D2 0 .176 0 .477 0 0 0 0 .954 0 .176
D3 0 .176 0 0 0 .176 0 0 0 .176 .176
Q 0 0 0 0 0 .176 0 0 .477 0 .176

于是計算绍刮,三篇文檔同查詢語句的相關(guān)性打分分別為:


11-1.png

11-2.png

11-3.png

于是文檔二相關(guān)性最高,先返回弊添,其次是文檔一录淡,最后是文檔三捌木。
到此為止油坝,我們可以找到我們最想要的文檔了。

以上信息檢索技術(shù)(Information retrieval)中的基本理論刨裆,Lucene是對這種基本理論的一種基本的的實踐澈圈。對上述索引創(chuàng)建和搜索過程所一個總結(jié),如圖:
此圖參照http://www.lucene.com.cn/about.htm 中文章《開放源代碼的全文檢索引擎Lucene》

12.png

1 索引過程:
a) 有一系列被索引文件
b) 被索引文件經(jīng)過語法分析和語言處理形成一系列詞(Term) 帆啃。
c) 經(jīng)過索引創(chuàng)建形成詞典和反向索引表瞬女。
d) 通過索引存儲將索引寫入硬盤。

2 搜索過程:
a) 用戶輸入查詢語句努潘。
b) 對查詢語句經(jīng)過語法分析和語言分析得到一系列詞(Term) 诽偷。
c) 通過語法分析得到一個查詢樹。
d) 通過索引存儲將索引讀入到內(nèi)存疯坤。
e) 利用查詢樹搜索索引报慕,從而得到每個詞(Term) 的文檔鏈表,對文檔鏈表進行交压怠,差眠冈,并得到結(jié)果文檔。
f) 將搜索到的結(jié)果文檔對查詢的相關(guān)性進行排序菌瘫。
g) 返回查詢結(jié)果給用戶蜗顽。

2.5 全文檢索場景

搜索引擎
站內(nèi)搜索
系統(tǒng)文件搜索

2.6 全文檢索相關(guān)技術(shù)

2.6.1 Lucene

Lucene 是一個高效的布卡,基于Java 的全文檢索庫。Lucene 能夠為文本類型的數(shù)據(jù)建立索引雇盖,所以你只要能把你要索引的數(shù)據(jù)格式轉(zhuǎn)化的文本的忿等,Lucene 就能對你的文檔進行索引和搜索。比如你要對一些 HTML 文檔崔挖、PDF 文檔進行索引的話这弧,你就首先需要把 HTML 文檔和 PDF 文檔轉(zhuǎn)化成文本格式的,然后將轉(zhuǎn)化后的內(nèi)容交給 Lucene 進行索引虚汛,然后把創(chuàng)建好的索引文件保存到磁盤或者內(nèi)存中匾浪,最后根據(jù)用戶輸入的查詢條件在索引文件上進行查詢。不指定要索引的文檔的格式也使 Lucene 能夠幾乎適用于所有的搜索應(yīng)用程序卷哩。
如果使用該技術(shù)實現(xiàn)蛋辈,需要對Lucene的API和底層原理非常了解,而且需要編寫大量的Java代碼将谊。

2.6.2 Solr

solr是一個高性能的冷溶、采用Java5開發(fā)的、基于Lucene的全文搜索服務(wù)器尊浓。同時對其進行了擴展逞频,提供了比Lucene更為豐富的查詢語言。同時實現(xiàn)了可配置栋齿,可擴展并對查詢性能進行了優(yōu)化苗胀,并且提供了一個完善的功能管理界面,是一款非常優(yōu)秀的全文搜索引擎瓦堵。
solr的工作方式是文檔通過Http利用XML加到一個搜索集合中基协,查詢該集合也是通過http收到一個XML/JSON響應(yīng)來實現(xiàn)。它的主要特性包括:高效菇用、靈活的緩存功能澜驮,垂直搜索功能,高亮顯示搜索結(jié)果惋鸥,通過索引復(fù)制來提高可用性杂穷,提供一套強大Data Schema來定義字段,、類型和設(shè)置文本分析卦绣,提供基于Web的管理界面等耐量。總結(jié)一句話就是用戶可以通過http請求向搜索引擎服務(wù)器提交一定格式的xml文件生成索引,也可以通過http get操作提出查詢的請求得到xml/json格式的返回結(jié)果迎卤。

2.6.3 ElasticSearch

ElasticSearch是一個基于Lucene的搜索服務(wù)器拴鸵。它提供了一個分布式多用戶能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java開發(fā)的劲藐,并作為Apache許可條款下的開放源碼發(fā)布八堡,是當前流行的企業(yè)級搜索引擎。設(shè)計用于云計算中聘芜,能夠達到實時搜索兄渺,穩(wěn)定,可靠汰现,快速挂谍,安裝使用方便。

2.6.4 Solr和ES的比較

檢索速度的比較
當單純的對已有數(shù)據(jù)進行搜索時瞎饲,Solr更快口叙。

13.png

當實時建立索引時, Solr會產(chǎn)生io阻塞,查詢性能較差嗅战,Elasticsearch具有明顯的優(yōu)勢妄田。


14.png

隨著數(shù)據(jù)量的增加,Solr的搜索效率會變得更低驮捍,而Elasticsearch卻沒有明顯的變化疟呐。


15.png

大型互聯(lián)網(wǎng)公司,實際生產(chǎn)環(huán)境測試东且,將搜索引擎從Solr轉(zhuǎn)到Elasticsearch以后的平均查詢速度有了50倍的提升启具。


16.png

17.png

最終的結(jié)論:Solr 是傳統(tǒng)搜索應(yīng)用的有力解決方案,但 Elasticsearch 更適用于新興的實時搜索應(yīng)用珊泳。

3 ElasticSearch介紹

3.1 ES基本概述

Elasticserach由來:許多年前鲁冯,一個叫Shay Banon的待業(yè)工程師跟隨他的新婚妻子來到倫敦,他的妻子想在倫敦學習做一名廚師旨椒。而他在倫敦尋找工作的期間晓褪,接觸到了Lucene的早期版本,他想為自己的妻子開發(fā)一個方便搜索菜譜的應(yīng)用综慎。
Elasticsearch發(fā)布的第一個版本是在2010年的二月份,從那之后勤庐,Elasticsearch便成了Github上最受人矚目的項目之一示惊,并且很快就有超過300名開發(fā)者加入進來貢獻了自己的代碼。后來Shay和另一位合伙人成立了公司專注打造Elasticsearch愉镰,他們對Elasticsearch進行了一些商業(yè)化的包裝和支持米罚。但是,Elasticsearch承諾丈探,永遠都將是開源并且免費的录择。
Elastic為主體的公司提供了很多優(yōu)秀的解決方案,拿到很多的投資,現(xiàn)已上市,后來收購 logstash、kibana及一些其他的服務(wù)油额。

  • ElasticSearch
  • Logstash
  • Kibana

3.2 ES是什么

Elasticsearch是一個開源的高擴展的分布式全文檢索引擎寝优。Elasticsearch也使用Java開發(fā)并使用Lucene作為其核心來實現(xiàn)所有索引和搜索的功能。但是它的目的是通過簡單的RESTfulAPI來隱藏Lucene的復(fù)雜性十减,從而讓全文搜索變得簡單。Elasticsearch是面向文檔型數(shù)據(jù)庫,一條數(shù)據(jù)在這里就是一個文檔须误,用JSON作為文檔序列化的格式,比如下面這條用戶數(shù)據(jù):

{ 
"name" : "John", 
"sex" : "Male", 
"age" : 25, 
"birthDate": "1990/05/01", 
"about" : "I love to go rock climbing", 
"interests": [ "sports", "music" ] 
}

3.3 為什么要使用ES

1)2013年初仇轻,GitHub拋棄了Solr京痢,采取ElasticSearch 來做PB級的搜索。
2)維基百科啟動以elasticsearch為基礎(chǔ)的核心搜索架構(gòu)篷店。
3)SoundCloud使用ElasticSearch為1.8億用戶提供即時而精準的音樂搜索服務(wù)历造。
4)百度目前廣泛使用ElasticSearch作為文本數(shù)據(jù)分析,采集百度所有服務(wù)器上的各類指標數(shù)據(jù)及用戶自定義數(shù)據(jù)船庇,通過對各種數(shù)據(jù)進行多維分析展示吭产,輔助定位分析實例異常或業(yè)務(wù)層面異常鸭轮。目前覆蓋百度內(nèi)部20多個業(yè)務(wù)線(包括casio臣淤、云分析、網(wǎng)盟窃爷、預(yù)測邑蒋、文庫、直達號按厘、錢包医吊、風控等),單集群最大100臺機器逮京,200個ES節(jié)點卿堂,每天導(dǎo)入30TB+數(shù)據(jù)。

實際項目開發(fā)實戰(zhàn)中懒棉,幾乎每個系統(tǒng)都會有一個搜索的功能草描,當搜索做到一定程度時,維護和擴展起來難度就會慢慢變大策严,所以很多公司都會把搜索單獨獨立出一個模塊穗慕,用ElasticSearch等來實現(xiàn)。

近年ElasticSearch發(fā)展迅猛妻导,已經(jīng)超越了其最初的純搜索引擎的角色逛绵,現(xiàn)在已經(jīng)增加了數(shù)據(jù)聚合分析可視化的特性怀各,如果你有數(shù)百萬的文檔需要通過關(guān)鍵詞進行定位時,ElasticSearch肯定是最佳選擇术浪。

當然瓢对,如果你的文檔是JSON的,你也可以把ElasticSearch當作一種“NoSQL數(shù)據(jù)庫”添吗,應(yīng)用ElasticSearch數(shù)據(jù)聚合分析(aggregation)的特性沥曹,針對數(shù)據(jù)進行多維度的分析。

3.4 ES有什么能力

Elasticsearch 是一個分布式可擴展的實時搜索和分析引擎碟联,一個建立在全文搜索引擎 Apache Lucene(TM) 基礎(chǔ)上的搜索引擎.
實際項目開發(fā)實戰(zhàn)中妓美,幾乎每個系統(tǒng)都會有一個搜索的功能,當數(shù)據(jù)達到很大且搜索要做到一定程度時鲤孵,維護和擴展難度就會越來越高壶栋,并且在全文檢索的速度上、結(jié)果內(nèi)容的推薦普监、分析以及統(tǒng)計聚合方面也很難達到我們預(yù)期效果贵试。Elasticsearch ,它不僅包括了全文搜索功能凯正,還可以進行以下工作:

  • 分布式實時文件存儲毙玻,并將每一個字段都編入索引,使其可以被搜索廊散。
  • 實時分析的分布式搜索引擎桑滩。
  • 可以擴展到上百臺服務(wù)器,處理PB級別的結(jié)構(gòu)化或非結(jié)構(gòu)化數(shù)據(jù)允睹。

3.5 使用場景

3.5.1 存儲

ES天然支持分布式运准,具備存儲海量數(shù)據(jù)的能力,其搜索和數(shù)據(jù)分析的功能都建立在ES存儲的海量的數(shù)據(jù)之上缭受;ES很方便的作為海量數(shù)據(jù)的存儲工具胁澳,特別是在數(shù)據(jù)量急劇增長的當下,ES結(jié)合爬蟲等數(shù)據(jù)收集工具可以發(fā)揮很大用處

3.5.2 搜索

ES使用倒排索引米者,每個字段都被索引且可用于搜索韭畸,更是提供了豐富的搜索api,在海量數(shù)據(jù)下近實時實現(xiàn)近秒級的響應(yīng)塘雳,基于Lucene的開源搜索引擎陆盘,為搜索引擎(全文檢索,高亮败明,搜索推薦等)提供了檢索的能力。

具體場景:

  1. Stack Overflow(國外的程序異常討論論壇)太防,IT問題妻顶,程序的報錯酸员,提交上去,有人會跟你討論和回答讳嘱,全文檢索幔嗦,搜索相關(guān)問題和答案,程序報錯了沥潭,就會將報錯信息粘貼到里面去邀泉,搜索有沒有對應(yīng)的答案
  2. GitHub(開源代碼管理),搜索上千億行代碼
  3. 電商網(wǎng)站钝鸽,檢索商品
  4. 日志數(shù)據(jù)分析汇恤,logstash采集日志,ES進行復(fù)雜的數(shù)據(jù)分析(ELK技術(shù))

3.5.3 數(shù)據(jù)分析

ES也提供了大量數(shù)據(jù)分析的api和豐富的聚合能力拔恰,支持在海量數(shù)據(jù)的基礎(chǔ)上進行數(shù)據(jù)的分析和處理因谎。

具體場景:
爬蟲爬取不同電商平臺的某個商品的數(shù)據(jù),通過ElasticSearch進行數(shù)據(jù)分析(各個平臺的歷史價格颜懊、購買力等等)

3.5.4 數(shù)據(jù)預(yù)警

結(jié)合第三方插件wathcer監(jiān)控數(shù)據(jù)

4 ES的架構(gòu)

18.png

4.1 網(wǎng)關(guān)Gateway層

Gateway是ES用來存儲索引文件的一個文件系統(tǒng)且它支持很多類型财岔。例如:本地磁盤、共享存儲(做snapshot的 時候需要用到)河爹、hadoop的hdfs分布式存儲匠璧、亞馬遜的S3。

職責:它的主要職責是用來對數(shù)據(jù)進行長持久化以及整個集群重啟之后可以通過gateway重新恢復(fù)數(shù)據(jù)咸这。
代表 es索引的持久化存儲方式夷恍,es默認是先把索引存放到內(nèi)存中,當內(nèi)存滿了時再持久化到硬盤炊苫。

數(shù)據(jù)安全:當這個es集群關(guān)閉再重新啟動時就會從gateway中讀取索引數(shù)據(jù)裁厅。es支持多種類型的gateway,有本地文件系統(tǒng)(默認)侨艾、分布式文件系統(tǒng)执虹、Hadoop的HDFS和amazon的s3云存儲服務(wù)。

存儲數(shù)據(jù): 存儲索引信息唠梨、集群信息袋励、mapping 等等

4.2 DLD

Gateway上層就是一個分布式的lucene框架。lucene是做檢索的当叭,但是它是一個單機的搜索引擎茬故,像這種es分布式搜索引擎系統(tǒng),雖然底層用lucene蚁鳖,但是需要在每個節(jié)點上都運行l(wèi)ucene進行相應(yīng)的索引磺芭、查詢以及更新,所以需要做成一個分布式的運行框架來滿足業(yè)務(wù)的需要醉箕。

4.3 四大模塊組件

DDL 之上就是一些ES的四大模塊 :

  • Index Module是索引模塊钾腺,就是對數(shù)據(jù)建立索引也就是通常所說的建立一些倒排索引等徙垫;
  • Search Module是搜索模塊,就是對數(shù)據(jù)進行查詢搜索放棒;
  • Mapping Module是數(shù)據(jù)映射與解析模塊姻报,就是你的數(shù)據(jù)的每個字段可以根據(jù)你建立的表結(jié)構(gòu)通過mapping進行映射解析。如果你沒有建立表結(jié)構(gòu)间螟,es就會根據(jù)你的數(shù)據(jù)類型推測你的數(shù)據(jù)結(jié)構(gòu)之后自己生成mapping吴旋,然后都是根據(jù)這個mapping進行解析你的數(shù)據(jù);
  • River Module厢破,在es2.0之后應(yīng)該是被取消了荣瑟,它的意思表示是第三方插件,例如可以通過一些自定義的腳本將傳統(tǒng)的數(shù)據(jù)庫(mysql)等數(shù)據(jù)源通過格式化轉(zhuǎn)換后直接同步到es集群里溉奕。這個River大部分是自己寫的褂傀,寫出來的東西質(zhì)量參差不齊,將這些東西集成到es中會引發(fā)很多內(nèi)部bug加勤,嚴重影響了es的正常應(yīng)用仙辟,所以在es2.0之后考慮將其去掉。

4.4 Discovery鳄梅、Scripting 和第三方插件

Discovery是ES的節(jié)點發(fā)現(xiàn)模塊叠国,不同機器上的ES節(jié)點要組成集群需要進行消息通信,集群內(nèi)部需要選舉master節(jié)點戴尸,這些工作都是由Discovery模塊完成粟焊。支持多種發(fā)現(xiàn)機制,如 Zen 孙蒙、EC2项棠、gce、Azure挎峦。Scripting用來支持在查詢語句中插入javascript香追、python等腳本語言,scripting模塊負責解析這些腳本坦胶,使用腳本語句性能稍低透典。ES也支持多種第三方插件。

4.5 Transport顿苇、JMX

再上層是ES的傳輸模塊和JMX傳輸模塊支持多種傳輸協(xié)議峭咒,如 Thrift、memecached纪岁、http凑队,默認使用http。JMX是java的管理框架幔翰,用來管理ES應(yīng)用顽决。

4.6 RESTful接口層

最上層就是ES暴露給我們的訪問接口短条。官方推薦的方案就是這種Restful接口导匣,直接發(fā)送http請求才菠,方便后續(xù)使用nginx做代理、分發(fā)包括可能后續(xù)會做權(quán)限的管理贡定,通過http很容易做這方面的管理赋访。 如果使用java客戶端,它是直接調(diào)用api缓待,在做負載均衡以及權(quán)限管理還是不太好做蚓耽。

5 ES集群架構(gòu)

5.1 核心概念

5.1.1 集群(Cluster)

ES集群是一個或多個節(jié)點的集合,它們共同存儲了整個數(shù)據(jù)集旋炒,并提供了聯(lián)合索引以及可跨所有節(jié)點的搜索能力步悠。多節(jié)點組成的集群擁有冗余能力,它可以在一個或幾個節(jié)點出現(xiàn)故障時保證服務(wù)的整體可用性瘫镇。集群靠其獨有的名稱進行標識鼎兽,默認名稱為“elasticsearch”。節(jié)點靠其集群名稱來決定加入哪個ES集群铣除,一個節(jié)點只能屬一個集群谚咬。
集群中有多個節(jié)點(node),其中有一個為主節(jié)點尚粘,這個主節(jié)點是可以通過選舉產(chǎn)生的择卦, 主從節(jié)點是對于集群內(nèi)部來說的。
ES的一個概念就是去中心化郎嫁,字面上理解就是無中心節(jié)點秉继,這是對于集群外部來說的,因為從外部來看ES 集群泽铛,在邏輯上是個整體尚辑,你與任何一個節(jié)點的通信和與整個ES集群通信是等價的。

5.1.2 節(jié)點(node)

一個節(jié)點是一個邏輯上獨立的服務(wù)厚宰,可以存儲數(shù)據(jù)并參與集群的索引和搜索功能腌巾,一個節(jié)點也有唯一的名字,群集通過節(jié)點名稱進行管理和通信.

5.1.2.1 主節(jié)點

主節(jié)點的主要職責是和集群操作相關(guān)的內(nèi)容铲觉,如創(chuàng)建或刪除索引澈蝙、跟蹤哪些節(jié)點是群集的一部分、并決定哪些分片分配給相關(guān)的節(jié)點撵幽。穩(wěn)定的主節(jié)點對集群的健康是非常重要的灯荧。
雖然主節(jié)點也可以協(xié)調(diào)節(jié)點,路由搜索和從客戶端新增數(shù)據(jù)到數(shù)據(jù)節(jié)點盐杂,但最好不要使用這些專用的主節(jié)點逗载。一個重要的原則是哆窿,盡可能做盡量少的工作。
對于大型的生產(chǎn)集群來說厉斟,推薦使用一個專門的主節(jié)點來控制集群挚躯,該節(jié)點將不處理任何用戶請求。
主節(jié)點配置如下(elasticsearch.yml):

node.master: true 
node.data: false

5.1.2.2 數(shù)據(jù)節(jié)點

持有數(shù)據(jù)和倒排索引擦秽。負責存儲數(shù)據(jù)码荔,提供建立索引和搜索索引的服務(wù)。 data節(jié)點消耗內(nèi)存和磁盤IO的性能比較大感挥。
數(shù)據(jù)節(jié)點配置如下(elasticsearch.yml):

node.master: false 
node.data: true

5.1.2.3 協(xié)調(diào)節(jié)點(客戶端節(jié)點)

它既不能保持數(shù)據(jù)也不能成為主節(jié)點缩搅,該節(jié)點可以響應(yīng)用戶的情況,把相關(guān)操作發(fā)送到其他節(jié)點触幼∨鸢辏客戶端節(jié)點會將客戶端請求路由到集群中合適的分片上。對于讀請求來說置谦,協(xié)調(diào)節(jié)點每次會選擇不同的分片處理請求堂鲤,以實現(xiàn)負載均衡。
數(shù)據(jù)節(jié)點配置如下(elasticsearch.yml):

node.master: false 
node.data: false

注意:master設(shè)置為true表示此節(jié)點可以成為master霉祸,但并不一定是master筑累。data設(shè)置為true表示可以存儲數(shù)據(jù),設(shè)置為false表示不可以存儲數(shù)據(jù)丝蹭。ES節(jié)點master和data屬性均默認為true慢宗,默認情況下是三功能節(jié)點合一的,既可以成為master候選節(jié)點奔穿,又可以存儲數(shù)據(jù)镜沽,還可以成為協(xié)調(diào)節(jié)點。

5.1.2.4 部落節(jié)點

部落節(jié)點可以跨越多個集群贱田,它可以接收每個集群的狀態(tài)缅茉,然后合并成一個全局集群的狀態(tài),它可以讀寫所有節(jié)點上的數(shù)據(jù)男摧。

5.1.3 索引(Index)

索引是具有類似特性的文檔的集合蔬墩,ES將數(shù)據(jù)存儲于一個或多個索引中。類比傳統(tǒng)的關(guān)系型數(shù)據(jù)庫領(lǐng)域來說耗拓,索引相當于SQL中的一個數(shù)據(jù)庫拇颅,或者一個數(shù)據(jù)存儲方案(schema)。索引由其名稱(必須為全小寫字符)進行標識乔询,并通過引用此名稱完成文檔的創(chuàng)建樟插、搜索、更新及刪除操作。一個ES集群中可以按需創(chuàng)建任意數(shù)目的索引黄锤。

5.1.4 文檔類型(Type)

類型是索引內(nèi)部的邏輯分區(qū)(category/partition)搪缨,然而其意義完全取決于用戶需求。因此鸵熟,一個索引內(nèi)部可定義一個或多個類型(type)副编。一般來說,類型就是為那些擁有相同的域的文檔做的預(yù)定義旅赢。例如齿桃,在索引中,可以定義一個用于存儲用戶數(shù)據(jù)的類型煮盼,一個存儲日志數(shù)據(jù)的類型,以及一個存儲評論數(shù)據(jù)的類型带污。類比傳統(tǒng)的關(guān)系型數(shù)據(jù)庫領(lǐng)域來說僵控,類型相當于“表”。

5.1.5 文檔(Document)

文檔是Lucene索引和搜索的原子單位鱼冀,它是包含了一個或多個域的容器报破,基于JSON格式進行表示。文檔由一個或多個域組成千绪,每個域擁有一個名字及一個或多個值充易,有多個值的域通常稱為“多值域”。每個文檔可以存儲不同的域集荸型,但同一類型下的文檔至應(yīng)該有某種程度上的相似之處盹靴。相當于數(shù)據(jù)庫的“記錄”。

5.1.6 映射(Mapping)

相當于數(shù)據(jù)庫中的schema瑞妇,用來約束字段的類型稿静,不過 Elasticsearch 的 mapping 可以自動根據(jù)數(shù)據(jù)創(chuàng)建。
ES中辕狰,所有的文檔在存儲之前都要首先進行分析改备。用戶可根據(jù)需要定義如何將文本分割成token、哪些token應(yīng)該被過濾掉以及哪些文本需要進行額外處理等等蔓倍。

5.1.7 分片(shard)

ES的分片(shard)機制可將一個索引內(nèi)部的數(shù)據(jù)分布地存儲于多個節(jié)點悬钳,它通過將一個索引切分為多個底層物理的Lucene索引完成索引數(shù)據(jù)的分割存儲功能,這每一個物理的Lucene索引稱為一個分片(shard)偶翅。
每個分片其內(nèi)部都是一個全功能且獨立的索引默勾,因此可由集群中的任意主機存儲。創(chuàng)建索引時倒堕,用戶可指定其分片的數(shù)量灾测,默認數(shù)量為5個。
Shard有兩種類型:primary和replica,即主shard及副本shard媳搪。

5.1.7.1 Primary shard

用于文檔存儲铭段,每個新的索引會自動創(chuàng)建5個Primary shard,當然此數(shù)量可在索引創(chuàng)建之前通過配置自行定義秦爆,不過序愚,一旦創(chuàng)建完成,其Primary shard的數(shù)量將不可更改等限。

5.1.7.2 Replica shard

是Primary Shard的副本爸吮,用于冗余數(shù)據(jù)及提高搜索性能。每個Primary shard默認配置了一個Replica shard望门,但也可以配置多個形娇,且其數(shù)量可動態(tài)更改。ES會根據(jù)需要自動增加或減少這些Replica shard的數(shù)量筹误。

ES集群可由多個節(jié)點組成桐早,各Shard分布式地存儲于這些節(jié)點上。ES可自動在節(jié)點間按需要移動shard厨剪,例如增加節(jié)點或節(jié)點故障時哄酝。簡而言之,分片實現(xiàn)了集群的分布式存儲,而副本實現(xiàn)了其分布式處理及冗余功能。

5.2 核心原理

  • ElasticSearch 的主旨是隨時可用和按需擴容脯宿。 擴容可以通過購買性能更強大(垂直擴容或縱向擴容) 或者數(shù)量更多的服務(wù)器(水平擴容或橫向擴容 )來實現(xiàn)。
  • 雖然 Elasticsearch 可以獲益于更強大的硬件設(shè)備搀军,但是垂直擴容是有極限的。真正的擴容能力是來自于水平擴容抡秆,即為集群添加更多的節(jié)點奕巍,并且將負載壓力和穩(wěn)定性分散到這些節(jié)點中。
  • 對于大多數(shù)的數(shù)據(jù)庫而言儒士,通常需要對應(yīng)用程序進行非常大的改動的止,才能利用上橫向擴容的新增資源。 與之相反的是着撩,ElastiSearch天生就是分布式的 诅福,它知道如何通過管理多節(jié)點來提高擴容性和可用性。 這也意味著你的應(yīng)用無需關(guān)注這個問題拖叙。
  • 一個運行中的 Elasticsearch 實例稱為一個 節(jié)點氓润,而集群是由一個或者多個擁有相同 cluster.name 配置的節(jié)點組成, 它們共同承擔數(shù)據(jù)和負載的壓力薯鳍。當有節(jié)點加入集群中或者從集群中移除節(jié)點時咖气,集群將會重新平均分布所有的數(shù)據(jù)。
  • 當一個節(jié)點被選舉成為主節(jié)點時, 它將負責管理集群范圍內(nèi)的所有變更崩溪,例如增加浅役、刪除索引,或者增加伶唯、刪除節(jié)點等觉既,而主節(jié)點并不需要涉及到文檔級別的變更和搜索等操作。所以當集群只擁有一個主節(jié)點的情況下乳幸,即使流量的增加它也不會成為瓶頸瞪讼。 任何節(jié)點都可以成為主節(jié)點。我們的示例集群就只有一個節(jié)點粹断,所以它同時也成為了主節(jié)點符欠。
  • 作為用戶,我們可以將請求發(fā)送到集群中的任何節(jié)點 姿染,包括主節(jié)點背亥。 每個節(jié)點都知道任意文檔所處的位置,并且能夠?qū)⑽覀兊恼埱笾苯愚D(zhuǎn)發(fā)到存儲我們所需文檔的節(jié)點悬赏。 無論我們將請求發(fā)送到哪個節(jié)點,它都能負責從各個包含我們所需文檔的節(jié)點收集回數(shù)據(jù)娄徊,并將最終結(jié)果返回給客戶端闽颇。 Elasticsearch 對這一切的管理都是透明的。

5.2.1 集群健康:

Elasticsearch 的集群監(jiān)控信息中包含了許多的統(tǒng)計數(shù)據(jù)寄锐,其中最為重要的一項就是集群健康 兵多, 它在 status 字段中展示為 green 、 yellow 或者 red 橄仆。

GET /_cluster/health

在一個不包含任何索引的空集群中剩膘,它將會有一個類似于如下所示的返回內(nèi)容:

{
   "cluster_name":          "elasticsearch",
   "status":                "green", 
   "timed_out":             false,
   "number_of_nodes":       1,
   "number_of_data_nodes":  1,
   "active_primary_shards": 0,
   "active_shards":         0,
   "relocating_shards":     0,
   "initializing_shards":   0,
   "unassigned_shards":     0
}
  • green:所有的主分片和副本分片都正常運行。
  • yellow:所有的主分片都正常運行盆顾,但不是所有的副本分片都正常運行怠褐。
  • red:有主分片沒能正常運行。

5.2.2 添加索引

我們往Elasticsearch添加數(shù)據(jù)時需要用到索引 —— 保存相關(guān)數(shù)據(jù)的地方您宪。索引實際上是指向一個或者多個物理 分片的邏輯命名空間 奈懒。
一個分片是一個底層的工作單元 ,它僅保存了全部數(shù)據(jù)中的一部分宪巨。在分片內(nèi)部機制中磷杏,我們將詳細介紹分片是如何工作的,而現(xiàn)在我們只需知道一個分片是一個 Lucene 的實例捏卓,以及它本身就是一個完整的搜索引擎极祸。 我們的文檔被存儲和索引到分片內(nèi),但是應(yīng)用程序是直接與索引而不是與分片進行交互
Elasticsearch 是利用分片將數(shù)據(jù)分發(fā)到集群內(nèi)各處的遥金。分片是數(shù)據(jù)的容器浴捆,文檔保存在分片內(nèi),分片又被分配到集群內(nèi)的各個節(jié)點里汰规。 當你的集群規(guī)模擴大或者縮小時汤功, Elasticsearch 會自動的在各節(jié)點中遷移分片,使得數(shù)據(jù)仍然均勻分布在集群里溜哮。
一個分片可以是主分片或者副本分片滔金。索引內(nèi)任意一個文檔都歸屬于一個主分片,所以主分片的數(shù)目決定著索引能夠保存的最大數(shù)據(jù)量茂嗓。技術(shù)上來說餐茵,一個主分片最大能夠存儲 Integer.MAX_VALUE - 128 個文檔,但是實際最大值還需要參考你的使用場景述吸,包括你使用的硬件忿族、文檔的大小和復(fù)雜程度、索引和查詢文檔的方式以及你期望的響應(yīng)時長蝌矛。一個副本分片只是一個主分片的拷貝道批。 副本分片作為硬件故障時保護數(shù)據(jù)不丟失的冗余備份,并為搜索和返回文檔等讀操作提供服務(wù)入撒。在索引建立的時候就已經(jīng)確定了主分片數(shù)隆豹,但是副本分片數(shù)可以隨時修改
讓我們在包含一個空節(jié)點的集群內(nèi)創(chuàng)建名為 blogs 的索引茅逮。 索引在默認情況下會被分配5個主分片璃赡,但是為了演示目的,我們將分配3個主分片和一份副本(每個主分片擁有一個副本分片):

PUT /blogs
{
   "settings" : {
      "number_of_shards" : 3,
      "number_of_replicas" : 1
   }
}

下圖是擁有一個索引的單節(jié)點集群:


19.png

如果我們現(xiàn)在查看集群健康献雅, 我們將看到如下內(nèi)容:

{
  "cluster_name": "elasticsearch",
  "status": "yellow", 
  "timed_out": false,
  "number_of_nodes": 1,
  "number_of_data_nodes": 1,
  "active_primary_shards": 3,
  "active_shards": 3,
  "relocating_shards": 0,
  "initializing_shards": 0,
  "unassigned_shards": 3, 
  "delayed_unassigned_shards": 0,
  "number_of_pending_tasks": 0,
  "number_of_in_flight_fetch": 0,
  "task_max_waiting_in_queue_millis": 0,
  "active_shards_percent_as_number": 50
}

集群的健康狀況為 yellow 則表示全部主分片都正常運行(集群可以正常服務(wù)所有請求)碉考,但是副本分片沒有全部處在正常狀態(tài)。 實際上挺身,所有3個副本分片都是 unassigned —— 它們都沒有被分配到任何節(jié)點侯谁。 在同一個節(jié)點上既保存原始數(shù)據(jù)又保存副本是沒有意義的,因為一旦失去了那個節(jié)點瞒渠,我們也將丟失該節(jié)點上的所有副本數(shù)據(jù)良蒸。
當前我們的集群是正常運行的,但是在硬件故障時有丟失數(shù)據(jù)的風險伍玖。

5.2.3 添加故障轉(zhuǎn)移

當集群中只有一個節(jié)點在運行時嫩痰,意味著會有一個單點故障問題——沒有冗余。 幸運的是窍箍,我們只需再啟動一個節(jié)點即可防止數(shù)據(jù)丟失串纺。
為了測試第二個節(jié)點啟動后的情況丽旅,你可以在同一個目錄內(nèi),完全依照啟動第一個節(jié)點的方式來啟動一個新節(jié)點(參考安裝并運行 Elasticsearch)纺棺。多個節(jié)點可以共享同一個目錄榄笙。當你在同一臺機器上啟動了第二個節(jié)點時,只要它和第一個節(jié)點有同樣的 cluster.name 配置祷蝌,它就會自動發(fā)現(xiàn)集群并加入到其中茅撞。但是在不同機器上啟動節(jié)點的時候,為了加入到同一集群巨朦,你需要配置一個可連接到的單播主機列表米丘。 詳細信息請查看最好使用單播代替組播

20.png

當?shù)诙€節(jié)點加入到集群后,3個副本分片將會分配到這個節(jié)點上——每個主分片對應(yīng)一個副本分片糊啡。這意味著當集群內(nèi)任何一個節(jié)點出現(xiàn)問題時拄查,我們的數(shù)據(jù)都完好無損。(TODO)
所有新近被索引的文檔都將會保存在主分片上棚蓄,然后被并行的復(fù)制到對應(yīng)的副本分片上堕扶。這就保證了我們既可以從主分片又可以從副本分片上獲得文檔。
cluster-health 現(xiàn)在展示的狀態(tài)為 green 梭依,這表示所有6個分片(包括3個主分片和3個副本分片)都在正常運行稍算。

{
  "cluster_name": "elasticsearch",
  "status": "green", 
  "timed_out": false,
  "number_of_nodes": 2,
  "number_of_data_nodes": 2,
  "active_primary_shards": 3,
  "active_shards": 6,
  "relocating_shards": 0,
  "initializing_shards": 0,
  "unassigned_shards": 0,
  "delayed_unassigned_shards": 0,
  "number_of_pending_tasks": 0,
  "number_of_in_flight_fetch": 0,
  "task_max_waiting_in_queue_millis": 0,
  "active_shards_percent_as_number": 100
}

5.2.4 水平擴容

怎樣為我們的正在增長中的應(yīng)用程序按需擴容呢?當啟動了第三個節(jié)點役拴,我們的集群如下圖所示邪蛔。

21.png

Node 1 和 Node 2 上各有一個分片被遷移到了新的 Node 3 節(jié)點,現(xiàn)在每個節(jié)點上都擁有2個分片扎狱,而不是之前的3個。 這表示每個節(jié)點的硬件資源(CPU, RAM, I/O)將被更少的分片所共享勃教,每個分片的性能將會得到提升淤击。
分片是一個功能完整的搜索引擎,它擁有使用一個節(jié)點上的所有資源的能力故源。 我們這個擁有6個分片(3個主分片和3個副本分片)的索引可以最大擴容到6個節(jié)點污抬,每個節(jié)點上存在一個分片,并且每個分片擁有所在節(jié)點的全部資源绳军。如果我們想擴容超過6個節(jié)點怎么辦印机?主分片的數(shù)目在索引創(chuàng)建時就已經(jīng)確定了下來。實際上门驾,這個數(shù)目定義了這個索引能夠存儲的最大數(shù)據(jù)量射赛。 但是,讀操作(搜索和返回數(shù)據(jù))可以同時被主分片或副本分片所處理奶是,所以當你擁有越多的副本分片時楣责,也將擁有越高的吞吐量竣灌。
在運行中的集群上是可以動態(tài)調(diào)整副本分片數(shù)目的 ,我們可以按需伸縮集群秆麸。讓我們把副本數(shù)從默認的 1 增加到 2 :

PUT /blogs/_settings
{
   "number_of_replicas" : 2
}

blogs 索引現(xiàn)在擁有9個分片:3個主分片和6個副本分片初嘹。這意味著我們可以將集群擴容到9個節(jié)點,每個節(jié)點上一個分片沮趣。相比原來3個節(jié)點時屯烦,集群搜索性能可以提升3倍。


22.png

當然房铭,如果只是在相同節(jié)點數(shù)目的集群上增加更多的副本分片并不能提高性能驻龟,因為每個分片從節(jié)點上獲得的資源會變少。 你需要增加更多的硬件資源來提升吞吐量育叁。但是更多的副本分片數(shù)提高了數(shù)據(jù)冗余量:按照上面的節(jié)點配置迅脐,我們可以在失去2個節(jié)點的情況下不丟失任何數(shù)據(jù)。

5.2.5 應(yīng)對故障

Elasticsearch 可以應(yīng)對節(jié)點故障豪嗽,接下來讓我們嘗試下這個功能谴蔑。 如果我們關(guān)閉第一個節(jié)點,這時集群的狀態(tài)為:


23.png

我們關(guān)閉的節(jié)點是一個主節(jié)點龟梦。而集群必須擁有一個主節(jié)點來保證正常工作隐锭,所以發(fā)生的第一件事情就是選舉一個新的主節(jié)點 Node 2 。
在我們關(guān)閉 Node 1 的同時也失去了主分片 1 和 2 计贰,并且在缺失主分片的時候索引也不能正常工作钦睡。 如果此時來檢查集群的狀況,我們看到的狀態(tài)將會為 red :不是所有主分片都在正常工作躁倒。
幸運的是荞怒,在其它節(jié)點上存在著這兩個主分片的完整副本, 所以新的主節(jié)點立即將這些分片在 Node 2 和 Node 3 上對應(yīng)的副本分片提升為主分片秧秉, 此時集群的狀態(tài)將會為 yellow 褐桌。
這個提升主分片的過程是瞬間發(fā)生的,如同按下一個開關(guān)一般象迎。
為什么我們集群狀態(tài)是 yellow 而不是 green 呢荧嵌?雖然我們擁有所有的三個主分片,但是同時設(shè)置了每個主分片需要對應(yīng)2份副本分片砾淌,而此時只存在一份副本分片啦撮。 所以集群不能為 green 的狀態(tài)。
不過我們不必過于擔心汪厨,如果我們同樣關(guān)閉了 Node 2 赃春,我們的程序依然可以保持在不丟任何數(shù)據(jù)的情況下運行,因為 Node 3 為每一個分片都保留著一份副本骄崩。
如果我們重新啟動 Node 1 聘鳞,集群可以將缺失的副本分片再次進行分配薄辅,那么集群的狀態(tài)也將如圖 “將參數(shù) number_of_replicas 調(diào)大到 2”所示。 如果 Node 1 依然擁有著之前的分片抠璃,它將嘗試去重用它們站楚。同時僅從主分片復(fù)制發(fā)生了修改的數(shù)據(jù)文件。

5.2.6 文檔存儲原理

創(chuàng)建索引的時候我們只需要指定分片數(shù)和副本數(shù)搏嗡,ES 就會自動將文檔數(shù)據(jù)分發(fā)到對應(yīng)的分片和副本中窿春。那么文件究竟是如何分布到集群的,又是如何從集群中獲取的呢采盒? Elasticsearch 雖然隱藏這些底層細節(jié)旧乞,讓我們好專注在業(yè)務(wù)開發(fā)中,但是我們深入探索這些核心的技術(shù)細節(jié)磅氨,這能幫助你更好地理解數(shù)據(jù)如何被存儲到這個分布式系統(tǒng)中尺栖。

5.2.6.1 文檔是如何路由到分片中的

當索引一個文檔的時候,文檔會被存儲到一個主分片中烦租。 Elasticsearch 如何知道一個文檔應(yīng)該存放到哪個分片中呢延赌?當我們創(chuàng)建文檔時,它如何決定這個文檔應(yīng)當被存儲在分片 1 還是分片 2 中呢叉橱?
首先這肯定不會是隨機的挫以,否則將來要獲取文檔的時候我們就不知道從何處尋找了。實際上窃祝,這個過程是根據(jù)下面這個公式?jīng)Q定的:

shard = hash(routing) % number_of_primary_shards

routing 是一個可變值掐松,默認是文檔的 _id ,也可以設(shè)置成一個自定義的值粪小。

PUT /index/item/id?routing = _id (默認) 
PUT /index/item/id?routing = user_id(自定義路由)---- 指定把某些值固定路由到某個分片上面大磺。 

routing 通過 hash 函數(shù)生成一個數(shù)字,然后這個數(shù)字再除以 number_of_primary_shards (主分片的數(shù)量)后得到 余數(shù) 探膊。這個分布在 0 到 number_of_primary_shards-1 之間的余數(shù)量没,就是我們所尋求的文檔所在分片的位置。
這就解釋了為什么我們要在創(chuàng)建索引的時候就確定好主分片的數(shù)量并且永遠不會改變這個數(shù)量:因為如果數(shù)量變化了突想,那么所有之前路由的值都會無效,文檔也再也找不到了究抓。
你可能覺得由于 Elasticsearch 主分片數(shù)量是固定的會使索引難以進行擴容猾担,所以在創(chuàng)建索引的時候合理的預(yù)分配分片數(shù)是很重要的。
所有的文檔 API( get 刺下、 index 绑嘹、 delete 、 bulk 橘茉、 update 以及 mget )都接受一個叫做 routing 的路由參數(shù) 工腋,通過這個參數(shù)我們可以自定義文檔到分片的映射姨丈。一個自定義的路由參數(shù)可以用來確保所有相關(guān)的文檔——例如所有屬于同一個用戶的文檔——都被存儲到同一個分片中。

5.2.6.2 主分片和副本分片如何交互

上面介紹了一個文檔是如何路由到一個分片中的擅腰,那么主分片是如何和副本分片交互的呢蟋恬?
假設(shè)有個集群由三個節(jié)點組成, 它包含一個叫 user 的索引趁冈,有兩個主分片歼争,每個主分片有兩個副本分片。相同分片的副本不會放在同一節(jié)點渗勘,所以我們的集群看起來如下圖所示:

24.png

我們可以發(fā)送請求到集群中的任一節(jié)點沐绒。每個節(jié)點都有能力處理任意請求。每個節(jié)點都知道集群中任一文檔位置旺坠,所以可以直接將請求轉(zhuǎn)發(fā)到需要的節(jié)點上乔遮。 在下面的例子中,將所有的請求發(fā)送到 Node 1 取刃,我們將其稱為協(xié)調(diào)節(jié)點(coordinating node)蹋肮。
當發(fā)送請求的時候,為了擴展負載蝉衣,更好的做法是輪詢集群中所有的節(jié)點括尸。
對文檔的新建、索引和刪除請求都是寫操作病毡,必須在主分片上面完成之后才能被復(fù)制到相關(guān)的副本分片濒翻。


25.png

以下是在主副分片和任何副本分片上面成功新建索引和刪除文檔所需要的步驟順序:
①客戶端向 Node 1 發(fā)送新建、索引或者刪除請求啦膜。
②節(jié)點使用文檔的 _id 確定文檔屬于分片 0 有送。請求會被轉(zhuǎn)發(fā)到 Node 3,因為分片 0 的主分片目前被分配在 Node 3 上僧家。
③Node 3 在主分片上面執(zhí)行請求雀摘。如果成功了,它將請求并行轉(zhuǎn)發(fā)到 Node1 和 Node2 的副本分片上八拱。一旦所有的副本分片都報告成功阵赠,Node 3 將向協(xié)調(diào)節(jié)點報告成功,協(xié)調(diào)節(jié)點向客戶端報告成功肌稻。
在客戶端收到成功響應(yīng)時清蚀,文檔變更已經(jīng)在主分片和所有副本分片執(zhí)行完成,變更是安全的爹谭。
在處理讀取請求時枷邪,協(xié)調(diào)結(jié)點在每次請求的時候都會通過輪詢所有的副本分片來達到負載均衡。
在文檔被檢索時诺凡,已經(jīng)被索引的文檔可能已經(jīng)存在于主分片上但是還沒有復(fù)制到副本分片东揣。在這種情況下践惑,副本分片可能會報告文檔不存在,但是主分片可能成功返回文檔嘶卧。一旦索引請求成功返回給用戶尔觉,文檔在主分片和副本分片都是可用的。

5.2.7 索引存儲原理

創(chuàng)建索引的時候脸候,我們通過Mapping 映射定義好索引的基本結(jié)構(gòu)信息穷娱,接下來我們肯定需要往 ES 里面新增業(yè)務(wù)文檔數(shù)據(jù)了,例如用戶运沦、日志等業(yè)務(wù)數(shù)據(jù)泵额。新增的業(yè)務(wù)數(shù)據(jù),我們根據(jù) Mapping 來生成對應(yīng)的倒排索引信息 携添。
嫁盲、我們一直說,Elasticsearch是一個基于Apache Lucene 的開源搜索引擎烈掠。Elasticsearch的搜索高效的原因并不是像Redis那樣重依賴內(nèi)存的羞秤,而是通過建立特殊的索引數(shù)據(jù)結(jié)構(gòu)--倒排索引--實現(xiàn)的。由于它的使用場景(處理PB級結(jié)構(gòu)化或非結(jié)構(gòu)化數(shù)據(jù))數(shù)據(jù)量大且需要持久化防止斷電丟失左敌,所以 Elasticsearch 的數(shù)據(jù)和索引存儲是依賴于服務(wù)器的硬盤瘾蛋。倒排索引可以說是Elasticsearch搜索高效和支持非結(jié)構(gòu)化數(shù)據(jù)檢索的主要原因了,但是倒排索引被寫入磁盤后是不可改變的矫限,它永遠不會修改哺哼。

5.2.7.1 段和提交點

倒排索引的不可變性,這點主要是因為 Elasticsearch 的底層是基于 Lucene叼风,而在 Lucene 中提出了按段搜索的概念取董,即,將一個索引文件拆分為多個子文件无宿,則每個子文件叫作段茵汰,每個段都是一個獨立的可被搜索的數(shù)據(jù)集,并且段具有不變性孽鸡,一旦索引的數(shù)據(jù)被寫入硬盤蹂午,就不可再修改

段的概念提出主要是因為:在早期全文檢索中為整個文檔集合建立了一個很大的倒排索引并將其寫入磁盤中彬碱。如果索引有更新画侣,就需要重新全量創(chuàng)建一個索引來替換原來的索引,這種方式在數(shù)據(jù)量很大時效率很低堡妒,并且由于創(chuàng)建一次索引的成本很高,所以對數(shù)據(jù)的更新不能過于頻繁溉卓,也就不能保證時效性皮迟。在底層采用分段的存儲模式搬泥,不僅解決上述問題,而且使它在讀寫時幾乎完全避免了鎖的出現(xiàn)伏尼,大大提升了讀寫性能忿檩。其實原理和ConcurrentHashMap 的分段鎖點類似。

Elasticsearch 中的倒排索引被設(shè)計成不可變的爆阶,有以下幾個方面優(yōu)勢:
①不需要鎖燥透。如果你從來不更新索引,你就不需要擔心多進程同時修改數(shù)據(jù)的問題辨图。
②一旦索引被讀入內(nèi)核的文件系統(tǒng)緩存班套,便會留在那里。由于其不變性故河,只要文件系統(tǒng)緩存中還有足夠的空間吱韭,那么大部分讀請求會直接請求內(nèi)存,而不會命中磁盤鱼的。這提供了很大的性能提升理盆。
③其它緩存(像filter緩存),在索引的生命周期內(nèi)始終有效凑阶。它們不需要在每次數(shù)據(jù)改變時被重建猿规,因為數(shù)據(jù)不會變化。
④寫入單個大的倒排索引允許數(shù)據(jù)被壓縮宙橱,減少磁盤 I/O和需要被緩存到內(nèi)存的索引的使用量姨俩。

每一個段本身都是一個倒排索引,但索引在 Lucene 中除表示所有段的集合外养匈,還增加了提交點的概念肪跋。

為了提升寫的性能,Lucene并沒有每新增一條數(shù)據(jù)就增加一個段翠勉,而是采用延遲寫的策略惩猫,每當有新增的數(shù)據(jù)時,就將其先寫入內(nèi)存中猬仁,然后批量寫入磁盤中帝璧。若有一個段被寫到硬盤,就會生成一個提交點湿刽,提交點就是一個列出了所有已知段和記錄所有提交后的段信息的文件的烁。

26.png

5.2.7.2 寫索引的流程

索引庫存儲流程.png

當分片所在的節(jié)點接收到來自協(xié)調(diào)節(jié)點的請求后,會將該請求寫入translog诈闺,并將文檔加入內(nèi)存緩存渴庆。如果請求在主分片上成功處理,該請求會并行發(fā)送到該分片的副本上。當translog被同步到全部的主分片及其副本上后襟雷,客戶端才會收到確認通知刃滓。

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

每30分鐘或者當translog很大的時候砰诵,translog會被清空,文件系統(tǒng)緩存會被同步捌显,這個過程在ES中稱為沖洗(flush)茁彭。在沖洗過程中,內(nèi)存中的緩沖將被清除苇瓣,內(nèi)容被寫入一個新段尉间。段的同步(fsync)將創(chuàng)建一個新的提交點,并將內(nèi)容刷新到磁盤击罪。舊的translog將被刪除并開始一個新的translog哲嘲。


27.png

從整個流程我們可以了解到以下幾個問題:

  • 為什么說 ES 搜索是近實時的?
    因為文檔索引在從內(nèi)存緩存被寫入到文件緩存系統(tǒng)時媳禁,雖然還沒有進行提交眠副、未被 flush 到磁盤,但是緩沖區(qū)的內(nèi)容已經(jīng)被寫入一個新段(segment6)中且新段可被搜索竣稽。這就是為什么我們說 Elasticsearch 是近實時搜索: 文檔的變化并不是立即對搜索可見囱怕,但會在一秒之內(nèi)變?yōu)榭梢姟?/li>
  • Elasticsearch 是怎樣保證更新被持久化在斷電時也不丟失數(shù)據(jù)?
    新索引文檔被寫入到內(nèi)存緩存時,同時會記錄一份到事務(wù)日志(translog)中毫别,translog 提供所有還沒有被刷到磁盤的操作的一個持久化紀錄娃弓。當 Elasticsearch 啟動的時候, 它會從磁盤中使用最后一個提交點去恢復(fù)已知的段岛宦,并且會重放 translog 中所有在最后一次提交后發(fā)生的變更操作台丛。
    translog 也被用來提供實時 CRUD 。當你試著通過ID查詢砾肺、更新挽霉、刪除一個文檔,它會在嘗試從相應(yīng)的段中檢索之前先檢查 translog 任何最近的變更变汪。這意味著它總是能夠?qū)崟r地獲取到文檔的最新版本侠坎。
段合并
28.png

由于自動刷新流程每秒會創(chuàng)建一個新的段 ,這樣會導(dǎo)致短時間內(nèi)的段數(shù)量暴增裙盾。而段數(shù)目太多會帶來較大的麻煩实胸。 每一個段都會消耗文件句柄他嫡、內(nèi)存和cpu運行周期。更重要的是庐完,每個搜索請求都必須輪流檢查每個段涮瞻,所以段越多搜索也就越慢。

Elasticsearch通過在后臺進行段合并來解決這個問題假褪。小的段被合并到大的段,然后這些大的段再被合并到更大的段近顷。段合并的時候會將那些舊的已刪除文檔從文件系統(tǒng)中清除生音,被刪除的文檔(或被更新文檔的舊版本)不會被拷貝到新的大段中。


29.png

5.2.7.3 如何更新索引

上文闡述了索引的持久化流程和倒排索引被設(shè)定為不可修改以及這樣設(shè)定的好處窒升。因為它是不可變的缀遍,你不能修改它,但是如果你需要讓一個新的文檔可被搜索饱须,這就涉及到索引的更新了域醇。索引不可被修改但又需要更新,這種看似矛盾的要求蓉媳,我們需要怎么做呢譬挚?

ES 的解決方法就是用更多的索引。什么意思酪呻?就是原來的索引不變减宣,我們對新的文檔再創(chuàng)建一個索引。這樣說完不知道大家有沒有疑惑或者沒理解玩荠,我們通過圖表的方式說明下漆腌。
假如我們現(xiàn)有兩個日志信息的文檔,信息如下:

Doc 1:the request param is name = 'zhang san' and age is 20.
Doc 2:the response result is code = 0000 and msg = 'success'.

這時候我們得到的倒排索引內(nèi)容(省略一部分)是:

詞項(term) 文檔(Doc)
the doc 1阶冈,doc 2
request doc 1
param doc 1闷尿,doc 2
is doc 1,doc 2
name doc 1
response doc 2
result doc 2
... ...

如果我們這時新增一個文檔 doc 3:the request param is name = 'li si' and sex is femal女坑,或者修改文檔 doc 2的內(nèi)容為:the response result is code = 9999 and msg = 'false'填具,這時 ES 是如何處理的呢?正如上文所述的堂飞,為了保留索引不變性灌旧,ES 會創(chuàng)建一個新的索引
對于新增的文檔索引信息如下:

詞項(term) 文檔(Doc)
the doc 3
request doc 3
param doc 3
is doc 3
name doc 3
sex doc 3
... ...

對于修改的文檔索引信息如下绰筛;

詞項(term) 文檔(Doc)
the doc 2
response doc 2
result doc 2
is doc 2
code doc 2
sex doc 2
... ...

通過增加新的補充索引來反映新近的修改枢泰,而不是直接重寫整個倒排索引。每一個倒排索引都會被輪流查詢到(從最早的開始)铝噩,查詢完后再對結(jié)果進行合并衡蚂。

正如上文所述那樣,對于修改的場景來說,同一個文檔這時磁盤中同時會有兩個索引數(shù)據(jù)一個是原來的索引毛甲,另一個是修改之后的索引年叮。

以正常邏輯來看,我們知道搜索的時候肯定以新的索引為標準玻募,但是段是不可改變的只损,所以既不能從把文檔從舊的段中移除,也不能修改舊的段來進行反映文檔的更新七咧。 取而代之的是跃惫,每個提交點會包含一個 .del文件,文件中會列出這些被刪除文檔的段信息艾栋。當一個文檔被 “刪除” 時爆存,它實際上只是在.del 文件中被記刪除。一個被標記刪除的文檔仍然可以被查詢匹配到蝗砾, 但它會在最終結(jié)果被返回前從結(jié)果集中移除先较。

文檔更新也是類似的操作方式。當一個文檔被更新時悼粮,舊版本文檔被標記刪除闲勺,文檔的新版本被索引到一個新的段中。 可能兩個版本的文檔都會被一個查詢匹配到矮锈,但被刪除的那個舊版本文檔在結(jié)果集返回前就已經(jīng)被移除霉翔。

5.3 集群選舉

5.3.1 涉及配置參數(shù)

如果同時啟動,按照nodeid進行排序苞笨,取出最小的做為master節(jié)點债朵。
如果不是同時啟動,則先啟動的候選master節(jié)點瀑凝,會競選為master節(jié)點序芦。

# 如果 node.master 設(shè)置為了false,則該節(jié)點沒資格參與 master 選舉粤咪。 
node.master = true 
# 默認3秒谚中,最好增加這個參數(shù)值,避免網(wǎng)絡(luò)慢或者擁塞寥枝,確保集群啟動穩(wěn)定性 
discovery.zen.ping_timeout: 3s 
# 用于控制選舉行為發(fā)生的集群最小master節(jié)點數(shù)量宪塔,防止腦裂現(xiàn)象 
discovery.zen.minimum_master_nodes : 2 
# 新節(jié)點加入集群的等待時間 
discovery.zen.join_timeout : 10s

5.3.2 新節(jié)點加入

節(jié)點完成選舉后,新節(jié)點加入囊拜,會發(fā)送 join request 到 master 節(jié)點某筐。默認會重試20次。

5.3.3 宕機再次選舉

如果宕機冠跷,集群node會再次進行ping 過程南誊,并選出一個新的master 身诺。一旦一個節(jié)點被明確設(shè)為一個客戶端節(jié)點( node.client設(shè)為true ),則不能再成為主節(jié)點( node.master會自動設(shè)為false )抄囚。

5.4 發(fā)現(xiàn)機制

那么有一個問題霉赡,ES內(nèi)部是如何通過一個相同的設(shè)置cluster.name 就能將不同的節(jié)點連接到同一個集群的?答案是Zen Discovery幔托。

Zen Discovery是Elasticsearch的內(nèi)置默認發(fā)現(xiàn)模塊(發(fā)現(xiàn)模塊的職責是發(fā)現(xiàn)集群中的節(jié)點以及選舉master節(jié)點)穴亏。它提供單播和基于文件的發(fā)現(xiàn),并且可以擴展為通過插件支持云環(huán)境和其他形式的發(fā)現(xiàn)重挑。Zen Discovery 與其他模塊集成迫肖,例如,節(jié)點之間的所有通信都使用Transport模塊完成攒驰。節(jié)點使用發(fā)現(xiàn)機制通過Ping的方式查找其他節(jié)點。

Elasticsearch 默認被配置為使用單播發(fā)現(xiàn)故爵,以防止節(jié)點無意中加入集群玻粪。只有在同一臺機器上運行的節(jié)點才會自動組成集群。

如果集群的節(jié)點運行在不同的機器上诬垂,使用單播劲室,你可以為 Elasticsearch 提供一些它應(yīng)該去嘗試連接的節(jié)點列表。 當一個節(jié)點聯(lián)系到單播列表中的成員時结窘,它就會得到整個集群所有節(jié)點的狀態(tài)很洋,然后它會聯(lián)系 master 節(jié)點,并加入集群隧枫。

這意味著單播列表不需要包含集群中的所有節(jié)點喉磁, 它只是需要足夠的節(jié)點,當一個新節(jié)點聯(lián)系上其中一個并且說上話就可以了官脓。

如果你使用 master 候選節(jié)點作為單播列表协怒,你只要列出三個就可以了。 這個配置在 elasticsearch.yml 文件中:

discovery.zen.ping.unicast.hosts: ["host1", "host2:port"]

節(jié)點啟動后先 ping 卑笨,如果discovery.zen.ping.unicast.hosts 有設(shè)置孕暇,則 ping 設(shè)置中的 host ,否則嘗試 ping localhost 的幾個端口赤兴, Elasticsearch 支持同一個主機啟動多個節(jié)點妖滔, Ping 的 response 會包含該節(jié)點的基本信息以及該節(jié)點認為的 master 節(jié)點。 選舉開始桶良,先從各節(jié)點認為的 master 中選座舍,規(guī)則很簡單,按照 id 的字典序排序艺普,取第一個簸州。 如果各節(jié)點都沒有認為的 master 鉴竭,則從所有節(jié)點中選擇,規(guī)則同上岸浑。

這里有個限制條件就是 discovery.zen.minimum_master_nodes 搏存,如果節(jié)點數(shù)達不到最小值的限制,則循環(huán)上述過程矢洲,直到節(jié)點數(shù)足夠可以開始選舉璧眠。 最后選舉結(jié)果是肯定能選舉出一個 master ,如果只有一個 local 節(jié)點那就選出的是自己读虏。 如果當前節(jié)點是 master 责静,則開始等待節(jié)點數(shù)達到 discovery.zen.minimum_master_nodes,然后提供服務(wù)盖桥。 如果當前節(jié)點不是 master 灾螃,則嘗試加入 master 。

Elasticsearch 將以上服務(wù)發(fā)現(xiàn)以及選主的流程叫做 ZenDiscovery 揩徊。

由于它支持任意數(shù)目的集群( 1- N )腰鬼,所以不能像 Zookeeper 那樣限制節(jié)點必須是奇數(shù),也就無法用投票的機制來選主塑荒,而是通過一個規(guī)則熄赡,只要所有的節(jié)點都遵循同樣的規(guī)則,得到的信息都是對等的齿税,選出來的主節(jié)點肯定是一致的彼硫。

但分布式系統(tǒng)的問題就出在信息不對等的情況,這時候很容易出現(xiàn)腦裂( Split-Brain )的問題凌箕,大多數(shù)解決方案就是設(shè)置一個 quorum 值拧篮,要求可用節(jié)點必須大于 quorum (一般是超過半數(shù)節(jié)點),才能對外提供服務(wù)牵舱。 Elasticsearch 中他托,這個 quorum 的配置就是 discovery.zen.minimum_master_nodes 。

5.5 故障探查

ES有兩種集群故障探查機制:

  • 通過master進行的仆葡,master會ping集群中所有的其他node赏参,確保它們是否是存活著的。
  • 每個node都會去ping master來確保master是存活的沿盅,否則會發(fā)起一個選舉過程把篓。

有下面三個參數(shù)用來配置集群故障的探查過程:

ping_interval : 每隔多長時間會ping一次node,默認是1s 
ping_timeout : 每次ping的timeout等待時長是多長時間腰涧,默認是30s 
ping_retries : 如果一個node被ping多少次都失敗了韧掩,就會認為node故障,默認是3次

5.6 腦裂問題

5.6.1 腦裂現(xiàn)象

由于部分節(jié)點網(wǎng)絡(luò)斷開窖铡,集群分成兩部分疗锐,且這兩部分都有master選舉權(quán)坊谁,就成形成一個與原集群一樣名字的集群,這種情況稱為集群腦裂(split-brain)現(xiàn)象滑臊。這個問題非常危險口芍,因為兩個新形成的集群會同時索引和修改集群的數(shù)據(jù)。

5.6.2 解決方案

# 決定選舉一個master最少需要多少master候選節(jié)點雇卷。默認是1鬓椭。 
# 這個參數(shù)必須大于等于為集群中master候選節(jié)點的quorum數(shù)量,也就是大多數(shù)关划。 
# quorum算法:master候選節(jié)點數(shù)量 / 2 + 1 
# 例如一個有3個節(jié)點的集群小染,minimum_master_nodes 應(yīng)該被設(shè)置成 3/2 + 1 = 2
discovery.zen.minimum_master_nodes:2 
# 等待ping響應(yīng)的超時時間,默認值是3秒。如果網(wǎng)絡(luò)緩慢或擁塞贮折,會造成集群重新選舉裤翩,建議略微調(diào)大這個值。 
# 這個參數(shù)不僅僅適應(yīng)更高的網(wǎng)絡(luò)延遲调榄,也適用于在一個由于超負荷而響應(yīng)緩慢的節(jié)點的情況岛都。 
discovery.zen.ping.timeout:10s 
# 當集群中沒有活動的Master節(jié)點后,該設(shè)置指定了哪些操作(read振峻、write)需要被拒絕(即阻塞執(zhí)行)。
# 有兩個設(shè)置值:all和write择份,默認為wirte扣孟。 
discovery.zen.no_master_block : write

5.6.3 場景分析

一個生產(chǎn)環(huán)境的es集群,至少要有3個節(jié)點荣赶,同時將discovery.zen.minimum_master_nodes設(shè)置為2凤价,那么這個是參數(shù)是如何避免腦裂問題的產(chǎn)生的呢?
比如我們有3個節(jié)點拔创,quorum是2±担現(xiàn)在網(wǎng)絡(luò)故障,1個節(jié)點在一個網(wǎng)絡(luò)區(qū)域剩燥,另外2個節(jié)點在另外一個網(wǎng)絡(luò)區(qū)域慢逾,不同的網(wǎng)絡(luò)區(qū)域內(nèi)無法通信。這個時候有兩種情況情況:
(1)如果master是單獨的那個節(jié)點灭红,另外2個節(jié)點是master候選節(jié)點侣滩,那么此時那個單獨的master節(jié)點因為沒有指定數(shù)量的候選master node在自己當前所在的集群內(nèi),因此就會取消當前master的角色变擒,嘗試重新選舉君珠,但是無法選舉成功。然后另外一個網(wǎng)絡(luò)區(qū)域內(nèi)的node因為無法連接到master娇斑,就會發(fā)起重新選舉策添,因為有兩個master候選節(jié)點材部,滿足了quorum,因此可以成功選舉出一個master唯竹。此時集群中就會還是只有一個master乐导。
(2)如果master和另外一個node在一個網(wǎng)絡(luò)區(qū)域內(nèi),然后一個node單獨在一個網(wǎng)絡(luò)區(qū)域內(nèi)摩窃。那么此時那個單獨的node因為連接不上master兽叮,會嘗試發(fā)起選舉,但是因為master候選節(jié)點數(shù)量不到quorum猾愿,因此無法選舉出master鹦聪。而另外一個網(wǎng)絡(luò)區(qū)域內(nèi),原先的那個master還會繼續(xù)工作蒂秘。這也可以保證集群內(nèi)只有一個master節(jié)點泽本。
綜上所述,通過在elasticsearch.yml 中配置discovery.zen.minimum_master_nodes: 2 姻僧,就可以避免腦裂問題的產(chǎn)生规丽。
但是因為ES集群是可以動態(tài)增加和下線節(jié)點的,所以可能隨時會改變 quorum 撇贺。所以這個參數(shù)也是可以通過api隨時修改的赌莺,特別是在節(jié)點上線和下線的時候,都需要作出對應(yīng)的修改松嘶。而且一旦修改過后艘狭,這個配置就會持久化保存下來。

PUT /_cluster/settings 
{ "persistent" : { "discovery.zen.minimum_master_nodes" : 2 } }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末翠订,一起剝皮案震驚了整個濱河市巢音,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌尽超,老刑警劉巖官撼,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異似谁,居然都是意外死亡傲绣,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門巩踏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來斜筐,“玉大人,你說我怎么就攤上這事蛀缝∏炅矗” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵屈梁,是天一觀的道長嗤练。 經(jīng)常有香客問我榛了,道長,這世上最難降的妖魔是什么煞抬? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任霜大,我火速辦了婚禮,結(jié)果婚禮上革答,老公的妹妹穿的比我還像新娘战坤。我一直安慰自己,他們只是感情好残拐,可當我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布途茫。 她就那樣靜靜地躺著,像睡著了一般溪食。 火紅的嫁衣襯著肌膚如雪囊卜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天错沃,我揣著相機與錄音栅组,去河邊找鬼。 笑死枢析,一個胖子當著我的面吹牛玉掸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播醒叁,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼司浪,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了辐益?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤脱吱,失蹤者是張志新(化名)和其女友劉穎智政,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體箱蝠,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡续捂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了宦搬。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片牙瓢。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖间校,靈堂內(nèi)的尸體忽然破棺而出矾克,到底是詐尸還是另有隱情,我是刑警寧澤憔足,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布胁附,位于F島的核電站酒繁,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏控妻。R本人自食惡果不足惜州袒,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望弓候。 院中可真熱鬧郎哭,春花似錦、人聲如沸菇存。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽撰筷。三九已至陈惰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間毕籽,已是汗流浹背抬闯。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留关筒,地道東北人溶握。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像蒸播,于是被迫代替她去往敵國和親睡榆。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容