日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

ElasticSearch探索之路(四)索引原理:倒排索引、列式存储、Fielddata、索引压缩、联合索引

發(fā)布時(shí)間:2024/4/11 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ElasticSearch探索之路(四)索引原理:倒排索引、列式存储、Fielddata、索引压缩、联合索引 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

  • 倒排索引
  • Term dictionary與Term index
  • 列式存儲(chǔ)——Doc Values
  • Fielddata
  • 索引壓縮
    • FOR編碼
    • Roaring Bitmaps
  • 聯(lián)合索引


倒排索引

例如,假設(shè)我們有兩個(gè)文檔,每個(gè)文檔的 content 域包含如下內(nèi)容:

  • The quick brown fox jumped over the lazy dog
  • Quick brown foxes leap over lazy dogs in summer
  • 為了創(chuàng)建倒排索引,首先我們需要借助分詞器,將每個(gè)文檔的 content 域拆分成單獨(dú)的詞(我們稱它為 詞條 或 tokens、term ),創(chuàng)建一個(gè)包含所有不重復(fù)詞條的排序列表,然后列出每個(gè)詞條出現(xiàn)在哪個(gè)文檔。

    Term Doc_1 Doc_2 ------------------------- Quick | | X The | X | brown | X | X dog | X | dogs | | X fox | X | foxes | | X in | | X jumped | X | lazy | X | X leap | | X over | X | X quick | X | summer | | X the | X | ------------------------

    如果我們想搜索 quick brown ,我們只需要查找包含每個(gè)詞條的文檔:

    Term Doc_1 Doc_2 ------------------------- brown | X | X quick | X | ------------------------ Total | 2 | 1

    這里我們匹配到了兩個(gè)文檔(為了節(jié)省空間,這里返回的只是文檔ID,最后再通過(guò)文檔id去查詢到具體文檔。)。對(duì)于搜索引擎來(lái)說(shuō),用戶總希望能夠先看到相關(guān)度更高的結(jié)果,因此實(shí)際使用時(shí)我們通過(guò)一些算法來(lái)進(jìn)行權(quán)重計(jì)算,將查詢的結(jié)果按照權(quán)重降序返回。


    Term dictionary與Term index

    Elasticsearch為了能夠快速的在倒排索引中找到某個(gè)term,他會(huì)按照字典序對(duì)所有的term進(jìn)行排序,再通過(guò)二分查找來(lái)找到term,這就是Term Dictionary,但即使有了Term Dictionary,O(logN)的磁盤讀寫仍然是影響性能的一大問(wèn)題。

    B-Tree通過(guò)減少磁盤尋道次數(shù)來(lái)提高查詢性能,Elasticsearch也是采用同樣的思路,直接通過(guò)內(nèi)存查找term,不讀磁盤,但是如果term太多,term dictionary也會(huì)很大,放內(nèi)存不現(xiàn)實(shí),于是有了Term Index,從下圖可以看出,Term Index其實(shí)就是一個(gè)Trie樹(前綴樹)

    Term index

    這棵樹不會(huì)包含所有的term,它包含的是term的一些前綴。通過(guò)term index可以快速地定位到term dictionary的某個(gè)offset,然后從這個(gè)位置再往后順序查找。

    查找流程

    為什么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ù)。


    列式存儲(chǔ)——Doc Values

    Doc values的存在是因?yàn)榈古潘饕粚?duì)某些操作是高效的。 倒排索引的優(yōu)勢(shì)在于查找包含某個(gè)項(xiàng)的文檔,而對(duì)于從另外一個(gè)方向的相反操作并不高效,即:確定哪些項(xiàng)是否存在單個(gè)文檔里,聚合需要這種次級(jí)的訪問(wèn)模式。

    以排序來(lái)舉例——雖然倒排索引的檢索性能非常快,但是在字段值排序時(shí)卻不是理想的結(jié)構(gòu)。

    • 在搜索的時(shí)候,我們能通過(guò)搜索關(guān)鍵詞快速得到結(jié)果集。
    • 當(dāng)排序的時(shí)候,我們需要倒排索引里面某個(gè)字段值的集合。換句話說(shuō),我們需要轉(zhuǎn)置倒排索引。

    轉(zhuǎn)置 結(jié)構(gòu)經(jīng)常被稱作 列式存儲(chǔ) 。它將所有單字段的值存儲(chǔ)在單數(shù)據(jù)列中,這使得對(duì)其進(jìn)行操作是十分高效的,例如排序、聚合等操作。

    在Elasticsearch中,Doc Values就是一種列式存儲(chǔ)結(jié)構(gòu),在索引時(shí)與倒排索引同時(shí)生成。也就是說(shuō)Doc Values和倒排索引一樣,基于 Segement生成并且是不可變的。同時(shí)Doc Values和倒排索引一樣序列化到磁盤。

    Doc Values常被應(yīng)用到以下場(chǎng)景:

    • 對(duì)一個(gè)字段進(jìn)行排序
    • 對(duì)一個(gè)字段進(jìn)行聚合
    • 某些過(guò)濾,比如地理位置過(guò)濾
    • 某些與字段相關(guān)的腳本計(jì)算

    下面舉一個(gè)例子,來(lái)講講它是如何運(yùn)作的

    假設(shè)存在以下倒排索引

    Term Doc_1 Doc_2 Doc_3 ------------------------------------ brown | X | X | dog | X | | X dogs | | X | X ------------------------------------

    那么其生成的DocValues如下(實(shí)際存儲(chǔ)時(shí)不會(huì)存儲(chǔ)doc_id,值所在的順位即為doc_id)

    Doc_id Values ------------------ Doc_1 | brown | Doc_1 | dog | Doc_2 | brown | Doc_2 | dogs | Doc_3 | dog | Doc_3 | dogs | ------------------

    假設(shè)我們需要計(jì)算出brown出現(xiàn)的次數(shù)

    GET /my_index/_search {"query":{"match":{"body":"brown"}},"aggs":{"popular_terms":{"terms":{"field":"body"}}},"size" : 0 }

    下面來(lái)分析上述請(qǐng)求在ES中是如何來(lái)進(jìn)行查詢的:

  • 定位數(shù)據(jù)范圍。通過(guò)倒排索引,來(lái)找到所有包含brown的doc_id。
  • 進(jìn)行聚合計(jì)算。借助doc_id在doc_values中定位到為brown的字段,此時(shí)進(jìn)行聚合累加得到計(jì)算結(jié)果。browm的count=2。

  • 但是doc_values仍存在一些問(wèn)題,其不支持analyzed類型的字段,因?yàn)檫@些字段在進(jìn)行文本分析時(shí)可能會(huì)被分詞處理,從而導(dǎo)致doc_values將其存儲(chǔ)為多行記錄。但是在我們實(shí)際使用時(shí),為什么仍然能對(duì)analyzed的字段進(jìn)行聚合操作呢?這時(shí)就需要介紹一下Fielddata


    Fielddata

    doc values不生成分析的字符串,那為什么這些字段仍然可以使用聚合呢?是因?yàn)槭褂昧薴ielddata的數(shù)據(jù)結(jié)構(gòu)。與doc values不同,fielddata構(gòu)建和管理100%在內(nèi)存中,常駐于JVM內(nèi)存堆。

    從歷史上看,fielddata 是所有字段的默認(rèn)設(shè)置。但是Elasticsearch已遷移到doc values以減少 OOM 的幾率。分析的字符串是仍然使用fielddata的最后一塊陣地。 最終目標(biāo)是建立一個(gè)序列化的數(shù)據(jù)結(jié)構(gòu)類似于doc values ,可以處理高維度的分析字符串,逐步淘汰 fielddata。

    它的一些特性如下

    • 延遲加載。如果你從來(lái)沒(méi)有聚合一個(gè)分析字符串,就不會(huì)加載fielddata到內(nèi)存中,其是在查詢時(shí)候構(gòu)建的。
    • 基于字段加載。 只有很活躍地使用字段才會(huì)增加fielddata的負(fù)擔(dān)。
    • 會(huì)加載索引中(針對(duì)該特定字段的) 所有的文檔,而不管查詢是否命中。邏輯是這樣:如果查詢會(huì)訪問(wèn)文檔 X、Y 和 Z,那很有可能會(huì)在下一個(gè)查詢中訪問(wèn)其他文檔。
    • 如果空間不足,使用最久未使用(LRU)算法移除fielddata。

    因此,在聚合字符串字段之前,請(qǐng)?jiān)u估情況:

    • 這是一個(gè)not_analyzed字段嗎?如果是,可以通過(guò)doc values節(jié)省內(nèi)存 。
    • 否則,這是一個(gè)analyzed字段,它將使用fielddata并加載到內(nèi)存中。這個(gè)字段因?yàn)镹-grams有一個(gè)非常大的基數(shù)?如果是,這對(duì)于內(nèi)存來(lái)說(shuō)極度不友好。


    索引壓縮

    FOR編碼

    在Elasticsearch中,為了能夠更方便的計(jì)算交集和并集,它要求倒排索引是有序的,而這個(gè)特點(diǎn)同時(shí)也帶來(lái)了一個(gè)額外的好處,我們可以使用增量編碼來(lái)壓縮倒排索引,也就是FOR(Frame of Reference)編碼

    增量編碼壓縮,將大數(shù)變小數(shù),按字節(jié)存儲(chǔ)

    FOR編碼分為三個(gè)步驟

    • 增量編碼
    • 增量分區(qū)
    • 位壓縮

    如下圖所示,如果我們的倒排索引中存儲(chǔ)的文檔id為[73, 300, 302, 332, 343, 372],那么經(jīng)過(guò)增量編碼后的結(jié)果則為[73, 227, 2, 30, 11, 29]。這種壓縮的好處在哪里呢?我們通過(guò)增量將原本的大數(shù)變成了小數(shù),使得所有的增量都在0~255之間,因此每一個(gè)值就只需要使用一個(gè)字節(jié)就可以存儲(chǔ),而不會(huì)使用int或者bigint,大大的節(jié)約了空間。

    接著,第二步我們將這些增量分到不同的區(qū)塊中(Lucene底層用了256個(gè)區(qū)塊,下面為了方便展示之用了兩個(gè))。

    第三步,我們計(jì)算出每組數(shù)據(jù)中最大的那個(gè)數(shù)所占用的bit位數(shù),例如下圖中區(qū)塊1最大的為227,所以只占用8個(gè)bit位,所以三個(gè)數(shù)總共占用3 * 8bits即3字節(jié)。而區(qū)塊2最大為29,只占用5個(gè)bit位,因此這三個(gè)數(shù)總共占用3 * 5bits即差不多2字節(jié)。通過(guò)這種方法,將原本6個(gè)整數(shù)從24字節(jié)壓縮到了5字節(jié),效果十分出色。

    ROF編碼實(shí)例


    Roaring Bitmaps

    FOR編碼對(duì)于倒排索引來(lái)說(shuō)效果很好,但對(duì)于需要存儲(chǔ)在內(nèi)存中的過(guò)濾器緩存等不太合適,兩者之間有很多不同之處:

    • 由于我們僅僅緩存那些經(jīng)常使用的過(guò)濾器,因此它的壓縮率并不需要像倒排索引那么高(倒排索引需要對(duì)每個(gè)詞都進(jìn)行編碼)。
    • 緩存過(guò)濾器的目的就是為了加速處理效率,因此它必須要比重新執(zhí)行過(guò)濾器要快,因此使用一個(gè)好的數(shù)據(jù)結(jié)構(gòu)和算法非常重要。
    • 緩存的過(guò)濾器存儲(chǔ)在內(nèi)存之中,而倒排索引通常存儲(chǔ)在磁盤中。

    基于以上的不同,對(duì)于緩存來(lái)說(shuō)FOR編碼并不適用,因此我們還需要考慮其他的一些選擇。

    • 整數(shù)數(shù)組:數(shù)組可能是我們馬上能想到的最簡(jiǎn)單的實(shí)現(xiàn)方式,我們將文檔id存儲(chǔ)在數(shù)組中,這樣就使得我們的迭代變得非常簡(jiǎn)單,但是這種方法的內(nèi)存利用率又十分低下,因?yàn)槊總€(gè)文檔都需要4個(gè)字節(jié)。
    • Bitmaps:在數(shù)據(jù)分布密集的下,位圖是一個(gè)很好的選擇。它本質(zhì)上就是一個(gè)數(shù)組,其中每一個(gè)文檔id占據(jù)一個(gè)位,用0和1來(lái)標(biāo)記文檔是否存在。這種方法大大節(jié)約了內(nèi)存,將一個(gè)文檔從4字節(jié)降低到了一個(gè)位,但是一旦數(shù)據(jù)分布稀疏,此時(shí)的位圖性能將大打折扣,因?yàn)闊o(wú)論數(shù)據(jù)量多少,位圖的大小都是由數(shù)據(jù)的上下區(qū)間來(lái)決定的。
    • Roaring Bitmaps:Roaring Bitmaps即是對(duì)位圖的一種優(yōu)化,它會(huì)根據(jù)16位最高位將倒排索引劃分為多塊,如第一個(gè)塊將對(duì)0到65535之間的值進(jìn)行編碼,第二個(gè)塊將在65536和131071之間進(jìn)行編碼。在每一個(gè)塊中,我們?cè)賹?duì)低16位進(jìn)行編碼,如果它的值小于4096在使用數(shù)組,否則就使用位圖。由于我們編碼的時(shí)候只會(huì)對(duì)低16位進(jìn)行編碼,因此在這里數(shù)組每個(gè)元素只需要2個(gè)字節(jié)

    Roaring Bitmaps原理

    為什么要使用4096作為數(shù)組和位圖選取的閾值呢?

    下面是官方給出的數(shù)據(jù)報(bào)告,在一個(gè)塊中只有文檔數(shù)量超過(guò)4096,位圖的內(nèi)存優(yōu)勢(shì)才會(huì)凸顯出來(lái)

    位圖與數(shù)組內(nèi)存利用對(duì)比

    這就是Roaring Bitmaps高效率的原因,它基于兩種完全不同的方案來(lái)進(jìn)行編碼,并根據(jù)內(nèi)存效率來(lái)動(dòng)態(tài)決定使用哪一種方案。

    官方也給出了幾種方案的性能測(cè)試

    迭代性能

    跳過(guò)性能——稀疏集

    跳過(guò)性能——密集集

    內(nèi)存占用

    構(gòu)造時(shí)間

    從上述對(duì)比可以看出,沒(méi)有一種方法是完美的,但是以下兩種方法的巨大劣勢(shì)使得它們不會(huì)被選擇

    • 數(shù)組:性能很好,但是內(nèi)存占用巨大。
    • Bitmaps:數(shù)據(jù)稀疏分布的時(shí)候內(nèi)存和性能都會(huì)大打折扣。

    因此在綜合考量下,Elasticsearch還是選擇使用Roaring Bitmaps,并且在很多大家了解的開源大數(shù)據(jù)框架中,也都使用了這一結(jié)構(gòu),如Hive、Spark、Kylin、Druid等。


    聯(lián)合索引

    如果多個(gè)字段索引的聯(lián)合查詢,倒排索引如何滿足快速查詢的要求呢?

    • 跳表:同時(shí)遍歷多個(gè)字段的倒排索引,互相skip。
    • 位圖:對(duì)多個(gè)過(guò)濾器分別求出位圖,對(duì)這幾個(gè)位圖做AND操作。

    Elasticsearch支持以上兩種的聯(lián)合索引方式,如果查詢的過(guò)濾器緩存到了內(nèi)存中(以位圖的形式),那么合并就是兩個(gè)位圖的AND。如果查詢的過(guò)濾器沒(méi)有緩存,那么就用跳表的方式去遍歷兩個(gè)硬盤中的倒排索引。

    假設(shè)有下面三個(gè)倒排索引需要聯(lián)合索引:

    假設(shè)有三個(gè)倒排索引
    • 如果使用跳表,則對(duì)最短的倒排索引中的每一個(gè)id,逐個(gè)在另外兩個(gè)倒排索引中查看是否存在,來(lái)判斷是否存在交集。
    • 如果使用位圖,則直接將幾個(gè)位圖按位與運(yùn)算,最終得到的結(jié)果就是最后的交集。

    總結(jié)

    以上是生活随笔為你收集整理的ElasticSearch探索之路(四)索引原理:倒排索引、列式存储、Fielddata、索引压缩、联合索引的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。