0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

為什么ElasticSearch復(fù)雜條件查詢比MySQL好?

數(shù)據(jù)分析與開發(fā) ? 來源:程序員歷小冰 ? 作者:程序員歷小冰 ? 2021-04-09 11:16 ? 次閱讀

熟悉 MySQL 的同學(xué)一定都知道,MySQL 對于復(fù)雜條件查詢的支持并不好。MySQL 最多使用一個條件涉及的索引來過濾,然后剩余的條件只能在遍歷行過程中進行內(nèi)存過濾。

上述這種處理復(fù)雜條件查詢的方式因為只能通過一個索引進行過濾,所以需要進行大量的 I/O 操作來讀取行數(shù)據(jù),并消耗 CPU 進行內(nèi)存過濾,導(dǎo)致查詢性能的下降。

而 ElasticSearch 因其特性,十分適合進行復(fù)雜條件查詢,是業(yè)界主流的復(fù)雜條件查詢場景解決方案,廣泛應(yīng)用于訂單和日志查詢等場景。

下面我們就一起來看一下,為什么 ElasticSearch 適合進行復(fù)雜條件查詢。

ElasticSearch 簡介

Elasticsearch 是開源的實時分布式搜索分析引擎,內(nèi)部使用 Lucene 做索引與搜索。它提供"準實時搜索"能力,并且能動態(tài)集群規(guī)模,彈性擴容。

Elasticsearch 使用 Lucene 作為其全文搜索引擎,用于處理純文本的數(shù)據(jù),但 Lucene 只是一個庫,提供建立索引、執(zhí)行搜索等接口,但不包含分布式服務(wù),這些正是 Elasticsearch 做的。

下面,我們來介紹一下 ElasticSearch 的相關(guān)概念。為了便于初學(xué)者理解,我們先將 ElasticSearch 中的概念和 MySQL 中的概念大致地進行對應(yīng)。但是二者在具體細節(jié)上還是有很多差異的,大家深入了解 ElasticSearch 就會將二者區(qū)分清楚,不能強行對比等同。

2d9fa832-9878-11eb-8b86-12bb97331649.png

ElasticSearch 中的索引 Index 類似于 MySQL 中的數(shù)據(jù)庫 Database;

ElasticSearch 中的類型 Type 類似于 MySQL 中的表 Table;需要注意,這個概念在 7.x 版本中被完全刪除,而且概念上和 Table 也有較大差異;

ElasticSearch 中的文檔 Document 類似于 MySQL 中的數(shù)據(jù)行 Row,每個文檔由多個字段 Filed 組成,這個Filed 就類似于 MySQL 的 Column;

ElasticSearch 中的映射 Mapping 是對索引庫中的索引字段及其數(shù)據(jù)類型進行定義,類似于關(guān)系型數(shù)據(jù)庫中的表結(jié)構(gòu) Schema;

ElasticSearch 使用自己的領(lǐng)域語言 Query DSL 來進行增刪改查,而 MySQL 使用 SQL 語言進行上訴操作。

ElasticSearch 還有一系列有關(guān)其分布式特性的概念,我們這里就暫不介紹了,等后續(xù)學(xué)習(xí)到其分布式特性時在進行介紹。

倒排索引

MySQL 有 B+ 樹索引,而 ElasticSearch 則是倒排索引 (Inverted Index),它通過倒排索引來實現(xiàn)比 MySQL 更快的過濾和復(fù)雜條件的查詢,此外,全文搜索功能也是依賴倒排索引才能實現(xiàn)。下面,我們就具體來看一下何為倒排索引。

倒排索引按照維基百科的描述,是存儲文檔內(nèi)容到文檔位置映射關(guān)系的數(shù)據(jù)庫索引結(jié)構(gòu)。不過只看定義,我是有點迷惑,這不是和 MySQL 的非主鍵索引類似嘛,為什么要叫它“倒排”呢?這個問題我目前也為搞清楚,可能要等到后續(xù)了解了其具體實現(xiàn)才能理解。

我們還是以書籍檢索為例,假設(shè)有以下數(shù)據(jù),每一行就是一個 Document,每個 Document 由 id、ISBN 號,作者名稱和評分組成。

2da88b00-9878-11eb-8b86-12bb97331649.png

