天天看點

Elasticsearch入門整理,索引原理

介紹

Elasticsearch 是一個分布式可擴充的實時搜尋和分析引擎,一個建立在全文搜尋引擎 Apache Lucene(TM) 基礎上的搜尋引擎.當然 Elasticsearch 并不僅僅是 Lucene 那麼簡單,它不僅包括了全文搜尋功能,還可以進行以下工作:

  • 分布式實時檔案存儲,并将每一個字段都編入索引,使其可以被搜尋。
  • 實時分析的分布式搜尋引擎。
  • 可以擴充到上百台伺服器,處理PB級别的結構化或非結構化資料。

基本概念

elasticsearch檔案存儲,elasticsearch是面向文檔的資料庫。一行資料就是一個文檔。用json作為文檔序列化的格式,例:

{

    "name" :     "John",

    "sex" :      "Male",

    "age" :      25,

    "birthDate": "1990/05/01",

    "about" :    "I love to go rock climbing",

    "interests": [ "sports", "music" ]

}

如果換做mysql,肯定是先建個user表,balabala一堆字段。

但在elasticsearch裡,這是一個文檔。這個文檔的類型是user,所有的類型都存在一個索引當中,

關系資料庫     ⇒ 資料庫          ⇒        表       ⇒            行          ⇒ 列(Columns)

Elasticsearch  ⇒ 索引(Index)   ⇒ 類型(type)  ⇒ 文檔(Docments)  ⇒ 字段(Fields)  

一個Elasticsearch叢集可以包含很多個索引,其中包含很多類型,這些類型有很多文檔,每個文檔又有很多字段。

互動方式,可以用http的restful API方式,例如插入一條記錄,可以發送一個請求 PUT   /megacorp/employee/1

{

    "name" :     "John",

    "sex" :      "Male",

    "age" :      25,

    "about" :    "I love to go rock climbing",

    "interests": [ "sports", "music" ]

}

索引

Elasticsearch索引的精髓:“一切設計都是為了提高搜尋的性能”

為了提高搜尋性能,會在其它方面做些犧牲(插入/更新),

比如更新一條記錄,就是直接put一個json對象,這個對象有很多字段,name,sex,age等,在這些資料插入到elasticsearch的同時,elasticsearch還會預設為這些字段建立索引,因為elasticsearch的核心工鞥是搜尋。

Elasticsearch是如何做到快速索引的

為什麼倒序索引比關系型資料庫的B-Tree索引快?

什麼是B-tree索引?

上大學讀書時老師教過我們,二叉樹查找效率是logN,同時插入新的節點不必移動全部節點,是以用樹型結構存儲索引,能同時兼顧插入和查詢的性能。是以在這個基礎上,再結合磁盤的讀取特性(順序讀/随機讀),傳統關系型資料庫采用了B-Tree/B+Tree這樣的資料結構:

Elasticsearch入門整理,索引原理

為了提高查詢的效率,減少磁盤尋道次數,将多個值作為一個數組通過連續區間存放,一次尋道讀取多個資料,同時也降低樹的高度。

什麼是反向索引?

Elasticsearch入門整理,索引原理

假設有這麼幾條資料(為了簡單,去掉about, interests這兩個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] |

Elasticsearch分别為每個field都建立了一個反向索引,Kate, John, 24, Female這些叫term,而[1,2]就是Posting List。Posting list就是一個int的數組,存儲了所有符合某個term的文檔id。

看到這裡,不要認為就結束了,精彩的部分才剛開始...

通過posting list這種索引方式似乎可以很快進行查找,比如要找age=24的同學,愛回答問題的小明馬上就舉手回答:我知道,id是1,2的同學。但是,如果這裡有上千萬的記錄呢?如果是想通過name來查找呢?

Term Dictionary

Elasticsearch為了能快速找到某個term,将所有的term排個序,二分法查找term,logN的查找效率,就像通過字典查找一樣,這就是Term Dictionary。現在再看起來,似乎和傳統資料庫通過B-Tree的方式類似啊,為什麼說比B-Tree的查詢快呢?

