lucene索引结构的整理
2011-04-10 13:12
260 查看
倒排索引结构,示例如下:
设有
2
篇文章:
文章1的内容为:Tom lives in
Guangzhou,I live in Guangzhou too.
文章2的内容为:He once lived in
Shanghai.
分词:英文以空格分隔,中文根据基础词典和扩展词典分隔为字和词。
去停用词、标点符号、大小写转换等,由
Analyzer
完成。
文章1的所有关键词为:[tom] [live]
[guangzhou] [i] [live] [guangzhou]
文章2的所有关键词为:[he] [live]
[shanghai]
建立倒排索引:
Term | 文章号(出现频率) | 出现位置 |
guangzhou | 1[2] | 3,6 |
he | 2[1] | 2 |
i | 1[1] | 4 |
live | 1[2],2[1] | 2,5,2 |
shanghai | 2[1] | 3 |
tom | 1[1] | 1 |
lucene将上面三列分别作为词典文件(Term
Dictionary)、频率文件(frequencies)、位置文件
(positions)保存。其中词典文件不仅保存有每个关键词,还保留了指向频率文件和位置文件的指针,通过指针可以找到该关键字的频率信息和位置信息。Lucene中使用了field的概念,用于表达信息所在位置(如标题中,文章中,url中),在建索引中,该field信息也记录在词典文件中,每个关键词都有一个field信息(因为每个关键字一定属于一个或多个field)。
压缩:
term
压缩:
关键词压缩为<前缀长度,后缀>。例如:当前词为“阿拉伯语”,上一个词为“阿拉伯”,那么“阿拉伯语”压缩为<3,语>
数字压缩:
数字只保存与上一个值的差值。例如当前文章号是16389,上一文章号是16382,压缩后保存7。
lucene
建立索引的流程:
指定生成
index
的目录;
创建
IndexWriter
对象
:初始化
segments
文件
IndexWriter writer = new IndexWriter(index_dir,new
StandardAnalyzer(),false);
第一个参数:生成
index
的目录;
第二个参数:生成索引所用的分析器;
第三个参数:
true
为重新索引,
false
为增量索引。
生成一个
document
对象
:
Document doc = new Document();
doc.add(new
Field(name,value,Field.Store.YES,Field.Index,TOKENIZED));
域的属性:是否
store
,是否
index
,是否切词
将
doc
加入
IndexWriter
对象
:
writer.add(doc);
优化并关闭
IndexWriter
对象
:
writer.Optimize();
//
将所有的
segments
合并为一个
writer.close();
lucene
索引文件结构:
逻辑结构:
每当往
index
中加入一个
doc
,都会新生成一个
segment
保存这个
doc
,通过判断合并
segment
。
最后通过优化索引的命令,将所有的
segment
合并为一个
index
。一般以一个
doc
为单位往
index
添加记录,一个
doc
有几个
field
组成,每个
field
保存不同的信息,可以指定在某个域搜索。
每个域由很多
term
组成。
物理结构:
segments:
s
egments
文件的结构是:
Format+version+counter+Count+<si0.name+si0.docCount>+<si1.name+si1.docCount>…+<sin.name+sin.docCount>
即:索引文件格式
+
index
修改次数
+segment
总数
+
给新生成的
segment
取一个随机的名字
+<segment0
名字
+segment0
文档数
>+<segment1
名字
+segment1
文档数
>
deletable:
lock:
index.lock
文件:
表示有进程在向索引中加入文档,或者是从索引中删除文档。这个文件防止很多文件同时修改一个索引。
segment:
count
:要删除的文件总数;
文件名:要删除的文件名字
域集合信息(
.fnm
文件):
.fnm
文件的结构是:
size+<fi0.name+bits0>+<fi1.name+bits1>+...+<fin.name+bitsn>
域值存储表:
.fdx
文件:
.fdt
文件:
标准化因子(
.nrm
文件):
项字典:
.tis
文件:
size:doc
中
term
的总数
indexInterval
:索引间隔,默认值为
128,
这个参数决定写入
tii
文件中的
term
的数量。
skipInterbal
:跳跃间隔,默认值为
16
。
start
:当前
term
与前一个
term
相同的前缀字符串的长度。
length
:去除了当前
term
与前一
term
共享的前缀字符串后余下的字符串长度。
text
:余下的字符串值。
fieldNumber
:其所在
field
的编号。
docFreq
:出现这个
term
的
doc
的数量。
freqpointerDelta:
决定了在
.frq
文件中当前词
frq
的位置。即相对于前一个词的数据的位置偏移量(如果是
0
则表示是在文件中的第一个词条)。
proxPointerDelta:
决定了在
.prx
文件中当前词
prox
的位置。即相对于前一个词的数据的位置偏移量(如果是
0
则表示是在文件中的第一个词条)。
skipOffset
:决定了在
.frq
文件里当前词条
SkipData
的位置。它是
TermFreq
的数据长度,
SkipDelta
只在
DocFreq
不小于
SkipInterval
时才存储。
.tii
文件:
项频数(
.frq
文件)
:
是差值?
项位置(
.prx
文件)
:
被删除文档(
.del
文件)
:
即:该
doc
中
field
的总数
+<
域
0
名字
+
域
0
基本信息
>+<
域
1
名字
+
域
1
基本信息
>
基本信息
:低第一位
1
表示已经索引,
0
表示未索引。低第二位
1
表示有
term
矢量,
0
表示没有
term
矢量
用于在域信息文件中定位一个特定文档的域信息。
文档
n
的域信息是第
n*8
开始的
Uint64
。
.fdt
文件的结构是:
storecount+<field0.number+bits0+field0.value>+<field1.number+bits1+field1.value>+…+<fieldn.number+bitsn+fieldn.value>
即
:
记录这个
doc
中需要存储的
filed
总数
+<
域
0
编号
+
域
0
属性(是否分词、是否包括
2
进制、是否压缩)
+
记录
filed
的具体内容
>
对每个
field
,都会生成一个
.f
文件,其中记录了每个
doc
在命中该域时乘的因子的大小。
.f
的文件结构是:
doc0.filed0.norm+
doc1.field0.norm+…+docn.field0.norm
每个
doc
都会使用哈希表
postingTable
来保存该
doc
所包含的
term
的信息
。
这个哈希表的
key
是
term
,值是
posting
,
posting
是实际记录一个
term
在这个
doc
中信息的类。
Posting
主要包含以下信息:
term
:一个
term
对象,表明这个
posting
是属于哪个
term
的;
freq:
记录这个
term
在
doc
中出现的频率;
positions[]:
记录这个
term
每次出现在
doc
的哪个位置;
offset
:保存与
termvector
相关的内容
。
将
这个
postingTable
写入文件
:
.tis
文件的结构:
FORMAT+size+indexInterval+skipInterbal+<term0.start+term0.length+term0.text+term0.FieldNumber+term0.DocFreq+term0.FreqpointerDelta+term0.proxPointerDelta+term0.skipOffset>+…+<termn.start+termn.length+termn.text+termn.FieldNumber+termn.DocFreq+termn.FreqpointerDelta+termn.proxPointerDelta+termn.skipOffset
>
即:
假设前一个
term
是
bit
,当前
term
是
bike
,则
start=2
,
length=2
,
text==
“
ke
”。
.tis
文件的索引,记录了
.tis
文件中部分
term
及在
.tis
文件中的位置,默认为
term
总数的
1/128
.frq
文件的结构
:
term0.freq+term1.freq+…+termn.freq
.prx
文件的结构
:
term0.position[0]+(term0.position[1]-term0.position[0])+
(term0.position[2]-term0.position[1])+…+(term0.position[n-1]-term0.position[n-2])+….(termn.position[n-1]-termn.position[n-2])
其中
term.position
表示该
term
在
field
中出现第
n+1
次时的偏移位置。
要删除的文件保存在
deletable
文件中,这个文件记录以下数据:
删除文件所采用的策略是
:当每次合并时尝试删除
deletable
中所标识的所有文件,有些文件可能正在被使用而无法删除,生成一个新的
deletable
文件取代原来的文件,记录新增的要删除的文件和保留那些没能够删除的文件。
segment
合并
:每添加一个文档,都会生成一个
segment
,再合并
segment
:
判断是否合并的规则:
设置一个
targetMergeDocs
,第一次合并时值为
10
,接下来依次×
10
,即第
2
次合并时值为
100
,第
3
次合并时值为
1000
,依次类推。每次判断时,读取一个
segment
的
doc
数,这时有
3
种可能。
1.
如果该
segment
的
doc
数大于等于当前的
targetMergeDocs
,则对该
segment
不进行任何操作;
2.
如小于,则把他的
doc
数和后续那些同样
doc
数小于
targetMergeDocs
的
segment
的
doc
数累加,直到累加所得的
doc
数大于或等于
targetMergeDocs
时,把这些参与了累加的
segment
合并成一个新的
segment
。
3.
如果累加到最后一个
segment
,累加的
doc
数还不大于等于
targetMergeDocs
的话,则不进行合并。
循环这个过程,直到再也不能合并时退出
MaybeMergeSegments()
合并举例
:
现在按照顺序,有如下几个
segment
,括号内为该
segment
的
doc
数:
s0(10),
s1(10), s2(10), s3(10), s4(10), s5(10), s6(10), s7(10), s8(10), s9(1), s10(1),
s11(1), s12(1), s13(1), s14(1), s15(1), s16(1), s17(1), s18(1), s19(1)
第一遍合并后:
s0(10),
s1(10), s2(10), s3(10), s4(10), s5(10), s6(10), s7(10), s8(10)
news9(10),s19(1)
第二遍合并后:
news0(100),s19(1)
在完成索引最后阶段,有个
Optimize()
函数,这是为了在最后把所有的
segment
全部
合并成最后的一个
segment
文件
,不论是否满足合并的条件。
相关文章推荐
- 3.5 实例讲解Lucene索引的结构设计
- Lucene的索引链结构_IndexChain
- Lucene索引文件结构图之一(prx&nrm&tvx&tvd&del&tvf)
- Lucene整理--索引的搜索
- Lucene索引文件结构图之一(segments&fnm&fdx&fdt)
- Lucene 源代码剖析-4 索引文件结构(1)
- Lucene索引文件结构图之一(tis&tii&frq)
- 基于lucene的案例开发:索引文件结构
- 边学边记(五) lucene索引结构二(segments_N)
- Lucene的索引链结构_IndexChain
- Lucene整理--索引的建立
- lucene索引结构(一)--segment元数据信息
- Lucene整理--索引的建立
- Lucene整理--索引的管理
- lucene索引结构(六)--词位置(.prx)倒排索引文件结构分析
- lucene的索引结构图
- lucene索引结构(三)-词项向量(TermVector)索引文件结构分析
- InnoDB表与索引结构相关知识整理
- Lucene索引结构
- InnoDB表与索引结构相关知识整理