給上述數(shù)據(jù)按照 ISBN 和 Author 建立的倒排索引如下所示。倒排索引是每個字段分開建立的,相互獨立。有兩個專門的術(shù)語,分別是索引 Term 和倒排表 Posting List。字段的值就是 Term,比如 N0007,而 Term 對應(yīng)的文檔 ID 的列表就是 Posting List,對應(yīng)圖中紅色的部分。

2db4f4f8-9878-11eb-8b86-12bb97331649.png

一般 Term 都是按照順序排序的,比如 Author 名稱就是按照字母序進行了排序,排序之后,當(dāng)我們搜索某一個 Term 時,就不需要從頭遍歷,而是采用二分查找。一系列排序后的 Term 就組成了索引表 Term Dictionary。

但是 Term Dictionary 往往很大,無法完整放入內(nèi)存,這是為了更快的查詢,還需要再給它創(chuàng)建索引,也就是 Term Index 。

ElasticSearch 使用 Burst-Trie 結(jié)構(gòu)來實現(xiàn) Term Index,它是一種前綴樹 Trie 的一種變種,它主要是將后綴進行了壓縮,降低了Trie的高度,從而獲取更好查詢性能。

Term Index 并不需要像 MySQL 的索引一樣,包含所有的 Term,而是包含的是這些 Term 的前綴。它就類似于字典的查詢目錄,可以進行快速定位到 Term Dictionary 的某一位置,然后再從這個位置向后查詢。

綜上, Alice,Alf,Arlan,Bob,Tom 等詞的倒排索引如下所示。綠色部分是 Term Index,藍色部分是 Term Dictionary,紅色部分是 Posting List。

2dbdad14-9878-11eb-8b86-12bb97331649.png

一般來說,Term Index 都是全部緩存在內(nèi)存中,查詢時,先通過其快速定位到 Term Dictionary 對應(yīng)的大致范圍,然后再進行磁盤讀取查找對應(yīng)的 Term,這樣就大大減少了磁盤 I/O 的次數(shù)。

聯(lián)合索引查詢

了解了 ElasticSearch 的倒排索引后,我們再來看看其如何處理復(fù)雜的聯(lián)合索引查詢。比如上述書籍例子中,我們需要查詢評分等于2.2并且作者名稱叫 Tom 的書籍。

理論上,我們只需要分別按照 Score 和 Author 字段的倒排索引進行查詢,獲取響應(yīng)的 Posting List,再將其做交集合并即可。

這里又要吐槽一下 MySQL,它是不支持這個合并操作的,它只能按照一個字段的索引進行查詢,然后根據(jù)另外一個字段的條件做內(nèi)存過濾。順便說一下,MySQL 的 join 功能也弱爆了,感興趣的同學(xué)可以了解一下。

而 ElasticSearch 則支持使用跳表 Skip List和 Bitset 的方式將數(shù)據(jù)集進行合并。

使用 Skip List 結(jié)構(gòu),同時遍歷 Score 和 Author 查詢出來的 Posting List,利用其 Skip List 結(jié)構(gòu),相互跳躍對比,得出合集。

使用 Bitset 結(jié)構(gòu),對 Score 和 Author 查詢出來的 Posting List 的值計算出各自的 Bitset,然后進行 AND 操作。

跳表合并策略

ElasticSearch 在存儲 Posting List 數(shù)據(jù)時,就保存了對應(yīng)的多級跳表結(jié)構(gòu)響應(yīng)的數(shù)據(jù),這也體現(xiàn)了其空間換時間的基本思想。

這里先介紹一下跳表的基本概念,它其實是一種可以進行二分查找的有序鏈表。跳表在原有的有序鏈表上面增加了多級索引,通過索引來實現(xiàn)快速查找。首先在最高級索引上查找最后一個小于當(dāng)前查找元素的位置,然后再跳到次高級索引繼續(xù)查找,直到跳到最底層為止,通過這種方式,加快了查詢的速度。

比如,按照 Score 查出來的 Posting List 為 [2,3,4,5,7,9,10,11],按照 Author 查出來的結(jié)果為 [3,8,9,12,13],則二者的跳表結(jié)構(gòu)如下圖所示。

2dd4c8dc-9878-11eb-8b86-12bb97331649.png

具體合并過程則是先選最短的 posting list,也就是 Author 的結(jié)果集,從其最小的一個 id 開始,將其作為當(dāng)前最大值。然后依次剩余 posting list 中查找大于或等于該值的位置。

