Lucene 是一種高效能、可伸縮的資訊搜尋(IR)庫,在 2000 年開源,最初由鼎鼎大名的 Doug Cutting 開發,是基於 Java 實現的高效能的開源專案。
Lucene 採用了基於倒排表的設計原理,可以非常高效地實現文字查詢,在底層採用了分段的儲存模式,使它在讀寫時幾乎完全避免了鎖的出現,大大提升了讀寫效能。
核心模組
Lucene 的寫流程和讀流程如下圖所示:
其中,虛線箭頭(a、b、c、d)表示寫索引的主要過程,實線箭頭(1-9)表示查詢的主要過程。
Lucene 中的主要模組及模組說明如下:
-
analysis:主要負責詞法分析及語言處理,也就是我們常說的分詞,通過該模組可最終形成儲存或者搜尋的最小單元 Term。
-
index 模組:主要負責索引的建立工作。
-
store 模組:主要負責索引的讀寫,主要是對檔案的一些操作,其主要目的是抽象出和平臺檔案系統無關的儲存。
-
queryParser 模組:主要負責語法分析,把我們的查詢語句生成 Lucene 底層可以識別的條件。
-
search 模組:主要負責對索引的搜尋工作。
-
similarity 模組:主要負責相關性打分和排序的實現
核心術語
下面介紹 Lucene 中的核心術語:
-
Term:是索引裡最小的儲存和查詢單元,對於英文來說一般是指一個單詞,對於中文來說一般是指一個分詞後的詞。
-
詞典(Term Dictionary,也叫作字典):是 Term 的集合。詞典的資料結構可以有很多種,每種都有自己的優缺點。
比如:排序陣列通過二分查詢來檢索資料:HashMap(雜湊表)比排序陣列的檢索速度更快,但是會浪費儲存空間。
FST(finite-state transducer)有更高的資料壓縮率和查詢效率,因為詞典是常駐記憶體的,而 FST 有很好的壓縮率,所以 FST 在 Lucene 的最新版本中有非常多的使用場景,也是預設的詞典資料結構。
-
倒排序(Posting List):一篇文章通常由多個片語成,倒排表記錄的是某個詞在哪些文章中出現過。
-
正向資訊:原始的文件資訊,可以用來做排序、聚合、展示等。
-
段(Segment):索引中最小的獨立儲存單元。一個索引檔案由一個或者多個段組成。在 Luence 中的段有不變性,也就是說段一旦生成,在其上只能有讀操作,不能有寫操作。
Lucene 的底層儲存格式如下圖所示,由詞典和倒排序兩部分組成,其中的詞典就是 Term 的集合:
詞典中的 Term 指向的文件連結串列的集合,叫做倒排表。詞典和倒排表是 Lucene 中很重要的兩種資料結構,是實現快速檢索的重要基石。
詞典和倒排表是分兩部分儲存的,在倒排序中不但儲存了文件編號,還儲存了詞頻等資訊。
在上圖所示的詞典部分包含三個詞條(Term):Elasticsearch、Lucene 和 Solr。詞典資料是查詢的入口,所以這部分資料是以 FST 的形式儲存在記憶體中的。
在倒排表中,“Lucene”指向有序連結串列 3,7,15,30,35,67,表示字串“Lucene”在文件編號為3、7、15、30、35、67的文章中出現過,Elasticsearch 和 Solr 同理。
檢索方式
在 Lucene 的查詢過程中的主要檢索方式有以下四種:
①單個詞查詢
指對一個 Term 進行查詢。比如,若要查詢包含字串“Lucene”的文件,則只需在詞典中找到 Term“Lucene”,再獲得在倒排表中對應的文件連結串列即可。
②AND
指對多個集合求交集。比如,若要查詢既包含字串“Lucene”又包含字串“Solr”的文件,則查詢步驟如下:
-
在詞典中找到 Term “Lucene”,得到“Lucene”對應的文件連結串列。
-
在詞典中找到 Term “Solr”,得到“Solr”對應的文件連結串列。
-
合併連結串列,對兩個文件連結串列做交集運算,合併後的結果既包含“Lucene”也包含“Solr”。
③OR
指多個集合求並集。比如,若要查詢包含字串“Luence”或者包含字串“Solr”的文件,則查詢步驟如下:
-
在詞典中找到 Term “Lucene”,得到“Lucene”對應的文件連結串列。
-
在詞典中找到 Term “Solr”,得到“Solr”對應的文件連結串列。
-
合併連結串列,對兩個文件連結串列做並集運算,合併後的結果包含“Lucene”或者包含“Solr”。
④NOT
指對多個集合求差集。比如,若要查詢包含字串“Solr”但不包含字串“Lucene”的文件,則查詢步驟如下:
-
在詞典中找到 Term “Lucene”,得到“Lucene”對應的文件連結串列。
-
在詞典中找到 Term “Solr”,得到“Solr”對應的文件連結串列。
-
合併連結串列,對兩個文件連結串列做差集運算,用包含“Solr”的文件集減去包含“Lucene”的文件集,運算後的結果就是包含“Solr”但不包含“Lucene”。
通過上述四種查詢方式,我們不難發現,由於 Lucene 是以倒排表的形式儲存的。
所以在 Lucene 的查詢過程中只需在詞典中找到這些 Term,根據 Term 獲得文件連結串列,然後根據具體的查詢條件對連結串列進行交、並、差等操作,就可以準確地查到我們想要的結果。
相對於在關係型資料庫中的“Like”查詢要做全表掃描來說,這種思路是非常高效的。
雖然在索引建立時要做很多工作,但這種一次生成、多次使用的思路也是非常高明的。
摘抄 石杉的架構筆記 錢丁君
本作品採用《CC 協議》,轉載必須註明作者和本文連結