通过实际的程序我们可以观察到,如果不使用复合文件格式来创建索引的话,索引文件很多,这些索引文件记录和Field,Term相关的种种信息,这些信息以一定的格式存放在文件中,并且使用了压缩等多项技术来减少空间占用,不过lucene的压缩结构并没有使查找效率明显降低,适当的冗余还是存在与索引中。
大部分的搜索(数据库)引擎都是用B树结构来维护索引,索引的更新会导致大量的IO操作,Lucene在实现中,对此稍微有所改进:不是维护一个索引文件,而是在扩展索引的时候不断创建新的索引文件,然后定期的把这些新的小索引文件合并到原先的大索引中(针对不同的更新策略,批次的大小可以调整),这样在不影响检索的效率的前提下,提高了索引的效率。
综上,lucene索引的特点是:小的索引文件,多次的归并操作,可以提供增量索引和批量索引,同时一定程度的压缩减少空间占用,不影响查找效率。
下面我们看一下lucene的索引文件及索引格式,由于lucene是一个开源的软件,所以索引的格式可能随着时间会不断变化,下面所列是lucene-2.2的索引
下面文件格式中出现的单词含义
*Num--------****的编号,便于lucene当中的List结构灵活处理
*Count-------****的个数
*Size--------****的大小
文件名 | 记录内容 | 作用 | 文件格式 | 相关的函数 |
segment_N | current active segment | 如果向该目录索引添加新的Document,IndexWriter加载该文件 | Format, Version, NameCounter, SegCount, <SegName, SegSize, DelGen, HasSingleNormFile, NumField, NormGenNumField, IsCompoundFile>SegCount | IndexWriter.init() |
deletable | Document删除的记录 | 下一次索引合并优化时,读取该文件,真正将Document的信息从segment中去掉 | lucene-2.2已经不使用该文件 | |
.fnm | field的名字 | FieldsCount, <FieldName, FieldBits> FieldsCount FieldsCount --> VInt FieldName --> String FieldBits --> Byte | DocumentWriter.addDocument() | |
.fdx | 记录field值在.fdt中的位置(field的索引) | 通过该文件可以实现随机访问field的值,该文件n×8的位置记录的是第n个Document 的field值在.fdt的起始位置 | <FieldValuesPosition> SegSize FieldValuesPosition --> Uint64 | IndexWriter.addDocument() |
.fdt | Segment中所有field的值 | 存储field的值 | <DocFieldData> SegSize DocFieldData --> FieldCount, <FieldNum, Bits, Value> FieldCount FieldCount --> VInt FieldNum --> VInt
| IndexWriter.addDocument |
.tis | 分词后的词条 | term的值进行一定程度的压缩,基本思想是: 当前term只记录与上一个term除最大相同前缀外的剩余部分,如:两个term:nice 和nicer,按这种方法,nicer只记录'r’字母 当然term还要记录前缀的长度 | TIVersion, TermCount, IndexInterval, SkipInterval, MaxSkipLevels, TermInfos TIVersion --> UInt32 TermCount --> UInt64 IndexInterval --> UInt32 SkipInterval --> UInt32 MaxSkipLevels --> UInt32 TermInfos --> <TermInfo> TermCount TermInfo --> <Term, DocFreq, FreqDelta, ProxDelta, SkipDelta> Term --> <PrefixLength, Suffix, FieldNum> Suffix --> String PrefixLength, DocFreq, FreqDelta, ProxDelta, SkipDelta | DocumentWriter.addDocument() TermInfosWriter.initialize() TermInfosWriter.add() |
.tii | tis的索引文件 | 该文件与上面的.tis比较相近,但是多了一个IndexDelta,它是tis中本条目的位置与上一个条目的位置的差值
| TIVersion, IndexTermCount, IndexInterval, SkipInterval, MaxSkipLevels, TermIndices TIVersion --> UInt32 IndexTermCount --> UInt64 IndexInterval --> UInt32 SkipInterval --> UInt32 TermIndices --> <TermInfo, IndexDelta> IndexTermCount IndexDelta --> VLong | Document.writePostings() TermInfosWriter.add() |
.frq | 包含term及term出现的频率 | 按文档序号递增的顺序排列 | <TermFreqs, SkipData> TermCount TermFreqs --> <TermFreq> DocFreq TermFreq --> DocDelta, Freq? SkipData --> <<SkipLevelLength, SkipLevel> NumSkipLevels-1, SkipLevel> <SkipDatum> SkipLevel --> <SkipDatum> DocFreq/(SkipInterval^(Level + 1)) SkipDatum --> DocSkip,PayloadLength?,FreqSkip,ProxSkip,SkipChildLevelPointer? DocDelta,Freq,DocSkip,PayloadLength,FreqSkip,ProxSkip --> VInt SkipChildLevelPointer --> VLong | Document.writePostings() |
.prx | term在Document中出现的位置 | 位置是按文档序号递增的顺序排列的 | <TermPositions> TermCount TermPositions --> <Positions> DocFreq Positions --> <PositionDelta,Payload?> Freq Payload --> <PayloadLength?,PayloadData> PositionDelta --> VInt PayloadLength --> VInt PayloadData --> bytePayloadLength | DocumentWriter.addDocument() DocumentWriter.invertDocument() addPosition() |
.nrm | 记录了boost和length的因素 | 在打分(Score)阶段使用,在索引时建立,压缩存储,计算公式见norm(t,d) | NormsHeader,<Norms> NumFieldsWithNorms Norms --> <Byte> SegSize NormsHeader --> 'N','R','M',Version Version --> Byte | Document.addDocument() DocumentWriter.writeNorm() |
.tvx | .tvd的索引 | 指向.tvd的位置 | TVXVersion<DocumentPosition> NumDocs TVXVersion --> Int DocumentPosition --> UInt64 | Document.addDocument Document.writePostings TermVectorWriter.addTerm |
.tvd | TVDVersion<NumFields, FieldNums, FieldPositions,> NumDocs TVDVersion --> Int NumFields --> VInt FieldNums --> <FieldNumDelta> NumFields FieldNumDelta --> VInt FieldPositions --> <FieldPosition> NumFields FieldPosition --> VLong | 同上 | ||
.tvf | TVFVersion<NumTerms, Position/Offset, TermFreqs> NumFields TVFVersion --> Int NumTerms --> VInt Position/Offset --> Byte TermFreqs --> <TermText, TermFreq, Positions?, Offsets?> NumTerms TermText --> <PrefixLength, Suffix> PrefixLength --> VInt Suffix --> String TermFreq --> VInt Positions --> <VInt>TermFreq Offsets --> <VInt, VInt>TermFreq | 同上 |
以上便是索引的主要文件,更多详细的内容请参照lucene的官方文档http://lucene.apache.org/java/docs/fileformats.html#Fields,同时结合程序,相信对lucene的索引会有更深的了解