elasticsearch為什么比mysql快


 
image.png
為什么 Elasticsearch/Lucene 檢索可以比 mysql 快

Mysql 只有 term dictionary 這一層,是以 b-tree 排序的方式存儲在磁盤上的。檢索一個 term 需要若干次的 random access 的磁盤操作。而 Lucene 在 term dictionary 的基礎上添加了 term index 來加速檢索,term index 以樹的形式緩存在內存中。從 term index 查到對應的 term dictionary 的 block 位置之后,再去磁盤上找 term,大大減少了磁盤的 random access 次數。

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

兩者對比
對於倒排索引,要分兩種情況:

1、基於分詞后的全文檢索

這種情況是es的強項,而對於mysql關系型數據庫而言完全是災難

因為es分詞后,每個字都可以利用FST高速找到倒排索引的位置,並迅速獲取文檔id列表

但是對於mysql檢索中間的詞只能全表掃(如果不是搜頭幾個字符)

2、精確檢索

這種情況我想兩種相差不大,有些情況下mysql的可能會更快些

如果mysql的非聚合索引用上了覆蓋索引,無需回表,則速度可能更快

es還是通過FST找到倒排索引的位置並獲取文檔id列表,再根據文檔id獲取文檔並根據相關度算分進行排序,但es還有個殺手鐧,即天然的分布式使得在大數據量面前可以通過分片降低每個分片的檢索規模,並且可以並行檢索提升效率

用filter時更是可以直接跳過檢索直接走緩存

什么是Term Index?

將分詞后的term進行排序索引,類似的mysql對於"term"(即主鍵,或者索引鍵)只是做了排序, 並且是大部分是放在磁盤上的,只有B+樹的上層才是放在內存中的,查詢仍然需要logN的訪問磁盤,而ES將term分詞排序后還做了一次索引,term index,即將term的通用前綴取出,構建成Trie樹
通過這個樹可以快速的定位到Term dictionary的本term的offset,再經過順序查找,便可以很快找到本term的posting list。

什么是FST?

答: Finite State Transducer
FST有兩個優點:1)空間占用小。通過對詞典中單詞前綴和后綴的重復利用,壓縮了存儲空間;2)查詢速度快。O(len(str))的查詢時間復雜度。

3 FST原理簡析

 lucene從4開始大量使用的數據結構是FST(Finite State Transducer)。FST有兩個優點:1)空間占用小。通過對詞典中單詞前綴和后綴的重復利用,壓縮了存儲空間;2)查詢速度快。O(len(str))的查詢時間復雜度。 下面簡單描述下FST的構造過程(工具演示:[http://examples.mikemccandless.com/fst.py?terms=&cmd=Build+it%21](http://examples.mikemccandless.com/fst.py?terms=&cmd=Build+it%21))。我們對“cat”、 “deep”、 “do”、 “dog” 、“dogs”這5個單詞進行插入構建FST(注:必須已排序)。 

1)插入“cat”

 插入cat,每個字母形成一條邊,其中t邊指向終點。
 
image.png

2)插入“deep”

與前一個單詞“cat”進行最大前綴匹配,發現沒有匹配則直接插入,P邊指向終點。
 
image.png

3)插入“do”
與前一個單詞“deep”進行最大前綴匹配,發現是d,則在d邊后增加新邊o,o邊指向終點。


 
image.png

4)插入“dog”
與前一個單詞“do”進行最大前綴匹配,發現是do,則在o邊后增加新邊g,g邊指向終點。


 
image.png

5)插入“dogs”
 
image.png

最終我們得到了如上一個有向無環圖。利用該結構可以很方便的進行查詢,如給定一個term “dog”,我們可以通過上述結構很方便的查詢存不存在,甚至我們在構建過程中可以將單詞與某一數字、單詞進行關聯,從而實現key-value的映射。
Term Dictionary為什么節約空間?

Term dictionary 在磁盤上是以分 block 的方式保存的,一個 block 內部利用公共前綴壓縮,比如都是 Ab 開頭的單詞就可以把 Ab 省去。這樣 term dictionary 可以比 b-tree 更節約磁盤空間。

3.6 聯合索引查詢

