一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

elasticsearch 索引秘密

 開心豆豆2010 2018-08-02

 Elasticsearch是通過(guò)Lucene的倒排索引技術(shù)實(shí)現(xiàn)比關(guān)系型數(shù)據(jù)庫(kù)更快的過(guò)濾。特別是它對(duì)多條件的過(guò)濾支持非常好,比如年齡在18和30之間,性別為女性這樣的組合查詢。倒排索引很多地方都有介紹,但是其比關(guān)系型數(shù)據(jù)庫(kù)的b-tree索引快在哪里?到底為什么快呢?

籠統(tǒng)的來(lái)說(shuō),b-tree索引是為寫入優(yōu)化的索引結(jié)構(gòu)。當(dāng)我們不需要支持快速的更新的時(shí)候,可以用預(yù)先排序等方式換取更小的存儲(chǔ)空間,更快的檢索速度等好處,其代價(jià)就是更新慢。要進(jìn)一步深入的化,還是要看一下Lucene的倒排索引是怎么構(gòu)成的。

這里有好幾個(gè)概念。我們來(lái)看一個(gè)實(shí)際的例子,假設(shè)有如下的數(shù)據(jù):

 

docid

年齡

性別

1

18

2

20

3

18

這里每一行是一個(gè)document。每個(gè)document都有一個(gè)docid。那么給這些document建立的倒排索引就是:

年齡

18

[1,3]

20

[2]

性別

 

[1,2]

[3]

可以看到,倒排索引是per field的,一個(gè)字段由一個(gè)自己的倒排索引。18,20這些叫做 term,而[1,3]就是posting list。Posting list就是一個(gè)int的數(shù)組,存儲(chǔ)了所有符合某個(gè)term的文檔id。那么什么是term dictionary 和 term index?

假設(shè)我們有很多個(gè)term,比如:

Carla,Sara,Elin,Ada,Patty,Kate,Selena

如果按照這樣的順序排列,找出某個(gè)特定的term一定很慢,因?yàn)閠erm沒有排序,需要全部過(guò)濾一遍才能找出特定的term。排序之后就變成了:

Ada,Carla,Elin,Kate,Patty,Sara,Selena

這樣我們可以用二分查找的方式,比全遍歷更快地找出目標(biāo)的term。這個(gè)就是 term dictionary。有了term dictionary之后,可以用 logN 次磁盤查找得到目標(biāo)。但是磁盤的隨機(jī)讀操作仍然是非常昂貴的(一次random access大概需要10ms的時(shí)間)。所以盡量少的讀磁盤,有必要把一些數(shù)據(jù)緩存到內(nèi)存里。但是整個(gè)term dictionary本身又太大了,無(wú)法完整地放到內(nèi)存里。于是就有了term index。term index有點(diǎn)像一本字典的大的章節(jié)表。比如:

A開頭的term ……………. Xxx頁(yè)

C開頭的term ……………. Xxx頁(yè)

E開頭的term ……………. Xxx頁(yè)

如果所有的term都是英文字符的話,可能這個(gè)term index就真的是26個(gè)英文字符表構(gòu)成的了。但是實(shí)際的情況是,term未必都是英文字符,term可以是任意的byte數(shù)組。而且26個(gè)英文字符也未必是每一個(gè)字符都有均等的term,比如x字符開頭的term可能一個(gè)都沒有,而s開頭的term又特別多。實(shí)際的term index是一棵trie 樹:

例子是一個(gè)包含 "A", "to", "tea", "ted", "ten", "i", "in", 和 "inn" 的 trie 樹。這棵樹不會(huì)包含所有的term,它包含的是term的一些前綴。通過(guò)term index可以快速地定位到term dictionary的某個(gè)offset,然后從這個(gè)位置再往后順序查找。再加上一些壓縮技術(shù)(搜索 Lucene Finite State Transducers) term index 的尺寸可以只有所有term的尺寸的幾十分之一,使得用內(nèi)存緩存整個(gè)term index變成可能。整體上來(lái)說(shuō)就是這樣的效果。

