<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          elasticsearch document的索引過程分析

          共 5600字,需瀏覽 12分鐘

           ·

          2021-03-26 20:43


          點(diǎn)擊上方藍(lán)色“邁莫coding”,選擇“設(shè)為星標(biāo)”

           

          一、預(yù)備知識(shí)

           

          1
          1.1、索引不可變

           

          看到這篇文章相信大家都知道es是倒排索引,不了解也沒關(guān)系,在我的另一篇博文中詳細(xì)分析了es的倒排索引機(jī)制。在es的索引過程中為了滿足一下特點(diǎn),落盤的es索引是不可變的。

           

          1 不需要鎖。如果從來不需要更新一個(gè)索引,就不必?fù)?dān)心多個(gè)程序同時(shí)嘗試修改。

          2 一旦索引被讀入文件系統(tǒng)的緩存(內(nèi)存),它就一直在那兒,因?yàn)椴粫?huì)改變。只要文件系統(tǒng)緩存有足夠的空間,大部分的讀會(huì)直接訪問內(nèi)存而不是磁盤。這有助于性能提升。

          3 在索引的聲明周期內(nèi),所有的其他緩存都可用。它們不需要在每次數(shù)據(jù)變化了都重建,使文本可以被搜索因?yàn)閿?shù)據(jù)不會(huì)變。寫入單個(gè)大的倒排索引,可以壓縮數(shù)據(jù),較少磁盤IO和需要緩存索引的內(nèi)存大小。

           

           

          當(dāng)然,不可變的索引有它的缺點(diǎn),首先是它不可變!你不能改變它。如果想要搜索一個(gè)新文 檔,必須重建整個(gè)索引。這不僅嚴(yán)重限制了一個(gè)索引所能裝下的數(shù)據(jù),還有一個(gè)索引可以被更新的頻次。所以es引入了動(dòng)態(tài)索引。 


          1
          1.2、動(dòng)態(tài)索引


          下一個(gè)需要解決的問題是如何在保持不可變好處的同時(shí)更新倒排索引。答案是,使用多個(gè)索引。不是重寫整個(gè)倒排索引,而是增加額外的索引反映最近的變化。每個(gè)倒排索引都可以按順序查詢,從最老的開始,最后把結(jié)果聚合。

           

          Elasticsearch底層依賴的Lucene,引入了 per-segment search 的概念。一個(gè)段(segment)是有完整功能的倒排索引,但是現(xiàn)在Lucene中的索引指的是段的集合,再加上提交點(diǎn)(commit point,包括所有段的文件),如圖1所示。新的文檔,在被寫入磁盤的段之前,首先寫入內(nèi)存區(qū)的索引緩存。然后再通過fsync將緩存中的段刷新到磁盤上,該段將被打開即段落盤之后開始能被檢索。

           

           

           

           看到這里如果對(duì)分段還是有點(diǎn)迷惑,沒關(guān)系,假如你熟悉java語(yǔ)言,ArrayList這個(gè)集合我們都知道是一個(gè)動(dòng)態(tài)數(shù)組,他的底層數(shù)據(jù)結(jié)構(gòu)其實(shí)就是數(shù)組,我們都知道數(shù)組是不可變的,ArrayList是動(dòng)過擴(kuò)容實(shí)現(xiàn)的動(dòng)態(tài)數(shù)組。

           

          在這里我們就可以將commit point理解成ArrayList,segment就是一個(gè)個(gè)小的數(shù)組。然后將其組合成ArrayList。假如你知道Java1.8的ConcurrentHashMap的分段鎖相信你理解這個(gè)分段就很容易了。 


          1
          1.3、幾個(gè)容易混淆的概念


           

          在es中“索引”是分片(shard)的集合,在lucene中“索引”從宏觀上來說就是es中的一個(gè)分片,從微觀上來說就是segment的集合。

           

          “document的索引過程”這句話中的這個(gè)“索引”,我們可以理解成es為document建立索引的過程。

           


          二、document索引過程


           

           

          文檔被索引的過程如上面所示,大致可以分為 內(nèi)存緩沖區(qū)buffer、translog、filesystem cache、系統(tǒng)磁盤這幾個(gè)部分,接下來我們梳理一下這個(gè)過程。

           

          • 階段1:

           

          這個(gè)階段很簡(jiǎn)單,一個(gè)document文檔第一步會(huì)同時(shí)被寫進(jìn)內(nèi)存緩沖區(qū)buffer和translog。

           

          • 階段2:

           

          refresh:內(nèi)存緩沖區(qū)的documents每隔一秒會(huì)被refresh(刷新)到filesystem cache中的一個(gè)新的segment中,segment就是索引的最小單位,此時(shí)segment將會(huì)被打開供檢索。也就是說一旦文檔被刷新到文件系統(tǒng)緩存中,其就能被檢索使用了。這也是es近實(shí)時(shí)性(NRT)的關(guān)鍵。后面會(huì)詳細(xì)介紹。

           

          • 階段3:

           

          merge:每秒都會(huì)有新的segment生成,這將意味著用不了多久segment的數(shù)量就會(huì)爆炸,每個(gè)段都將十分消耗文件句柄、內(nèi)存、和cpu資源。這將是系統(tǒng)無法忍受的,所以這時(shí),我們急需將零散的segment進(jìn)行合并。ES通過后臺(tái)合并段解決這個(gè)問題。小段被合并成大段,再合并成更大的段。然后將新的segment打開供搜索,舊的segment刪除。

           

          • 階段4:

           

          flush:經(jīng)過階段3合并后新生成的更大的segment將會(huì)被flush到系統(tǒng)磁盤上。這樣整個(gè)過程就完成了。但是這里留一個(gè)包袱就是flush的時(shí)機(jī)。在后面介紹translog的時(shí)候會(huì)介紹。

           

          不要著急,接下來我們將以上步驟拆分開來詳細(xì)分析一下。

           

          1
          2.1、近實(shí)時(shí)化搜索(NRT)


          在早起的lucene中,只有當(dāng)segement被寫入到磁盤,該segment才會(huì)被打開供搜索,和我們上面所說的當(dāng)doc被刷新到filesystem cache中生成新的segment就將會(huì)被打開。

          因?yàn)?per-segment search 機(jī)制,索引和搜索一個(gè)文檔之間是有延遲的。新的文檔會(huì)在幾分鐘內(nèi)可以搜索,但是這依然不夠快。磁盤是瓶頸。提交一個(gè)新的段到磁盤需要 fsync 操作,確保段被物理地寫入磁盤,即時(shí)電源失效也不會(huì)丟失數(shù)據(jù)。但是 fsync 是昂貴的,它不能在每個(gè)文檔被索引的時(shí)就觸發(fā)。

           

          所以需要一種更輕量級(jí)的方式使新的文檔可以被搜索,這意味這移除 fsync 。

           

          位于Elasticsearch和磁盤間的是文件系統(tǒng)緩存。如前所說,在內(nèi)存索引緩存中的文檔被寫入新的段,但是新的段首先寫入文件系統(tǒng)緩存,這代價(jià)很低,之后會(huì)被同步到磁盤,這個(gè)代價(jià)很大。但是一旦一個(gè)文件被緩存,它也可以被打開和讀取,就像其他文件一樣。

           

          在es中每隔一秒寫入內(nèi)存緩沖區(qū)的文檔就會(huì)被刷新到filesystem cache中的新的segment,也就意味著可以被搜索了。這就是ES的NRT——近實(shí)時(shí)性搜索。

           

          簡(jiǎn)單介紹一下refresh API

           

          如果你遇到過你新增了doc,但是沒檢索到,很可能是因?yàn)檫€未自動(dòng)進(jìn)行refresh,這是你可以嘗試手動(dòng)刷新

           

          POST /student/_refresh
           

          性能優(yōu)化

           

          在這里我們需要知道一點(diǎn)refresh過程是很消耗性能的。如果你的系統(tǒng)對(duì)實(shí)時(shí)性要求不高,可以通過API控制refresh的時(shí)間間隔,但是如果你的新系統(tǒng)很要求實(shí)時(shí)性,那你就忍受它吧。

           

          如果你對(duì)系統(tǒng)的實(shí)時(shí)性要求很低,我們可以調(diào)整refresh的時(shí)間間隔,調(diào)大一點(diǎn)將會(huì)在一定程度上提升系統(tǒng)的性能。

           


          PUT /student{  "settings": {    "refresh_interval": "30s"   }}


           

          1
          2.2、合并段——merge

           

          通過每秒自動(dòng)刷新創(chuàng)建新的段,用不了多久段的數(shù)量就爆炸了。有太多的段是一個(gè)問題。每個(gè)段消費(fèi)文件句柄,內(nèi)存,cpu資源。更重要的是,每次搜索請(qǐng)求都需要依次檢查每個(gè)段。段越多,查詢?cè)铰?/span>

           

          ES通過后臺(tái)合并段解決這個(gè)問題。小段被合并成大段,再合并成更大的段。

           

          這是舊的文檔從文件系統(tǒng)刪除的時(shí)候。舊的段不會(huì)再?gòu)?fù)制到更大的新段中。這個(gè)過程你不必做什么。當(dāng)你在索引和搜索時(shí)ES會(huì)自動(dòng)處理。

           

          我們?cè)賮砜偨Y(jié)一下段合并的過程。

           

          1 選擇一些有相似大小的segment,merge成一個(gè)大的segment
          2 將新的segment flush到磁盤上去
          3 寫一個(gè)新的commit point,包括了新的segment,并且排除舊的那些segment
          4 將新的segment打開供搜索
          5 將舊的segment刪除

           

          optimize API與性能優(yōu)化

           

          optimize API 最好描述為強(qiáng)制合并段API。它強(qiáng)制分片合并段以達(dá)到指定 max_num_segments 參數(shù)。這是為了減少段的數(shù)量(通常為1)達(dá)到提高搜索性能的目的。

           

          在特定的環(huán)境下, optimize API 是有用的。典型的場(chǎng)景是記錄日志,這中情況下日志是按照每天,周,月存入索引。舊的索引一般是只可讀的,它們是不可能修改的。這種情況下,把每個(gè)索引的段降至1是有效的。搜索過程就會(huì)用到更少的資源,性能更好:

           

          POST /logstash-2019-10-01/_optimize?max_num_segments=1
           

           一般場(chǎng)景下盡量不要手動(dòng)執(zhí)行,讓它自動(dòng)默認(rèn)執(zhí)行就可以了

           

          1
          2.3、容災(zāi)與可靠存儲(chǔ)


          沒用 fsync 同步文件系統(tǒng)緩存到磁盤,我們不能確保電源失效,甚至正常退出應(yīng)用后,數(shù)據(jù)的安全。為了ES的可靠性,需要確保變更持久化到磁盤。

           

          我們說過一次全提交同步段到磁盤,寫提交點(diǎn),這會(huì)列出所有的已知的段。在重啟,或重新 打開索引時(shí),ES使用這次提交點(diǎn)決定哪些段屬于當(dāng)前的分片。

           

          當(dāng)我們通過每秒的刷新獲得近實(shí)時(shí)的搜索,我們依然需要定時(shí)地執(zhí)行全提交確保能從失敗中 恢復(fù)。但是提交之間的文檔怎么辦?我們也不想丟失它們。

           

          上面doc索引流程的階段1,doc分別被寫入到內(nèi)存緩沖區(qū)和translog,然后每秒都將會(huì)把內(nèi)存緩沖區(qū)的docs刷新到filesystem cache中的新segment,然后眾多segment會(huì)進(jìn)行不斷的壓縮,小段被合并成大段,再合并成更大的段。每次refresh操作后,內(nèi)存緩沖區(qū)的docs被刷新到filesystem cache中的segemnt中,但是tanslog仍然在持續(xù)的增大增多。當(dāng)translog大到一定程度,將會(huì)發(fā)生一個(gè)commit操作也就是全量提交。

           

          詳細(xì)過程如下:

           

          1、 doc寫入內(nèi)存緩沖區(qū)和translog日志文件2、 每隔一秒鐘,內(nèi)存緩沖區(qū)中的docs被寫入到filesystem cache中新的segment,此時(shí)segment被打開并供檢索使用3、 內(nèi)存緩沖區(qū)被清空4、 重復(fù)1~3,新的segment不斷添加,內(nèi)存緩沖區(qū)不斷被清空,而translog中的數(shù)據(jù)不斷累加5、 當(dāng)translog長(zhǎng)度達(dá)到一定程度的時(shí)候,commit操作發(fā)生  5-1、 內(nèi)存緩沖區(qū)中的docs被寫入到filesystem cache中新的segment,打開供檢索使用  5-2、 內(nèi)存緩沖區(qū)被清空  5-3、 一個(gè)commit ponit被寫入磁盤,標(biāo)明了所有的index segment  5-4、 filesystem cache中的所有index segment file緩存數(shù)據(jù),被fsync強(qiáng)行刷到磁盤上  5-5、 現(xiàn)有的translog被清空,創(chuàng)建一個(gè)新的translog

           

           

          其實(shí)到這里我們發(fā)現(xiàn)fsync還是沒有被舍棄的,但是我們通過動(dòng)態(tài)索引和translog技術(shù)減少了其使用頻率,并實(shí)現(xiàn)了近實(shí)時(shí)搜索。其次通過以上步驟我們發(fā)現(xiàn)flush操作包括filesystem cache中的segment通過fsync刷新到硬盤以及translog的清空兩個(gè)過程。es默認(rèn)每30分鐘進(jìn)行一次flush操作,但是當(dāng)translog大到一定程度時(shí)也會(huì)進(jìn)行flush操作。

           

          對(duì)應(yīng)過程圖如下

           

           

           

          5-5步驟不難發(fā)現(xiàn)只有內(nèi)存緩沖區(qū)中的docs全部刷新到filesystem cache中并fsync到硬盤,translog才會(huì)被清除,這樣就保證了數(shù)據(jù)不會(huì)丟失,因?yàn)橹灰猼ranslog存在,我們就能根據(jù)translog進(jìn)行數(shù)據(jù)的恢復(fù)。

           

          簡(jiǎn)單介紹一下flush API

           

          手動(dòng)flush如下所示,但是并不建議使用。但是當(dāng)要重啟或關(guān)閉一個(gè)索引,flush該索引是很有用的。當(dāng)ES嘗試恢復(fù)或者重新打開一個(gè)索引時(shí),它必須重放所有事務(wù)日志中的操作,所以日志越小,恢復(fù)速度越快。

           

          POST /student/_flush


          三、更新和刪除


          前面我們說過es的索引是不可變的,那么更新和刪除是如何進(jìn)行的呢?

           

          段是不可變的,所以文檔既不能從舊的段中移除,舊的段也不能更新以反映文檔最新的版本。相反,每一個(gè)提交點(diǎn)包括一個(gè).del文件,包含了段上已經(jīng)被刪除的文檔。當(dāng)一個(gè)文檔被刪除,它實(shí)際上只是在.del文件中被標(biāo)記為刪除,依然可以匹配查詢,但是最終返回之前會(huì)被從結(jié)果中刪除。

           

          文檔的更新操作是類似的:當(dāng)一個(gè)文檔被更新,舊版本的文檔被標(biāo)記為刪除,新版本的文檔在新的段中索引。也許該文檔的不同版本都會(huì)匹配一個(gè)查詢,但是更老版本會(huì)從結(jié)果中刪除。

           

           


          分割線



          原文地址:https://www.cnblogs.com/hello-shf/p/11553317.html 



          往期推薦


          elasticsearch入門篇

          你真的知道怎么實(shí)現(xiàn)一個(gè)延遲隊(duì)列嗎?

          go語(yǔ)言十分鐘入門教程

          SQL語(yǔ)句中where條件為什么寫上1=1

          mysql那些事兒|深入淺出mysql索引(上)

          【七天從零實(shí)現(xiàn)ORM框架】Day03:對(duì)象表結(jié)構(gòu)映射

          go那些事兒|go反射使用第二彈(ValueOf)


          文章也會(huì)持續(xù)更新,可以微信搜索「 邁莫coding 」第一時(shí)間閱讀。每天分享優(yōu)質(zhì)文章、大廠經(jīng)驗(yàn)、大廠面經(jīng),助力面試,是每個(gè)程序員值得關(guān)注的平臺(tái)。




          1. 你點(diǎn)的每個(gè)贊,我都認(rèn)真當(dāng)成了喜歡



          瀏覽 40
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  影音先锋一区二区三区视频特色 | 日一级毛片 | 免费做爱网站在线观看 | 美女一级片 | 无码免费婬AV片在线观看 |