倒排索引原理學習筆記

segment

segment即是倒排索引,
特點是隻能 合併或者刪除,不能修改

每一次refresh會產生一次 segment, 然後合併存儲。

每個搜索請求需要訪問所有segment,

參數 說明
index.merge.policy.floor_segment 默認2MB,小於該值的segment優先被合併
index.merge.policy.max_merge_at_once 默認10,一次最多合併多少segment
index.merge.policy.max_merged_segment 默認5GB,超過該值的segment不合並
index.merge.policy.max_merge_at_once_explicit 顯式調用一次最多合併多少個segment

refresh: 將document轉換爲segment的過程,在ES中數據會從index-buffer到filesystem-cache的過程。

倒排索引主要分兩個部分
單詞詞典
倒排文件

es寫入工作流程

在這裏插入圖片描述

先寫入內存 buffer,在 buffer 裏的時候數據是搜索不到的;同時將數據寫入 translog 日誌文件。

如果 buffer 快滿了,或者到一定時間,就會將內存 buffer 數據 refresh 到一個新的 segment file 中

但是此時數據不是直接進入 segment file 磁盤文件,而是先進入 os cache 。這個過程就是 refresh。

每隔 1 秒鐘,es 將 buffer 中的數據寫入一個新的 segment file,每秒鐘會產生一個新的磁盤文件 segment file

這個 segment file 中就存儲最近 1 秒內 buffer 中寫入的數據。

但是如果 buffer 裏面此時沒有數據,那當然不會執行 refresh 操作

如果 buffer 裏面有數據,默認 1 秒鐘執行一次 refresh 操作,刷入一個新的 segment file 中。

操作系統裏面,磁盤文件其實都有一個東西,叫做 os cache,即操作系統緩存

就是說數據寫入磁盤文件之前,會先進入 os cache,先進入操作系統級別的一個內存緩存中去。只要 buffer中的數據被 refresh 操作刷入 os cache中,這個數據就可以被搜索到了。

es讀數據流程

在這裏插入圖片描述
可以通過 doc id 來查詢,會根據 doc id 進行 hash,判斷出來當時把 doc id 分配到了哪個 shard 上面去,從那個 shard 去查詢。

客戶端發送請求到任意一個 node,成爲 coordinate node。
coordinate node 對 doc id 進行哈希路由,將請求轉發到對應的 node,此時會使用 round-robin隨機輪詢算法,在 primary shard 以及其所有 replica 中隨機選擇一個,讓讀請求負載均衡。
接收請求的 node 返回 document 給 coordinate node。
coordinate node 返回 document 給客戶端。
es 搜索數據過程
es 最強大的是做全文檢索,就是比如你有三條數據:

java真好玩兒啊java好難學啊j2ee特別牛

你根據 java 關鍵詞來搜索,將包含 java的 document 給搜索出來。es 就會給你返回:java真好玩兒啊,java好難學啊。

客戶端發送請求到一個 coordinate node。
協調節點將搜索請求轉發到所有的 shard 對應的 primary shard 或 replica shard,都可以。
query phase:每個 shard 將自己的搜索結果(其實就是一些 doc id)返回給協調節點,由協調節點進行數據的合併、排序、分頁等操作,產出最終結果。
fetch phase:接着由協調節點根據 doc id 去各個節點上拉取實際的 document 數據,最終返回給客戶端。
寫請求是寫入 primary shard,然後同步給所有的 replica shard;

讀請求可以從 primary shard 或 replica shard 讀取,採用的是隨機輪詢算法。

倒排索引基本概念解釋

倒排索引(Inverted Index):倒排索引是實現“單詞-文檔矩陣”的一種具體存儲形式,通過倒排索引,可以根據單詞快速獲取包含這個單詞的文檔列表。倒排索引主要由兩個部分組成:“單詞詞典”和“倒排文件”。

   單詞詞典(Lexicon):搜索引擎的通常索引單位是單詞,單詞詞典是由文檔集合中出現過的所有單詞構成的字符串集合,單詞詞典內每條索引項記載單詞本身的一些信息以及指向“倒排列表”的指針。

   倒排列表(PostingList):倒排列表記載了出現過某個單詞的所有文檔的文檔列表及單詞在該文檔中出現的位置信息,每條記錄稱爲一個倒排項(Posting)。根據倒排列表,即可獲知哪些文檔包含某個單詞。

   倒排文件(Inverted File):所有單詞的倒排列表往往順序地存儲在磁盤的某個文件裏,這個文件即被稱之爲倒排文件,倒排文件是存儲倒排索引的物理文件。

在這裏插入圖片描述

簡單示例

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

lucene 存儲詞典方式

類HashMap , B+樹, FST

FST:

我們就用Alice和Alan這兩個單詞爲例,來看下FST的構造過程。首先對所有的單詞做一下排序爲“Alice”,“Alan”。

插入“Alan”
在這裏插入圖片描述

插入“Alice”
在這裏插入圖片描述

這樣你就得到了一個有向無環圖,有這樣一個數據結構,就可以很快查找某個人名是否存在。FST在單term查詢上可能相比hashmap並沒有明顯優勢,甚至會慢一些。但是在範圍,前綴搜索以及壓縮率上都有明顯的優勢。

在通過FST定位到倒排鏈後,有一件事情需要做,就是倒排鏈的合併。因爲查詢條件可能不止一個,例如上面我們想找name=“alan” and age="18"的列表。lucene是如何實現倒排鏈的合併呢。這裏就需要看一下倒排鏈存儲的數據結構

SkipList

爲了能夠快速查找docid,lucene採用了SkipList這一數據結構。SkipList有以下幾個特徵:

元素排序的,對應到我們的倒排鏈,lucene是按照docid進行排序,從小到大。
跳躍有一個固定的間隔,這個是需要建立SkipList的時候指定好,例如下圖以間隔是3
SkipList的層次,這個是指整個SkipList有幾層

在這裏插入圖片描述

有了這個SkipList以後比如我們要查找docid=12,原來可能需要一個個掃原始鏈表,1,2,3,5,7,8,10,12。有了SkipList以後先訪問第一層看到是然後大於12,進入第0層走到3,8,發現15大於12,然後進入原鏈表的8繼續向下經過10和12。
有了FST和SkipList的介紹以後,我們大體上可以畫一個下面的圖來說明lucene是如何實現整個倒排結構的:
在這裏插入圖片描述

有了這張圖,我們可以理解爲什麼基於lucene可以快速進行倒排鏈的查找和docid查找,下面就來看一下有了這些後如何進行倒排鏈合併返回最後的結果。

倒排合併

假如我們的查詢條件是name = “Alice”,那麼按照之前的介紹,首先在term字典中定位是否存在這個term,如果存在的話進入這個term的倒排鏈,並根據參數設定返回分頁返回結果即可。這類查詢,在數據庫中使用二級索引也是可以滿足,那lucene的優勢在哪呢。假如我們有多個條件,例如我們需要按名字或者年齡單獨查詢,也需要進行組合 name = “Alice” and age = "18"的查詢,那麼使用傳統二級索引方案,你可能需要建立兩張索引表,然後分別查詢結果後進行合併,這樣如果age = 18的結果過多的話,查詢合併會很耗時。那麼在lucene這兩個倒排鏈是怎麼合併呢。
假如我們有下面三個倒排鏈需要進行合併。
在這裏插入圖片描述
在lucene中會採用下列順序進行合併:

在termA開始遍歷,得到第一個元素docId=1
Set currentDocId=1
在termB中 search(currentDocId) = 1 (返回大於等於currentDocId的一個doc),

因爲currentDocId ==1,繼續
如果currentDocId 和返回的不相等,執行2,然後繼續
到termC後依然符合,返回結果
currentDocId = termC的nextItem
然後繼續步驟3 依次循環。直到某個倒排鏈到末尾。
整個合併步驟我可以發現,如果某個鏈很短,會大幅減少比對次數,並且由於SkipList結構的存在,在某個倒排中定位某個docid的速度會比較快不需要一個個遍歷。可以很快的返回最終的結果。從倒排的定位,查詢,合併整個流程組成了lucene的查詢過程,和傳統數據庫的索引相比,lucene合併過程中的優化減少了讀取數據的IO,倒排合併的靈活性也解決了傳統索引較難支持多條件查詢的問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章