以上都是單field索引,如果多個field索引的聯合查詢,比如查詢age=18 AND gender=女,倒排索引如何滿足快速查詢的要求呢?大致過程如下:根據過濾條件 age=18 的先從term index找到18在term dictionary的大概位置,然后再從term dictionary里精確地找到18這個term,然后得到一個posting list或者一個指向posting list位置的指針。然后再查詢gender=女的過程也是類似的。最后得出 age=18 AND gender=女,就是把兩個 posting list 做一個“與”的合並。

這個理論上的“與”合並的操作可不容易。對於mysql來說,如果你給age和gender兩個字段都建立了索引,查詢的時候只會選擇其中最selective的來用,然后另外一個條件是在遍歷行的過程中在內存中計算之后過濾掉。那么要如何才能聯合使用兩個索引呢?有兩種辦法:

a)使用skip list數據結構。同時遍歷gender和age的posting list,互相skip;

b)使用bitset數據結構,對gender和age兩個filter分別求出bitset,對兩個bitset做AND操作。

利用 Skip List 合並

 
image.png

以上是三個posting list。我們現在需要把它們用AND的關系合並,得出posting list的交集。首先選擇最短的posting list,然后從小到大遍歷。遍歷的過程可以跳過一些元素,比如我們遍歷到綠色的13的時候,就可以跳過藍色的3了,因為3比13要小。

整個過程如下:

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],所需的時間比完整遍歷三個posting list要快得多。但是前提是每個list需要指出Advance這個操作,快速移動指向的位置。什么樣的list可以這樣Advance往前做蛙跳?skip list:

從概念上來說,對於一個很長的posting list,比如:

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

我們可以把這個list分成三個block:

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

然后可以構建出skip list的第二層:

[1,101,255]

1,101,255分別指向自己對應的block。這樣就可以很快地跨block的移動指向位置了。

Lucene自然會對block再次進行壓縮。其壓縮方式是上文提到的Frame Of Reference。補充一點的是,利用skip list,除了跳過了遍歷的成本,也跳過了解壓縮這些壓縮過的block的過程,從而節省了cpu。

利用bitset合並

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

倒排索引

搜索時


 
image.png

當我們搜索關鍵字“pie”時,搜索引擎快速查找匹配到的記錄

Filesystem Cache

查詢億級數據毫秒級返回
性能優化的殺手鐧:Filesystem Cache
你往 ES 里寫的數據,實際上都寫到磁盤文件里去了,查詢的時候,操作系統會將磁盤文件里的數據自動緩存到 Filesystem Cache 里面去。

整個過程,如下圖所示:


 
image.png

ES 的搜索引擎嚴重依賴於底層的 Filesystem Cache,你如果給 Filesystem Cache 更多的內存,盡量讓內存可以容納所有的 IDX Segment File 索引數據文件,那么你搜索的時候就基本都是走內存的,性能會非常高。

性能差距究竟可以有多大?我們之前很多的測試和壓測,如果走磁盤一般肯定上秒,搜索性能絕對是秒級別的,1 秒、5 秒、10 秒。

但如果是走 Filesystem Cache,是走純內存的,那么一般來說性能比走磁盤要高一個數量級,基本上就是毫秒級的,從幾毫秒到幾百毫秒不等。

來看一個真實的案例:某個公司 ES 節點有 3 台機器,每台機器看起來內存很多 64G,總內存就是 64 * 3 = 192G。

每台機器給 ES JVM Heap 是 32G,那么剩下來留給 Filesystem Cache 的就是每台機器才 32G,總共集群里給 Filesystem Cache 的就是 32 * 3 = 96G 內存。

而此時,整個磁盤上索引數據文件,在 3 台機器上一共占用了 1T 的磁盤容量,ES 數據量是 1T,那么每台機器的數據量是 300G。

這樣性能好嗎?

Filesystem Cache 的內存才 100G,十分之一的數據可以放內存,其他的都在磁盤,然后你執行搜索操作,大部分操作都是走磁盤,性能肯定差。

歸根結底,你要讓 ES 性能好,最佳的情況下,就是你的機器的內存,至少可以容納你的總數據量的一半。

根據我們自己的生產環境實踐經驗,最佳的情況下,是僅僅在 ES 中就存少量的數據。

也就是說,你要用來搜索的那些索引,如果內存留給 Filesystem Cache 的是 100G,那么你就將索引數據控制在 100G 以內。這樣的話,你的數據幾乎全部走內存來搜索,性能非常之高,一般可以在1秒以內。