Term Index

B-Tree通過減少磁盤尋道次數來提高查詢性能,Elasticsearch也是采用同樣的思路,直接通過記憶體查找term,不讀磁盤,但是如果term太多,term dictionary也會很大,放記憶體不現實,于是有了Term Index,就像字典裡的索引頁一樣,A開頭的有哪些term,分别在哪頁,可以了解term index是一顆樹

Elasticsearch入門整理,索引原理

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

Elasticsearch入門整理,索引原理

是以term index不需要存下所有的term,而僅僅是他們的一些字首與Term Dictionary的block之間的映射關系,再結合FST(Finite State Transducers)的壓縮技術,可以使term index緩存到記憶體中。從term index查到對應的term dictionary的block位置之後,再去磁盤上找term,大大減少了磁盤随機讀的次數。

啥是FST?

假設我們現在要将mop, moth, pop, star, stop and top(term index裡的term字首)映射到序号:0,1,2,3,4,5(term dictionary的block位置)。最簡單的做法就是定義個Map<string, integer="">,大家找到自己的位置對應入座就好了,但從記憶體占用少的角度想想,有沒有更優的辦法呢?答案就是:FST

Elasticsearch入門整理,索引原理

⭕️表示一種狀态

-->表示狀态的變化過程,上面的字母/數字表示狀态變化和權重

将單詞分成單個字母通過⭕️和-->表示出來,0權重不顯示。如果⭕️後面出現分支,就标記權重,最後整條路徑上的權重加起來就是這個單詞對應的序号。

