lucene倒排表在内存中的缓存结构2--一个demo

跟进TermHashPerField里面的add()方法,

新增term

为term即将存储的[docId,freq]信息、posi等信息,在bytePool中申请slice(内存空间),并将对应的slice起始位置作为[docId,freq]和posi等信息的结束位置写入intPool(由于还没存入信息,所以用起始位置作为结束位置),两个信息在bytePool中分别存在独立的slice中。
调用FreqProxTermsWriterPerField的newTerm方法,首先将该term的lastdocId置为当前docId,将freq置为1,将docCodes置为当前docId << 1,左移一位目的是,最后一位为0,表示后面跟随freq信息,在addTerm时可以看到其他处理,这个优化是因为大多数term都只会出现一次,另开一个int存储比较浪费。
然后在bytePool中写入posi等信息,并调整intPool中posi信息的最后一位下标。
已有term

调用FreqProxTermsWriterPerField的addTerm方法,首先判断当前处理的docId和该term最后一次处理的docId是否一样,如果一样,则证明这是一个doc分词出的相同term,需要累加freq,但是不需要更新docId;如果不一样,则证明上一次的doc已经处理完毕,应当将上次的所有信息刷入内存池,我们以不一样为例讲解下。
如果不是一个docId,则证明上一个文档刚处理结束,当前所有记录的信息都是上一个doc的。如果出现频率的频率等于1,则没必要写入freq信息,直接把docCodes最后一位置为1,写入docCodes即可。否则,直接写入docCodes(此时docCodes最后一位为0,在newTerm的时候有设置),并且写入freq信息。
写入完成后,则上一个doc处理完毕,开始处理当前文档。首先将termFreq设置为1,表明这是当前文档第一次出现这个term,然后设置docCodes,采用差值设置,并左移一位,将最后一位置为0,原理同newTerm。
然后写入posi等信息,原理通newTerm。
 

测试demo

private Document getDocument(String value) throws Exception {
        Document doc = new Document();
        FieldType fieldType = new FieldType();
        fieldType.setIndexOptions(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS);
        fieldType.setTokenized(true);
        Field pathField = new Field("name", value, fieldType);
        //向document中添加信息
        doc.add(pathField);
        return doc;
    }

    //创建索引
    public void writeToIndex() throws Exception {
        //需要创建索引的数据位置
        Document document = getDocument("lucene1");
        writer.addDocument(document);
        // breakpoint1
        document = getDocument("lucene2 lucene2");
        writer.addDocument(document);
        // breakpoint2
        document = getDocument("lucene2 lucene2 test lucene2 lucene2");
        writer.addDocument(document);
        // breakpoint3
    }

 

breakpoint1

下标    postingsArray.textStarts    postingsArray.intStarts    postindesArray.byteStarts    intPool    bytePool
0    0    0    8    8    7
1    0    0    0    14    108
2    0    0    0    0    117
3    0    0    0    0    99
4    0    0    0    0    101
5    0    0    0    0    110
6    0    0    0    0    101
7    0    0    0    0    49
8    0    0    0    0    0
9    0    0    0    0    0
10    0    0    0    0    0
11    0    0    0    0    0
12    0    0    0    0    16
13    0    0    0    0    0
14    0    0    0    0    0
15    0    0    0    0    0
16    0    0    0    0    0
17    0    0    0    0    16
 

在这个断点,只有一个term出现,lucene1的termId为0。

textStarts[0] = 0,表示term字面值在bytePool中第0位开始,bytePool[0] = 7,表示term长度为7,bytePool中1~7为term字面值。

8~12是第一个slice,用来存储[docId,freq],最后一位16表示没有向后延伸。

13~17是第二个slice,用来存储posi等信息,最后一位16表示没有向后延伸。

再来看intStarts[0] = 0,表示term相关信息在intPool中第0位开始,由于有posi信息,则在intPool中需要占两个位置。因此intPool[0]和intPool[1]分别表示这个term在bytePool中[docId,freq]和posi等信息的结束位置+1