現(xiàn)在我們可以回答“為什么Elasticsearch/Lucene檢索可以比mysql快了。Mysql只有term dictionary這一層,是以b-tree排序的方式存儲(chǔ)在磁盤上的。檢索一個(gè)term需要若干次的random access的磁盤操作。而Lucene在term dictionary的基礎(chǔ)上添加了term index來(lái)加速檢索,term index以樹的形式緩存在內(nèi)存中。從term index查到對(duì)應(yīng)的term dictionary的block位置之后,再去磁盤上找term,大大減少了磁盤的random access次數(shù)。

額外值得一提的兩點(diǎn)是:term index在內(nèi)存中是以FST(finite state transducers)的形式保存的,其特點(diǎn)是非常節(jié)省內(nèi)存。Term dictionary在磁盤上是以分block的方式保存的,一個(gè)block內(nèi)部利用公共前綴壓縮,比如都是Ab開頭的單詞就可以把Ab省去。這樣term dictionary可以比b-tree更節(jié)約磁盤空間。

如何聯(lián)合索引查詢?

所以給定查詢過(guò)濾條件 age=18 的過(guò)程就是先從term index找到18在term dictionary的大概位置,然后再?gòu)膖erm dictionary里精確地找到18這個(gè)term,然后得到一個(gè)posting list或者一個(gè)指向posting list位置的指針。然后再查詢 gender=女 的過(guò)程也是類似的。最后得出 age=18 AND gender=女 就是把兩個(gè) posting list 做一個(gè)“與”的合并。

這個(gè)理論上的“與”合并的操作可不容易。對(duì)于mysql來(lái)說(shuō),如果你給age和gender兩個(gè)字段都建立了索引,查詢的時(shí)候只會(huì)選擇其中最selective的來(lái)用,然后另外一個(gè)條件是在遍歷行的過(guò)程中在內(nèi)存中計(jì)算之后過(guò)濾掉。那么要如何才能聯(lián)合使用兩個(gè)索引呢?有兩種辦法:

  • 使用skip list數(shù)據(jù)結(jié)構(gòu)。同時(shí)遍歷gender和age的posting list,互相skip;
  • 使用bitset數(shù)據(jù)結(jié)構(gòu),對(duì)gender和age兩個(gè)filter分別求出bitset,對(duì)兩個(gè)bitset做AN操作。

PostgreSQL 從 8.4 版本開始支持通過(guò)bitmap聯(lián)合使用兩個(gè)索引,就是利用了bitset數(shù)據(jù)結(jié)構(gòu)來(lái)做到的。當(dāng)然一些商業(yè)的關(guān)系型數(shù)據(jù)庫(kù)也支持類似的聯(lián)合索引的功能。Elasticsearch支持以上兩種的聯(lián)合索引方式,如果查詢的filter緩存到了內(nèi)存中(以bitset的形式),那么合并就是兩個(gè)bitset的AND。如果查詢的filter沒有緩存,那么就用skip list的方式去遍歷兩個(gè)on disk的posting list。

利用 Skip List 合并

以上是三個(gè)posting list。我們現(xiàn)在需要把它們用AND的關(guān)系合并,得出posting list的交集。首先選擇最短的posting list,然后從小到大遍歷。遍歷的過(guò)程可以跳過(guò)一些元素,比如我們遍歷到綠色的13的時(shí)候,就可以跳過(guò)藍(lán)色的3了,因?yàn)?比13要小。

整個(gè)過(guò)程如下

Next -> 2
Advance(2) -> 13
Advance(13) -> 13
Already on 13
Advance(13) -> 13 MATCH!!!
Next -> 17
Advance(17) -> 22
Advance(22) -> 98
Advance(98) -> 98
Advance(98) -> 98 MATCH!!!

最后得出的交集是[13,98],所需的時(shí)間比完整遍歷三個(gè)posting list要快得多。但是前提是每個(gè)list需要指出Advance這個(gè)操作,快速移動(dòng)指向的位置。什么樣的list可以這樣Advance往前做蛙跳?skip list:

從概念上來(lái)說(shuō),對(duì)于一個(gè)很長(zhǎng)的posting list,比如:

[1,3,13,101,105,108,255,256,257]

我們可以把這個(gè)list分成三個(gè)block:

[1,3,13] [101,105,108] [255,256,257]

然后可以構(gòu)建出skip list的第二層:

[1,101,255]

1,101,255分別指向自己對(duì)應(yīng)的block。這樣就可以很快地跨block的移動(dòng)指向位置了。

Lucene自然會(huì)對(duì)這個(gè)block再次進(jìn)行壓縮。其壓縮方式叫做Frame Of Reference編碼。示例如下:

考慮到頻繁出現(xiàn)的term(所謂low cardinality的值),比如gender里的男或者女。如果有1百萬(wàn)個(gè)文檔,那么性別為男的posting list里就會(huì)有50萬(wàn)個(gè)int值。用Frame of Reference編碼進(jìn)行壓縮可以極大減少磁盤占用。這個(gè)優(yōu)化對(duì)于減少索引尺寸有非常重要的意義。當(dāng)然mysql b-tree里也有一個(gè)類似的posting list的東西,是未經(jīng)過(guò)這樣壓縮的。

因?yàn)檫@個(gè)Frame of Reference的編碼是有解壓縮成本的。利用skip list,除了跳過(guò)了遍歷的成本,也跳過(guò)了解壓縮這些壓縮過(guò)的block的過(guò)程,從而節(jié)省了cpu。

利用bitset合并

Bitset是一種很直觀的數(shù)據(jù)結(jié)構(gòu),對(duì)應(yīng)posting list如:

[1,3,4,7,10]

對(duì)應(yīng)的bitset就是:

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

每個(gè)文檔按照文檔id排序?qū)?yīng)其中的一個(gè)bit。Bitset自身就有壓縮的特點(diǎn),其用一個(gè)byte就可以代表8個(gè)文檔。所以100萬(wàn)個(gè)文檔只需要12.5萬(wàn)個(gè)byte。但是考慮到文檔可能有數(shù)十億之多,在內(nèi)存里保存bitset仍然是很奢侈的事情。而且對(duì)于個(gè)每一個(gè)filter都要消耗一個(gè)bitset,比如age=18緩存起來(lái)的話是一個(gè)bitset,18<=age<25是另外一個(gè)filter緩存起來(lái)也要一個(gè)bitset。

所以秘訣就在于需要有一個(gè)數(shù)據(jù)結(jié)構(gòu):

  • 可以很壓縮地保存上億個(gè)bit代表對(duì)應(yīng)的文檔是否匹配filter;
  • 這個(gè)壓縮的bitset仍然可以很快地進(jìn)行AND和 OR的邏輯操作。

Lucene使用的這個(gè)數(shù)據(jù)結(jié)構(gòu)叫做 Roaring Bitmap。

其壓縮的思路其實(shí)很簡(jiǎn)單。與其保存100個(gè)0,占用100個(gè)bit。還不如保存0一次,然后聲明這個(gè)0重復(fù)了100遍。