關于權重計算,有個示範工具(http://examples.mikemccandless.com/fst.py?terms=&cmd=Build+it%21)規則,自己試試就知道了

FST以位元組的方式存儲所有的term,這種壓縮方式可以有效的縮減存儲空間,使得term index足以放進記憶體,但這種方式也會導緻查找時需要更多的CPU資源。

後面的更精彩,看累了的同學可以喝杯咖啡……

壓縮技巧

Elasticsearch裡除了上面說到用FST壓縮term index外,對posting list也有壓縮技巧。 

小明喝完咖啡又舉手了:"posting list不是已經隻存儲文檔id了嗎?還需要壓縮?"

嗯,我們再看回最開始的例子,如果Elasticsearch需要對同學的性别進行索引(這時傳統關系型資料庫已經哭暈在廁所……),會怎樣?如果有上千萬個同學,而世界上隻有男/女這樣兩個性别,每個posting list都會有至少百萬個文檔id。 Elasticsearch是如何有效的對這些文檔id壓縮的呢?

Frame Of Reference

增量編碼壓縮,将大數變小數,按位元組存儲

首先,Elasticsearch要求posting list是有序的(為了提高搜尋的性能,再任性的要求也得滿足),這樣做的一個好處是友善壓縮,看下面這個圖例:

Elasticsearch入門整理,索引原理

如果數學不是體育老師教的話,還是比較容易看出來這種壓縮技巧的。

原理就是通過增量,将原來的大數變成小數僅存儲增量值,再精打細算按bit排好隊,最後通過位元組存儲,而不是大大咧咧的盡管是2也是用int(4個位元組)來存儲。

Roaring bitmaps

說到Roaring bitmaps,就必須先從bitmap說起。Bitmap是一種資料結構,假設有某個posting list:

[1,3,4,7,10]

對應的bitmap就是:

[1,0,1,1,0,0,1,0,0,1]

非常直覺,用0/1表示某個值是否存在,比如10這個值就對應第10位,對應的bit值是1,這樣用一個位元組就可以代表8個文檔id,舊版本(5.0之前)的Lucene就是用這樣的方式來壓縮的,但這樣的壓縮方式仍然不夠高效,如果有1億個文檔,那麼需要12.5MB的存儲空間,這僅僅是對應一個索引字段(我們往往會有很多個索引字段)。于是有人想出了Roaring bitmaps這樣更高效的資料結構。

Bitmap的缺點是存儲空間随着文檔個數線性增長,Roaring bitmaps需要打破這個魔咒就一定要用到某些指數特性:

将posting list按照65535為界限分塊,比如第一塊所包含的文檔id範圍在0~65535之間,第二塊的id範圍是65536~131071,以此類推。再用<商,餘數>的組合表示每一組id,這樣每組裡的id範圍都在0~65535内了,剩下的就好辦了,既然每組id不會變得無限大,那麼我們就可以通過最有效的方式對這裡的id存儲。

Elasticsearch入門整理,索引原理

程式員的世界裡除了1024外,65535也是一個經典值,因為它=2^16-1,正好是用2個位元組能表示的最大數,一個short的存儲機關,注意到上圖裡的最後一行“If a block has more than 4096 values, encode as a bit set, and otherwise as a simple array using 2 bytes per value”,如果是大塊,用節省點用bitset存,小塊就大方點,2個位元組我也不計較了,用一個short[]存着友善。

那為什麼用4096來區分大塊還是小塊呢?

個人了解:這句話好像有點問題,檔案塊一般是4KB的,4096*2bytes = 8192bytes = 2*4KB, 磁盤一次尋道可以順序把一個小塊的内容都讀出來,超過4KB了,需要兩次讀。

聯合索引

上面說了半天都是單field索引,如果多個field索引的聯合查詢,反向索引如何滿足快速查詢的要求呢?

  • 利用跳表(Skip list)的資料結構快速做“與”運算,或者
  • 利用上面提到的bitset按位“與”

先看看跳表的資料結構:

Elasticsearch入門整理,索引原理

将一個有序連結清單level0,挑出其中幾個元素到level1及level2,每個level越往上,選出來的指針元素越少,查找時依次從高level往低查找,比如55,先找到level2的31,再找到level1的47,最後找到55,一共3次查找,查找效率和2叉樹的效率相當,但也是用了一定的空間備援來換取的。

假設有下面三個posting list需要聯合索引:

Elasticsearch入門整理,索引原理

如果使用跳表,對最短的posting list中的每個id,逐個在另外兩個posting list中查找看是否存在,最後得到交集的結果。

如果使用bitset,就很直覺了,直接按位與,得到的結果就是最後的交集。

總結和思考

Elasticsearch的索引思路:

“将磁盤裡的東西盡量搬進記憶體,減少磁盤随機讀取次數(同時也利用磁盤順序讀特性),結合各種奇技淫巧的壓縮算法(FST,roaring bitmaps),用及其苛刻的态度使用記憶體。”

是以,對于使用Elasticsearch進行索引時需要注意:

  • 不需要索引的字段,一定要明确定義出來,因為預設是自動建索引的
  • 同樣的道理,對于String類型的字段,不需要analysis的也需要明确定義出來,因為預設也是會analysis的
  • 選擇有規律的ID很重要,随機性太大的ID(比如java的UUID)不利于查詢

關于最後一點,個人認為有多個因素:

其中一個(也許不是最重要的)因素: 上面看到的壓縮算法,都是對Posting list裡的大量ID進行壓縮的,那如果ID是順序的,或者是有公共字首等具有一定規律性的ID,壓縮比會比較高;

另外一個因素: 可能是最影響查詢性能的,應該是最後通過Posting list裡的ID到磁盤中查找Document資訊的那步,因為Elasticsearch是分Segment存儲的,根據ID這個大範圍的Term定位到Segment的效率直接影響了最後查詢的性能,如果ID是有規律的,可以快速跳過不包含該ID的Segment,進而減少不必要的磁盤讀次數,具體可以參考這篇如何選擇一個高效的全局ID方案(評論也很精彩)

轉:http://blog.pengqiuyuan.com/ji-chu-jie-shao-ji-suo-yin-yuan-li-fen-xi/