byteStarts[0] = 8,表示term的[docId,freq]信息在bytePool中从第8个字节开始。

intPool[0] = 8,表示[docId,freq]在bytePool中结束位置 + 1 。为什么明明有一个doc,但是intPool[0]中指示[doc,freq]的结束位置为8,等于byteStarts[0]呢,相当于没有任何信息呢?原因是虽然doc1已经处理完毕,但是此时对于lucene1这个term,没有其他的doc,所以这个信息还没有被写入intPool,仍存在lucene1的这个term的docCodes、freq数组中

intPool[1] = 14,表示pos等信息的结束位置为14,这个信息的长度可以通过[docId,freq]的数量计算出来,分词后的每一个term都会存这个信息,因此这个信息长度为sum(freq)。这里可以看到值为0。这个要分两部分看,二进制最后一位为0,表示没有后续信息,前7位为0,表示term在这个field原生值分词后的第一位。

到这里,breakpoint1的所有信息都分析完毕
 

breakpoint2

下标    postingsArray.textStarts    postingsArray.intStarts    postindesArray.byteStarts    intPool    bytePool
0    0    0    8    8    7
1    18    2    26    14    108
2    0    0    0    26    117
3    0    0    0    33    99
4    0    0    0    0    101
5    0    0    0    0    110
6    0    0    0    0    101
7    0    0    0    0    49
8    0    0    0    0    0
9    0    0    0    0    0
10    0    0    0    0    0
11    0    0    0    0    0
12    0    0    0    0    16
13    0    0    0    0    0
14    0    0    0    0    0
15    0    0    0    0    0
16    0    0    0    0    0
17    0    0    0    0    16
18    0    0    0    0    7
19    0    0    0    0    108
20    0    0    0    0    117
21    0    0    0    0    99
22    0    0    0    0    101
23    0    0    0    0    110
24    0    0    0    0    101
25    0    0    0    0    50
26    0    0    0    0    0
27    0    0    0    0    0
28    0    0    0    0    0
29    0    0    0    0    0
30    0    0    0    0    16
31    0    0    0    0    0
32    0    0    0    0    2
33    0    0    0    0    0
34    0    0    0    0    0
35    0    0    0    0    16
 

在这个断点,lucene2的termId为1。

textStarts[1] = 18,表示term字面值在bytePool中第18位开始,bytePool[18] = 7,表示term长度为7,bytePool中19~25为term字面值。

26~30是第一个slice,用来存储[docId,freq],最后一位16表示没有向后延伸。

31~35是第二个slice,用来存储posi等信息,最后一位16表示没有向后延伸。

再来看intStarts[1] = 2,表示term相关信息在intPool中第2位开始,由于有posi信息,则在intPool中需要占两个位置。因此intPool[2]和intPool[3]分别表示这个term在bytePool中[docId,freq]和posi等信息的结束位置+1

byteStarts[1] = 26,表示term的[docId,freq]信息在bytePool中从第26个字节开始。

intPool[2] = 26,表示[docId,freq]在bytePool中结束位置 + 1 。为什么等于byteStarts[1],原因同lucene1

intPool[3] = 33,表示pos等信息的结束位置为3。可以看到bytePool[31] = 0,表示在分词列表中出现的位置是0,后面不跟随其他信息,bytePool[32] = 2,表示在分词列表中出现的位置是1,后面不跟随其他信息。

到这里,breakpoint2的所有信息都分析完毕。
 

breakpoint3

