劉靜
摘 要:索引是搜索引擎的核心概念,優(yōu)化索引提高使用效率是當(dāng)前主要研究?jī)?nèi)容。文中研究了Lucene索引文件的內(nèi)部結(jié)構(gòu),包括Lucene索引文件格式、文件組成、索引創(chuàng)建過程,并重點(diǎn)研究了段Segment文件的存儲(chǔ)結(jié)構(gòu)。經(jīng)研究表明,創(chuàng)建單一且重用的文檔實(shí)例以及提高使用的內(nèi)存大小可有效提高索引使用效率。
關(guān)鍵詞:索引;Lucene;搜索引擎
中圖分類號(hào):TP393 文獻(xiàn)標(biāo)識(shí)碼:A
1 引言(Introduction)
在Lucence中包括了幾個(gè)基礎(chǔ)的概念,分別是索引、段、文檔、域和項(xiàng)。其中索引由段構(gòu)成,段由文檔構(gòu)成,因此索引可以理解為包含了多個(gè)文檔的序列。文檔由域構(gòu)成,域由項(xiàng)構(gòu)成,項(xiàng)是索引中最小構(gòu)成單位,其本質(zhì)是一個(gè)字符串。段是索引數(shù)據(jù)存儲(chǔ)的基本單元,多個(gè)段之間彼此獨(dú)立,當(dāng)添加新的文檔時(shí)將生成新的段,且段可以合并。
充分理解Lucence索引的內(nèi)部結(jié)構(gòu),對(duì)于在當(dāng)前互聯(lián)網(wǎng)大數(shù)據(jù)、云存儲(chǔ)應(yīng)用環(huán)境下較好使用Lucence進(jìn)行具有重要意義。
2 索引文件結(jié)構(gòu)(Index file structure)
2.1 倒排索引
對(duì)文本信息進(jìn)行檢查的系統(tǒng)索引可以分為包括正排索引和倒排索引[1]。在實(shí)際使用中根據(jù)屬性值查找記錄的過程稱為倒排索引。倒排表中存儲(chǔ)了文檔中包含的單詞,文檔編號(hào)差值、單詞出現(xiàn)次數(shù)等,這些信息即組成了倒排索引項(xiàng)term。
倒排索引入口為索引項(xiàng)term,通過倒排索引可以找到包含每個(gè)term的文檔集合稱為記錄表,記錄表中包含文檔號(hào)及term在該文檔中的出現(xiàn)頻率。
Lucene為了使得基于term的檢索效率更高,索引存儲(chǔ)terms的統(tǒng)計(jì)數(shù)據(jù)。Lucene的索引采用了倒排索引。它可以列舉,包含一個(gè)term的所有文檔。這與自然關(guān)聯(lián)規(guī)則相反,即由documents列舉它所包含的terms。
2.2 域Fields
Lucene在存儲(chǔ)域數(shù)據(jù)時(shí),域中文本被逐字的以非倒轉(zhuǎn)方式進(jìn)行轉(zhuǎn)換,這些被倒排存儲(chǔ)的域數(shù)據(jù)可以被索引[2]。每個(gè)域的文本被拆分為多個(gè)索引項(xiàng)以便被高效索引,另一種方式為域中文本作為一個(gè)索引項(xiàng)進(jìn)行索引。
2.3 片斷
Lucene索引可以由多個(gè)復(fù)合的子索引或片段構(gòu)成。每個(gè)段是一個(gè)完全獨(dú)立的索引,它可以分離提取進(jìn)行檢索。檢索過程如下:(1)當(dāng)添加新的文檔時(shí)創(chuàng)建新的段;(2)對(duì)現(xiàn)有段進(jìn)行合并。在索引檢索時(shí),會(huì)涉及多個(gè)索引或段,因此每一個(gè)索引都隱含包括了一組段。段及文檔關(guān)系如圖1所示。
圖1 Lucene索引片段
Fig.1 The Lucene index fragments
2.4 文檔編號(hào)
在Lucene的內(nèi)部對(duì)于每個(gè)文檔使用一個(gè)整數(shù)編號(hào)進(jìn)行標(biāo)識(shí)。初始情況下,第一個(gè)被加進(jìn)來的文檔其文檔編號(hào)為0,之后加進(jìn)來的文檔其編號(hào)自動(dòng)遞增,采用自動(dòng)遞增方式對(duì)文檔進(jìn)行編號(hào),編號(hào)不會(huì)出現(xiàn)重復(fù)。但需注意的是,文檔編號(hào)可以被手動(dòng)更改。因此在維護(hù)時(shí)若出現(xiàn)更改情況,需特別考慮此編號(hào)是否在更大的范圍內(nèi)被使用。通常的做法是為每個(gè)段空間設(shè)置文檔編號(hào)的范圍,以保證段之間的文檔編號(hào)不重復(fù)。
綜上所述,文檔編號(hào)若發(fā)生修改可能導(dǎo)致錯(cuò)誤,因此文檔編號(hào)應(yīng)先進(jìn)行設(shè)計(jì),在應(yīng)用時(shí)不輕易、不頻繁修改文檔編號(hào),只有當(dāng)出現(xiàn)文檔在某段空間是統(tǒng)一的,且需要在整個(gè)系統(tǒng)中使用時(shí)必須修改情況下才進(jìn)行修改。
3 索引文件(Index file)
3.1 索引文件
Lucene索引文件包含多種,其使用不同的文件擴(kuò)展名來進(jìn)行標(biāo)識(shí)。同時(shí)文件名稱可以標(biāo)識(shí)不同的版本。常見的文件擴(kuò)展名有:fnm文件存儲(chǔ)域名和域?qū)傩浴dt文件存儲(chǔ)域數(shù)據(jù)、fdx文件存儲(chǔ)在fdt文件中的偏移位置、frq存儲(chǔ)文件中項(xiàng)的位置數(shù)據(jù)等。對(duì)于段文件命名通常為segments_x,其中x即為最新修改版本,文件segments.gen存儲(chǔ)當(dāng)前版本值。以下文件存在于每個(gè)索引index中,并且只有一份。
(1)Segments文件
索引中活動(dòng)的Segments被存儲(chǔ)在segment info文件中,segments_N,在索引中可能會(huì)包含一個(gè)或多個(gè)segments_N文件。然而,最大一代的那個(gè)文件是活動(dòng)的片斷文件(這時(shí)更舊的segments_N文件依然存在是因?yàn)樗鼈儠簳r(shí)還不能被刪除,或者,一個(gè)writer正在處理提交請(qǐng)求,或者一個(gè)用戶定義的IndexDeletionPolicy正被使用。這個(gè)文件按照名稱列舉每一個(gè)片斷,詳細(xì)描述分離的標(biāo)準(zhǔn)和要?jiǎng)h除的文件,并且還包含了每一個(gè)片斷的大小。
(2)Lock文件
寫鎖文件名為“write.lock”,存儲(chǔ)在默認(rèn)的索引目錄中。如果鎖目錄和索引目錄不一致的,寫鎖將被命名為“XXXX-write.lock”,其中“XXXX”是一個(gè)特殊唯一的前綴,來源于索引目錄的路徑。
(3)Deletable文件
Lucene的刪除機(jī)制類似于操作系統(tǒng)回收站,在執(zhí)行刪除操作時(shí),相當(dāng)于對(duì)索引進(jìn)行了“刪除標(biāo)記”并未真實(shí)刪除,當(dāng)索引下次優(yōu)化或合并時(shí)再從索引中真正刪除。
(4)Compound文件
Compound文件格式成一個(gè)簡(jiǎn)單的容器,其用來服務(wù)所有Segment包含的文件(除了.del文件)。
3.2 Segment
每個(gè)段中的文件是由后綴名來區(qū)分的。
(1)域集合信息
域的信息以集合形式存儲(chǔ),域集合文件后綴為.fnm。在當(dāng)前的情況下,fieldbits僅用于低位,索引的域值是1,未索引的域值為0,文件中各域根據(jù)次序進(jìn)行編號(hào),即認(rèn)為值0是第一個(gè)域文件,值1是下一個(gè)域文件等。Fields將使用它們?cè)谶@個(gè)文件中的順序來編號(hào)。需要注意的是,就像文檔編號(hào)一樣,field編號(hào)與片斷是相關(guān)的。
(2)域索引和域值
域值存儲(chǔ)表使用兩種文件存儲(chǔ),分別為:域索引表(.fdx文件)文件,這個(gè)文件包含指向域值的指針FieldvaluesPosition,類型為UInt64類型,此指針指向了某域值在域文件中的位置。因?yàn)橛蛑滴募ǘㄩL(zhǎng)的數(shù)據(jù)信息,較易隨機(jī)訪問;域值(.fdt文件)文件,每個(gè)文檔的域值信息包括了字段FieldData、DocFieldData、FieldCount、FieldNum、Bits和value。
(3)項(xiàng)字典
項(xiàng)Term字典使用如下兩種文件存儲(chǔ),第一種是存儲(chǔ)term信息(TermInfoFile)的文件,即.tis文件,另一種是存儲(chǔ)term信息索引的文件,即.tii文件。TermInfoFile文件按照Term來排序,排序方法首先按照Term的field名稱(按照UTF-16字符編碼)排序,然后按照Term的Text字符串(UTF-16編碼)排序。項(xiàng)的命名上前綴通常上是相同的,然后以字的后綴組成。
(4)項(xiàng)頻率數(shù)據(jù)
Term頻率數(shù)據(jù)文件(.frq文件)存儲(chǔ)容納了每一個(gè)term的文檔列表,以及該term出現(xiàn)在該文檔中的頻率(出現(xiàn)次數(shù)frequency,如果omitTf設(shè)置為fals時(shí)才存儲(chǔ))。
關(guān)于skip levels,每一個(gè)term可以有多個(gè)skip levels。一個(gè)term的skip levels的數(shù)目等于NumSkipLevels=Min(MaxSkipLevels,floor(log(DocFreq/log(SkipInterval))))。對(duì)一個(gè)skip level來說SkipData記錄的數(shù)目等于DocFreq/(SkipInterval^(Level+1))。然而最低的skip level等于Level=0。例如假設(shè)SkipInterval=4, MaxSkipLevels=2,DocFreq=35,則skip level 0有8個(gè)SkipData記錄,在TermFreqs序列中包含第3、7、11、15、19、23、27和31個(gè)文檔的編號(hào)。Skip level 1則有兩個(gè)SkipData記錄,在TermFreqs中包含了第15和第31個(gè)文檔的編號(hào)。在所有l(wèi)evel>0之上的SkipData記錄中包含一個(gè)SkipChildLevelPointer,指向(referencing)level-1中相應(yīng))的SkipData記錄。在這個(gè)例子中,level 1中的記錄15有一個(gè)指針指向level 0中的記錄15,level 1中的記錄31有一個(gè)指針指向level 0中的記錄31。
(5)項(xiàng)位置
Positions位置信息數(shù)據(jù)文件(.prx文件)容納了每一個(gè)term出現(xiàn)在所有文檔中的位置的列表,可以利用這些信息來參與對(duì)索引結(jié)果的排序。如果在fields中的omitTf設(shè)置為true時(shí)將不會(huì)在此文件中存儲(chǔ)任何信息,并且如果索引中所有fields中的omitTf都設(shè)置為true,此.prx文件將不會(huì)存在。
(6)標(biāo)準(zhǔn)化因子文件
在Lucene 2.1版本之前,每一個(gè)索引都有一個(gè)norm文件給每一個(gè)文檔都保存了一個(gè)字節(jié)。對(duì)每一個(gè)文檔來說,那些.f[0-9]包含了一個(gè)字節(jié)容納一個(gè)被編碼的分?jǐn)?shù),值為對(duì)hits結(jié)果集來說在那個(gè)field中被相乘得出的分?jǐn)?shù)。每一個(gè)分離的norm文件在適當(dāng)?shù)臅r(shí)候?yàn)閺?fù)合的和非復(fù)合的segment片斷創(chuàng)建。在Lucene 2.1及以上版本,只有一個(gè)norm文件容納了所有norms數(shù)據(jù):一個(gè)分離的norm文件在一個(gè)存在的segment的norm數(shù)據(jù)被更改的時(shí)候被創(chuàng)建,當(dāng)field N被修改時(shí),一個(gè)分離的norm文件.sN被創(chuàng)建,用來維護(hù)該field的norm數(shù)據(jù)。
(7)項(xiàng)向量文件
Term向量的支持是field基本組成中對(duì)一個(gè)field來說的可選項(xiàng),它包含如下三種文件:a.文檔索引或.tvx文件:對(duì)每個(gè)文檔來說,它把偏移存儲(chǔ)進(jìn)文檔數(shù)據(jù)(.tvd)文件和域field數(shù)據(jù)(.tvf)文件。b.文檔或.tvd文件:對(duì)每個(gè)文檔來說,它包含fields的數(shù)目,有term向量的fields的列表,還有指向term向量域文件(.tvf)中的域信息的指針列表。該文件用于映射出那些存儲(chǔ)了term向量的fields,以及這些field信息在.tvf文件中的位置。c.域field或.tvf文件:對(duì)每個(gè)存儲(chǔ)了term向量的field來說,該文件包含了一個(gè)term的列表,及它們的頻率,還有可選的位置和偏移信息。
(8)刪除的文檔
刪除的文檔(.del)文件是可選的,而且僅當(dāng)一個(gè)segment存在有被刪除的文檔時(shí)才存在。即使對(duì)每一單個(gè)segment,它也是維護(hù)復(fù)合segment的外部數(shù)據(jù)。
4 索引創(chuàng)建過程(The index creation process)
為了使用Lucene的索引數(shù)據(jù),必須先將其轉(zhuǎn)換為文本標(biāo)記的數(shù)據(jù)流,并用它來創(chuàng)建一個(gè)文檔對(duì)象,其中包含的域成員來容納這些文本數(shù)據(jù)[3,4]。一旦你準(zhǔn)備的文檔對(duì)象,可以調(diào)用IndexWriter類的adddocument方法傳遞對(duì)象到Lucene寫索引。當(dāng)這樣做時(shí)Lucene進(jìn)行分析數(shù)據(jù),以便更適合索引。
文檔的索引過程是通過DocumentsWriter的內(nèi)部數(shù)據(jù)處理鏈完成的,DocumentsWriter可以實(shí)現(xiàn)同時(shí)添加多個(gè)文檔并將它們寫入一個(gè)臨時(shí)的segment中,完成后再由IndexWriter和SegmentMerger合并到統(tǒng)一的segment中去。DocumentsWriter支持多線程處理,即多個(gè)線程同時(shí)添加文檔,它會(huì)為每個(gè)請(qǐng)求分配一個(gè)DocumentsWriterThreadState對(duì)象來監(jiān)控此處理過程。處理時(shí)通過DocumentsWriter初始化時(shí)建立的DocFieldProcessor管理的索引處理鏈來完成的,依次處理為DocFieldConsumers、DocInverter、TermsHash、FreqProxTermsWriter、TermVectorsTermsWriter、NormsWriter以及StoredFieldsWriter等。
DocFieldProcessorPerThread.processDocument()方法是處理一個(gè)文檔的調(diào)度函數(shù),負(fù)責(zé)整理文檔的各個(gè)fields數(shù)據(jù),并創(chuàng)建相應(yīng)的DocFieldProcessorPerField對(duì)象來依次處理每一個(gè)field。該方法首先調(diào)用索引鏈表的startDocument()來初始化各項(xiàng)數(shù)據(jù),然后依次遍歷每一個(gè)fields,將它們建立一個(gè)以field名字計(jì)算的hash值為key的hash表,值為DocFieldProcessorPerField類型。如果hash表中已存在該field,則更新該FieldInfo(調(diào)用FieldInfo.update()方法),如果不存在則創(chuàng)建一個(gè)新的DocFieldProcessorPerField來加入hash表中。該hash表會(huì)存儲(chǔ)包括當(dāng)前添加文檔的所有文檔的fields信息,并根據(jù)FieldInfo.update()來合并相同field名字的域設(shè)置信息。
建立hash表的同時(shí),生成針對(duì)該文檔的fields[]數(shù)組(只包含該文檔的fields,但會(huì)共用相同的fields數(shù)組,通過lastGen來控制當(dāng)前文檔),如果field名字相同,則將Field添加到DocFieldProcessorPerField中的fields數(shù)組中。建立完fields后再將此fields數(shù)組按field名字排序,使得寫入的vectors等數(shù)據(jù)也按此順序排序。之后開始正式的文檔處理,通過遍歷fields數(shù)組依次調(diào)用DocFieldProcessorPerField的processFields()方法進(jìn)行,完成后調(diào)用finishDocument()完成后序工作,如寫入FieldInfos等。
5 結(jié)論(Conclusion)
Lucene自發(fā)布至今已有近15年時(shí)間,近幾年互聯(lián)網(wǎng)的快速發(fā)展更使得產(chǎn)生的數(shù)據(jù)呈指數(shù)級(jí)增長(zhǎng),Lucene面對(duì)海量數(shù)據(jù)進(jìn)行處理,因此在檢索時(shí)針對(duì)索引的優(yōu)化至關(guān)重要。牢固掌握Lucene索引原理對(duì)于解決當(dāng)前大數(shù)據(jù)環(huán)境下數(shù)據(jù)檢索以及提高檢索效率打下良好基礎(chǔ)。
參考文獻(xiàn)(References)
[1] 王冬.一種增量倒排索引結(jié)構(gòu)的設(shè)計(jì)與實(shí)現(xiàn)[J].吉林大學(xué)學(xué) 報(bào),2007,45(06):953-958.
[2] 黃軼文.搜索引擎原理與快速開發(fā)應(yīng)用[J].科技信息,2010, (36):570-571.
[3] 何偉,等.基于Lucene的全文搜索引擎的設(shè)計(jì)與實(shí)現(xiàn)[J].2006, 25(9):88-90.
[4] 李曉麗,杜振龍.基于Lucence的個(gè)性化搜索引擎研究[J].計(jì) 算機(jī)工程,2010,36(19):258-260.
作者簡(jiǎn)介:
劉 靜(1982-),女,碩士,講師.研究領(lǐng)域:計(jì)算機(jī)應(yīng)用 教學(xué).