比如說你現在有一行數據:id,name,age .... 30 個字段。但是你現在搜索,只需要根據 id,name,age 三個字段來搜索。

如果你傻乎乎往 ES 里寫入一行數據所有的字段,就會導致 90% 的數據是不用來搜索的。

但是呢,這些數據硬是占據了 ES 機器上的 Filesystem Cache 的空間,單條數據的數據量越大,就會導致 Filesystem Cahce 能緩存的數據就越少。

其實,僅僅寫入 ES 中要用來檢索的少數幾個字段就可以了,比如說就寫入 es id,name,age 三個字段。

然后你可以把其他的字段數據存在 MySQL/HBase 里,我們一般是建議用 ES + HBase 這么一個架構。

1. ES一定快嗎?
1.1. 數據庫的索引是B+tree結構
主鍵是聚合索引 其他索引是非聚合索引,見下圖

如果是一般搜索,一般從非聚集索樹上搜索出id,然后再到聚集索引樹上搜索出需要的內容。

1.2. elasticsearch倒排索引原理


Term Index 以樹的形式保存在內存中,運用了FST+壓縮公共前綴方法極大的節省了內存,通過Term Index查詢到Term Dictionary所在的block再去磁盤上找term減少了IO次數

Term Dictionary 排序后通過二分法將檢索的時間復雜度從原來N降低為logN

1.3. 兩者對比
對於倒排索引,要分兩種情況:

1.3.1. 基於分詞后的全文檢索
這種情況是es的強項,而對於mysql關系型數據庫而言完全是災難

因為es分詞后,每個字都可以利用FST高速找到倒排索引的位置,並迅速獲取文檔id列表

但是對於mysql檢索中間的詞只能全表掃(如果不是搜頭幾個字符)

1.3.2. 精確檢索
這種情況我想兩種相差不大,有些情況下mysql的可能會更快些

如果mysql的非聚合索引用上了覆蓋索引,無需回表,則速度可能更快

es還是通過FST找到倒排索引的位置並獲取文檔id列表,再根據文檔id獲取文檔並根據相關度算分進行排序,但es還有個殺手鐧,即天然的分布式使得在大數據量面前可以通過分片降低每個分片的檢索規模,並且可以並行檢索提升效率

用filter時更是可以直接跳過檢索直接走緩存

1.3.3. 多條件查詢
mysql除非使用聯合索引,將每個查詢的字段都建立聯合索引,如果是兩個字段上有兩個不同的索引,那么mysql將會選擇一個索引使用,然后將得到的結果寫入內存中使用第二個條件過濾后,得到最終的答案

es,可以進行真正的聯合查詢,將兩個字段上查詢出來的結果進行“並”操作或者“與”操作,如果是filter可以使用bitset,如果是非filter使用skip list進行

2. ES如何快速檢索
籠統的來說查詢數據的快慢和什么有關,首先是查詢算法的優劣,其次是數據傳輸的快慢,以及查詢數據的數據量大小。

一般的機械硬盤的讀性能在100MB/s左右,再不升級高級硬件的情況下,想要優化查詢性能,得從查詢算法以及數據量大小兩個方面做優化

首先看下es的倒排索引結構,從中分析es為了提高檢索性能做了那些的努力

es會為每個term做一個倒排索引

docId

年齡

性別

1 18 女
2 20 女
3 18

年齡

Posting List

18 【1,3】
20 【2】
性別

Posting List

女 【1,2】
男 【3】
可以看到,倒排索引是per field的,一個字段由一個自己的倒排索引。18,20這些叫做 term,而[1,3]就是posting list。Posting list就是一個int的數組,存儲了所有符合某個term的文檔id。

我們可以發現,只要找到term,我們就能找到對應的文檔id集合。類似與字典的功能

那么問題來了,海量的數據中我們怎么尋找對應的term?

2.1. Term Index
怎么實現一個字典呢?我們馬上想到排序數組,即term字典是一個已經按字母順序排序好的數組,數組每一項存放着term和對應的倒排文檔id列表。每次載入索引的時候只要將term數組載入內存,通過二分查找即可。這種方法查詢時間復雜度為Log(N),N指的是term數目,占用的空間大小是O(N*str(term))。排序數組的缺點是消耗內存,即需要完整存儲每一個term,當term數目多達上千萬時,占用的內存將不可接受。

