1. 程式人生 > >通過Lucene索引檔案學習Lucene索引過程

通過Lucene索引檔案學習Lucene索引過程

#DocPosition     記錄文件在fdx的起始地址,這樣通過改地址能夠直接定位到文件

(4)fdx
圖片

#FORMAT          整型,4個位元組,3.6.2常量為3,對應00 00 00 03
#
DocPosition      每個文件對應一個開始地址,一共有3個文件,每個開始地址佔用一個Long即8個位元組

(5)fdt,fdx建立資料物件呼叫關係 
圖片 
這個過程只是組裝正向資訊的過程,此時,還有沒重新整理到磁碟上

(6)fdt,fdx重新整理磁碟物件呼叫關係
圖片


2.tis,tii
tis,tii儲存(詞Term)反向索引資訊,tis儲存詞的資料資訊,tii儲存詞的索引(位置)資訊
tis資料資訊在addDocument已經完成最重要的方向索引結構,在往檔案上刷的時候只是重新排序和合並不同域資訊等處理。 IndexWriter.addDocument       |      ---DocumentsWriter.updateDocument              |            ----DocumentsWriterThreadState                           |                        ------DocFieldProcessorPerThread.processDocument                                        |                                     ------DocFieldProcessorPerField.processFields 這個方法用於組裝最重要的方向索引結構,這裡有幾點需要了解的地方 #Lucene的反向索引並不傳說中的連結串列的方式儲存的,而是用幾個陣列及輔助資訊儲存的。 #該方法是以域為單位分詞的,也就是說相同的詞在不同的域是分別儲存的,在flush的時候才做合併處理。 反向資訊儲存資料結構是有主儲存結構和輔助結構共同完成的 #主儲存結構有三個陣列,分別是bytePool,charPool,intPool charPool 儲存分詞後的Term資料資訊 bytePool 儲存Term的詞頻及位置資訊,初始化每個詞有10個位元組空間,前5個位元組儲存詞頻資訊,後5個位元組儲存詞位置資訊 intPool和bytePool是配合使用的,每個詞在intPool分配兩個位元組,用於指向bytePool的儲存詞頻和位置資訊的地址, #輔助結構有postingsHash,postingsArray,fieldState等構成. postingsHash 記錄詞的是否出現過, postingsArray 記錄當前此在charPool,bytePool,intPool的位置等資訊 fieldState  記錄當前域的最大詞頻、當前詞頻,該詞在域的postion和offset資訊 通過上面一系列物件的組合就得到了反向索引的初步結構. 通過一個例項展示lucene反向索引是如何儲存的,例如對下面兩條記錄進行索引 id          name            syno 5159   依雲50ml     evian依雲,evian 5160   依雲150ml    evian依雲,evian 讀入5159,個物件變化如下: 首先用postionsHash判斷該詞在該域上是否儲存過,5159沒有儲存過 charPool     5,1,5,9, ,                                   ?? 5169後面跟一個佔位符,用於詞的分隔 bytePool     0,0,0,0,16,0,0,0,0,16                        ?? 前五個位元組儲存該詞的詞頻,第一個位元組儲存這個詞第一次出現的詞頻,                                                              因為只有下一個域也hash到這個詞,才能+1,目前是0,第二組5個位元組用於儲存詞在該域的位置。 intPool      0,6                                          ?? 0指向該詞在byte寫入的位置,因為5159詞頻沒有發生變化,所以第一個位元組為0,                                                              5159這個詞已經儲存在bytePool[5]的位置上,所以下一個寫入的位置+1變成6                                                              位置儲存的計算是位運算,例如第一個位置0<<1等於0,第二個位置1<<1等於2,以此類推。 通過StandardAnalyzer分析name域 讀取"依" charPool     5,1,5,9, ,依, ,                                    bytePool     0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,2,0,0,0,16                         intPool      0,6,10,16 讀取"雲" charPool     5,1,5,9, ,依, ,雲, ,                                    bytePool     0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,4,0,0,0,16                         intPool      0,6,10,16,20,26 讀取"50ml"    charPool     5,1,5,9, ,依, ,雲, ,5,0,m,l, , bytePool     0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,8,0,0,0,16                         intPool      0,6,10,16,20,26,30,36 通過StandardAnalyzer分析syno域 讀取"evian" charPool     5,1,5,9, ,依, ,雲, ,5,0,m,l, ,e,v,i,a,n, ,                                    bytePool     0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,8,0,0,0,16,0,0,0,0,16,0,0,0,0,16                         intPool      0,6,10,16,20,26,30,36,40,46 讀取"依" charPool     5,1,5,9, ,依, ,雲, ,5,0,m,l, ,e,v,i,a,n, ,依, ,          ??注意,目前相同詞在不同域是獨立儲存的 bytePool     0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,8,0,0,0,16,0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,2,0,0,0,16                         intPool      0,6,10,16,20,26,30,36,40,46,50,56 讀取"雲" charPool     5,1,5,9, ,依, ,雲, ,5,0,m,l, ,e,v,i,a,n, ,依, ,雲, ,                                    bytePool     0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,8,0,0,0,16,0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,2,0,0,0,16,0,0,0,0,16,4,0,0,0,16             intPool      0,6,10,16,20,26,30,36,40,46,50,56,60,66 讀取"evian",這裡變化很關鍵 通過postionsHash發現該域evian儲存過,獲得postingsArray定位到intPool位置,不在寫入evian,intPool[9]++變成了47 charPool     5,1,5,9, ,依, ,雲, ,5,0,m,l, ,e,v,i,a,n, ,依, ,雲, ,                                    bytePool     0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,8,0,0,0,16,0,0,0,0,16,0,2,0,0,16,0,0,0,0,16,2,0,0,0,16,0,0,0,0,16,4,0,0,0,16             intPool      0,6,10,16,20,26,30,36,40,47,50,56,60,66 讀取"5160" charPool     5,1,5,9, ,依, ,雲, ,5,0,m,l, ,e,v,i,a,n, ,依, ,雲, ,5,1,6,0 bytePool      0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,8,0,0,0,16,0,0,0,0,16,0,2,0,0,16,0,0,0,0,16,2,0,0,0,16,0,0,0,0,16,4,0,0,0,16,0,0,0,0,16,0,0,0,0,16       intPool      0,6,10,16,20,26,30,36,40,47,50,56,60,66,70,76 讀取"依" 通過postingsHash得知該詞在該域儲存過,charPool不需要新增新詞,通過postingsArray定位到intPool位置,將詞頻加1,將postion+=1 charPool     5,1,5,9, ,依, ,雲, ,5,0,m,l, ,e,v,i,a,n, ,依, ,雲, ,5,1,6,0 bytePool      0,0,0,0,16,0,0,0,0,16,1,0,0,0,16,0,2,0,0,16,0,0,0,0,16,0,0,0,0,16,0,0,0,0,16,8,0,0,0,16,0,0,0,0,16,0,2,0,0,16,0,0,0,0,16,2,0,0,0,16,0,0,0,0,16,4,0,0,0,16,0,0,0,0,16,0,0,0,0,16 intPool      0,6,11,17,20,26,30,36,40,47,50,56,60,66,70,76 下面依次類推 有一種情況,就是如果一個詞在域出現的次數大於4次,儲存空間就不夠了,這有一個跳躍表的概念。 &&&至此,反向索引資訊已經初步完成構建。 在寫入tis和tii資訊之前,會先把詞頻(frq)和詞位置(prx)寫入到檔案中 (1)frq儲存term的文件id及詞頻資訊,這裡有兩點需要注意 #frq是以域順序儲存的,例如會先儲存id域所有詞的詞頻,才會儲存name域的詞頻 #文件每個域id是以差值規則儲存的,例如name域有一個term,分別在文件0,1,2出現過1次,Lucene儲存文件id的值分別是0<<1|1 ,1<<1|1 2<<1|1,分別儲存的是1,3,5  如果詞頻大於1,那麼文件id儲存規則為id<<1,並且還有一個位元組儲存頻率 id                syno 5159       evian依雲,evian 5160       evian依雲,evian 例如上面的資訊,frq儲存過程如下: #讀入5159,  文件id=0, 0<<1|1 = 1, frq檔案儲存01 #讀取5160, 文件id=1, (1-0)<<1|1 = 3  儲存03 #讀取evian  此時"evian"在文件id=0出現2次,文件id=1出現2次,儲存00 02 01 02 #讀取"依"   此時"依"在文件id=0出現1次,文件id=1出現1次,    儲存01 03 #讀取"雲"   此時"依"在文件id=0出現1次,文件id=1出現1次,    儲存01 03 在lucene儲存詞的順序的時候會通過hash的方式儲存到陣列的位置,上面的儲存方法是正確的,詞的順序會有所不同 (2)prx儲存詞的位置資訊 通frq一樣,prx是以域順序儲存的,例如會先儲存id域所有詞的詞頻,才會儲存name域的詞頻 id                syno 5159       evian依雲,evian 5160       evian依雲,evian 例如上面的資訊,prx儲存過程如下: 讀取5159  在文件id=0在第0個位置,prx檔案儲存00 讀取5160  在文件id=1在第0個位置,prx檔案儲存00 讀取evail  在文件id=0,的第一個位置和第三個位置 在id=1第一個位置和第三個位置,所以儲存00 03 00 03 讀取"依"  在文件id=0的第一個位置和id=1的第一個位置,所以儲存01 01 讀取"雲"  在文件id=0的第二個位置和id=1的第二個位置,所以儲存02 02 在IndexWriter.flush方法分別依次往磁碟刷入frq,prx,tis,tii資訊 tis格式如下:    FORMAT    ---   TermCount     ---    indexInterval   ---   skipInterval   ---   maxSkipLevels  ---   TermInfo ...    (Int)             (Long)                 (Int)                 (Int)                 (Int)           --------   |      StartLength   ---    Length   ---    DataBytes   ---    fieldNumber   ---    docFreq    ---  freqPointer  ---  proxPointer                                                                                                                         (VInt)           (VInt)           (bytes)              (VInt)            (VInt)           (VLong)              (VLong)  @FORMAT               當前.tis檔案版本號 3.6.2(-4) @TermCount            詞個數,初次寫入0佔位,在關閉檔案的時候,才寫入真實的個數 @indexInterval        索引間隔(128) @skipInterval         跳過間隔(16) @maxSkipLevels        最大跳躍層數(10) @TermInfo             詞資訊 @#StartLength       開始長度         @#Length            詞所佔的位元組數 @#DataBytes         詞資料資訊 @#fieldNumber       域序號 @#docFreq           該詞在文件出現的頻率 @#freqPointer       詞頻指標,儲存該詞頻指標和最後一個詞頻指標之差         @#proxPointer       位置指標,儲存該詞位置指標和最後一個詞位置指標之差         @#skipOffset        可選項,如果詞頻大於skipInterval,則該位記錄位置偏移 tii格式如下    FORMAT    ---   TermIndexCount   ---    indexInterval   ---   skipInterval   ---   maxSkipLevels  ---   TermIndexInfo ...    (Int)             (Long)                 (Int)                 (Int)                 (Int)                --------        |      StartLength   ---    Length   ---    DataBytes   ---    fieldNumber   ---    docFreq    ---  freqPointer   ---   proxPointer   ---  filePointer                                                                                                                           (VInt)           (VInt)           (bytes)              (VInt)            (VInt)           (VLong)                 (VLong)             (VLong) @FORMAT               當前.tis檔案版本號 3.6.2(-4) @TermIndexCount       詞索引個數,初次寫入0佔位,在關閉檔案的時候,才寫入真實的個數 @indexInterval        索引間隔 預設(128) @skipInterval         跳過間隔 預設(16) @maxSkipLevels        最大跳躍層數 預設(10) @TermInfo             詞資訊 @#StartLength       開始長度         @#Length            詞所佔的位元組數 @#DataBytes         詞資料資訊 @#fieldNumber       域序號 @#docFreq           該詞在文件出現的頻率 @#freqPointer       詞頻指標,儲存該詞頻指標和最後一個詞頻指標之差         @#proxPointer       位置指標,儲存該詞位置指標和最後一個詞位置指標之差         @#filePointer       域索引檔案指標 frq,prx,tis,tii儲存流程 IndexWriter.doflush      |    -----DocumentWriter.doFlush              |            ----DocFieldProcessor.flush                      |                    -----DocInverter.flush                              |                            -----TermHash.flush                                    |                                  ----- FreqProxTermsWriter.appendPostings  在這個方法依次完成frq,prx,tii,tis寫入。

