elasticsearch原理学习
用es也差不多一年左右了,但是都是只会用,底层做了什么一窍不通,没有核心竞争力,循序渐进,一个一个攻破,理解的多了,读的多了,自然能力就上去了,es底层是基于lucene的,所以今天先从lucene下手
如何看lucene代码? 现在的代码太多,可能会比较干扰阅读,我找了最初代的lucene代码,只有750k,先看下
lucene1.4文档翻译整理:
/* 每个索引里有以下这几类文件: field names: 存储该段中所有的属性名 stored field values: 每个文档每个field的原始值,跟db的格式差不多 term dictionary: 包含这个段中所有索引属性的词元,包含词元的文本、包含该词元的文档数,还有指向该词元频率数据(在文档中出现的次数)和位置数据(这个词元在文档中的位置,相对于哪里?位置的格式是什么样的?)的指针 term frequency data: 词元频率数据,所有在词元字典中出现的词元被哪些文档包含,以及每个文档中出现该词元的次数 term proximity data: 词元位置数据,所有在词元字典中出现的词元在所属文档中的位置 normalization factors: 每个文档的每个属性都会存储一个命中因子分,当该属性被搜索结果命中的时候会将该值乘上命中的得分作为最终的得分 term vectors: 词元向量,每个文档的每个属性都存储一个词元列表,每个词元项包含词元的单词以及词元的频率(什么数据?) 在词元字典中已经存储了词元的所有信息,为什么还要在这里按照文档和属性纬度来存储词元信息 文件名格式: 每个段下面的文件都有相同的文件名,但是有不同的后缀以区分不同的功能,一个索引下的所有段不必须在一个文件夹下 lucene1.4的数据类型 lucene索引数据是以字节为单位,有以下几种数据类型: byte: 字节型,所有其他的数据类型都是基于字节做的封装 Uint32: 32位无符号整型值,4个字节,高字节优先(就是从后往前看,为什么要高字节优先?) Uint64: 64位无符号整形值,8字节,高字节优先 Vint: 变长整形值,该类型可能包含多个字节,仍然是高字节优先,每个字节的第一位不存储数据值,存储后面是否还有字节的标示 chars: 存储字符按照UTF-8编码的unicode编码 String: string是复合数据类型,首先是一个Vint标示字符串有多长,然后跟chars 索引级别文件: segments: 记录该索引下所有段的信息, 格式为: Format,Version,SegCount,<SegName,SegSize> * SegCount。 Format用于标示lucene版本 Version记录索引被修改了多少次,比如增加、删除文档 SegCount表示段的个数 剩下的是按顺序放置的段信息,每个段包含段名和该段包含的文档个数 commit.lock: 当该文件存在的时候,表示有进程正在重写segments文件和删除过期的段索引文件,或者进程正在读取segments文件和segments文件中记录的段索引文件。这个文件锁的作用就是在当前进程读取或者写segments文件后,正准备读或者写segments文件中记录的段索引文件时禁止其他进程删除段索引文件。 write.lock: 当该文件存在时,表示正有进程在添加文档到索引或者正在删除索引文件,该文件锁防止在一个进程在操作时其他进程修改索引数据 段级别文件: .fnm([f]ield [n]a[m]e): 记录该段中所有的属性信息, 格式为:FieldsCount,<FieldName,FieldBits> * FieldsCount FieldsCount表示属性个数(如果该段中不同的文档属性个数不一样怎么办? 这里存的是超集?) FieldName: 属性名 FieldBits: 记录该属性的一些配置信息,第一位字节表示该属性是否索引,第二位表示该属性是否有词元向量数据 在其他文件中的属性编号也是在该文件中记录的,属性在这个文件中的顺序就是它的编号,第一个是0,第二个是1.。。。。 .fdt([f]ield [d]a[t]a): 该文件中记录了该段中所有文档的原始数据,类似于数据库格式: 每一行一个文档,每一列为一个属性 格式为: <DocFieldData> * SegSize DocFieldData就是每个文档的数据 DocFieldData格式为: FieldCount,<FieldNum,Bits,Value> * FieldCount FieldCount表示该文档的属性 Bits的第一位表示该属性是否分词(为什么是否分词是对每个文档设置的?不是应该对所有文档设置的么?) Value就是属性值 .fdx([f]ield [d]ata inde[x]): 该文件是为了方便通过文档号查询该文档的属性数据(就是上面的fdt里的数据), 格式为:<FieldValuesPosition> * SegSize FieldValuesPosition表示的是某个文档的属性数据在上面fdt文件中的偏移,类型为Uint64,8字节,比如要找编号为0的文档的属性数据,那就到该文件(.fdt)文件中的 0 * 8字节处读取出来一个值,然后去上面的属性值文件中去用这个值表示偏移处取出来值就是该文档的属性数据。 已上都是属性相关的文件,下面开始词元相关的文件 首先是词元字典: 词元字典文件包含两个文件,一个是词元信息文件,一个是词元信息索引文件 .tis([t]erm [i]nfo[s]): 存储词元信息 格式为: TIVersion,TermCount,IndexInterval,SkipInterval,<TermInfo> * TermCount TermCount表示有多少个词元 IndexInterval是用于词元信息索引文件中用的,词元信息索引文件就是把该文件中按照IndexInterval间隔取出来一些词元放到词元信息索引文件中,方便对词元信息文件的随机访问 TermInfo的格式为: Term,DocFreq,FreqDelta,ProxDelta,SkipDelta Term就表示的词元,词元的存储是按照前缀公用的形式,就是前后两个词元如果前缀有相同的,那后一个词元只用存后缀就行了,格式为: <PrefixLength,Suffix,FieldNum>, PrefixLength表示和前一个词元重合的前缀长度,Suffix表示词元的后缀,FieldNum表示该词元所属的属性的编号 DocFreq表示有多少文档包含该词元 FreqDelta表示词元频率数据在.frq文件中的偏移,存储的形式是前一个该数据的差值,比如第一个词元的该值为100,第二个词元的该值为150,那第二个词元存储的该值就是150 - 100 = 50 ProxDelta表示词元在文档中出现的位置的数据在.prx文件中的偏移,存储形式跟FreqDelta一样 SkipDelta表示该词元SkipData数据(不知道这个数据是干什么用的)在.frq文件中的偏移,因为.frq文件中已经存储了词元的频率数据,而每个词元都有频率数据和SkipData数据,成对出现,在.frq文件中的每个词元先存储频率数据,再存储SkipData数据,所以这个SkipDelta存储的是这个SkipData所属的词元的频率数据的字节长度,这样通过频率数据的偏移 + 频率数据的长度 就是这个词元的SkipData数据 .tii([t]erm [i]nfo [i]ndex): 存储词元信息索引数据,这里存储的是上面.tis文件中IndexInterval指定的间隔的词元信息 格式为: IndexTermCount,<TermInfo,IndexDelta> * IndexTermCount IndexTermCount表示该文件中一共有多少个索引词元 TermInfo和上面.tis文件中一样 IndexDelta表示当前这个词元在.tis文件中的偏移,存储的形式也是差值存储,就是后面词元存储的该值是前一个词元该值的差 然后是词元频率文件,上面词元字典里存的每个词元都包含一个FreqDelta就是指向该文件中指定词元位置的 .frq([f][r]e[q]uency): 存储词元频率数据 格式为:<TermFreqs,SkipData> * TermCount TermFreqs存储了词元的的频率数据,包括哪些文档包含了该文档,该词元在每个文档中的出现的频率 TermFreqs是一个列表,里面每个项是包含该词元的某个文档以及该词元在该文档中出现的次数,SkipData就是按照上面词元字典里存储的SkipInterval间隔,将TermFreqs里指定项的相关属性存储起来,所以SkipData也是一个列表,这个列表的长度等于TermFreqs的长度除以IndexInterval TermFreqs的格式为 <TermFreq> * DocFreq DocFreq和上面词元字典里存的是一样的 TermFreq的格式为: DocDelta,Freq? DocDelta和Freq都是Vint类型的值,lucene为了节省存储空间,用DocDelta的最后一位来决定是否需要存储Freq的值,如果Freq=1,则不单独用Vint存储Freq了,将DocDelta的值向左移动一位,然后最后空出来的一位设置为1,如果Freq>1,则也需要将DocDelta左移一位,空出来的最后一个设置为0,Freq用单独的一个Vint存储 SkipData的格式:<SkipDatum> * (DocFreq / SkipInterval) SkipData就是从TermFreqs列表中按照SkipInterval抽取出来的子集,然后在设置一些额外的属性,所以SkipData是一个列表 SkipDatum格式为: <DocDelta,FreqDelta,ProxDelta> DocDelta表示按照SkipInterval抽取出来的文档ID,比如现在TermFreqs里存储了35个文档数据,SkipInterval的值为16,那SkipData列表里存储的DocDelta列表为 15,31 FreqDelta存储了TermFreqs里指定项(词元 + 包含该词元的某文档)相对于.frq文档开始的字节数,为了节省存储空间按照差值规则存储 ProxDelta存储了TermFreqs里指定项(词元 + 包含该词元的某文档)相对于.prx文件文档开始出的字节数,为了节省存储空间按照差值规则存储 下面是位置数据 .prx(position,不知道这个文件名是怎么取的): 这个文件里存的是每个词元在某个文档中出现的位置 格式为:<TermPositions> * TermCount 这个TermCount和词元字典里存储的TermCount是一样的 TermPositions格式为: <Postions> * DocFreq DocFreq和词元字典里的DocFreq是一样的,存储了有多少个文档包含了该词元 Postions存储了每个文档中该词元的位置,格式为: <PositoinDelta> * Freq Freq表示这个词元在这个文档中出现的次数,在frq文件中的DocFreq里存储的有这个值 PositoinDelta是Vint类型的值,存储了这个词元在这个文档中出现的具体位置,(好像没有说这个里存储的是字节数还是词元数)为了节省存储空间,该值也是使用差值存储规则 最后一个关于词元的文件是一系列词元向量文件,这几个文件建立了文档到词元的关联关系 .tvf(term vector for field): 这个是存储了属性到词元的关联文件 格式为: TVFVersion<NumTerms,NumDistinct,TermFreqs> * NumFields NumFields是属性的个数,这个我理解应该是该段中的所有属性个数,和fnm文件中存储的FieldsCount一样 NumTerms是该属性包含的所有词元数 NumDistinct没有用 TermFreqs存储了词元的文本值和词元的频率数据指针,指向.frq文件中该词元的数据 TermFreqs格式为: <TermText,TermFreq> * NumTerms TermText就是词元的文本,使用前缀规则存储节省空间 TermFreq是一个Vint类型的数据,记录了.frq文件中该词元的数据位置 .tvd(term vector for document): 这个文件存储了文档到属性的关联关系,这里的属性指的是上面.tvf里的属性 格式为: TVDVersion,<NumFields,FieldNums,FieldPositions> * NumDocs NumDocs表示该段中的文档数,和索引级别文件segments中存储的segSize应该是一样的 NumFields标识该文档中的属性数 FieldNums格式为<FieldDelta>*NumFields,存储的是所有属性的编号,这里的编号对应于.fnm文件里体现的编号 FieldPositions存储了这些属性到.tvf文件中的指针,格式为: <FieldPosition> * NumFields tvx(term vector index): 这个文件是tvd文件的索引文件,方便通过文档编号随机访问tvd用的 格式为: TVXVersion<DocumentPosition> * NumDocs DocumentPosition是一个固定Uint64类型的数字,直接通过文档编号 * 8在这个文件中存储的值就是tvd文件中的偏移量
在官方文档里少了个.cfs文件,1.4版本的lucene在最后会将所有的文件和合并到一个文件里,不知道为什么要这么做,cfs文件格式为:
.cfs格式为: 文件数量,<文件内容偏移,文件名> * 文件数量,<文件内容> * 文件数量
*/