3.5中TermInfosReader的改进学习

From Evernote:

3.5中TermInfosReader的改进

Clipped from: alires:///MsgHistory/recent_tribe.htm?cssname=default

Ref:

1. https://issues.apache.org/jira/browse/LUCENE-2205

2. http://www.nearinfinity.com/blogs/aaron_mccurry/my_first_lucene_patch.html

原来思路:

-  private final Term[] indexTerms;
-  private final TermInfo[] indexInfos;
-  private final long[] indexPointers  

1) 利用这三个字段来保存Term相关信息。

2) 每隔128保存一个Term信息,因此保存的实际上是总共term的  1/128 。

3) 因为term是有顺序的,在查找一个term的位置时,使用的是binary search, 在比较时使用的是String 的compare 

3.5中修改点:

1.  将 term, termInfo indexPointer信息保存到 TermInfosReaderIndex 类中。

2. Term中包含的信息: 

   String field;  (1) 哪个字段

   String text;    (2)  什么内容

   

   TermInfo 中包含的信息: 

    /** The number of documents which contain the term. */

  int docFreq = 0;   (3)   

  long freqPointer = 0;  (4)

  long proxPointer = 0;   (5)

  int skipOffset;         (6)

3. TermInfosReaderIndex 中的处理:

 for (int i = 0; indexEnum.next(); i++) {

      Term term = indexEnum.term();

      if (currentField != term.field) {

        currentField = term.field;

        fieldStrs.add(currentField);

        fieldCounter++;

      }

      TermInfo termInfo = indexEnum.termInfo();

      indexToTerms.set(i, dataOutput.getPosition());

      dataOutput.writeVInt(fieldCounter);    //(1)来代替term中的field 

      dataOutput.writeString(term.text());   // (2) 来代替 term中的text

      dataOutput.writeVInt(termInfo.docFreq);  //(3) termInfo中的docFreq

      if (termInfo.docFreq >= skipInterval) {

        dataOutput.writeVInt(termInfo.skipOffset);   //(6) termInfo中的 skipOffset,在某种情况下被省略掉

      }

      dataOutput.writeVLong(termInfo.freqPointer);    //(4) termInfo中的freqPointer

      dataOutput.writeVLong(termInfo.proxPointer);    //(5) ternInof中的proxPointer

      dataOutput.writeVLong(indexEnum.indexPointer);

      for (int j = 1; j < indexDivisor; j++) {

        if (!indexEnum.next()) {

          break;

        }

      }

    }

   从这里可以看出除了field内存使用fieldCount来代替之外,其它都是没有变化。

   term中field使得int来代替有一个好处是尽量使用number来代替string,同时对number和string保持一个映射关系。

诱饵:

前段时间在 jvm 群有提到在系统启动里Forest,Catserver会占用大量内存,导致full gc。

后来有同学 提到 :

我们在做ip查找的时候,把所有的string放一起了。。。。 

用的时候来new string 

用数组压缩掉 

对象少多了 

而且可以减少重复string。 

这样做, 就会成为 3个数组对象+一个string对象 

gc毫无压力 

数组对象主要是 ip地址对应的属性,各种距离。 

哈哈,面对这类场景,我很自然地又想到了 lucene-2205 这个issue,因为我本身对这个issue中某些点印象比较深刻:

这是3.5中的一个重大的 optimization, 当时官方release说明中是:  Very substantial (3-5X) RAM reduction required to hold the terms index on opening an IndexReader

这个issue 从created 到resolved在历时一年多,当然在lucene中历时这么久的也还有其它issue

从这个issue也说明了只要去思考,提出方案,然后在别人指导下,还是可以取得不错成绩的。这issue的提出及解决者就是第一次向lucene社区提交patch,

针对这个issue, Doug Cutting   也出来冒泡了,提了好几点意见:

It’d probably be better not to make TermInfosReaderIndex and its subclasses public, to reduce the APIs that must be supported long-term.
针对这点,我们大部分同学是否有所感想? 因为我目前看到的是:很多人只习惯public,private ,至于是否真的需要public有考虑过这么多吗?针对这个public,这次法洛斯项目中,也是被坑了一次,完全可以写点总结。

Term :   简单来说就是用来表示某个field对应的一个word,(因为分词,一个field可以有多个word,因此也会有多个term)

因为有权重,高亮,span query等功能,需要表示这个term的相关信息,会有TermInfo对象:

lucene 3.5 及之前版本中,对term相关信息在内存中的保存方式:

实现类: TermInfosReader 

 用来保存term信息的是:

 private final Term[] indexTerms ;

  private final TermInfo[] indexInfos;

  private final long [] indexPointers;

  这也是我们平常一般思维习惯上组织信息的方式,分别用3个数组来表示整个索引对应的term信息。

   

      在对象初始化时,从文件中读取相应信息填充这三个数组:

让我们从基本知识点来考虑内存占用情况:

Terms[]

TermInfos[]

long[] 
如果从对象上来说,Term, TermInfos中都没有一丁点多的属性,也没有属性可以合并之类的优化方案了。同时对象中也都已经使用了primitive类型(String 除外啊,:))。

       java占用内存,很容易被忽略的一点是reference。

       任何单一的引用会占用 4个字节(32位机器)or 8字节(64位机器)。从引用角度来看,我们可以算一下保存term信息的这几个数组会占用多少内存

Terms[] 占用内存是 length * 3,  其中一个引用是Term对象,另外两个是Term中的String 属性

TermInfos[] length*1,  只是TermInfos占用一个引用,因为其内部都是primitive

long[]   只占用 1 reference, 可以忽略了吧

因此假设索引中有10亿个term(不要觉得很夸张,一个document中可能包含几十个term,如果以一个doc中包含50个term来算,只需要2KW document), 在 32位机器上,保存这部分内存中的term(对,是内存中的term,并不是全部term)需要  125M = 1,000,000,000/128 *(3+1 reference)*4bytes,在目前都是64位天下的情况下,就会占用 250M。 如果把128变成 64,那在64位机器下就是500M占用,这可是只是reference占用的内存啊。

如何把这些reference占用的内存省下来呢?  砍掉对象,在内存中不再以对象方式组织信息,以bytesArray来保存原始信息,同时为了能反向找出对象信息,偏移量信息的int数组是必须的

我们再看一下这样的思考来保存term相关信息的话,实际代码是如何实现的:

有了偏移量,在get时,查找相关term信息就不会是问题啦,主要是也不想再写了,哈哈。

Tagged , . Bookmark the permalink.

发表评论

电子邮件地址不会被公开。 必填项已用 * 标注

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>