3.nrm
nrm檔案儲存著所有文件每個域的評分因子,這個因子的演算法需要一些預知識,這裡面評分因子的演算法在評分那篇日誌會細說
.nrm檔案儲存結構:  FORMAT   ----- perFieldNorm   (Int)             (byte) @FORMAT               nrm版本號,預設{'N','R','M',-1} @perFieldNorm         文件域標準化因子的值 .nrm儲存流程 IndexWriter.doflush      |    -----DocumentWriter.doFlush              |            ----DocFieldProcessor.flush                      |                    -----DocInverter.flush                             |                           ----InvertedDocEndConsumer.flush

4.fnm
儲存了段的一些資料資訊,例如一個段(segement)包含多個域(field),每個域都包含著一些元資料資訊,儲存在.fnm檔案中,.fnm檔案格式如下:  FORMAT  ---   FIELDSIZE  ----  fieldInfo ..  (VInt)         (VInt)            -----    |     fileldName   ----  fieldAttribute                                           (String)           (byte)                                                              ------    |          --                               --                  --                         --                       --       --         --               --                                                                         (如果索引文件及詞頻)         (如果索引文件)         (儲存有效負載Payloads)       (omitNorms標準化因子)                       (是否儲存詞向量)  (是否被索引)      
@FORMAT           FNM版本號  -3(3.6.2) @FIELDSIZE        域數量 @fieldInfo        域欄位資訊     @#fieldName         域名稱     @#fieldAttribute    域屬性欄位,一個位元組,8位,每位代表不同的屬性含義,注意:只用了6位