比如上述結(jié)果集中,先去 Score 結(jié)果集中查找 3,找到后,就表明 3是二者的合集元素之一;然后再重新開啟一輪,選取 Author 結(jié)果集中 3 的下一個值 8 ,去 Score 結(jié)果集查詢 8,發(fā)現(xiàn)了大于等于 8 的最小的值是 9 ,所以不可能有共同的值 8,然后再去 Author 結(jié)果集查找 9 ,發(fā)現(xiàn)其大于等于 9 的最小值是 12,所以再去 Score 結(jié)果集中查找大于等于 12的值,發(fā)現(xiàn)并不存在;最終得出二者的合集就只有 [3]。

在查詢過程中,每個 posting list 都可以根據(jù)當(dāng)前 id 通過 skip list 快速跳過不符合的 id 值,加速整個合并取交集的過程。

ElasticSearch 對于較長的 posting list 也會使用 Frame Of Reference 進行壓縮編碼,減少了磁盤占用,減少了索引尺寸。有關(guān)具體存儲結(jié)構(gòu)的實現(xiàn)我們后續(xù)再進行細聊。

Bitset 合并策略

ElasticSearch 除了使用 skipList 來進行數(shù)據(jù)磁盤讀取時的合并操作外,還會將一些查詢條件對應(yīng)的結(jié)果集 posting list 進行內(nèi)存緩存,也就是所謂的 Filter Cache,為了后續(xù)再次復(fù)用。

為了減少內(nèi)存緩存所消耗的內(nèi)存空間大小,ElasticSearch 沒有使用單純的數(shù)組和 bitset 來存儲 posting list,而是使用要壓縮效率更高的 Roaring Bitmap。

我們可以先來講一下單純數(shù)組或 bitset 數(shù)據(jù)結(jié)構(gòu)為什么并不使用。比如如下一道較為常見的面試題目:

給定含有 40 億個不重復(fù)的位于 [0, 2^32 - 1] 區(qū)間內(nèi)的整數(shù)的集合,如何快速判定某個數(shù)是否在該集合內(nèi)?

如果我們要使用 unsigned long 數(shù)組來存儲它的話,也就需要消耗 40億 * 32 位 = 160 Byte,大致是 16000 MB。

如果要使用位圖 Bitset 來存儲的話,即某個數(shù)位于原集合內(nèi),就將它對應(yīng)的位圖內(nèi)的比特置為1,否則保持為0。這樣只需要消耗 2 ^ 32 位 = 512 MB,這可只有原來的 3.2 % 左右。

但是,Bitset 也有其缺陷,也就是稀疏存儲的問題,比如上述集合并不是 40億,而是只有2、3個,那么 Bitset 中只有少數(shù)幾位是1,其他位都是 0,但是它仍然占用了 512 MB。

而 RoaringBitmap 就是為了解決稀疏存儲的問題。下圖就是 RoaringBitmap 的基本原理示意圖。

2e0350bc-9878-11eb-8b86-12bb97331649.png

首先,如上圖所示,計算出32位無符號整數(shù)和 65536 的除數(shù)和余數(shù)。其含義表示,將32位無符號整數(shù)按照高16位分桶,即最多可能有2^16=65536個桶,術(shù)語懲治為 container。存儲數(shù)據(jù)時,按照數(shù)據(jù)的高16位找到 container(找不到就會新建一個),再將低16位放入container中。也就是說,一個 RoaringBitmap 就是很多container的集合。

然后 container 內(nèi)具體的存儲結(jié)構(gòu)要根據(jù)存入其內(nèi)數(shù)據(jù)的基數(shù)來決定。

基數(shù)小于 2 ^ 12 次方即 4096時,使用unsigned short類型的有序數(shù)組來存儲,最大消耗空間就是 8 KB;

基數(shù)大于 4096 時,則使用大小為 2 ^ 16 次方的普通 bitset 來存儲,固定消耗 8 KB。當(dāng)然,有些時候也會對 bitset 進行行程長度編碼(RLE)壓縮,進一步減少空間占用。

ElasticSearch 就是使用 Roaring Bitmap 來緩存不同條件查詢出來的 posting list,然后再進行與操作計算出最終結(jié)果集。

