介紹
Elasticsearch 是一個分布式可擴(kuò)展的實時搜索和分析引擎,一個建立在全文搜索引擎 Apache Lucene(TM) 基礎(chǔ)上的搜索引擎.當(dāng)然 Elasticsearch 并不僅僅是 Lucene 那么簡單,它不僅包括了全文搜索功能,還可以進(jìn)行以下工作:
1.分布式實時文件存儲,并將每一個字段都編入索引,使其可以被搜索。
2.實時分析的分布式搜索引擎。
3.可以擴(kuò)展到上百臺服務(wù)器,處理PB級別的結(jié)構(gòu)化或非結(jié)構(gòu)化數(shù)據(jù)。
基本概念
先說Elasticsearch的文件存儲,Elasticsearch是面向文檔型數(shù)據(jù)庫,一條數(shù)據(jù)在這里就是一個文檔,用JSON作為文檔序列化的格式,比如下面這條用戶數(shù)據(jù):

用Mysql這樣的數(shù)據(jù)庫存儲就會容易想到建立一張User表,有balabala的字段等,在Elasticsearch里這就是一個文檔,當(dāng)然這個文檔會屬于一個User的類型,各種各樣的類型存在于一個索引當(dāng)中。這里有一份簡易的將Elasticsearch和關(guān)系型數(shù)據(jù)術(shù)語對照表:
關(guān)系數(shù)據(jù)庫? ? ? 數(shù)據(jù)庫 ? 表? ? ? 行? ? ? 列(Columns)
Elasticsearch? ? 索引(Index)? ? 類型(type)? ? 文檔(Docments)? ? 字段(Fields)
一個 Elasticsearch 集群可以包含多個索引(數(shù)據(jù)庫),也就是說其中包含了很多類型(表)。這些類型中包含了很多的文檔(行),然后每個文檔中又包含了很多的字段(列)。Elasticsearch的交互,可以使用Java API,也可以直接使用HTTP的Restful API方式,比如我們打算插入一條記錄,可以簡單發(fā)送一個HTTP的請求:

索引
Elasticsearch最關(guān)鍵的就是提供強(qiáng)大的索引能力了,其實InfoQ的這篇時間序列數(shù)據(jù)庫的秘密(2)——索引寫的非常好,我這里也是圍繞這篇結(jié)合自己的理解進(jìn)一步梳理下,也希望可以幫助大家更好的理解這篇文章。
Elasticsearch索引的精髓:
一切設(shè)計都是為了提高搜索的性能
另一層意思:為了提高搜索的性能,難免會犧牲某些其他方面,比如插入/更新,否則其他數(shù)據(jù)庫不用混了。前面看到往Elasticsearch里插入一條記錄,其實就是直接PUT一個json的對象,這個對象有多個fields,比如上面例子中的name, sex, age, about, interests,那么在插入這些數(shù)據(jù)到Elasticsearch的同時,Elasticsearch還默默1的為這些字段建立索引--倒排索引,因為Elasticsearch最核心功能是搜索。
Elasticsearch是如何做到快速索引的
InfoQ那篇文章里說Elasticsearch使用的倒排索引比關(guān)系型數(shù)據(jù)庫的B-Tree索引快,為什么呢?
什么是B-Tree索引?
上大學(xué)讀書時老師教過我們,二叉樹查找效率是logN,同時插入新的節(jié)點不必移動全部節(jié)點,所以用樹型結(jié)構(gòu)存儲索引,能同時兼顧插入和查詢的性能。因此在這個基礎(chǔ)上,再結(jié)合磁盤的讀取特性(順序讀/隨機(jī)讀),傳統(tǒng)關(guān)系型數(shù)據(jù)庫采用了B-Tree/B+Tree這樣的數(shù)據(jù)結(jié)構(gòu):

為了提高查詢的效率,減少磁盤尋道次數(shù),將多個值作為一個數(shù)組通過連續(xù)區(qū)間存放,一次尋道讀取多個數(shù)據(jù),同時也降低樹的高度。
什么是倒排索引?

繼續(xù)上面的例子,假設(shè)有這么幾條數(shù)據(jù)(為了簡單,去掉about, interests這兩個field):

ID是Elasticsearch自建的文檔id,那么Elasticsearch建立的索引如下:

Posting List
Elasticsearch分別為每個field都建立了一個倒排索引,Kate, John, 24, Female這些叫term,而[1,2]就是Posting List。Posting list就是一個int的數(shù)組,存儲了所有符合某個term的文檔id。
看到這里,不要認(rèn)為就結(jié)束了,精彩的部分才剛開始...
通過posting list這種索引方式似乎可以很快進(jìn)行查找,比如要找age=24的同學(xué),愛回答問題的小明馬上就舉手回答:我知道,id是1,2的同學(xué)。但是,如果這里有上千萬的記錄呢?如果是想通過name來查找呢?
Term Dictionary
Elasticsearch為了能快速找到某個term,將所有的term排個序,二分法查找term,logN的查找效率,就像通過字典查找一樣,這就是Term Dictionary。現(xiàn)在再看起來,似乎和傳統(tǒng)數(shù)據(jù)庫通過B-Tree的方式類似啊,為什么說比B-Tree的查詢快呢?
Term Index
B-Tree通過減少磁盤尋道次數(shù)來提高查詢性能,Elasticsearch也是采用同樣的思路,直接通過內(nèi)存查找term,不讀磁盤,但是如果term太多,term dictionary也會很大,放內(nèi)存不現(xiàn)實,于是有了Term Index,就像字典里的索引頁一樣,A開頭的有哪些term,分別在哪頁,可以理解term index是一顆樹:

這棵樹不會包含所有的term,它包含的是term的一些前綴。通過term index可以快速地定位到term dictionary的某個offset,然后從這個位置再往后順序查找。

所以term index不需要存下所有的term,而僅僅是他們的一些前綴與Term Dictionary的block之間的映射關(guān)系,再結(jié)合FST(Finite State Transducers)的壓縮技術(shù),可以使term index緩存到內(nèi)存中。從term index查到對應(yīng)的term dictionary的block位置之后,再去磁盤上找term,大大減少了磁盤隨機(jī)讀的次數(shù)。
這時候愛提問的小明又舉手了:"那個FST是神馬東東啊?"
一看就知道小明是一個上大學(xué)讀書的時候跟我一樣不認(rèn)真聽課的孩子,數(shù)據(jù)結(jié)構(gòu)老師一定講過什么是FST。但沒辦法,我也忘了,這里再補(bǔ)下課:
FSTs are finite-state machines thatmapaterm (byte sequence)to an arbitraryoutput.
假設(shè)我們現(xiàn)在要將mop, moth, pop, star, stop and top(term index里的term前綴)映射到序號:0,1,2,3,4,5(term dictionary的block位置)。最簡單的做法就是定義個Map,大家找到自己的位置對應(yīng)入座就好了,但從內(nèi)存占用少的角度想想,有沒有更優(yōu)的辦法呢?答案就是:FST(理論依據(jù)在此,但我相信99%的人不會認(rèn)真看完的)

聯(lián)合索引
上面說了半天都是單field索引,如果多個field索引的聯(lián)合查詢,倒排索引如何滿足快速查詢的要求呢?
1.利用跳表(Skip list)的數(shù)據(jù)結(jié)構(gòu)快速做“與”運算,或者
2.利用上面提到的bitset按位“與”
先看看跳表的數(shù)據(jù)結(jié)構(gòu):

將一個有序鏈表level0,挑出其中幾個元素到level1及l(fā)evel2,每個level越往上,選出來的指針元素越少,查找時依次從高level往低查找,比如55,先找到level2的31,再找到level1的47,最后找到55,一共3次查找,查找效率和2叉樹的效率相當(dāng),但也是用了一定的空間冗余來換取的。
假設(shè)有下面三個posting list需要聯(lián)合索引:

如果使用跳表,對最短的posting list中的每個id,逐個在另外兩個posting list中查找看是否存在,最后得到交集的結(jié)果。
如果使用bitset,就很直觀了,直接按位與,得到的結(jié)果就是最后的交集
總結(jié)和思考
Elasticsearch的索引思路:
將磁盤里的東西盡量搬進(jìn)內(nèi)存,減少磁盤隨機(jī)讀取次數(shù)(同時也利用磁盤順序讀特性),結(jié)合各種奇技淫巧的壓縮算法,用及其苛刻的態(tài)度使用內(nèi)存。
所以,對于使用Elasticsearch進(jìn)行索引時需要注意:
1.不需要索引的字段,一定要明確定義出來,因為默認(rèn)是自動建索引的
2.同樣的道理,對于String類型的字段,不需要analysis的也需要明確定義出來,因為默認(rèn)也是會analysis的
3.選擇有規(guī)律的ID很重要,隨機(jī)性太大的ID(比如java的UUID)不利于查詢
關(guān)于最后一點,個人認(rèn)為有多個因素:
其中一個(也許不是最重要的)因素: 上面看到的壓縮算法,都是對Posting list里的大量ID進(jìn)行壓縮的,那如果ID是順序的,或者是有公共前綴等具有一定規(guī)律性的ID,壓縮比會比較高;
另外一個因素: 可能是最影響查詢性能的,應(yīng)該是最后通過Posting list里的ID到磁盤中查找Document信息的那步,因為Elasticsearch是分Segment存儲的,根據(jù)ID這個大范圍的Term定位到Segment的效率直接影響了最后查詢的性能,如果ID是有規(guī)律的,可以快速跳過不包含該ID的Segment,從而減少不必要的磁盤讀次數(shù),具體可以參考這篇如何選擇一個高效的全局ID方案。
轉(zhuǎn):https://www.cnblogs.com/dreamroute/p/8484457.html