前言
最近 TL 分享了下 《Elasticsearch基礎(chǔ)整理》http://www.reibang.com/p/e8226138485d 悠反,蹭著這個機(jī)會残黑。寫個小文鞏固下馍佑,本文主要講 ES -> Lucene
的底層結(jié)構(gòu),然后詳細(xì)描述新數(shù)據(jù)寫入 ES 和 Lucene 的流程和原理萍摊。這是基礎(chǔ)理論知識挤茄,整理了一下,希望能對 Elasticsearch 感興趣的同學(xué)有所幫助冰木。
一穷劈、Elasticsearch & Lucene 是什么
什么是 Elasticsearch ?
Elasticsearch 是一個基于 Apache Lucene(TM) 的開源搜索引擎踊沸。
那 Lucene 是什么歇终?
無論在開源還是專有領(lǐng)域,Lucene 可以被認(rèn)為是迄今為止最先進(jìn)逼龟、性能最好的评凝、功能最全的搜索引擎庫,并通過簡單的 RESTful API 來隱藏 Lucene 的復(fù)雜性腺律,從而讓全文搜索變得簡單奕短。
Elasticsearch 不僅僅是 Lucene 和全文搜索,我們還能這樣去描述它:
- 分布式的實(shí)時文件存儲匀钧,每個字段都被索引并可被搜索
- 分布式的實(shí)時分析搜索引擎
- 可以擴(kuò)展到上百臺服務(wù)器翎碑,處理 PB 級結(jié)構(gòu)化或非結(jié)構(gòu)化數(shù)據(jù)
二、Elasticsearch & Lucene 的關(guān)系
就像很多業(yè)務(wù)系統(tǒng)是基于 Spring 實(shí)現(xiàn)一樣之斯,Elasticsearch 和 Lucene 的關(guān)系很簡單:Elasticsearch 是基于 Lucene 實(shí)現(xiàn)的日杈。ES 基于底層這些包,然后進(jìn)行了擴(kuò)展佑刷,提供了更多的更豐富的查詢語句莉擒,并且通過 RESTful API 可以更方便地與底層交互。類似 ES 還有 Solr 也是基于 Lucene 實(shí)現(xiàn)的瘫絮。
在應(yīng)用開發(fā)中涨冀,用 Elasticsearch 會很簡單。但是如果你直接用 Lucene麦萤,會有大量的集成工作蝇裤。
因此,入門 ES 的同學(xué)频鉴,稍微了解下 Lucene 即可。如果往高級走恋拍,還是需要學(xué)習(xí) Lucene 底層的原理垛孔。因?yàn)榈古潘饕⒋蚍謾C(jī)制施敢、全文檢索原理周荐、分詞原理等等狭莱,這些都是不會過時的技術(shù)。
三概作、新文檔寫入流程
3.1 數(shù)據(jù)模型
如圖
- 一個 ES Index (索引腋妙,比如商品搜索索引、訂單搜索索引)集群下讯榕,有多個 Node (節(jié)點(diǎn))組成骤素。每個節(jié)點(diǎn)就是 ES 的實(shí)例。
- 每個節(jié)點(diǎn)上會有多個 shard (分片)愚屁, P1 P2 是主分片 R1 R2 是副本分片
- 每個分片上對應(yīng)著就是一個 Lucene Index(底層索引文件)
- Lucene Index 是一個統(tǒng)稱济竹。由多個 Segment (段文件,就是倒排索引)組成霎槐。每個段文件存儲著就是 Doc 文檔送浊。
3.2 Lucene Index
lucene 中,單個倒排索引文件稱為 segment丘跌。其中有一個文件袭景,記錄了所有 segments 的信息,稱為 commit point:
- 文檔 create 新寫入時闭树,會生成新的 segment耸棒。同樣會記錄到 commit point 里面
- 文檔查詢,會查詢所有的 segments
- 當(dāng)一個段存在文檔被刪除蔼啦,會維護(hù)該信息在 .liv 文件里面
3.3 新文檔寫入流程
新文檔創(chuàng)建或者更新時榆纽,進(jìn)行如下流程:
更新不會修改原來的 segment,更新和創(chuàng)建操作都會生成新的一個 segment捏肢。數(shù)據(jù)哪里來呢奈籽?先會存在內(nèi)存的 bugger 中,然后持久化到 segment 鸵赫。
數(shù)據(jù)持久化步驟如下:write -> refresh -> flush -> merge
3.3.1 write 過程
一個新文檔過來衣屏,會存儲在 in-memory buffer 內(nèi)存緩存區(qū)中,順便會記錄 Translog辩棒。
這時候數(shù)據(jù)還沒到 segment 狼忱,是搜不到這個新文檔的。數(shù)據(jù)只有被 refresh 后一睁,才可以被搜索到钻弄。那么 講下 refresh 過程
3.3.2 refresh 過程
refresh 默認(rèn) 1 秒鐘,執(zhí)行一次上圖流程者吁。ES 是支持修改這個值的窘俺,通過 index.refresh_interval 設(shè)置 refresh (沖刷)間隔時間。refresh 流程大致如下:
- in-memory buffer 中的文檔寫入到新的 segment 中复凳,但 segment 是存儲在文件系統(tǒng)的緩存中瘤泪。此時文檔可以被搜索到
- 最后清空 in-memory buffer灶泵。注意: Translog 沒有被清空,為了將 segment 數(shù)據(jù)寫到磁盤
文檔經(jīng)過 refresh 后对途, segment 暫時寫到文件系統(tǒng)緩存赦邻,這樣避免了性能 IO 操作,又可以使文檔搜索到实檀。refresh 默認(rèn) 1 秒執(zhí)行一次惶洲,性能損耗太大。一般建議稍微延長這個 refresh 時間間隔劲妙,比如 5 s湃鹊。因此,ES 其實(shí)就是準(zhǔn)實(shí)時镣奋,達(dá)不到真正的實(shí)時币呵。
3.3.3 flush 過程
上個過程中 segment 在文件系統(tǒng)緩存中,會有意外故障文檔丟失侨颈。那么余赢,為了保證文檔不會丟失,需要將文檔寫入磁盤哈垢。那么文檔從文件緩存寫入磁盤的過程就是 flush妻柒。寫入次怕后,清空 translog耘分。
translog 作用很大:
- 保證文件緩存中的文檔不丟失
- 系統(tǒng)重啟時举塔,從 translog 中恢復(fù)
- 新的 segment 收錄到 commit point 中
具體可以看官方文檔:https://www.elastic.co/guide/en/elasticsearch/reference/7.3/indices-flush.html
3.3.4 merge 過程
上面幾個步驟,可見 segment 會越來越多求泰,那么搜索會越來越慢央渣?怎么處理呢?
通過 merge 過程解決:
- 就是各個小段文件渴频,合并成一個大段文件芽丹。段合并過程
- 段合并結(jié)束,舊的小段文件會被刪除
- .liv 文件維護(hù)的刪除文檔卜朗,會通過這個過程進(jìn)行清除
四拔第、小結(jié)
如這個圖,ES 寫入原理不難场钉,記住關(guān)鍵點(diǎn)即可蚊俺。
write -> refresh -> flush
- write:文檔數(shù)據(jù)到內(nèi)存緩存,并存到 translog
- refresh:內(nèi)存緩存中的文檔數(shù)據(jù)逛万,到文件緩存中的 segment 泳猬。此時可以被搜到
- flush 是緩存中的 segment 文檔數(shù)據(jù)寫入到磁盤
寫入的原理告訴我們,考慮的點(diǎn)很多:性能、數(shù)據(jù)不丟失等等
(完)
參考資料:
Java微服務(wù)資料暂殖,加我微w信x:bysocket01 (加的人,一般很帥)