后記

至此,我們也算了解了 ElasticSearch 為什么比 MySQL 更適合復(fù)雜條件查詢,但是有好就有弊,因為為了查詢做了這么多的準備工作,ElasticSearch 的插入速度就會慢于 MySQL,而且數(shù)據(jù)存入 ES 后并不是立馬就能檢索到。

原文標題:為什么 ElasticSearch 比 MySQL 更適合復(fù)雜條件搜索

文章出處:【微信公眾號:數(shù)據(jù)分析與開發(fā)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

責(zé)任編輯:haq

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 開源
    +關(guān)注

    關(guān)注

    3

    文章

    3190

    瀏覽量

    42252
  • MySQL
    +關(guān)注

    關(guān)注

    1

    文章

    794

    瀏覽量

    26359

原文標題:為什么 ElasticSearch 比 MySQL 更適合復(fù)雜條件搜索

文章出處:【微信號:DBDevs,微信公眾號:數(shù)據(jù)分析與開發(fā)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    MySQL知識點匯總

    大家,這部分被稱為DQL部分,是每個學(xué)習(xí)MySQL必須要學(xué)會的部分,下面就讓我來介紹MySQL中的其他部分。
    的頭像 發(fā)表于 08-05 15:27 ?329次閱讀
    <b class='flag-5'>MySQL</b>知識點匯總

    分庫分表后復(fù)雜查詢的應(yīng)對之道:基于DTS實時性ES寬表構(gòu)建技術(shù)實踐

    分表,通過分庫分表應(yīng)對存系統(tǒng)讀寫性能瓶頸和存儲瓶頸;分庫分表幫我們解決問題的同時,也帶來了復(fù)雜性;比如多條件的分頁查詢,多條件的聯(lián)表查詢變得
    的頭像 發(fā)表于 06-25 18:30 ?786次閱讀
    分庫分表后<b class='flag-5'>復(fù)雜</b><b class='flag-5'>查詢</b>的應(yīng)對之道:基于DTS實時性ES寬表構(gòu)建技術(shù)實踐

    MySQL聯(lián)表查詢優(yōu)化

    條數(shù)據(jù),本意想查出
    的頭像 發(fā)表于 04-24 12:33 ?487次閱讀
    <b class='flag-5'>MySQL</b>聯(lián)表<b class='flag-5'>查詢</b>優(yōu)化

    查詢SQL在mysql內(nèi)部是如何執(zhí)行?

    我們知道在mySQL客戶端,輸入一條查詢SQL,然后看到返回查詢的結(jié)果。這條查詢語句在 MySQL 內(nèi)部到底是如何執(zhí)行的呢?本文跟大家探討一
    的頭像 發(fā)表于 01-22 14:53 ?495次閱讀
    <b class='flag-5'>查詢</b>SQL在<b class='flag-5'>mysql</b>內(nèi)部是如何執(zhí)行?

    MySQL密碼忘記了怎么辦?MySQL密碼快速重置方法步驟命令示例!

    是重置MySQL密碼的詳細步驟和示例命令。 在開始重置MySQL密碼之前,請確保你具備管理員或超級用戶的權(quán)限。此外,請注意,在重置密碼之前,將會中斷所有正在運行的MySQL進程,因此,請確保在進行此操作之前已備份
    的頭像 發(fā)表于 01-12 16:06 ?681次閱讀

    導(dǎo)致MySQL索引失效的情況以及相應(yīng)的解決方法

    導(dǎo)致MySQL索引失效的情況以及相應(yīng)的解決方法? MySQL索引的目的是提高查詢效率,但有些情況下索引可能會失效,導(dǎo)致查詢變慢或效果不如預(yù)期。下面將詳細介紹導(dǎo)致
    的頭像 發(fā)表于 12-28 10:01 ?694次閱讀

    MySQL執(zhí)行過程:如何進行sql 優(yōu)化

    (1)客戶端發(fā)送一條查詢語句到服務(wù)器; (2)服務(wù)器先查詢緩存,如果命中緩存,則立即返回存儲在緩存中的數(shù)據(jù); (3)未命中緩存后,MySQL 通過關(guān)鍵字將 SQL 語句進行解析,并生成一顆對應(yīng)的解析樹,
    的頭像 發(fā)表于 12-12 10:19 ?361次閱讀
    <b class='flag-5'>MySQL</b>執(zhí)行過程:如何進行sql 優(yōu)化

    sql語句where條件查詢

    SQL是一種用于管理和操作關(guān)系型數(shù)據(jù)庫的編程語言。其中,WHERE子句是用于過濾查詢結(jié)果的重要部分。通過WHERE條件,我們可以指定一系列條件,以僅返回滿足條件的記錄。本文將探討WHE
    的頭像 發(fā)表于 11-23 11:28 ?1039次閱讀

    MySQL數(shù)據(jù)庫基礎(chǔ)知識

    的基礎(chǔ)知識,包括其架構(gòu)、數(shù)據(jù)類型、表操作、查詢語句和數(shù)據(jù)導(dǎo)入導(dǎo)出等方面。 MySQL 數(shù)據(jù)庫架構(gòu) MySQL 數(shù)據(jù)庫由多個組件組成,包括服務(wù)器、存儲引擎和客戶端等。MySQL 服務(wù)器是
    的頭像 發(fā)表于 11-21 11:09 ?904次閱讀

    Redis的分頁+多條件模糊查詢組合實現(xiàn)方案

    Redis是key-value類型的內(nèi)存數(shù)據(jù)庫,通過key直接取數(shù)據(jù)雖然很方便,但是并未提供像mysql那樣方便的sql條件查詢支持。因此我們需要借助Redis提供的結(jié)構(gòu)和功能去自己實現(xiàn)模糊
    的頭像 發(fā)表于 11-20 14:26 ?815次閱讀
    Redis的分頁+多<b class='flag-5'>條件</b>模糊<b class='flag-5'>查詢</b>組合實現(xiàn)方案

    介紹4種常用的MySQL同步ES方案

    在實際項目開發(fā)中,我們經(jīng)常將 MySQL 作為業(yè)務(wù)數(shù)據(jù)庫,ES 作為查詢數(shù)據(jù)庫,用來實現(xiàn)讀寫分離,緩解 MySQL 數(shù)據(jù)庫的查詢壓力,應(yīng)對海量數(shù)據(jù)的
    的頭像 發(fā)表于 11-20 10:45 ?628次閱讀
    介紹4種常用的<b class='flag-5'>MySQL</b>同步ES方案

    mysql基礎(chǔ)語句大全

    MySQL是一個開放源碼的關(guān)系型數(shù)據(jù)庫管理系統(tǒng),使用SQL作為其查詢語言。它是Web開發(fā)中常用的數(shù)據(jù)庫管理系統(tǒng)之一。MySQL的語法十分豐富,可以執(zhí)行各種數(shù)據(jù)庫操作,包括創(chuàng)建、修改、刪除和查詢
    的頭像 發(fā)表于 11-16 16:42 ?1809次閱讀

    MySQL中增刪改查的例子

    MySQL是一種常用的關(guān)系型數(shù)據(jù)庫管理系統(tǒng),它具有強大的數(shù)據(jù)處理和數(shù)據(jù)存儲能力。在MySQL中,我們可以使用各種命令來進行數(shù)據(jù)的增加、刪除、修改和查詢操作。下面將詳細介紹MySQL中各
    的頭像 發(fā)表于 11-16 15:39 ?657次閱讀

    redis與mysql的區(qū)別

    對的形式,可以是字符串、哈希、列表、集合、有序集合等數(shù)據(jù)結(jié)構(gòu)。這種數(shù)據(jù)模型使得Redis非常適合用于緩存、消息隊列、計數(shù)器等場景。 MySQL是一種關(guān)系型數(shù)據(jù)庫,采用表格的形式組織數(shù)據(jù),每個表包含多個行和列。它支持復(fù)雜的數(shù)據(jù)查詢
    的頭像 發(fā)表于 11-16 11:21 ?981次閱讀

    Python 更新 Elasticsearch 的幾種方法

    今天總結(jié)一下通過 Python 更新 Elasticsearch 數(shù)據(jù)的幾個方法 Elasticsearch 是一個實時的分布式搜索分析引擎,它能讓你以前所未有的速度和規(guī)模,去探索你的數(shù)據(jù)。它被用作
    的頭像 發(fā)表于 11-01 10:11 ?1070次閱讀
    Python 更新 <b class='flag-5'>Elasticsearch</b> 的幾種方法