常用的字典數據結構

數據結構

優缺點

排序列表Array/List 使用二分法查找,不平衡
HashMap/TreeMap 性能高,內存消耗大,幾乎是原始數據的三倍
Skip List 跳躍表,可快速查找詞語,在lucene、redis、Hbase等均有實現。相對於TreeMap等結構,特別適合高並發場景
Trie 適合英文詞典,如果系統中存在大量字符串且這些字符串基本沒有公共前綴,則相應的trie樹將非常消耗內存
Double Array Trie 適合做中文詞典,內存占用小,很多分詞工具均采用此種算法
Ternary Search Tree 三叉樹,每一個node有3個節點,兼具省空間和查詢快的優點
Finite State Transducers (FST) 一種有限狀態轉移機,Lucene 4有開源實現,並大量使用
可以看出在Lucene中使用的是FST,FST有兩個優點:1)空間占用小。通過對詞典中單詞前綴和后綴的重復利用,壓縮了存儲空間;2)查詢速度快。O(len(str))的查詢時間復雜度。

 

FST的性能如何

FST壓縮率一般在3倍~20倍之間,相對於TreeMap/HashMap的膨脹3倍,內存節省就有9倍到60倍

數據結構

HashMap

TreeMap

FST

數據量100w(100w次查詢) 12ms
9ms

377ms
性能雖不如TreeMap和HashMap,但也算良好,能夠滿足大部分應用的需求

 

雖然,我們使用了FST壓縮Term,但是在ES中term還是太多,是不可能將全部的term拖入內存中的。Term Index保存的不是完整的term,而是保存term一些前綴,可以通過這個前綴快速的定位到磁盤上的block

這樣,整個term index的大小可以只有所有term的幾十分之一,使得用內存緩存整個term index變成可能

 

2.2. Term Dictionary
通過term index,我們已經可以拿到需要查詢的term的前綴所指向的block地址,進行一次random access(一次random access大概需要10ms的時間)就可以讀取這個block中的信息

剩下我們查詢對應的term只需要遍歷即可,但是如果是海量數據,那么這個時間復雜度O(N)也不能接受。並且可能所要查詢的term不在這個block上(可能在下塊block上),那么O(N) 我們將N次random access

為了提高term Dictionary的查詢效率,term Dictionary是排序好了的結構,這樣我們可以在其做二分查找將時間復雜度降低為LogN,那么IO的次數也將大大消耗

上面提到了,提高查詢效率,除了改進算法,還有降低數據量大小,Term dictionary在磁盤上是以分block的方式保存的,一個block內部利用公共前綴壓縮,比如都是Ab開頭的單詞就可以把Ab省去。

每個Block最多存48個Term, 如果相同前綴的Term很多的話,Block會分出一個子Block,很顯然父Block的公共前綴是子Block公共前綴的前綴。

 

這樣一次讀取磁盤的傳輸數據量大小也將大大降低

這樣我們就根據term得到了文檔id集合,然后通過路由算法去指定分片中查詢內容

3. ES如何做聯合查詢
在MySQL中,除非使用聯合索引,將每個查詢的字段都建立聯合索引,如果是兩個字段上有兩個不同的索引,那么mysql將會選擇一個索引使用,然后將得到的結果寫入內存中使用第二個條件過濾后,得到最終的答案

而在es中, 可以使用真正的聯合查詢

查詢過濾條件 age=18 的過程就是先從 term index 找到 18 在 term dictionary 的大概位置,然后再從 term dictionary 里精確地找到 18 這個 term,然后得到一個 posting list 或者一個指向 posting list 位置的指針。然后再查詢 gender= 女 的過程也是類似的。最后得出 age=18 AND gender= 女 就是把兩個 posting list 做一個“與”的合並。

這個理論上的“與”合並的操作可不容易。對於 mysql 來說,如果你給 age 和 gender 兩個字段都建立了索引,查詢的時候只會選擇其中最 selective 的來用,然后另外一個條件是在遍歷行的過程中在內存中計算之后過濾掉。那么要如何才能聯合使用兩個索引呢?有兩種辦法:

使用 skip list 數據結構。同時遍歷 gender 和 age 的 posting list,互相 skip;
使用 bitset 數據結構,對 gender 和 age 兩個 filter 分別求出 bitset,對兩個 bitset 做 AN 操作。
如果查詢的filter緩存到了內存中,使用bitset形式,否則用skip list形式遍歷在磁盤上的posting list

3.1. Skip List


以上是三個 posting list。我們現在需要把它們用 AND 的關系合並,得出 posting list 的交集。首先選擇最短的posting list,然后從小到大遍歷。遍歷的過程可以跳過一些元素,比如我們遍歷到綠色的13的時候,就可以跳過藍色的3了,因為3比13要小。

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

 

但是這里我們又要考慮一個問題,如果這個posting太長怎么辦?

從概念上來說,對於一個很長的posting list,比如:[1,3,13,101,105,108,255,256,257];我們可以把這個list分成三個block:[1,3,13] [101,105,108] [255,256,257];然后可以構建出skip list的第二層:[1,101,255];1,101,255分別指向自己對應的block。這樣就可以很快地跨block的移動指向位置了。Lucene自然會對這個block再次進行壓縮。

其壓縮方式叫做Frame Of Reference編碼。

 

比如一個詞對應的文檔id列表為[73, 300, 302, 332,343, 372] ,id列表首先要從小到大排好序;第一步增量編碼就是從第二個數開始每個數存儲與前一個id的差值,即300-73=227,302-300=2。。。,一直到最后一個數;第二步就是將這些差值放到不同的區塊,Lucene使用256個區塊;第三步位壓縮,計算每組3個數中最大的那個數需要占用bit位數,比如30、11、29中最大數30最小需要5個bit位存儲,這樣11、29也用5個bit位存儲,這樣才占用15個bit,不到2個字節,壓縮效果很好

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

因為這個Frame of Reference的編碼是有解壓縮成本的。利用skip list,除了跳過了遍歷的成本,也跳過解壓全部這些壓縮過的block的過程,從而節省了cpu(相當於只要解壓skip list查詢到的那些block)

 

3.2. BitSet合並
Frame Of Reference壓縮算法對於倒排表來說效果很好,但對於需要存儲在內存中的Filter緩存等不太合適,兩者之間有很多不同之處:倒排表存儲在磁盤,針對每個詞都需要進行編碼,而Filter等內存緩存只會存儲那些經常使用的數據,而且針對Filter數據的緩存就是為了加速處理效率,對壓縮算法要求更高。

緩存之前執行結果的目的就是為了加速響應,本質上是對一系列doc id進行合理的壓縮存儲然后解碼並進行與、或、亦或等邏輯運算

方案一:首先一個簡單的方案是使用數組存儲,這樣每個doc id占用4個字節,如果有100M個文檔,大約需要400M的內存,比較占用資源,不是一個很好的方式。
方案二:BitMap位圖方式,用一個bit位(0或者1)來代表一個doc id的存在與否(JDK也有內置的位圖類即BitSet);與數組方式類似,只不過這里只用1個bit代表一個文檔,節約了存儲(100M bits = 12.5MB),這是一種很好的方式。BitSet進行and,or操作,十分方便容易,位運算即可
但是BitSet有一個缺陷:對於一個比較稀疏的文檔列表就浪費了很多的存儲空間,極端情況段內doc id范圍0~2^31-1,一個位圖就占用(2^31-1)/8/1024/1024=256M的空間,有壓縮改進的空間

從Lucene 5 開始采用了一種改進的位圖方式,即Roaring BitMaps,它是一個壓縮性能比bitmap更好的位圖實現

 

針對每個文檔id,得到其對應的元組,即括號中第一個數為除以65535的值,第二個數時對65535取余的值
按照除以65535的結果將元組划分到特定的區塊中,示例中有0、2、3三個區塊
對於每個區塊,如果元素個數大於4096個,采用BitSet編碼,否則對於區塊中每個元素使用2個字節編碼(取余之后最大值65535使用2個字節即可表示,選擇數組存儲)
為什么使用4096作為一個閾值,經驗證超過4096個數后,使用BitMap方式要比使用數組方式要更高效。

 

3.2.1. 性能對比
y軸越大,性能越好;x軸越小,文檔越稀疏

3.2.2. 內存占用


