ES近實(shí)時(shí)搜索原理

關(guān)系圖:
image.png

Segment(段):Lucene里面的一個(gè)數(shù)據(jù)集概念
提交點(diǎn)文件:有一個(gè)列表存放著所有已知的所有段
ES底層是基于Lucene,最核心的概念就是Segment(段),每個(gè)段本身就是一個(gè)倒排索引。
ES中的Index由多個(gè)段的集合和commit point(提交點(diǎn))文件組成。

提交點(diǎn)文件中有一個(gè)列表存放著所有已知的段,下面是一個(gè)帶有1個(gè)提交點(diǎn)和3個(gè)段的Index示意圖:


image.png

Doc新增提交主要過程如下:
一、寫入磁盤后可見:
1、Doc寫入Buffer
Doc會(huì)先被搜集到內(nèi)存中的Buffer內(nèi),這個(gè)時(shí)候還無法被搜索到,如下圖所示:


image.png

2、 Doc Commit
每隔一段時(shí)間,會(huì)將buffer提交,在flush磁盤后打開新段使得搜索可見,詳細(xì)過程如下:

(1)創(chuàng)建一個(gè)新段,作為一個(gè)追加的倒排索引,寫入到磁盤(文件系統(tǒng)緩存)
(2)將新的包含新段的Commit Point(提交點(diǎn))寫入磁盤(文件系統(tǒng)緩存)
(3)磁盤進(jìn)行fsync,主要是將文件系統(tǒng)緩存中等待的寫入操作全部物理寫入到磁盤,保證數(shù)據(jù)不會(huì)在發(fā)生錯(cuò)誤時(shí)丟失
(4)這個(gè)新的段被開啟, 使得段內(nèi)文檔對(duì)搜索可見
(5)將內(nèi)存中buffer清除,又可以把新的Doc寫入buffer了
下面展示了這個(gè)過程完成后的段和提交點(diǎn)的狀態(tài):


image.png

通過這種方式,可以使得新文檔從被索引到可被搜索間的時(shí)間間隔在數(shù)分鐘,但是還不夠快。因?yàn)榇疟P需要fsync,這個(gè)就成為性能瓶頸。我們前面提到過Doc會(huì)先被從buffer刷入段寫入文件系統(tǒng)緩存(很快),那么就自然想到在這個(gè)階段就讓文檔對(duì)搜索可見,隨后再被刷入磁盤(較慢)。
二、寫入磁盤前可見:
ES寫入原理流程圖.png

Commits in Lucene

為了數(shù)據(jù)安全,每次的索引變更都最好要立刻刷盤, 所以 Commit 操作意味著將Segment 合并并寫入磁盤。保證內(nèi)存數(shù)據(jù)盡量不丟。刷盤是很重的 IO 操作, 所以為了機(jī)器性能和近實(shí)時(shí)搜索, 并不會(huì)刷盤那么及時(shí)。

Translog

新文檔被索引意味著文檔會(huì)被首先寫入內(nèi)存 buffer 和 translog 文件。每個(gè) shard 都對(duì)應(yīng)一個(gè) translog 文件。


translog.png

Refresh in Elasticsearch

在 elasticsearch 中, _refresh操作默認(rèn)每秒執(zhí)行一次,意味著將內(nèi)存 buffer 的數(shù)據(jù)寫入到一個(gè)新的Segment 中,這個(gè)時(shí)候索引變成了可被檢索的。


refresh.png

Flush in Elasticsearch

Flush操作意味著將內(nèi)存buffer的數(shù)據(jù)全都寫入新的Segments中,并將內(nèi)存中所有的Segments全部刷盤,并且清空translog日志的過程。


flush.png

1、refresh:
Lucene支持對(duì)新段寫入和打開 - 可以使文檔在沒有完全刷入硬盤的狀態(tài)下就能對(duì)搜索可見,而且是一個(gè)開銷較小的操作,可以頻繁進(jìn)行。
下面是一個(gè)已經(jīng)將Docs刷入段但還沒有完全提交的示意圖:


image.png

我們可以看到,新段雖然還沒有被完全提交,但是已經(jīng)對(duì)搜索可見了。
這種對(duì)新段的巧妙操作過程被稱為refresh,默認(rèn)執(zhí)行的時(shí)間間隔是1秒,這就是ES被稱為近實(shí)時(shí)搜索的原因。

2、translog
為了避免在兩次commit操作間隔時(shí)間發(fā)生異常導(dǎo)致Doc丟失,ES中采用了一個(gè)事務(wù)日志記錄每次對(duì)ES的操作。加上translog后新增文檔流程如下:
文檔被添加到buffer同時(shí)追加到translog,如圖:


image.png

進(jìn)行refresh操作,清空buffer,文檔可被搜索但尚未flush到磁盤。translog不會(huì)清空,如圖:
image.png

繼續(xù)步驟1的過程:
每隔一段時(shí)間(例如translog變得太大),index會(huì)被flush到磁盤,新的translog文件被創(chuàng)建,這個(gè)commit完整執(zhí)行結(jié)束。在這個(gè)操作后,會(huì)發(fā)生以下事件:
(1)所有內(nèi)存中的buffer會(huì)被寫入新段
(2)buffer被清空
(3)一個(gè)提交點(diǎn)被寫入磁盤
(4)文件系統(tǒng)緩存通過fsync flush
(5)之前的舊translog被刪除

