Elasticsearch 是一個(gè)分布式可擴(kuò)展的實(shí)時(shí)搜索和分析引擎廓八,一個(gè)建立在全文搜索引擎 Apache Lucene(TM) 基礎(chǔ)上的搜索引擎途乃。當(dāng)然 Elasticsearch 并不僅僅是 Lucene 那么簡(jiǎn)單冤竹,它不僅包括了全文搜索功能驻粟,還可以進(jìn)行以下工作:
????●?分布式實(shí)時(shí)文件存儲(chǔ)坷檩,并將每一個(gè)字段都編入索引栖雾,使其可以被搜索抄腔。
????● 實(shí)時(shí)分析的分布式搜索引擎瓢湃。
????●?可以擴(kuò)展到上百臺(tái)服務(wù)器,處理PB級(jí)別的結(jié)構(gòu)化或非結(jié)構(gòu)化數(shù)據(jù)赫蛇。
基本概念
先說(shuō)Elasticsearch的文件存儲(chǔ)绵患,Elasticsearch是面向文檔型數(shù)據(jù)庫(kù),一條數(shù)據(jù)在這里就是一個(gè)文檔悟耘,用JSON作為文檔序列化的格式藏雏,比如下面這條用戶數(shù)據(jù):
{
??? "name" :???? "John",
??? "sex" :????? "Male",
??? "age" :????? 25,
??? "birthDate":"1990/05/01",
??? "about" :??? "I love to go rock climbing",
??? "interests": ["sports", "music" ]
}
用Mysql這樣的數(shù)據(jù)庫(kù)存儲(chǔ)就會(huì)容易想到建立一張User表,有balabala的字段等,在Elasticsearch里這就是一個(gè)文檔掘殴,當(dāng)然這個(gè)文檔會(huì)屬于一個(gè)User的類型赚瘦,各種各樣的類型存在于一個(gè)索引當(dāng)中。這里有一份簡(jiǎn)易的將Elasticsearch和關(guān)系型數(shù)據(jù)術(shù)語(yǔ)對(duì)照表:
關(guān)系數(shù)據(jù)庫(kù)??? ? 數(shù)據(jù)庫(kù) ? 表?? ? 行???? 列(Columns)
Elasticsearch? ? 索引(Index)?? ? 類型(type)?? 文檔(Docments)? ? 字段(Fields)?
一個(gè) Elasticsearch 集群可以包含多個(gè)索引(數(shù)據(jù)庫(kù))奏寨,也就是說(shuō)其中包含了很多類型(表)起意。這些類型中包含了很多的文檔(行),然后每個(gè)文檔中又包含了很多的字段(列)病瞳。
Node與Cluster
Elastic 本質(zhì)上是一個(gè)分布式數(shù)據(jù)庫(kù)揽咕,允許多臺(tái)服務(wù)器協(xié)同工作,每臺(tái)服務(wù)器可以運(yùn)行多個(gè) Elastic 實(shí)例套菜。
單個(gè) Elastic 實(shí)例稱為一個(gè)節(jié)點(diǎn)(node)亲善。一組節(jié)點(diǎn)構(gòu)成一個(gè)集群(cluster)。
Index
Elastic 會(huì)索引所有字段逗柴,經(jīng)過(guò)處理后寫入一個(gè)反向索引(Inverted Index)蛹头。查找數(shù)據(jù)的時(shí)候,直接查找該索引戏溺。
所以渣蜗,Elastic 數(shù)據(jù)管理的頂層單位就叫做 Index(索引)。它是單個(gè)數(shù)據(jù)庫(kù)的同義詞旷祸。每個(gè) Index (即數(shù)據(jù)庫(kù))的名字必須是小寫耕拷。
下面的命令可以查看當(dāng)前節(jié)點(diǎn)的所有 Index。
$ curl -X GET 'http://localhost:9200/_cat/indices?v'
Type
Document 可以分組托享,比如weather這個(gè) Index 里面骚烧,可以按城市分組(北京和上海),也可以按氣候分組(晴天和雨天)闰围。這種分組就叫做 Type止潘,它是虛擬的邏輯分組,用來(lái)過(guò)濾 Document辫诅。
不同的 Type 應(yīng)該有相似的結(jié)構(gòu)(schema)凭戴,舉例來(lái)說(shuō),id字段不能在這個(gè)組是字符串炕矮,在另一個(gè)組是數(shù)值么夫。這是與關(guān)系型數(shù)據(jù)庫(kù)的表的一個(gè)區(qū)別。性質(zhì)完全不同的數(shù)據(jù)(比如products和logs)應(yīng)該存成兩個(gè) Index肤视,而不是一個(gè) Index 里面的兩個(gè) Type(雖然可以做到)档痪。
下面的命令可以列出每個(gè) Index 所包含的 Type。
$ curl 'localhost:9200/_mapping?pretty=true'
根據(jù)規(guī)劃邢滑,Elastic 6.x 版只允許每個(gè) Index 包含一個(gè) Type腐螟,7.x 版將會(huì)徹底移除 Type。
Document
Index 里面單條的記錄稱為 Document(文檔)。許多條 Document 構(gòu)成了一個(gè) Index乐纸。
Document 使用 JSON 格式表示衬廷,下面是一個(gè)例子。
{
? "user": "張三",
? "title": "工程師",
? "desc": "數(shù)據(jù)庫(kù)管理"
}
同一個(gè) Index 里面的 Document汽绢,不要求有相同的結(jié)構(gòu)(scheme)吗跋,但是最好保持相同,這樣有利于提高搜索效率宁昭。
Elasticsearch的交互跌宛,可以使用Java API,也可以直接使用HTTP的Restful API方式积仗,比如我們打算插入一條記錄疆拘,可以簡(jiǎn)單發(fā)送一個(gè)HTTP的請(qǐng)求:
PUT /megacorp/employee/1?
{
??? "name" :???? "John",
??? "sex" :????? "Male",
??? "age" :????? 25,
??? "about" :??? "I love to go rock climbing",
??? "interests": ["sports", "music" ]
}
更新,查詢也是類似這樣的操作寂曹,具體操作手冊(cè)可以參見Elasticsearch權(quán)威指南哎迄。
倒排索引到底是啥?
假如說(shuō)你現(xiàn)在不用搜索引擎稀颁,單純使用數(shù)據(jù)庫(kù)來(lái)存放和搜索一些數(shù)據(jù)芬失,比如說(shuō)放了一些論壇的帖子數(shù)據(jù)吧楣黍,那么這個(gè)數(shù)據(jù)的格式大致如下:
那么這個(gè)時(shí)候匾灶,比如我們要是用數(shù)據(jù)庫(kù)來(lái)進(jìn)行搜索包含“Java”這個(gè)關(guān)鍵字的所有帖子,大致SQL如下:
咱們姑且不論這個(gè)數(shù)據(jù)庫(kù)層面也有支持全文檢索的一些特殊索引類型租漂,或者數(shù)據(jù)庫(kù)層面是怎么執(zhí)行的阶女,這個(gè)不是本文討論的重點(diǎn),你就看看數(shù)據(jù)庫(kù)的數(shù)據(jù)格式以及搜索的方式就好了哩治。但是如果你通過(guò)搜索引擎類的技術(shù)來(lái)存放帖子的內(nèi)容秃踩,他是可以建立倒排索引的。也就是說(shuō)业筏,你把上述的幾行數(shù)據(jù)放到搜索引擎里憔杨,這個(gè)倒排索引的數(shù)據(jù)大致看起來(lái)如下:
關(guān)鍵詞??? id
Java????? [1, 2, 3]
語(yǔ)言????? [1]
面試????? [3]
資源????? [2]
所謂的倒排索引,就是把你的數(shù)據(jù)內(nèi)容先分詞蒜胖,每句話分成一個(gè)一個(gè)的關(guān)鍵詞,然后記錄好每個(gè)關(guān)鍵詞對(duì)應(yīng)出現(xiàn)在了哪些id標(biāo)識(shí)的數(shù)據(jù)里。那么你要搜索包含“Java”關(guān)鍵詞的帖子旧蛾,直接掃描這個(gè)倒排索引瞬项,在倒排索引里找到“Java”這個(gè)關(guān)鍵詞對(duì)應(yīng)的那些數(shù)據(jù)的id就好了。然后你可以從其他地方根據(jù)這幾個(gè)id找到對(duì)應(yīng)的數(shù)據(jù)就可以了朋沮,這個(gè)就是倒排索引的數(shù)據(jù)格式以及搜索的方式蛇券,上面這種利用倒排索引查找數(shù)據(jù)的方式,也被稱之為全文檢索。
什么叫做分布式搜索引擎纠亚?
其實(shí)要知道什么叫做分布式搜索引擎塘慕,你首先得知道,假如我們就用一臺(tái)機(jī)器部署一個(gè)搜索引擎系統(tǒng)菜枷,然后利用上述的那種倒排索引來(lái)存儲(chǔ)數(shù)據(jù)苍糠,同時(shí)支持一些全文檢索之類的搜索功能,那么會(huì)有什么問(wèn)題啤誊?
其實(shí)還是很簡(jiǎn)單岳瞭,假如說(shuō)你現(xiàn)在要存儲(chǔ)1TB的數(shù)據(jù),那么放在一臺(tái)機(jī)器還是可以的蚊锹。
但是如果你要存儲(chǔ)超過(guò)10TB瞳筏,100TB,甚至1000TB的數(shù)據(jù)呢牡昆?你用一臺(tái)機(jī)器放的下嗎姚炕?
當(dāng)然是放不下的了,你的機(jī)器磁盤空間是不夠的丢烘。
大家看一下下面的圖:
所以這個(gè)時(shí)候柱宦,你就得用分布式搜索引擎了,也就是要使用多臺(tái)機(jī)器來(lái)部署搜索引擎集群播瞳。
比如說(shuō)掸刊,假設(shè)你用的是Elasticsearch(后面簡(jiǎn)寫為:ES)。
現(xiàn)在你總共有3TB的數(shù)據(jù)赢乓,那么你搞3臺(tái)機(jī)器忧侧,每臺(tái)機(jī)器上部署一個(gè)ES進(jìn)程,管理那臺(tái)機(jī)器上的1TB數(shù)據(jù)就可以了牌芋。
這樣不就可以把3TB的數(shù)據(jù)分散在3臺(tái)機(jī)器上來(lái)存儲(chǔ)了蚓炬?這不就是索引數(shù)據(jù)的分布式存儲(chǔ)嗎?而且躺屁,你在搜索數(shù)據(jù)的時(shí)候肯夏,不就可以利用3臺(tái)機(jī)器來(lái)對(duì)分布式存儲(chǔ)后的數(shù)據(jù)進(jìn)行搜索了?每臺(tái)機(jī)器上的ES進(jìn)程不都可以對(duì)一部分?jǐn)?shù)據(jù)搜索犀暑?這不就是分布式的搜索驯击?
是的,這就是所謂的分布式搜索引擎:把大量的索引數(shù)據(jù)拆散成多塊母怜,每臺(tái)機(jī)器放一部分余耽,然后利用多臺(tái)機(jī)器對(duì)分散之后的數(shù)據(jù)進(jìn)行搜索,所有操作全部是分布在多臺(tái)機(jī)器上進(jìn)行苹熏,形成了完整的分布式的架構(gòu)碟贾。
同樣币喧,我們來(lái)看下面的圖,直觀的感受一下袱耽。
Shard數(shù)據(jù)分片機(jī)制
那么這個(gè)時(shí)候大家考慮一下杀餐,比如說(shuō)你有一個(gè)index,專門存放論壇里的帖子朱巨,現(xiàn)在論壇里的帖子有1億史翘,占用了1TB的磁盤空間,這個(gè)還好說(shuō)冀续。
如果這個(gè)帖子有10億琼讽,100億,占用了10TB洪唐、甚至100TB的磁盤空間呢钻蹬?
那你這個(gè)index的數(shù)據(jù)還能在一臺(tái)機(jī)器上存儲(chǔ)嗎?答案明顯是不能的凭需。
這個(gè)時(shí)候问欠,你必須得支持這個(gè)index的數(shù)據(jù)分布式存儲(chǔ)在多臺(tái)機(jī)器上,利用多臺(tái)機(jī)器的磁盤空間來(lái)承載這么大的數(shù)據(jù)量粒蜈。
而且顺献,需要保證每臺(tái)機(jī)器上對(duì)這個(gè)index存儲(chǔ)的數(shù)據(jù)量不要太大,因?yàn)榭刂茊闻_(tái)機(jī)器上這個(gè)index的數(shù)據(jù)量枯怖,可以保證他的搜索性能更高注整。
所以這里就引入了一個(gè)概念:Shard數(shù)據(jù)分片結(jié)構(gòu)。每個(gè)index你都可以指定創(chuàng)建多少個(gè)shard嫁怀,每個(gè)shard就是一個(gè)數(shù)據(jù)分片设捐,會(huì)負(fù)責(zé)存儲(chǔ)這個(gè)index的一部分?jǐn)?shù)據(jù)借浊。
比如說(shuō)index里有3億帖子塘淑,占據(jù)3TB數(shù)據(jù)。然后這個(gè)index你設(shè)置了3個(gè)shard蚂斤。
那么每個(gè)shard就可以包含一個(gè)1TB大小的數(shù)據(jù)分片存捺,每個(gè)shard在集群里的一臺(tái)機(jī)器上,這樣就形成了利用3臺(tái)機(jī)器來(lái)分布式存儲(chǔ)一個(gè)index的數(shù)據(jù)的效果了曙蒸。
大家看下面的圖:
現(xiàn)在index里的3TB數(shù)據(jù)分布式存儲(chǔ)在了3臺(tái)機(jī)器上捌治,每臺(tái)機(jī)器上有一個(gè)shard,每個(gè)shard負(fù)責(zé)管理這個(gè)index的其中1TB數(shù)據(jù)的分片纽窟。
而且肖油,另外一個(gè)好處是,假設(shè)我們要對(duì)這個(gè)index的3TB數(shù)據(jù)運(yùn)行一個(gè)搜索臂港,是不是可以發(fā)送請(qǐng)求到3臺(tái)機(jī)器上去森枪?
3臺(tái)機(jī)器上的shard直接可以分布式的并行對(duì)一部分?jǐn)?shù)據(jù)進(jìn)行搜索视搏,起到一個(gè)分布式搜索的效果,大幅度提升海量數(shù)據(jù)的搜索性能和吞吐量县袱。
Replica多副本數(shù)據(jù)冗余機(jī)制
但是現(xiàn)在有一個(gè)問(wèn)題浑娜,假如說(shuō)3臺(tái)機(jī)器中的其中一臺(tái)宕機(jī)了,此時(shí)怎么辦呢式散?
是不是這個(gè)index的3TB數(shù)據(jù)的1/3就丟失了筋遭?因?yàn)樯厦嬗?TB的數(shù)據(jù)分片沒(méi)了。
所以說(shuō)暴拄,還需要為了實(shí)現(xiàn)高可用使用Replica多副本數(shù)據(jù)冗余機(jī)制漓滔。
在Elasticsearch里,就是支持對(duì)每個(gè)index設(shè)置一個(gè)replica數(shù)量的乖篷,也就是每個(gè)shard對(duì)應(yīng)的replica副本的數(shù)量次和。
比如說(shuō)你現(xiàn)在一個(gè)index有3個(gè)shard,你設(shè)置對(duì)每個(gè)shard做1個(gè)replica副本那伐,那么此時(shí)每個(gè)shard都會(huì)有一個(gè)replica shard踏施。
這個(gè)初始的shard就是primary shard,而且primary shard和replica shard是絕對(duì)不會(huì)放在一臺(tái)機(jī)器上的罕邀,避免一臺(tái)機(jī)器宕機(jī)直接一個(gè)shard的副本也同時(shí)丟失了畅形。
我們?cè)賮?lái)看下面的圖,感受一下:
在上述的replica機(jī)制下诉探,每個(gè)primary shard都有一個(gè)replica shard在別的機(jī)器上日熬,任何一臺(tái)機(jī)器宕機(jī),都可以保證數(shù)據(jù)不會(huì)丟失肾胯,分布式搜索引擎繼續(xù)可用竖席。
Elasticsearch默認(rèn)是支持每個(gè)index是5個(gè)primary shard,每個(gè)primary shard有1個(gè)replica shard作為副本敬肚。
Elasticsearch是如何做到快速索引的
Elasticsearch使用的倒排索引比關(guān)系型數(shù)據(jù)庫(kù)的B-Tree索引快毕荐,為什么呢?
什么是B-Tree索引?
上大學(xué)讀書時(shí)老師教過(guò)我們艳馒,二叉樹查找效率是logN憎亚,同時(shí)插入新的節(jié)點(diǎn)不必移動(dòng)全部節(jié)點(diǎn),所以用樹型結(jié)構(gòu)存儲(chǔ)索引弄慰,能同時(shí)兼顧插入和查詢的性能第美。因此在這個(gè)基礎(chǔ)上,再結(jié)合磁盤的讀取特性(順序讀/隨機(jī)讀)陆爽,傳統(tǒng)關(guān)系型數(shù)據(jù)庫(kù)采用了B-Tree/B+Tree這樣的數(shù)據(jù)結(jié)構(gòu):
為了提高查詢的效率什往,減少磁盤尋道次數(shù),將多個(gè)值作為一個(gè)數(shù)組通過(guò)連續(xù)區(qū)間存放慌闭,一次尋道讀取多個(gè)數(shù)據(jù)别威,同時(shí)也降低樹的高度第献。
什么是倒排索引?
繼續(xù)上面的例子,假設(shè)有這么幾條數(shù)據(jù)(為了簡(jiǎn)單兔港,去掉about, interests這兩個(gè)field):
| ID | Name | Age? |? Sex????|
| -- |:------------:| -----:| -----:|
| 1? | Kate???????? | 24 | Female
| 2? | John???????? | 24 | Male
| 3? | Bill???????? ?| 29 | Male
ID是Elasticsearch自建的文檔id庸毫,那么Elasticsearch建立的索引如下:
Name:
| Term | Posting List |
| -- |:----:|
| Kate | 1 |
| John | 2 |
| Bill??| 3 |
Age:
| Term | Posting List |
| -- |:----:|
| 24 | [1,2] |
| 29 | 3 |
Sex:
| Term | Posting List |
| -- |:----:|
| Female | 1 |
| Male????| [2,3] |
Posting List
Elasticsearch分別為每個(gè)field都建立了一個(gè)倒排索引,Kate, John, 24, Female這些叫term衫樊,而[1,2]就是Posting List飒赃。Posting list就是一個(gè)int的數(shù)組,存儲(chǔ)了所有符合某個(gè)term的文檔id科侈。
看到這里载佳,不要認(rèn)為就結(jié)束了,精彩的部分才剛開始...
通過(guò)posting list這種索引方式似乎可以很快進(jìn)行查找臀栈,比如要找age=24的同學(xué)蔫慧,愛回答問(wèn)題的小明馬上就舉手回答:我知道,id是1权薯,2的同學(xué)姑躲。但是,如果這里有上千萬(wàn)的記錄呢盟蚣?如果是想通過(guò)name來(lái)查找呢黍析?
Term Dictionary
Elasticsearch為了能快速找到某個(gè)term,將所有的term排個(gè)序屎开,二分法查找term阐枣,logN的查找效率,就像通過(guò)字典查找一樣奄抽,這就是Term Dictionary“剑現(xiàn)在再看起來(lái),似乎和傳統(tǒng)數(shù)據(jù)庫(kù)通過(guò)B-Tree的方式類似啊逞度,為什么說(shuō)比B-Tree的查詢快呢额划?
Term Index
B-Tree通過(guò)減少磁盤尋道次數(shù)來(lái)提高查詢性能,Elasticsearch也是采用同樣的思路第晰,直接通過(guò)內(nèi)存查找term锁孟,不讀磁盤彬祖,但是如果term太多茁瘦,term dictionary也會(huì)很大,放內(nèi)存不現(xiàn)實(shí)储笑,于是有了Term Index甜熔,就像字典里的索引頁(yè)一樣,A開頭的有哪些term突倍,分別在哪頁(yè)腔稀,可以理解term index是一顆樹:
這棵樹不會(huì)包含所有的term盆昙,它包含的是term的一些前綴。通過(guò)term index可以快速地定位到term dictionary的某個(gè)offset焊虏,然后從這個(gè)位置再往后順序查找淡喜。
所以term index不需要存下所有的term,而僅僅是他們的一些前綴與Term Dictionary的block之間的映射關(guān)系诵闭,再結(jié)合FST(Finite State Transducers)的壓縮技術(shù)炼团,可以使term index緩存到內(nèi)存中。從term index查到對(duì)應(yīng)的term dictionary的block位置之后疏尿,再去磁盤上找term瘟芝,大大減少了磁盤隨機(jī)讀的次數(shù)。
聯(lián)合索引
上面說(shuō)了半天都是單field索引褥琐,如果多個(gè)field索引的聯(lián)合查詢锌俱,倒排索引如何滿足快速查詢的要求呢?
利用跳表(Skip list)的數(shù)據(jù)結(jié)構(gòu)快速做“與”運(yùn)算敌呈,或者利用上面提到的bitset按位“與”
先看看跳表的數(shù)據(jù)結(jié)構(gòu):
將一個(gè)有序鏈表level0贸宏,挑出其中幾個(gè)元素到level1及l(fā)evel2,每個(gè)level越往上磕洪,選出來(lái)的指針元素越少锚赤,查找時(shí)依次從高level往低查找,比如55褐鸥,先找到level2的31线脚,再找到level1的47,最后找到55叫榕,一共3次查找浑侥,查找效率和2叉樹的效率相當(dāng),但也是用了一定的空間冗余來(lái)?yè)Q取的晰绎。
假設(shè)有下面三個(gè)posting list需要聯(lián)合索引:
如果使用跳表寓落,對(duì)最短的posting list中的每個(gè)id,逐個(gè)在另外兩個(gè)posting list中查找看是否存在荞下,最后得到交集的結(jié)果伶选。
如果使用bitset,就很直觀了尖昏,直接按位與仰税,得到的結(jié)果就是最后的交集。
總結(jié)和思考
Elasticsearch的索引思路:
將磁盤里的東西盡量搬進(jìn)內(nèi)存抽诉,減少磁盤隨機(jī)讀取次數(shù)(同時(shí)也利用磁盤順序讀特性)陨簇,結(jié)合各種奇技淫巧的壓縮算法,用及其苛刻的態(tài)度使用內(nèi)存迹淌。
所以河绽,對(duì)于使用Elasticsearch進(jìn)行索引時(shí)需要注意:
不需要索引的字段己单,一定要明確定義出來(lái),因?yàn)槟J(rèn)是自動(dòng)建索引的
同樣的道理耙饰,對(duì)于String類型的字段纹笼,不需要analysis的也需要明確定義出來(lái),因?yàn)槟J(rèn)也是會(huì)analysis的
選擇有規(guī)律的ID很重要苟跪,隨機(jī)性太大的ID(比如java的UUID)不利于查詢
參考:
https://www.cnblogs.com/dreamroute/p/8484457.html
https://juejin.im/post/5c49ae25f265da613d7c6635