一、前言
二、數(shù)據(jù)庫搜索和ES搜索
2.1 數(shù)據(jù)庫搜索三個問題
容量問題: 電商網(wǎng)站商品上億條時,涉及到單表數(shù)據(jù)過大必須拆分表,數(shù)據(jù)庫磁盤占用過大必須分庫(mycat)。
性能問題: mysql實現(xiàn)模糊查詢必須使用 like關(guān)鍵字, 只有 后模糊 才能走索引,前模糊和全模糊都不會走索引,比如查詢“筆記本電腦”等關(guān)鍵詞時,上億條數(shù)據(jù)的商品名字段逐行掃描,性能跟不上。
不能分詞: 只能搜索完全和關(guān)鍵詞一樣的數(shù)據(jù),那么數(shù)據(jù)量小時,搜索“筆記電腦”,“電腦”數(shù)據(jù)要不要給用戶。
正式由于這些缺陷,在數(shù)據(jù)量比較小的站內(nèi)搜索/垂直搜索,可以使用數(shù)據(jù)庫,但是對于PB級別大數(shù)據(jù)量的互聯(lián)網(wǎng)搜索,肯定不會使用數(shù)據(jù)庫搜索。
Lucene的誕生解決了數(shù)據(jù)庫的這三個問題,容量非常大,存儲大數(shù)據(jù)量不需要分庫分表;模糊查詢性能非??欤欢疫€需要使用使用分詞查詢。Lucene 其實就是一個jar包,里面封裝了全文檢索的引擎、搜索的算法代碼。開發(fā)時,引入lucen的jar包,通過api開發(fā)搜索相關(guān)業(yè)務(wù)。底層會在磁盤建立索引庫。
但是Lucene 有一個最大的缺陷,它是一個單實例搜索引擎,這不符合分布式的互聯(lián)網(wǎng)的需求,所以有了elasticsearch,它是一個多實例的分布式集群搜索引擎,elasticsearch 每個實例就是一個 lucence ,各個節(jié)點之間是平等的/平級的,沒有主從關(guān)系。
elasticsearch 是使用Java語言編寫的,與springboot項目集成起來非常方便。
2.2 Elasticsearch
Elasticsearch官網(wǎng):https://www.elastic.co/cn/products/elasticsearch1.3.2
2.2.1 Elasticsearch的兩個功能
Elasticsearch的兩個功能:分布式的搜索引擎和數(shù)據(jù)分析引擎
搜索:互聯(lián)網(wǎng)搜索、電商網(wǎng)站站內(nèi)搜索、OA系統(tǒng)查詢
數(shù)據(jù)分析:電商網(wǎng)站查詢近一周哪些品類的圖書銷售前十;新聞網(wǎng)站,最近3天閱讀量最高的十個關(guān)鍵詞,輿情分析。
lucene和elasticsearch的區(qū)別
Lucene:最先進、功能最強大的搜索庫,直接基于lucene開發(fā),非常復(fù)雜,api復(fù)雜
Elasticsearch:基于lucene,封裝了許多l(xiāng)ucene底層功能,提供簡單易用的restful api接口和許多語言的客戶端,如java的高級客戶端(Java High Level REST Client)和底層客戶端(Java Low Level REST Client)
2.2.2 Elasticsearch的兩個特點
分布式:ES自動可以將海量數(shù)據(jù)分散到多臺服務(wù)器上去存儲和檢索,進行并行查詢,提高搜索效率。相對的,Lucene是單機應(yīng)用。
近實時:數(shù)據(jù)庫上億條數(shù)據(jù)查詢,搜索一次耗時幾個小時,是批處理(batch-processing)。而es只需秒級即可查詢海量數(shù)據(jù),所以叫近實時。秒級。
2.3 Elasticsearch核心概念 vs. 數(shù)據(jù)庫核心概念
關(guān)系型數(shù)據(jù)庫(比如Mysql) | 非關(guān)系型數(shù)據(jù)庫(Elasticsearch) |
---|---|
數(shù)據(jù)庫Database | 索引Index |
表Table | 索引Index(原為Type) |
數(shù)據(jù)行Row | 文檔Document |
數(shù)據(jù)列Column | 字段Field |
約束 Schema | 映射Mapping |
此外,ES也對應(yīng)MySQL,有 order by、group by 這些操作,還有avg sum max min一系列聚合函數(shù)。我們現(xiàn)在對上表中ES各個概念介紹。
2.3.1 Index:索引
包含一堆有相似結(jié)構(gòu)的文檔數(shù)據(jù)。
索引創(chuàng)建規(guī)則:
(1) 僅限小寫字母
(2) 不能包含\、/、 *、?、"、<、>、|、#以及空格符等特殊符號
(3) 從7.0版本開始不再包含冒號
(4) 不能以-、_或+開頭
(5) 不能超過255個字節(jié)(注意它是字節(jié),因此多字節(jié)字符將計入255個限制)
2.3.2 Type:類型
每個索引里都可以有一個或多個type,type是index中的一個邏輯數(shù)據(jù)分類,一個type下的document,都有相同的fifield。7.x版本正式被去除。
問題:ES為什么要引入Type?
回答:因為關(guān)系型數(shù)據(jù)庫比非關(guān)系型數(shù)據(jù)庫的概念提出的早,而且很成熟,應(yīng)用廣泛。所以,后來很多NoSQL(包括:MongoDB,Elasticsearch等)都參考并延用了傳統(tǒng)關(guān)系型數(shù)據(jù)庫的基本概念。由于需要有一個對應(yīng)關(guān)系型數(shù)據(jù)庫表的概念,所以type應(yīng)運而生。
問題:ES各個版本中的Type?
回答:在 5.X 版本中,一個 index 下可以創(chuàng)建多個 type;
在 6.X 版本中,一個 index下只能存在一個 type;
在7.X 版本中,直接去除了 type 的概念,就是說index 不再會有 type。
問題:為什么要7.X版本去除Type?
答: 因為 Elasticsearch 設(shè)計初期,是直接查考了關(guān)系型數(shù)據(jù)庫的設(shè)計模式,存在了 type(數(shù)據(jù)表)的概念。但是,其搜索引擎是基于 Lucene 的,這種 “基因”決定了 type 是多余的。 Lucene 的全文檢索功能之所以快,是因為 倒序索引 的存在。而這種 倒序索引 的生成是基于 index 的,而并非 type。多個type 反而會減慢搜索的速度。為了保持 Elasticsearch “一切為了搜索” 的宗旨,適當(dāng)?shù)淖鲂└淖儯ㄈコ?type)也是無可厚非的,也是值得的。
問題:為何不是在 6.X 版本開始就直接去除 type,而是要逐步去除type?
回答:因為歷史原因,前期 Elasticsearch 支持一個 index 下存在多個 type的,而且,有很多項目在使用Elasticsearch 作為數(shù)據(jù)庫。如果直接去除 type 的概念,不僅是很多應(yīng)用 Elasticsearch 的項目將面臨業(yè)務(wù)、功能和代碼的大改,而且對于 Elasticsearch 官方來說,也是一個巨大的挑戰(zhàn)(這個是傷筋動骨的大手術(shù),很多涉及到 type源碼是要修改的)。所以,權(quán)衡利弊,采取逐步過渡的方式,最終,推遲到 7.X 版本才完成 “去除 type” 這個 革命性的變革。
2.3.3 Document:文檔
es中的最小數(shù)據(jù)單元。一個document就像數(shù)據(jù)庫中的一條記錄。通常以json格式顯示。多個document存儲于一個索引(Index)中。
2.3.4 Field:字段
就像數(shù)據(jù)庫中的列(Columns),定義每個document應(yīng)該有的字段。
2.3.5 Shard:分片
index數(shù)據(jù)過大時,將index里面的數(shù)據(jù),分為多個shard,分布式的存儲在各個服務(wù)器上面??梢灾С趾A繑?shù)據(jù)和高并發(fā),提升性能和吞吐量,充分利用多臺機器的cpu。
2.3.6 Replica:副本
在分布式環(huán)境下,任何一臺機器都會隨時宕機,如果宕機,index的一個分片沒有,導(dǎo)致此index不能搜索。所以,為了保證數(shù)據(jù)的安全,我們會將每個index的分片進行備份,存儲在另外的機器上。保證少數(shù)機器宕機es集群仍可以搜索。能正常提供查詢和插入的分片我們叫做主分片(primary shard),其余的我們就管他們叫做備份的分片(replica shard)。
2.4 Elasticsearch文檔存儲
先說Elasticsearch的文件存儲,Elasticsearch是面向文檔型數(shù)據(jù)庫,一條數(shù)據(jù)在這里就是一個文檔,用JSON作為文檔序列化的格式,比如下面這條用戶數(shù)據(jù):
{
"name" : "carl",
"sex" : "Male",
"age" : 18,
"birthDate": "1990/05/10",
"interests": [ "sports", "music" ]
}
用Mysql這樣的數(shù)據(jù)庫存儲就會容易想到建立一張User表,有name,sex等字段,在Elasticsearch里這就是一個文檔,當(dāng)然這個文檔會屬于一個User的類型,各種各樣的類型存在于一個索引當(dāng)中。這里有一份簡易的將Elasticsearch和關(guān)系型數(shù)據(jù)術(shù)語對照表:
關(guān)系數(shù)據(jù)庫 ? 數(shù)據(jù)庫 ? 表 ? 行 ? 列(Columns)
Elasticsearch ? 索引(Index) ? 類型(type) (7.x版本正式將type剔除) ? 文檔 (Docments) ? 字段(Fields)
一個 Elasticsearch 集群可以包含多個索引(數(shù)據(jù)庫),也就是說其中包含了很多類型(表)。這些類型中包含了很多的文檔(行),然后每個文檔中又包含了很多的字段(列)。Elasticsearch的交互,可以使用JavaAPI,也可以直接使用HTTP的Restful API方式,
三、ElasticSearch高性能設(shè)計
ElasticSearch基于Lucene,同時解決了關(guān)系型數(shù)據(jù)庫的單表容量問題、查詢性能問題、不能分詞查詢問題,即實現(xiàn)了存得多、查得快、查得聰明,那么,ElasticSearch是如何做到的呢?這都要歸功于ElasticSearch內(nèi)部的一系列高性能設(shè)計,讓我們來認(rèn)識一下。
3.1 倒排索引的設(shè)計讓ElasticSearch查詢更快
為什么ElasticSearch比傳統(tǒng)的數(shù)據(jù)庫查詢更快,因為ElasticSearch是基于倒排索引,但是傳統(tǒng)數(shù)據(jù)庫是基于B樹/B+樹。
傳統(tǒng)數(shù)據(jù)庫:二叉樹查找效率是O(n),同時插入新的節(jié)點不必移動全部節(jié)點,所以用樹型結(jié)構(gòu)存儲索引,能同時兼顧插入和查詢的性能(AVL)。因此在這個基礎(chǔ)上,再結(jié)合磁盤的讀取特性(順序讀/隨機讀)(多路查找樹,B樹)。傳統(tǒng)的關(guān)系型數(shù)據(jù)庫采用的是B-Tree/B+Tree這樣的數(shù)據(jù)結(jié)構(gòu):為了提高查詢的效率,減少磁盤尋道次數(shù),將多個值作為一個數(shù)組通過連續(xù)區(qū)間存放,一次尋道讀取多個數(shù)據(jù),同時也降低樹的高度。
ElasticSearch:ES的搜索數(shù)據(jù)結(jié)構(gòu)模型基于倒排索引。倒排索引是指數(shù)據(jù)存儲時,進行分詞建立term索引庫。倒排索引源于實際應(yīng)用中需要根據(jù)屬性的值來查找記錄。這種索引表中的每一項都包括一個屬性值和具有該屬性值的各記錄的地址。由于不是由記錄來確定屬性值,而是由屬性值來確定記錄的位置,因而稱為倒排索引(inverted index)。帶有倒排索引的文件我們稱為倒排索引文件,簡稱倒排文件(inverted file)。
問題:ElasticSearch結(jié)構(gòu)化數(shù)據(jù)怎么儲存呢?
回答:
我們拿到三條結(jié)構(gòu)化數(shù)據(jù):
ID | Name | Age | Sex |
---|---|---|---|
1 | 叮咚 | 18 | Female |
2 | Tom | 50 | Male |
3 | carl | 18 | Male |
ID是Elasticsearch自建的文檔id,那么Elasticsearch建立的索引如下:
Name:
Term | Posting List |
---|---|
叮咚 | 1 |
TOM | 2 |
carl | 3 |
Age:
Term | Posting List |
---|---|
50 | 2 |
18 | [1,3] |
Sex:
Term | Posting List |
---|---|
Female | 1 |
Male | [2,3] |
Elasticsearch分別為每個field都建立了一個從該filed到ID的映射關(guān)系,這個映射關(guān)系就被稱為倒排索引。無論是何種類型的倒排索引,里面的 ID 都是存儲到一個 Posting List 的結(jié)構(gòu)中,這個 Posting List 就被稱為是倒排表。
從上面三個表中,左邊列Tom,carl, 18, Female這些叫term(分類索引),而右邊列[1,2]就是Posting List(倒排表)。Posting list就是一個int的數(shù)組,存儲了所有符合某個term的文檔id。
通過posting list這種索引方式可以很快進行查找,比如要找age=18的詞條,就是1和3。但是,如果這里有上千萬的記錄呢?答案是Term Dictionary。
Term Dictionary(詞典)
Elasticsearch為了能快速找到某個term,將所有的term排個序,二分法查找term,log(N)的查找效率,就像通過字典查找一樣,這就是Term Dictionary?,F(xiàn)在好像跟我們的傳統(tǒng)B樹的方式一樣啊 。那么我們的ES有什么進步呢?答案是將一個更小的詞典Term Index存放到內(nèi)存里面。
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)系,可以使term index緩存到內(nèi)存中。從term index查到對應(yīng)的term dictionary的block位置之后,再去磁盤上找term,大大減少了磁盤隨機讀的次數(shù)。
block塊:文件系統(tǒng)不是一個扇區(qū)一個扇區(qū)的來讀數(shù)據(jù),太慢了,所以有了block(塊)的概念,它是一個塊一個塊的讀取的,block才是文件存取的最小單位。
3.2 FST的設(shè)計讓ElasticSearch用最小的內(nèi)存存儲Term Index
Finite StateTransducers 簡稱 FST,通常中文譯作有窮狀態(tài)轉(zhuǎn)換器或者有限狀態(tài)傳感器,F(xiàn)ST是一項將一個字節(jié)序列映射到block塊的技術(shù)。
假設(shè)我們現(xiàn)在要將mop, moth, pop, star, stop and top(term index里的term前綴)映射到序號:0,1, 2,3,4,5(term dictionary的block位置)。最簡單的做法就是定義個Map<string, integer=“”>,找到自己的位置對應(yīng)入座就好了,但從內(nèi)存占用少的角度想想,有沒有更優(yōu)的辦法呢?答案就是:FST。
看圖可知:
mop = 0 + 0 + 0 = 0
moth = 0 + 0 + 1 + 0 = 1
pop = 2 + 0 + 0 = 2
star = 3 + 0 + 0 +0 = 3
stop = 3 +0 +1 + 0 = 4
top = 5 + 0 + 0 = 5
? 表示一種狀態(tài) , -->表示狀態(tài)的變化過程,上面的字母/數(shù)字表示狀態(tài)變化和權(quán)重。將單詞分成單個字母通過? 和–>表示出來,0權(quán)重不顯示。如果? 后面出現(xiàn)分支,就標(biāo)記權(quán)重,最后整條路徑上的權(quán)重加起來就是這個單詞對應(yīng)的序號。當(dāng)遍歷上面的每一條邊的時候,都會加上這條邊的輸出,比如當(dāng)輸入是 stop 的時候會經(jīng)過 s/3 和 o/1 ,相加得到的排序的順序是 4 ;而對于 mop ,得到的排序的結(jié)果是 0 。
但是這個樹并不會包含所有的term,而是很多term的前綴,通過這些前綴快速定位到這個前綴所屬的磁盤的block,再從這個block去找文檔列表。為了壓縮詞典的空間,實際上每個block都只會保存block內(nèi)不同的部分,比如 mop 和 moth 在同一個以 mo 開頭的block,那么在對應(yīng)的詞典里面只會保存 p 和 th ,這樣空間利用率提高了一倍。
使用有限狀態(tài)轉(zhuǎn)換器在內(nèi)存消耗上面要比遠(yuǎn)比 SortedMap 要少,但是在查詢的時候需要更多的CPU資源。維基百科的索引就是使用的FST,只使用了69MB的空間,花了大約8秒鐘,就為接近一千萬個詞條建立了索引,使用的堆空間不到256MB。
在ES中有一種查詢叫模糊查詢(fuzzy query),根據(jù)搜索詞和字段之間的編輯距離來判斷是否匹配。在ES4.0之前,模糊查詢會先讓檢索詞和所有的term計算編輯距離篩選出所有編輯距離內(nèi)的字段;在ES4.0之后,采用了由Robert開發(fā)的,直接通過有限狀態(tài)轉(zhuǎn)換器就可以搜索指定編輯距離內(nèi)的單詞的方法,將模糊查詢的效率提高了超過100倍。
現(xiàn)在已經(jīng)把詞典壓縮成了詞條索引,尺寸已經(jīng)足夠小到放入內(nèi)存,通過索引能夠快速找到文檔列表?,F(xiàn)在又有另外一個問題,把所有的文檔的id放入磁盤中會不會占用了太多空間?如果有一億個文檔,每個文檔有10個字段,為了保存這個posting list就需要消耗十億個integer的空間,磁盤空間的消耗也是巨大的,ES采用了一個更加巧妙的方式來保存所有的id。這就是壓縮技巧之Frame Of Reference。
3.3 Frame Of Reference的設(shè)計讓ElasticSearch用最小的磁盤存儲Posting List
Frame Of Reference 可以翻譯成索引幀
Elasticsearch里除了上面說到用FST壓縮term index外,對posting list也有壓縮技巧。為了方便壓縮,Elasticsearch要求posting list是有序的(為了提高搜索的性能,再任性的要求也得滿足)。同時為了減小存儲空間,所有的id都會進行delta編碼(即增量編碼)。
比如現(xiàn)在有id列表 [73, 300, 302, 332, 343, 372] ,轉(zhuǎn)化成每一個id相對于前一個id的增量值(第一個id的前一個id默認(rèn)是0,增量就是它自己)列表是 [73, 227, 2, 30, 11, 29] 。在這個新的列表里面,所有的id都是小于255的,所以每個id只需要一個字節(jié)存儲。
解釋:Elasticsearch要求posting list是有序的,除了第一個id以外,后面的id都存儲為前一個id的增量,則
當(dāng) id列表為 [73, 300, 302, 332, 343, 372] ,Elasticsearch不會直接存儲這個id列表,而是會計算增量,
第一個id為73,直接存儲;
第二個id為300,但是不會存儲300這個值,而是存儲 300-73 =227 這個增量;
第三個id為302,但是不會存儲302這個值,而是存儲 302-(73+227) =2 這個增量;
以此類推,最后Elasticsearch的posting list實際存儲的是 [73, 227, 2, 30, 11, 29] ,這樣存儲的數(shù)字就變小了,可以用更小的數(shù)據(jù)類型來存儲每個值,從而達到節(jié)約磁盤空間。
實際上ES會做的更加精細(xì),它會把所有的文檔分成很多個block,每個block正好包含256個文檔,然后單獨對每個文檔進行增量編碼,計算出存儲這個block里面所有文檔最多需要多少位來保存每個id,并且把這個位數(shù)作為頭信息(header)放在每個block 的前面。這個技術(shù)叫Frame of Reference,翻譯成索引幀。
比如對上面的數(shù)據(jù)進行壓縮(假設(shè)每個block只有3個文件而不是256),壓縮過程如下
如果直接存儲六個int類型,每個int類型4字節(jié),需要24字節(jié);但是使用增量編碼,然后拆分到不同block里面,最后加一個header信息,表示數(shù)字個數(shù),因為每個block中存儲數(shù)字256以內(nèi),所以header中數(shù)字最大值位256,只需要 8位(一個字節(jié))就足夠存儲header。然后對于第一個block中最大值位227,2 ^7 =128 ,2 ^8 =256,所以每個數(shù)字需要8位,則第一個block需要四字節(jié)(header頭占一個字節(jié),后面三個數(shù)字每個占一個字節(jié))。對于第二個block中最大值是30,因為 2 ^5 =32 ,只需要 5 位就可以存放,所以第二個block只需要三字節(jié)(header一個字節(jié),每個數(shù)字5位,就是5*3=15位,就是兩字節(jié)(16位)就夠了),所以兩個block只需要7個字節(jié),如果不使用增量編碼,直接存儲int需要24字節(jié),占用磁盤空間僅僅 1/3 .
增量編碼的本質(zhì)上posting list有序排列,然后存儲增量數(shù)字比原數(shù)字小,用每個數(shù)字用更少的位數(shù)就可以表示。
8個二進制位構(gòu)成一個字節(jié)。這種壓縮算法的原理就是通過增量,將原來的大數(shù)變成小數(shù)僅存儲增量值,再精打細(xì)算按bit排好隊,最后通過字節(jié)存儲,而不是大大咧咧的盡管是2也是用int(4個字節(jié))來存儲。
在返回結(jié)果的時候,其實也并不需要把所有的數(shù)據(jù)直接解壓然后一股腦全部返回,可以直接返回一個迭代器 iterator ,直接通過迭代器的 next 方法逐一取出壓縮的id,這樣也可以極大的節(jié)省計算和內(nèi)存開銷。
通過以上的方式可以極大的節(jié)省posting list的空間消耗,提高查詢性能。不過ES為了提高filter過濾器查詢的性能,還做了更多的工作,那就是緩存。
3.4 Roaring Bitmaps的設(shè)計讓ElasticSearch用最小的磁盤存儲緩存filter
ES會緩存頻率比較高的filter查詢,其中的原理也比較簡單,即生成 (fitler, segment數(shù)據(jù)空間) 和id列表的映射,但是和倒排索引不同,我們只把常用的filter緩存下來而倒排索引是保存所有的,并且filter緩存應(yīng)該足夠快,不然直接查詢不就可以了。ES直接把緩存的filter放到內(nèi)存里面,映射的posting list放入磁盤中。
ES在filter緩存使用的壓縮方式和倒排索引的壓縮方式并不相同,filter緩存使用了roaring bitmap的數(shù)據(jù)結(jié)構(gòu),在查詢的時候相對于上面的Frame of Reference方式CPU消耗要小,查詢效率更高,代價就是需要的存儲空間(磁盤)更多。
Roaring Bitmap是由int數(shù)組和bitmap這兩個數(shù)據(jù)結(jié)構(gòu)改良過的成果——int數(shù)組速度快但是空間消耗大,bitmap相對來說空間消耗小但是不管包含多少文檔都需要12.5MB的空間,即使只有一個文件也要12.5MB的空間,這樣實在不劃算,所以權(quán)衡之后就有了下面的Roaring Bitmap。
3.4.1 認(rèn)識Roaring Bitmaps數(shù)據(jù)結(jié)構(gòu)
Bitmap是一種數(shù)據(jù)結(jié)構(gòu),假設(shè)有某個posting list:[1,3,4,7,10]
對應(yīng)的bitmap就是:[1,0,1,1,0,0,1,0,0,1]
非常直觀,用0/1表示某個值是否存在,比如10這個值就對應(yīng)第10位,對應(yīng)的bit值是1,這樣用一個字節(jié)就可以代表8個文檔id,舊版本(5.0之前)的Lucene就是用這樣的方式來壓縮的,但這樣的壓縮方式仍然不夠高效,如果有1億個文檔,那么需要12.5MB的存儲空間,這僅僅是對應(yīng)一個索引字段(我們往往會有很多個索引字段)。于是有人想出了Roaring bitmaps這樣更高效的數(shù)據(jù)結(jié)構(gòu)。
解釋:如果有1億個文檔,那么需要12.5MB的存儲空間
bitmap就是按位存儲,空間變?yōu)樵瓉淼?/8,1億 = 110 ^8 ,除以 110 ^6 (1M)= 100 ,再次除以 8 = 12.5,所以需要 12.5M
Bitmap的缺點是存儲空間隨著文檔個數(shù)線性增長,Roaring bitmaps需要打破這個魔咒就一定要用到某些指數(shù)特性.
(1) Roaring Bitmap首先會根據(jù)每個id的高16位分配id到對應(yīng)的block里面,比如第一個block里面id應(yīng)該都是在0到65535之間,第二個block的id在65536和131071之間
(2) 對于每一個block里面的數(shù)據(jù),根據(jù)id數(shù)量分成兩類
a. 如果數(shù)量小于4096,就是用short數(shù)組保存
b. 數(shù)量大于等于4096,就使用bitmap保存
3.4.2 ES中使用Roaring Bitmaps數(shù)據(jù)結(jié)構(gòu)
在每一個block里面,一個數(shù)字實際上只需要2個字節(jié)來保存就行了,因為高16位在這個block里面都是相同的,高16位就是block的id,block id和文檔的id都用short保存。
至于4096這個分界線,因為當(dāng)數(shù)量小于4096的時候,如果用bitmap就需要8kB的空間,而使用2個字節(jié)的數(shù)組空間消耗就要少一點。比如只有2048個值,每個值2字節(jié),一共只需要4kB就能保存,但是bitmap需要8kB。
解釋:在每一個block里面,一個數(shù)字實際上只需要2個字節(jié)來保存就行了
因為 N%66536 的值是 0~65535 ,需要的位數(shù)是16位,所以兩個字節(jié)就可以了,直接使用java中的short類型就可以了
解釋:對于每一個block里面的數(shù)據(jù),如果數(shù)量小于4096,就是用short數(shù)組保存,每個數(shù)組元素都是一個short類型;對于每一個block里面的數(shù)據(jù),如果數(shù)量大于等于4096,就使用bitmap保存。
對于數(shù)量等于4096(2 ^12 = 4 * 2 ^10 =4k)的時候,如果用bitmap就需要8kB的空間,如果用short數(shù)組也是8KB
short數(shù)組:4096個值就是2 ^12 = 4k,然后 4k * 2B = 8kB
bitmap:因為 N%66536 的值是 0~65535 ,所以每個block中存儲的最大數(shù)量就是 2 ^16,對于bitmap來說,只需要 8KB 就可以滿足了 8KB = 8 * 1K * 1B(8b) = 2 ^3 * 2 ^10 * 2 ^3 = 2 ^16
解釋:只有2048個值,每個值2字節(jié),使用short類型一共只需要4kB就能保存,但是bitmap需要8kB
short數(shù)組:2048個值就是2 ^11 = 2k,然后 2k * 2B = 4kB
bitmap:因為 N%66536 的值是 0~65535 ,所以每個block中存儲的最大數(shù)量就是 2 ^16,對于bitmap來說,只需要 8KB 就可以滿足了 8KB = 8 * 1K * 1B(8b) = 2 ^3 * 2 ^10 * 2 ^3 = 2 ^16
小結(jié):無論是存儲1個數(shù)字還是65536個數(shù)字,使用bitmap都需要8KB,當(dāng)存儲的數(shù)字量小于4096的時候,使用short數(shù)組更好,因為占內(nèi)存更小。
由此見得,Elasticsearch使用的倒排索引確實比關(guān)系型數(shù)據(jù)庫的B-Tree索引快。
3.5 跳表實現(xiàn)多個field的聯(lián)合索引做倒排索引
上面說了半天都是單field索引,如果多個field索引的聯(lián)合查詢,倒排索引如何滿足快速查詢的要求呢?答案是利用跳表(Skip list)的數(shù)據(jù)結(jié)構(gòu)快速做“與”運算,或者利用上面提到的bitset按位“與” 來實現(xiàn)。
3.5.1 認(rèn)識跳表這個數(shù)據(jù)結(jié)構(gòu)
先看看跳表的數(shù)據(jù)結(jié)構(gòu):
將一個有序鏈表level0,挑出其中幾個元素到level1及l(fā)evel2,每個level越往上,選出來的指針元素越少,查找時依次從高level往低查找,比如45,先找到level2的25,最后找到45,查找效率和2叉樹的效率相當(dāng),但也是用了一定的空間冗余來換取的。
3.5.2 跳表實現(xiàn)多個field的聯(lián)合索引做倒排索引
假設(shè)有下面三個posting list需要聯(lián)合索引:
如果使用跳表,對最短的posting list中的每個id,逐個在另外兩個posting list中查找看是否存在,最后得到交集的結(jié)果。
如果使用bitset(基于bitMap),就很直觀了,直接按位與,得到的結(jié)果就是最后的交集。
注意,這是我們倒排索引實現(xiàn)聯(lián)合索引的方式,不是我們ES就是這樣操作的。
3.6 總結(jié)和思考
Elasticsearch的索引思路是將磁盤里的東西盡量搬進內(nèi)存,減少磁盤隨機讀取次數(shù)(同時也利用磁盤順序讀特性),結(jié)合各種奇技淫巧的壓縮算法,用及其苛刻的態(tài)度使用內(nèi)存。
所以,對于使用Elasticsearch進行索引時需要注意:
(1) 不需要索引的字段,一定要明確定義出來,因為默認(rèn)是自動建索引的
(2) 對于String類型的字段,不需要analysis(分詞)的也需要明確定義出來,因為默認(rèn)也是會analysis的
(3) 選擇有規(guī)律的ID很重要,隨機性太大的ID(比如java的UUID)不利于查詢,因為壓縮算法都是對Posting list里的大量ID進行壓縮的,那如果ID是順序的,或者是有公共前綴等具有一定規(guī)律性的ID,壓縮比會比較高;
四、尾聲
ElasticSearch高性能設(shè)計,完成了。文章來源:http://www.zghlxwxcb.cn/news/detail-430693.html
天天打碼,天天進步??!文章來源地址http://www.zghlxwxcb.cn/news/detail-430693.html
到了這里,關(guān)于ElasticSearch_12_ES的高性能設(shè)計的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!