4. ES如何做聚合排序查詢
現在我們想一下,es如何做聚合排序查詢的

首先我們拿mysql做例子,如果沒有建立索引,我們需要全遍歷一份,到內存進行排序,如果有索引,會在索引樹上進行進行范圍查詢(因為索引是排序了的)

那么在es中,如果是做排序,lucene會查詢出所有的文檔集合的排序字段,然后再次構建出一個排序好的文檔集合。es是面向海量數據的,這樣一來內存爆掉的可能性是十分大的

我們可以從上面看出,es的倒排索引其實是不利於做排序的。因為存儲的是字段→文檔id的關系。無法得到需要排序字段的全部值。

為此。es采用Doc Value解決排序,聚合等操作的處理

4.1. Doc Value
DocValues 是一種按列組織的存儲格式,這種存儲方式降低了隨機讀的成本。傳統的按行存儲是這樣的:

 

1 和 2 代表的是 docid。顏色代表的是不同的字段。

改成按列存儲是這樣的:

 

按列存儲的話會把一個文件分成多個文件,每個列一個。對於每個文件,都是按照 docid 排序的。這樣一來,只要知道 docid,就可以計算出這個 docid 在這個文件里的偏移量。也就是對於每個 docid 需要一次隨機讀操作。

那么這種排列是如何讓隨機讀更快的呢?秘密在於 Lucene 底層讀取文件的方式是基於 memory mapped byte buffer 的,也就是 mmap。這種文件訪問的方式是由操作系統去緩存這個文件到內存里。這樣在內存足夠的情況下,訪問文件就相當於訪問內存。那么隨機讀操作也就不再是磁盤操作了,而是對內存的隨機讀。

那么為什么按行存儲不能用 mmap 的方式呢?因為按行存儲的方式一個文件里包含了很多列的數據,這個文件尺寸往往很大,超過了操作系統的文件緩存的大小。而按列存儲的方式把不同列分成了很多文件,可以只緩存用到的那些列,而不讓很少使用的列數據浪費內存。

按列存儲之后,一個列的數據和前面的 posting list 就差不多了。很多應用在 posting list 上的壓縮技術也可以應用到 DocValues 上。這不但減少了文件尺寸,而且提高數據加載的速度。因為我們知道從磁盤到內存的帶寬是很小的,普通磁盤也就每秒 100MB 的讀速度。利用壓縮,我們可以把數據以壓縮的方式讀取出來,然后在內存里再進行解壓,從而獲得比讀取原始數據更高的效率。

DocValue將隨機讀取變成了順序讀取,隨機讀的時候也是按照 DocId 排序的。所以如果讀取的 DocId 是緊密相連的,實際上也相當於把隨機讀變成了順序讀了。Random_read(100), Random_read(101), Random_read(102) 就相當於 Scan(100~102) 了。

 

在es中,因為分片的存在,數據被拆分成多份,放在不同機器上。但是給用戶體驗卻只有一個庫一樣

對於聚合查詢,其處理是分兩階段完成的:

Shard 本地的 Lucene Index 並行計算出局部的聚合結果;
收到所有的 Shard 的局部聚合結果,聚合出最終的聚合結果。
這種兩階段聚合的架構使得每個 shard 不用把原數據返回,而只用返回數據量小得多的聚合結果。這樣極大的減少了網絡帶寬的消耗

 

5. 總結
ElasticSearch為了提高檢索性能,無所不用的壓縮數據,減少磁盤的隨機讀,以及及其苛刻的使用內存,使用各種快速的搜索算法等手段

追求更快的檢索算法 — 倒排,二分

更小的數據傳輸--- 壓縮數據(FST,ROF,公共子前綴壓縮)

更少的磁盤讀取--- 順序讀(DocValue順序讀)

 

PS:為什么Mysql不使用真正的聯合查詢呢?

第一:MySQL沒有為每個字段都創建索引,如果是真正的聯合查詢,可能存在A字段有索引,B字段沒有索引,那么根據B字段查詢時候就是全表掃描了

第二:MySQL的量沒有ES大,ES面向的是海量數據,MySQL做聯合查詢的時候,可能根據A字段得到的數據只有100或者1000行,在執行B字段過濾,效率很高。但是ES可能經過A字段的搜索有1千萬,2千萬的數據,執行遍歷的過濾效率底下。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM