術語解釋:
Lucene:是apache軟件基金會4 jakarta項目組的一個子項目,是一個開放源代碼的全文檢索引擎工具包,即它不是一個完整的全文檢索引擎,而是一個全文檢索引擎的架構,提供了完整的 查詢引擎和索引引擎,部分文本分析引擎(英文與德文兩種西方語言)。Lucene的目的是為軟件開發人員提供一個簡單易用的工具包,以方便的在目標系統中 實現全文檢索的功能,或者是以此為基礎建立起完整的全文檢索引擎。
二分查找: 二分查找又稱折半查找,優點是比較次數少,查找速度快,平均性能好;其缺點是要求待查表為有序表, 且插入刪除困難。因此,折半查找方法適用於不經常變動而查找頻繁的有序列表。首先,假設表中元素是按升序排列,將表中間位置記錄的關鍵字與查找關鍵字比 較,如果兩者相等,則查找成功;否則利用中間位置記錄將表分成前、后兩個子表,如果中間位置記錄的關鍵字大於查找關鍵字,則進一步查找前一子表,否則進一 步查找后一子表。重復以上過程,直到找到滿足條件的記錄,使查找成功,或直到子表不存在為止,此時查找不成功。
單機版的lucene只能應對千萬級,或百萬級的索引,通過修改索引,能夠支持10億以上索引的檢索。
當前lucene首次加載需要讀取整個索引文件,如果數據量較大,索引文件也很大,會照成內存瓶頸。
Lucene 使用倒排索引進行文檔的檢索,假設存在三個文檔
中華人民共和國
人民英雄
中華美食
Lucene在創建索引的的過程中,會將三個文檔按照分詞結果進行倒排,組成一個倒排表tis文件
中華 {1,3}
人民 {1,2}
共和國 {1}
英雄 {2}
美食 {3}
這樣當用戶搜索“中華”這個關鍵詞的時候,根據倒排 “中華 {1,3}”就可以知道在文檔1和3中含有此關鍵詞,文檔1和3就會返回給用戶。
我們通常將倒排表中的每個詞語叫做一個term,如果想知道,人民這個term對應那些文檔,必須先知道 人民這個term在倒排表中位置,然后才能知道這個term (人民)對應那些文檔({,2}),故首先要進行的是對term的查找,找到term所在的位置
以上述倒排表為例,假設,中華在倒排表中的偏移量為1,人民的偏移量為2,共和國為3,英雄為4,美食為5
這里的偏移量為距離文件起始的位置,知道了偏移量,就可以通過seek操作,將文件指針直接定位到目標term所在的位置,然后就可以讀取文檔ID,也就是查詢的結果了。
另外很重要的一點是,lucene創建索引的時候會保證倒排表的term是有序的。
Lucene采用128跳躍表的方式創建索引,原理如下
由於lucene索引本身是有序的特點,lucene會在索引文件里存儲一些關鍵term,
假設倒排表里一共有1280個term,那么第1個term,第129個term,257…,1281 一個11個關鍵term以及偏移量會存儲在索引文件tii中。
在進行term的檢索前,lucene會將tii文件中的所有關鍵term,加載到內 存里,以數組的形式存儲,當然也是有序的。當我們要檢索一個term,首先會在內存里檢索此term會落在那兩個關鍵term之間,因為有序的原因,目標 term肯定會在這兩個term之間,然后根據兩個關鍵term中較小的term的偏移量,從倒排表tis文件中的偏移量的位置之后開始查找,由於跳躍表 的間隔是128位,那么最多比較128次就可以查找到這個term了。
128跳躍表存在的問題
在進行term檢索前,索引文件tii要全部加載到內存里,如果term的數量比較 少,那么不會存在問題,但是如果term特別多,比如說10億,那么也要消耗幾十G的內存(視term的長度不同而不同),普通物理機器一般是沒有這么大 的內存的,所以導致lucene因程序崩潰而無法進行檢索。另外每次加載索引也是很消耗時間的,如果初級開發者,使用不當,每次都是打開與關閉 lucene,那就會導致索引文件被反復重復加載,也是影響檢索性能的。
那怎么解決?
Lucene倒排表tis文件有個顯著的特點是,term是有序的,而偏移量是定長的long類型,那么正好適合還用二分查找(折半查找)。
Tii索引文件存儲的內容修改為lucene倒排表tis中的每個term的偏移量。根據偏移量我們可以到tis文件中得到對應的term的值。
假設倒排表中一共有1000個term,我們的目標term在第501個term上, 那么二分查找首先會根據中間(折半)位置的term進行比較,也就是根第500個位置的term進行比較,發現目標term應該在500到1000這個區 間內,然后這個區間內在進行折半查找,定位在500~750這個區間,然后進一步定位500~625,500~564,500~532,500~516, 最終找到目標term的偏移量。
在這個過程中,索引文件沒有加載到內存里,對內存的依賴較少,假設有100億個term,那么最壞的情況,進行折半的次數為34次。
另外由於折半的特點,1/2,1/4,1/8,1/16…這些點都是高命中的點,可以 根據物理機器的內存多少,也可以預先加載到內存里,但是與128跳躍表相比,這里加載內存里的都是高命中的區域,對內存的使用率會高很多。如果緩存 16384個位置,那么久可以額外減少14次seek,那么100億僅僅需要20次的文件seek,但是如果跟原先的進行對比的話,舊的128跳躍表最壞 情況下需要128次seek,平均為64次seek,遠遠高於二分法的seek次數。而且二分法由於可以在高命中點使用cache,可以進一步減少 cache的數量。
存在問題以及細節的優化
隨着term數量的增加,折半查找引起的seek的次數會增加,10000個term要進行12次查找,10萬要進行15次,100萬進行20次,1000萬23次,一億26次,10億29次,100億34次。
2. Term壓縮方式由原先,存儲上一條記錄的差異,存儲關鍵點的差異(這樣會照成壓縮比降低,但是二分法必須這樣做)
3.如果索引二分查找文檔差異<128則,保留原先鏈表順序查找,調用scan方法(這樣做盡管讀的次數增多,但考慮磁盤的物理特點,操作系統通常有文件緩沖區,連續的數據讀取速度會比不斷的跳躍的seek快,物理硬盤適合讀取連續的數據),這樣可以用1~128此的連續seek,來減少約6~7次左右的跳躍性的seek.
4. 由於norms同樣非常消耗內存,這里創建索引的時候禁用norms,待以后改進此處,當前lucene也存在此問題,不過也可以采用同樣的二分法來解決此問題,禁止全部加載進內存。
1. Lucene TermInfosReader使用的128位的跳躍表,示例如下
在檢索的時候,跳躍表需要加載內存里,在千萬級別內的term,檢索速度比較理想(但是也需要1~128次的seek),但是如果term數量達到億的級別,有可能會突破單台機器物理內存的限制,目前業界幾乎都是采用分布式,打散成多個索引的方式來減少這個跳躍表的長度,但是單機也是能夠支持上十億key、value的檢索(這個地方可以采用定長的數據類型,而且luceneTermInfos這種結構,本身就是有序的,可以支持二分法查找,如果在結合cache,不但不會像跳躍表那樣耗費太多的內存,因減少了seek的數量,檢索時間也會有所提升,而且支持無限大的term數量,取決於硬盤大小)
當前lucene首次加載需要讀取整個索引文件,一般需要成長連接的方式,對開發者要求較高,采用二分法的索引文件就沒有此問題。
下表為對100W~10億條md5值進行創建索引以及查詢的情況
讀的時間為查詢10W條md5的時間,單位毫秒
寫為創建完整索引的時間,單位為毫秒。
記錄數 |
讀10W記錄時間 |
每條記錄時間 |
創建索引時間 |
索引總大小 |
Tii文件大小 |
|
一百萬 |
13667 |
0.13667 |
14338 |
87.6 MB |
7.62 MB |
|
二百萬 |
14400 |
0.144 |
25508 |
175 MB |
15.2 MB |
|
一千萬 |
20234 |
0.20234 |
120262 |
4.26 GB |
381 MB |
|
一億 |
2289399 |
22.89399 |
1360215 |
8.51 GB |
762 MB |
|
五億 |
3793413 |
37.93413 |
12249876 |
42.6 GB |
3.72 GB |
|
十億 |
5063614 |
50.63614 |
27365596 |
85.2 GB |
7.45 GB |
|
Lucene壓縮算法簡介
Lucene采用壓縮的方式進行創建索引
對於字符串類型
文件鏈表的第一條記錄存儲的是完整的信息,第二條記錄存儲的是跟第一條記錄的差異。
舉例來說,第一條記錄為 abcdefg,如果第二條記錄為abcdefh,他們的差異只有h,故第二條記錄僅僅會存儲一個長度,加上差異的字符,就是存儲6+h
對於lucene這種索引結構來說,由於索引是排序過的,故壓縮比非常可觀。
數值類型的
常見對於文件偏移量的壓縮,與字符串形式的壓縮類似,但采用的與前一條記錄的差值進行存儲,如果第一條記錄為8,第二條記錄為9,則第二條僅僅存儲9-8=1,,lucene存儲整形也是變長的索引,lucene通常是以append的方式創建索引,故這種壓縮方式很有效。
新索引對於壓縮的改變
由於上述lucene的 壓縮方式,在修改成二分法的時候,無法使用,故需要放棄一定的壓縮率。我們采取定義關鍵點的方式來壓縮,關鍵點存儲完整數據,后面的點跟關鍵點比較差異, 這個跟視頻圖像壓縮的關鍵幀非常相似,關鍵幀存儲完整的圖像信息,后面的幀僅僅存儲差異,變化。這樣可以節省計算差異的時間消耗(對lucene來說這點微不足道,不是這里解決的主要問題),但是壓縮比會降低。