lucene7.5的数据结构
文章目錄
- 2. lucene的數據結構
- 2.1 索引的邏輯層次
- 2.2 lucene 存儲的數據概述
- 2.2.1 lucene的存儲模塊
- 2.2.2 lucene的一個index的文件
- 2.2.3 lucene 的數據類型
- 2.2.4 ducoment number
- 2.3 正向信息的存儲
- 2.3.1 .fnm 文件
- 2.3.1.1 一級數據
- 2.3.1.2 二級數據Field
- 2.3.2 XXX.fdx,XXX.fdt文件
- 2.3.2.1 .fdt 文件
- 2.3.2.2 .fdx 文件
- 2.3.3 XXX.tvx,XXX.tvd
- 2.3.4 .nvd,.nvm
- 2.3.4.1 nvd中到底存儲了什么
- 2.3.4.2 .nvd文件
- 2.3.4.3 .nvm文件
- 2.3.4.4 根據DocId查找norm的過程簡述
- 2.4 反向索引信息的存儲
- 2.4.1 XXX.tim,XXX.tip
- 2.4.1.1 XXX.tip
- 2.4.1.1 XXX.tim
- 2.4.2 XXX.doc
- 2.4.3 XXX.pos文件
- 2.4.4 XXX.pay文件
- 2.4.5 在倒排中的查詢邏輯
- 2.5 docvalues信息的存儲 .dvd .dvm
- 2.6 多維數值類信息point的存儲 .dim .dii
- 待處理問題
2. lucene的數據結構
2.1 索引的邏輯層次
在lucene中索引存儲的邏輯層次有多個層次,從大到小依次是
2.2 lucene 存儲的數據概述
2.2.1 lucene的存儲模塊
在lucene中,因為lucene是一個存儲系統,所以他的首要任務是以一定的數據結構將輸入進來的數據(doc)保存起來,在檢索的時候才能將這些信息返回給用戶
同時,為了能夠支撐高效的檢索,還要有額外的數據結構來支撐高效檢索
為了支撐高效檢索,lucene創造了倒排索引,同時,為了能夠滿足更加復雜的查詢,比如按照某一個字段進行排序等功能,lucene又在3.x引入了docvalues 正排索引的概念,為了支持數值查詢或者地理位置查詢等多維度的查詢,lucene使用了BKD-Tree,來支撐數值型的range查詢。
綜上,目前lucene的數據結構模塊包括
2.2.2 lucene的一個index的文件
基于lucene7.5 打開對應的文件夾,看看lucene的一個索引中包含哪些文件
| Segments File | segments_N | Stores information about a commit point | 存儲一次commit point信息 |
| Lock File | write.lock | The Write lock prevents multiple IndexWriters from writing to the same file. | 寫鎖,防止多個indxer-writer寫同一個文件 |
| Segment Info | .si | Stores metadata about a segment | 保存一個segment的元數據信息,lucene版本,segment內的doc-count |
| Compound File | .cfs, .cfe | An optional “virtual” file consisting of all the other index files for systems that frequently run out of file handles. | 復合文件,可以沒有,就是多個索引的一些信息都存儲在里面,分析數據結構的時候可以先不關注者一點 |
| Fields | .fnm | Stores information about the fields | 保存doc的field的元信息 |
| Field Index | .fdx | Contains pointers to field data | .fdt的索引文件 |
| Field Data | .fdt | The stored fields for documents | 存儲每個doc的具體的內容,按照doc-numer分組,每組存儲了一個doc的field信息 |
| Term Dictionary | .tim | The term dictionary, stores term info | 保存term的詞典 |
| Term Index | .tip | The index into the Term Dictionary | term詞典的索引文件 |
| Frequencies | .doc | Contains the list of docs which contain each term along with frequency | 詞典中的每個term指向的列表,每一項包含了docId+term-frequency信息(詞頻信息) |
| Positions | .pos | Stores position information about where a term occurs in the index | 倒排保存term在field中經過tekenAnalizer后的term所在的位置,是第幾個term |
| Payloads | .pay | Stores additional per-position metadata information such as character offsets and user payloads | 詞典中的每個term指向的列表,每一項都包含了文檔的元數據信息或者用戶自定義的一些信息 |
| Norms | .nvd, .nvm | Encodes length and boost factors for docs and fields | 長度歸一化,doc-boost, field-boost的記錄信息 |
| Per-Document Values | .dvd, .dvm | Encodes additional scoring factors or other per-document information. | 保存了額外的排序因子,或者是每個文檔獨有的信息 |
| Term Vector Index | .tvx | Stores offset into the document data file | .tvd的索引文件 |
| Term Vector Data | .tvd | Contains term vector data. | 保存了每個doc的每個field的term vector |
| Live Documents | .liv | Info about what documents are live | 只有在一個segment中包含被刪除的文檔時才會生成,它記錄了當前段中沒有被刪除的文檔號 |
| Point values | .dii, .dim | Holds indexed points, if any | 存儲point類型的數據,int,long等 |
2.2.3 lucene 的數據類型
變長的整數類型,它可能包含多個Byte,對于每個Byte的8位,其中后7位表示數值,最高1位表示是否還有另一個Byte,0表示沒有,1表示有。
越前面的Byte表示數值的低位,越后面的Byte表示數值的高位。
例如130化為二進制為 1000, 0010,總共需要8位,一個Byte表示不了,因而需要兩個Byte來表示,第一個Byte表示后7位,并且在最高位置1來表示后面還有一個Byte,所以為(1) 0000010,第二個Byte表示第8位,并且最高位置0來表示后面沒有其他的Byte了,所以為(0) 0000001。
2.2.4 ducoment number
在開始正式了解各個文件之前,我們要說一下ducoment number 的概念
數據庫內通過主鍵來唯一標識一行,而Lucene的Index通過DocId來唯一標識一個Doc。這個DocId也就是document number
不過有幾點要特別注意:
Lucene內最核心的倒排索引,本質上就是Term到所有包含該Term的文檔的DocId列表的映射。所以Lucene內部在搜索的時候會是一個兩階段的查詢,第一階段是通過給定的Term的條件找到所有Doc的DocId列表,第二階段是根據DocId查找Doc。Lucene提供基于Term的搜索功能,也提供基于DocId的查詢功能。就是倒排索引和正排信息共同支撐了整個查詢過程。
2.3 正向信息的存儲
包含正向信息的文件有:
- segments_N保存了此索引包含多少個段,每個段包含多少篇文檔。
- XXX.fnm保存了此段包含了多少個域,每個域的名稱及索引方式。
- XXX.fdx,XXX.fdt保存了此段包含的所有文檔,每篇文檔包含了多少域,每個域保存了那些信息。
- XXX.tvx,XXX.tvd 保存了此段包含多少文檔,每篇文檔包含了多少域,每個域包含了多少詞,每個詞的字符串,位置等信息。
因為segments_N信息含量相對較少,我們就不再具體介紹,看看其他幾個文件都存儲了什么吧。
2.3.1 .fnm 文件
對應的官方文檔的信息參考這里
Header,FieldsCount, [{FieldName,FieldNumber, FieldBits,DocValuesBits,DocValuesGen,Attributes}]*FieldsCount FieldsCount,Footer2.3.1.1 一級數據
2.3.1.2 二級數據Field
一個filed包含的信息:
對于長度較小的字段不建議開啟term verctor,因為只需要重新做一遍分詞即可拿到term信息,而針對長度較長或者分詞代價較大的字段,則建議開啟term vector。Term vector的用途主要有兩個,一是關鍵詞高亮,二是做文檔間的相似度匹配(more-like-this)。
0: not indexed 不進行索引
1: indexed as FieldInfo.IndexOptions.DOCS_ONLY 索引文檔編號
2: indexed as FieldInfo.IndexOptions.DOCS_AND_FREQS 索引文檔編號、關鍵詞頻率
3: indexed as FieldInfo.IndexOptions.DOCS_AND_FREQS_AND_POSITIONS 索引文檔編號、關鍵詞頻率、位置
4: indexed as FieldInfo.IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS 索引文檔編號、關鍵詞頻率
0:NONE
1:NUMERIC
2:BINARY
3:SORTED
4:SORTED_SET
5:SORTED_NUMERIC
該字段描述了存儲當前域的索引文件的格式(format),比如說當前是一個DocValues的域,那么Attributes的字段會有下面的值:
PerFieldDocValuesFormat.format:Lucene70
表示使用Lucene70這種格式來生成索引文件.dvd、.dvm。
4.0以后Field.Index廢棄,使用FieldType來進行設置,并通過setIndexOptions方法設置索引選項
StringField 不分詞并索引的字段,搜索時整字段匹配
TextField 分詞并索引
StoredField 僅保存,不索引
這里只是api層面的變化,實際的存儲結構并沒有變化。
2.3.2 XXX.fdx,XXX.fdt文件
這兩個信息存儲了segment中文檔的原始信息
2.3.2.1 .fdt 文件
.fdt存儲了當前segment中所有doc的field信息,是按照documnet-number來分組的。在新的文檔中,因為存儲上壓縮的考慮等,信息不是很容易理解。
fdt中的信息是按照Chunk 來進行組織的,每128個doc組成一個chunk,每個chunk包含 DocBase,ChunkDoc,DocFieldCounts,DocLengths,CompressedDocs
CompressedDocs 中的數據結構
通過這里也可以看出,每個doc的信息就得到了存儲。但是如果知道了一個文檔號,想要獲取這個文檔的內容,可能要遍歷這些chunk,則是很慢的一個操作,所以lucene增加了一個.fdx文件來做為索引文件,加快根據docId提取文檔內容的過程。
2.3.2.2 .fdx 文件
.fdx中存儲的主要是一個Block列表,每當.fdt文件中生成1024個chunk的時候便會在.fdx中生成一個Block。
Block的數據分為三個主要部分:
正常的一個工作邏輯是,我們有了一個docId,
2.3.3 XXX.tvx,XXX.tvd
參考這里
.tvd 存儲的是每個doc的每個field的term vector,按照doc進行分組, .tvx 是.tvd的索引文件。
.tvd文件存儲了每個doc的每個filed的terms,term 對應的frequencies, positions, offsets 信息。
.tvd文件中主要的數據結構類似.fdt文件,是由一個個chunk組成的,但是chunk中的數據結構略有不同。
每個chunk中的結構如下:
0x01:包含位置position信息
0x02:包含偏移offset信息
0x04:包含負載payload信息
2.3.4 .nvd,.nvm
.nvm文件保存索引字段加權因子的元數據,.nvd文件保存索引字段加權數據
2.3.4.1 nvd中到底存儲了什么
關于nvd文件中具體存儲了什么,一度非常疑惑,感覺lucene并沒有直接說清楚,后面自己想到,norm是打分排序的一個歸一化因子組合,不同的相似度計算算法可能需要是不一樣的。
可以參考這個問題
比如之前的金典的TF/IDF算法對norm的需求是:
由此想到norm應該會因為你選擇了不同的相似度計算算法而不一樣。在網上也翻閱到,lucene確實有很多norm的計算方式
abstract long Similarity.computeNorm(FieldInvertState state) Computes the normalization value for a field, given the accumulated state of term processing for this field (see FieldInvertState).long BM25Similarity.computeNorm(FieldInvertState state) long MultiSimilarity.computeNorm(FieldInvertState state) long PerFieldSimilarityWrapper.computeNorm(FieldInvertState state) long SimilarityBase.computeNorm(FieldInvertState state) Encodes the document length in the same way as TFIDFSimilarity.long TFIDFSimilarity.computeNorm(FieldInvertState state) float DefaultSimilarity.lengthNorm(FieldInvertState state) Implemented as state.getBoost()*lengthNorm(numTerms), where numTerms is getLength() if DefaultSimilarity.setDiscountOverlaps(boolean) is false, else it's getLength() - getNumOverlap().abstract float TFIDFSimilarity.lengthNorm(FieldInvertState state) Compute an index-time normalization value for this field instance.所以nvd中是一個開放式的多個byte的數據結構,各路開發者可以按照自己喜歡的方式來產生和解析norm即可
2.3.4.2 .nvd文件
因為norm的信息主要是基于域進行設置的,所以他的統計維度是按照field進行統計的。就是統計了某個field每個doc在該field下的norm值
.nvd文件的主體內容是一個數組 [FieldData]*NumFields ,這里的NumFields 是指Field的數量,應該是指text的field的數量
FieldData的內容是:
2.3.4.3 .nvm文件
.nvm文件保存索引字段加權因子的元數據。
.nvm的數據結構主要是一個數組的Entry,[Entry]*NumFields
Entry中的結構如下
2.3.4.4 根據DocId查找norm的過程簡述
因為norm是在相似度計算階段適用,所以是在通過query在term詞典中過濾得到了倒排DocIdlist之后的事情。
有了TargetDocId,和query適用的Field信息,
到此,正向索引的信息基本上是描述完了,接下來就是看看反向的信息有哪些了。
2.4 反向索引信息的存儲
保存了詞典到倒排表的映射:詞(Term) –> 文檔(Document)
包含反向信息的文件有:
- XXX.tim,XXX.tip保存了詞典(Term Dictionary),也即此段包含的所有的詞按字典順序的排序。
- XXX.doc保存了倒排表,也即包含每個詞的文檔ID列表。
- XXX.pos保存了倒排表中每個詞在包含此詞的文檔中的位置。
- XXX.pay保存倒排表中的每個詞在包含該詞的文檔中的offset和payload信息
反向索引信息就是我們常說的倒排表,也是實現文本近似查詢的關鍵支撐。
2.4.1 XXX.tim,XXX.tip
在lucene7.5中對倒排表的term dictionary (詞典)部分進行了比較大的改動。之前是使用跳躍表來實現的有序term的快速查詢?,F在改進到了FST(Finite State Transducer)有限狀態轉換機。FST的具體實現相對比較復雜,我們這里用類比的方式解釋一下。FST其實和AC自動機很像。AC自動機是基于Trie-Tree的有限狀態自動機,目的是為了實現多模式的字符串匹配。Trie樹可以實現多個模式串的前綴壓縮,后綴suffix是開放的結構,lucene為了進一步節約存儲空間使用FST,FST基于Trie樹更進一步,對后綴也做了壓縮,整個數據結構類似于一個盜夢空間中的陀螺,中間粗大,兩端都收于一點,而且查詢效率也很高。前綴查詢也很easy。
2.4.1.1 XXX.tip
XXX.tip部分存儲的是FST,而且tip為每個filed都存儲了一個FST結構。
總體結構 NumFields NumFields, DirOffset
這樣的話就可以通過DirOffset找到第一個IndexStartFP的位置,然后按照field-number就可以找到對應的IndexStartFP,這個應該很短,就是NumFields 個vlong。然后再通過IndexStartFP找到對應的FSTIndex.
這里需要強調的一點是,FST并不存儲term的全部數據,而是term的前綴信息,比如有terms: abc ,abe, abfg 那么FST存儲的是ab, 也就是他們的公共前綴,剩下的c,e,fg等存儲在FST指向的XXX.tim文件當中。這樣也避免了FST過大,同時又能夠有比較好的查詢效果。
2.4.1.1 XXX.tim
.tim 的主要結構 NumBlocks, FieldSummary, DirOffset,
EntryCount:??EntryCount描述了當前的OuterNode中包含多個entries,即包含了多少個term的信息。
SuffixLength、StatsLength、MetaLength:??這三個值分別描述了所有term的Suffix、TermStats、TermMetadata在.tim文件中的數據長度,在讀取.tim時用來確定讀取Suffix、TermStats、TermMetadata的范圍區間。
SuffixLength: 這是一個字節序列,實際上會被解析成一個suffix數組,數組中每一個元素被分為兩個部分,length,SuffixValue
TermStats: TermStats中又包含了兩個元素DocFreq和TotalTermFreq
TermMetadata: TermMetadata中包含的信息比較多,所有和外部關聯的信息都在這里,比如指向.doc,.pos , .pay文件的指針
2.4.2 XXX.doc
.doc文件存儲的是倒排表中的每個term指向的DocIdList,當然,還有term在當前doc中出現的次數
主體結構為 <TermFreqs, SkipData?>TermCount
有一個數組的<TermFreqs, SkipData?>列表,大小為term的總數,所以這個文件還是有一定的大小的。
2.4.3 XXX.pos文件
.pos文件存儲的主要是term在單個文檔中的位置信息。有時候為了加快查詢速速,也會存儲一些payload信息和offset信息。
但是大部分場景實際上只需要positions信息就夠了,就不會去加載.pay文件了,只需要加載.pos文件即可
主體結構是這樣: TermCount
TermPositions:單個TermPositions存儲了該term在每個doc的中的position信息,TermPositions之間按照term進行排序,TermPositions內部按照docId進行排序。
2.4.4 XXX.pay文件
.pay文件中主要存儲的是payload信息和offset信息
主體結構是<TermPayloads, TermOffsets?> TermCount
TermPayloads: 單個TermPayloads存儲了該term在每個doc中的payload信息,順序同TermPositions
TermOffsets: 單個TermOffsets存儲了該term在每個doc中的offset信息,順序同TermPositions
2.4.5 在倒排中的查詢邏輯
一個query進來,首先會被分詞產生過個term,然后在.tip文件中查找對應field的FST(Finite State Transducer) 根據FST查找到的信息去.tim文件中查找具體的term信息,
在.tim中找到對應的TermStats,TermMetadata以后,可以從TermStats中拿到當前term的DF值(即出現了該term的文檔數量),從TermMetadata中拿到對應的指向.doc中的跳躍表,docIdList的指針,指向.pos .pay的指針等。
假如多個term的關系是and,那么就會根據.doc中的skipList進行docIdList合并,或者有position的要求,還要判斷term在各個doc中的position信息是否滿足要求,然后進行打分,這個時候一般需要根據docId里面存儲的TF(term frequency term在文檔中出現的次數)
根據打分公司進行排序即可。
到這里lucene的倒排和正排信息基本上都梳理的差不多了。
2.5 docvalues信息的存儲 .dvd .dvm
doc-values信息是為了能夠根據doc-number快速的查找到該doc對應的某個屬性,在實際應用中最多的場景用于提供給搜索結果一個排序規則。
lucene 使用.dvd .dvm存儲doc-values信息,.dvd存儲doc-values的data, .dvm存儲doc-values的元數據。
lucene現有的doc-values種類有
BinaryDocValues
SortedSetDocValues
SortedDocValues
SortedNumericDocValues
NumericDocValues
實現的存儲結構不同,但是支撐的功能基本上是一致的
可以參看
這里
這里
2.6 多維數值類信息point的存儲 .dim .dii
從Lucene6.0開始出現點數據(Point Value)的概念,通過將多維度的點數據生成KD-tree結構,來實現快速的單維度的范圍查詢(比如 IntPoint.newRangeQuery)以及N dimesional shape intersection filtering。
索引文件.dim中的數據結構由一系列的block組成,在內存中展現為一顆滿二叉樹(單維度可能不是,這塊內容會在介紹數值類型的范圍查詢時候介紹),并且葉子節點描述了所有的點數據。
單個維度的數值查詢一般比較容易實現,比如使用二叉搜索樹(BST),紅黑樹(RBT),B-Tree等都可以實現。
但是多維度就沒有這么容易實現了,但是基本也是基于樹結構的基本形式來實現的。
對于多維數據的實現的樹有
R-Tree(R樹): R-tree
R±Tree(R+樹): R+ tree
R*-Tree(R樹): R_tree
K-D-Tree(K維樹): k-d tree
K-D-B樹(K維B樹): K-D-B-tree
BKD-Tree
Segment-Tree(線段樹): Segment tree
其實這些樹具體的數據結構我也大部分都不了解,只是知道他們適用于多維空間查詢
lucene 采用的是BKD-Tree來做多維空間查詢索引
BKD-Tree實際上是對K-D-B樹(K維B樹)的一種優化實現,可以實現更加高效的修改操作。
lucene的point類型有
.dim 文件存儲了每個filed的數據和索引index
.dii 文件存儲了元信息,主要是存儲了每個filed的index在.dim文件中的地址
待處理問題
標準化因子包括哪些,有doc級別的,有field級別的,是如何存儲的
Field.Index 索引選項
Index.ANALYZED 進行分詞和索引
Index.NOT_ANALYZED 進行索引,但是不進行分詞
Index.ANALYZED_NOT_NORMS 進行分詞但是不存儲norms信息,這個norms信息包含了索引時間和權值等
Index.NOT_ANALYZED_NOT_NORMS 既部分詞也不存儲norms信息
Index.NO 不進行索引
4.0以后Field.Index廢棄
StringField 不分詞并索引的字段,搜索時整字段匹配
TextField 分詞并索引
StoredField 僅保存,不索引
Lucene7之后,去除了Index時的boost加權操作。這里應該是指使用了BM25之后,boost的作用越來越小吧。
總結
以上是生活随笔為你收集整理的lucene7.5的数据结构的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: lucene的数据类型
- 下一篇: 搜索的一般过程