5.segments_n
segments_n儲存段的元資料資訊
FORMAT  --  VERSION  --  counter   --segSize ---   SegmentInfo ..   ---   userData            ---  digest (int)       (long)        (int)       (int)           ----             (stirngstringMap)         (Long)        |    version   ---  name     ----  docCount  ---- delGen     ----  docStoreOffset ----hasSingleNormFile --   normGen     ----  isCompoundFile  ---   delCount  ---  hasProx  ---   diagnostics        --- hasVectors                                                                        (String)      (String)          (Int)         (Long)              (Int)                 (byte)         (Int)|變長              (byte)             (int)         (byte)       (stirngstringmap)          (byte) @FORMAT        索引格式版本號  -11(3.6.2) @VERSION       索引版本(取當前時間) @counter       下一個新段的名稱,例如.fdt,.fdx.. @segSize       段大小,例如0.fnm,1.fnm,段大小為2 @SegmentInfo   段資訊,如果有多個段,則有多個段資訊    #@version   lucene版本號"3.6.2"     #@name      段名稱,例如第一個段"_0"    #@docCount  該段包含的文件數    #@delGen    #@docStoreOffset      如果為-1,則此段獨立儲存域資訊和詞向量,檔案為0.fdt,0.fdx,如果不為-1,則域資訊和詞向量則記錄的偏移量    #@hasSingleNormFile   如果為1,則搜尋評分因子則存在.nrm檔案中,否則每個域則單獨儲存在.fN檔案中    #@normGen    #@isCompoundFile      是否為複合檔案    #@delCount            刪除文件數量    #@hasProx             是否儲存詞頻和位置資訊    #@diagnostics         作業系統資訊&jvm版本等資訊    #@hasVectors @userData     使用者設定資訊 @digest       摘要

6.segments.gen
是一個輔助檔案,記錄當前段的增長編號的