下标    postingsArray.textStarts    postingsArray.intStarts    postindesArray.byteStarts    intPool    bytePool
0    0    0    8    8    7
1    18    2    26    14    108
2    36    4    41    28    117
3    0    0    0    56    99
4    0    0    0    41    101
5    0    0    0    47    110
6    0    0    0    0    101
7    0    0    0    0    49
8    0    0    0    0    0
9    0    0    0    0    0
10    0    0    0    0    0
11    0    0    0    0    0
12    0    0    0    0    16
13    0    0    0    0    0
14    0    0    0    0    0
15    0    0    0    0    0
16    0    0    0    0    0
17    0    0    0    0    16
18    0    0    0    0    7
19    0    0    0    0    108
20    0    0    0    0    117
21    0    0    0    0    99
22    0    0    0    0    101
23    0    0    0    0    110
24    0    0    0    0    101
25    0    0    0    0    50
26    0    0    0    0    2
27    0    0    0    0    2
28    0    0    0    0    0
29    0    0    0    0    0
30    0    0    0    0    16
31    0    0    0    0    0
32    0    0    0    0    0
33    0    0    0    0    0
34    0    0    0    0    0
35    0    0    0    0    51
36    0    0    0    0    4
37    0    0    0    0    116
38    0    0    0    0    101
39    0    0    0    0    115
40    0    0    0    0    116
41    0    0    0    0    0
42    0    0    0    0    0
43    0    0    0    0    0
44    0    0    0    0    0
45    0    0    0    0    16
46    0    0    0    0    4
47    0    0    0    0    0
48    0    0    0    0    0
49    0    0    0    0    0
50    0    0    0    0    16
51    0    0    0    0    2
52    0    0    0    0    0
53    0    0    0    0    2
54    0    0    0    0    4
55    0    0    0    0    2
56    0    0    0    0    0
57    0    0    0    0    0
58    0    0    0    0    0
59    0    0    0    0    0
60    0    0    0    0    0
61    0    0    0    0    0
62    0    0    0    0    0
63    0    0    0    0    0
64    0    0    0    0    17
65    0    0    0    0    0
66    0    0    0    0    0
67    0    0    0    0    0
 

在这个断点,lucene2是已经出现过的term,会把doc1的信息刷入bytePool,test是新的term,会单独存储并分配slic。

这个field总共会分出5个term:lucene2、lucene2、test、lucene2、lucene2。我们一个个分析信息是如何写入bytePool中的。

第一个lucene2
首先,会发现这是已有的term,termId = 1,addTerm时发现上次的docId是1,这次的docId是2,会先将上次doc的信息刷入bytePool。
上次的docId为1,由于termFreq = 2,需要跟随freq信息,因此将docId左移一位的值直接写入bytePool,然后写入freq,注意freq使用vInt写入的,但是此时freq = 2,只需要一个字节,所以写入的值是2.
向intPool查询当前可以写入的位置,intPool[1] = 26,因此第26个字节写入2表示docId,并且后面跟随freq,第27个字节写入2,表示freq = 2,并设置[docId,freq]结束位置为28。
然后,更新lastDocId等信息,并写入新的term posi等信息。
第二个lucene2
这个没什么好说的,就是正常的addTerm,更新freq,写入posi等信息,freq列表为下标31~34,值为0、2、0、2。
test
新的term出现了,和之前新term处理方式一样,写入term字面值(bytePool下标3640),申请[docId,freq]的splic(4145),申请posi等信息的slice并写入(46~50),写入的值为4,二进制最后一位为0表示不跟随其他信息,右移一位为2表示在分词链中第2个出现,因此posi结束位置为47,[doc,freq]信息还没刷入bytePool,结束位置为41。
第三个lucene2
正常执行addTerm方法,但是在写入posi等信息的时候,要写入的位置是35,这个位置值16表示这是slice的末尾,不能写入值。slice要扩容,并将3234的信息复制到新扩容的区域,重新申请slice得到的slice起始位置为51,将3235四个字节合并表示51,因此3234为0,35表示51,将原本32到34的值复制到5153,因此51~53的置为2、0、2,新的词在分词列表中处于第3位,上一个lucene2处于第1位,采用差值法,应当写入2,左移一位将末尾置0,表示后面没有其他信息,因此54位置写入的值为4。
第四个lucene2
同第二个lucene2,直接在55的位置写入2,将posi信息结束位置修改为53。
到这里,breakpoint3的所有信息都分析完毕
 

 

posted @ 2021-10-27 14:21  車輪の唄  阅读(22)  评论(0编辑  收藏  举报  来源