這兩種合并使用索引的方式都有其用途。Elasticsearch對(duì)其性能有詳細(xì)的對(duì)比(https://www./blog/frame-of-reference-and-roaring-bitmaps)。簡(jiǎn)單的結(jié)論是:因?yàn)镕rame of Reference編碼是如此 高效,對(duì)于簡(jiǎn)單的相等條件的過(guò)濾緩存成純內(nèi)存的bitset還不如需要訪問(wèn)磁盤的skip list的方式要快。

如何減少文檔數(shù)?

一種常見的壓縮存儲(chǔ)時(shí)間序列的方式是把多個(gè)數(shù)據(jù)點(diǎn)合并成一行。Opentsdb支持海量數(shù)據(jù)的一個(gè)絕招就是定期把很多行數(shù)據(jù)合并成一行,這個(gè)過(guò)程叫compaction。類似的vivdcortext使用mysql存儲(chǔ)的時(shí)候,也把一分鐘的很多數(shù)據(jù)點(diǎn)合并存儲(chǔ)到mysql的一行里以減少行數(shù)。

這個(gè)過(guò)程可以示例如下:

12:05:00

10

12:05:01

15

12:05:02

14

12:05:03

16

合并之后就變成了:

12:05

10

15

14

16

可以看到,行變成了列了。每一列可以代表這一分鐘內(nèi)一秒的數(shù)據(jù)。

Elasticsearch有一個(gè)功能可以實(shí)現(xiàn)類似的優(yōu)化效果,那就是Nested Document。我們可以把一段時(shí)間的很多個(gè)數(shù)據(jù)點(diǎn)打包存儲(chǔ)到一個(gè)父文檔里,變成其嵌套的子文檔。示例如下:

{timestamp:12:05:01, idc:sz, value1:10,value2:11}
{timestamp:12:05:02, idc:sz, value1:9,value2:9}
{timestamp:12:05:02, idc:sz, value1:18,value:17}

可以打包成:

{
max_timestamp:12:05:02, min_timestamp: 1205:01, idc:sz,
records: [
		{timestamp:12:05:01, value1:10,value2:11}
{timestamp:12:05:02, value1:9,value2:9}
{timestamp:12:05:02, value1:18,value:17}
]
}

這樣可以把數(shù)據(jù)點(diǎn)公共的維度字段上移到父文檔里,而不用在每個(gè)子文檔里重復(fù)存儲(chǔ),從而減少索引的尺寸。

(圖片來(lái)源:https://www./watch?v=Su5SHc_uJw8,F(xiàn)aceting with Lucene Block Join Query)

在存儲(chǔ)的時(shí)候,無(wú)論父文檔還是子文檔,對(duì)于Lucene來(lái)說(shuō)都是文檔,都會(huì)有文檔Id。但是對(duì)于嵌套文檔來(lái)說(shuō),可以保存起子文檔和父文檔的文檔id是連續(xù)的,而且父文檔總是最后一個(gè)。有這樣一個(gè)排序性作為保障,那么有一個(gè)所有父文檔的posting list就可以跟蹤所有的父子關(guān)系。也可以很容易地在父子文檔id之間做轉(zhuǎn)換。把父子關(guān)系也理解為一個(gè)filter,那么查詢時(shí)檢索的時(shí)候不過(guò)是又AND了另外一個(gè)filter而已。前面我們已經(jīng)看到了Elasticsearch可以非常高效地處理多filter的情況,充分利用底層的索引。

使用了嵌套文檔之后,對(duì)于term的posting list只需要保存父文檔的doc id就可以了,可以比保存所有的數(shù)據(jù)點(diǎn)的doc id要少很多。如果我們可以在一個(gè)父文檔里塞入50個(gè)嵌套文檔,那么posting list可以變成之前的1/50。

原文地址:http://www./cn/articles/database-timestamp-02?utm_source=infoq&utm_medium=related_content_link&utm_campaign=relatedContent_articles_clk

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    男人和女人黄 色大片| 亚洲欧美日本国产不卡| 亚洲国产精品无遮挡羞羞| 日韩一区中文免费视频| 少妇熟女精品一区二区三区| 国产女同精品一区二区| 蜜臀人妻一区二区三区| 欧美亚洲综合另类色妞| 91精品国自产拍老熟女露脸| 国产偷拍精品在线视频| 国产原创激情一区二区三区| 91精品日本在线视频| 91天堂素人精品系列全集| 亚洲精品中文字幕在线视频| 国产亚洲欧美日韩精品一区| 好东西一起分享老鸭窝| 国产精品伦一区二区三区在线 | 91香蕉视频精品在线看| 日韩成人动画在线观看| 色婷婷中文字幕在线视频| 国产一区一一一区麻豆| 视频一区二区三区自拍偷| 国产熟女一区二区三区四区| 老司机这里只有精品视频| 国产精品白丝久久av| 久久精品国产在热久久| 内射精子视频欧美一区二区| 极品少妇嫩草视频在线观看| 在线一区二区免费的视频| 国产日产欧美精品大秀| 国产精品大秀视频日韩精品| 暴力性生活在线免费视频| 国产成人av在线免播放观看av | 久久精品国产99精品最新| 麻豆精品视频一二三区| 午夜精品黄片在线播放| 五月婷婷亚洲综合一区| 国产精品午夜视频免费观看| 日韩aa一区二区三区| 国产又粗又长又爽又猛的视频| 日韩欧美国产高清在线|