Welcome

首页 / 软件开发 / .NET编程技术 / Lucene 3.0.0细节初窥(2)-研究在索引过程中的缓存

Lucene 3.0.0细节初窥(2)-研究在索引过程中的缓存2011-01-25 博客园 LeftNotEasyLucene有一个问题一直困扰着我, 就是如何在索引文件的时候节省空间, 合理的分配不大也不小的空间有助于在提高搜索速度的同时也能够监测内存的使用情况, 在内存使用到达某个阈值的时候可以触发合并的操作

之前在写一个小程序, 来实现类似于Lucene索引文件的时候, 我是用c++写的, 没有使用内存池, 需要的时候就找操作系统"要"一块, 不需要的时候就还给它, 这样不仅仅在内存的频繁的分配中造成大量的内存碎片, 而且没有用内存池还会带来操作系统的换页情况, 使得本来一共就3M左右的文件读取到内存中, 进行分词操作后要70M(当然是增加了一些属性了, 不过也没有这么高吧!), 把豆腐都盘成肉价钱了.

另外还是声明一下: 本文的著作权归LeftNoteasy所有, 如果需要转载请保留这句话并注明出处.

在TermsHashPerField中有三个Pool, CharBlockPool, IntBlockPool, ByteBlockPool. 三者之间非常类似, 其实我觉得Lucene的开发者应该加以统一, 把代码进行规范化处理, 我看到Lucene中随处可见重复的代码, 这样不仅给开发带来很多重复的工作, 而且对维护也不一定好, 如果那一天发现某个算法可以改进或者出现了bug, 那估计得忙上一阵子了.

三个缓存都是来自于TermsHashPerThread, 也就是说, 每个线程都有独立的Pool,这样的设计很好, 可以利用多核来对索引进行加速.

1. CharBlockPool

1: final class CharBlockPool
2: {
3: char[][] buffers = new char[10][];
4: int bufferUpTo = -1;
5: int charUpTo = DocumentWriter.CHAR_BLOCK_SIZE;
6: char[] buffer;
7: int charOffset = -DocumentWriter.CHAR_BLOCK_SIZE;
8: }

这里给出了CharBlockPool的主要成员变量, Lucene的Buffer设计有一个特点, 就是采用了二维数组, 就像房子一样, 最开始是个平房, 如果不够住了就往天上盖, 越盖越高, 当然, 是当前层住不下了才盖新的一层.

buffers就是这个楼房

bufferUpTo是这个楼房有多少层

charUpTo是我们在当前层的定位(where we are)

charOffset是我们总体的偏移量.

下面看看CharBlockPool的nextBuffer()函数, 也就是房子不够, 修楼房的函数:

1: public void nextBuffer() {
2: if (1+bufferUpto == buffers.length) {
3: char[][] newBuffers = new char[(int) (buffers.length*1.5)][];
4: System.arraycopy(buffers, 0, newBuffers, 0, buffers.length);
5: buffers = newBuffers;
6: }
7: buffer = buffers[1+bufferUpto] = docWriter.getCharBlock();
8: bufferUpto++;
9:
10: charUpto = 0;
11: charOffset += DocumentsWriter.CHAR_BLOCK_SIZE;
12: }