下面示意圖展示了這個(gè)狀態(tài):


image.png

3、translog的作用
(1)通過前文描述我們知道translog其實(shí)就記錄了還尚未被flush到磁盤的操作。當(dāng) Elasticsearch 啟動(dòng)的時(shí)候, 它會(huì)從磁盤中使用最后一個(gè)提交點(diǎn)去恢復(fù)已知的段,并且會(huì)重放 translog 中所有在最后一次提交后發(fā)生的變更操作。
translog 也被用來提供實(shí)時(shí) CRUD 。當(dāng)你試著通過ID來RUD一個(gè)Doc,它會(huì)在從相關(guān)的段檢索之前先檢查 translog 中最新的變更。
(2)translog的安全性
默認(rèn)translog是每5秒或是每次請(qǐng)求完成后被fsync到磁盤(在主分片和副本分片都會(huì))。也就是說,如果你發(fā)起一個(gè)index, delete, update請(qǐng)求寫入translog并被fsync到主分片和副本分片的磁盤前不會(huì)返回200狀態(tài)。
這樣會(huì)帶來一些性能損失,可以通過設(shè)為異步fsync,但是必須接受由此帶來的丟失少量數(shù)據(jù)的風(fēng)險(xiǎn):
PUT /my_index/_settings
{
    "index.translog.durability": "async",
    "index.translog.sync_interval": "5s"
}

4、flush
flush就是執(zhí)行commit清空、干掉老translog的過程。默認(rèn)每個(gè)分片30分鐘或者是translog過于大的時(shí)候自動(dòng)flush一次??梢酝ㄟ^flush API手動(dòng)觸發(fā),但是只會(huì)在重啟節(jié)點(diǎn)或關(guān)閉某個(gè)索引的時(shí)候這樣做,因?yàn)檫@可以讓未來ES恢復(fù)的速度更快(translog文件更小)。
三、 ES對(duì)Doc刪除的處理
(1)刪除一個(gè)ES文檔不會(huì)立即從磁盤上移除,它只是被標(biāo)記成已刪除。因?yàn)槎问遣豢勺兊模晕臋n既不能從舊的段中移除,舊的段也不能更新以反映文檔最新的版本。
ES的做法是,每一個(gè)提交點(diǎn)包括一個(gè).del文件(還包括新段),包含了段上已經(jīng)被標(biāo)記為刪除狀態(tài)的文檔。所以,當(dāng)一個(gè)文檔被做刪除操作,實(shí)際上只是在.del文件中將該文檔標(biāo)記為刪除,依然會(huì)在查詢時(shí)被匹配到,只不過在最終返回結(jié)果之前會(huì)被從結(jié)果中刪除。ES將會(huì)在用戶之后添加更多索引的時(shí)候,在后臺(tái)進(jìn)行要?jiǎng)h除內(nèi)容的清理。
(2)Doc刪除與段合并的關(guān)系
通過每秒自動(dòng)刷新創(chuàng)建新的段,用不了多久段的數(shù)量就爆炸了,每個(gè)段消費(fèi)大量文件句柄,內(nèi)存,cpu資源。更重要的是,每次搜索請(qǐng)求都需要依次檢查每個(gè)段。段越多,查詢?cè)铰?br> ES通過后臺(tái)合并段解決這個(gè)問題。ES利用段合并的時(shí)機(jī)來真正從文件系統(tǒng)刪除那些version較老或者是被標(biāo)記為刪除的文檔。被刪除的文檔(或者是version較老的)不會(huì)再被合并到新的更大的段中。
ES對(duì)一個(gè)不斷有數(shù)據(jù)寫入的索引處理流程如下:
索引過程中,refresh會(huì)不斷創(chuàng)建新的段,并打開它們。
合并過程會(huì)在后臺(tái)選擇一些小的段合并成大的段,這個(gè)過程不會(huì)中斷索引和搜索。合并過程如圖:


image.png

兩個(gè)已提交的段 和一個(gè)未提交的段合并為一個(gè)更大的段。從上圖可以看到,段合并之前,舊有的Commit和沒Commit的小段皆可被搜索。
(3)段合并后的操作:
a、新的段flush到硬盤
b、編寫一個(gè)包含新段的新提交點(diǎn),并排除舊的較小段。
c、新的段打開供搜索
d、舊的段被刪除
合并完成后新的段可被搜索,舊的段被刪除,如下圖所示:


image.png

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

注:
什么情況下要強(qiáng)制刷新:
1、reindex后,手動(dòng)修改refresh,由-1(不刷新)改為想要的刷新值
2、在讀多,寫少時(shí),可以強(qiáng)制不刷新,因?yàn)槊繉懭胍粭l數(shù)據(jù)就會(huì)產(chǎn)生一個(gè)新段,查詢時(shí)就會(huì)查一次,降低效率
3、即時(shí)性要求高,:如廣告立馬需要被看到,需要手動(dòng)強(qiáng)制刷新

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容