注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Koala++'s blog

计算广告学 RTB

 
 
 

日志

 
 

Lucene源代码分析[11]  

2009-07-05 09:20:28|  分类: Lucene |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

         我们在分析合并索引的时候,一直都假设SegmentReader是可以正常读到我们所需要的内容的,但是它本身是如何实现的,我们并不清楚。

         再转回去分析一次,IndexWriteraddDocument函数中:

public void addDocument(Document doc, Analyzer analyzer) throws IOException {

    DocumentWriter dw = new DocumentWriter(ramDirectory, analyzer, this);

    dw.setInfoStream(infoStream);

    String segmentName = newSegmentName();

    dw.addDocument(segmentName, doc);

    synchronized (this) {

       segmentInfos.addElement(new SegmentInfo(segmentName, 1,

              ramDirectory));

       maybeMergeSegments();

    }

}

    每加一个Document对象,都会调用一次这个函数,而segmentInfos也就是segmentInfo的一个集合了,那么如何区别集合中的元素,我们可以从它的构造函数中看出来是用segmentName来区别的,而segmentName是由newSegmentName函数产生的:

private final synchronized String newSegmentName() {

    return "_" + Integer.toString(segmentInfos.counter++,

Character.MAX_RADIX);

}

    Character.MAX_RADIX值是36,也就是把segmentInfoscounter这个记数值转换成36进制的数的字符串。最前面有”_”符号。

    然后我们看到segmentInfos加入元素的函数,发现segmentName似乎是唯一区别元素的标志,我们看一下SegmentInfo

final class SegmentInfo {

    public String name; // unique name in dir

    public int docCount; // number of docs in seg

    public Directory dir; // where segment resides

 

    public SegmentInfo(String name, int docCount, Directory dir) {

       this.name = name;

       this.docCount = docCount;

       this.dir = dir;

    }

}

    和所猜测的一样,注释上写namedir中唯一的一个名字,而我们也可以看到,每次就写入了一个Document,所以docCount在上面是1dir不用说,当然是ramDirectory

    那么也就是说加入了很多文档,这些文档写在内存Dirctory中,名字是_o_1_2,要等到某一个时刻,将它们合并起来,最终显示成我们所见到的那样。

    但是我们前面一直看的都是IndexWriter.close中合并,在我们以前的例子中没有问题,但是如果我们加入了上万条,上百成条Document,等到close的时候再合并,是不是不太现实,因为内存不太可能装的下那么多Document,也就是我再一次讲了似对非对的内容。也许大家也都注意到了maybeMergeSegments函数:

private final void maybeMergeSegments() throws IOException {

    long targetMergeDocs = minMergeDocs;

    while (targetMergeDocs <= maxMergeDocs) {

       // find segments smaller than current target size

       int minSegment = segmentInfos.size();

       int mergeDocs = 0;

       while (--minSegment >= 0) {

           SegmentInfo si = segmentInfos.info(minSegment);

           if (si.docCount >= targetMergeDocs)

              break;

           mergeDocs += si.docCount;

       }

 

       if (mergeDocs >= targetMergeDocs) // found a merge to do

           mergeSegments(minSegment + 1);

       else

           break;

 

       targetMergeDocs *= mergeFactor; // increase target size

    }

}

         这里的minMergeDocs就是新版本中的maxBufferedDocs,看新版本中的名字差不多知道,它就是索引写到磁盘之前,可以保存多少个Document,默认值是10maxMergeDocs默认值是Integer型的最大值,maxMergeDocs的道理要转个弯才能理解,过会再讲。

         我们看中间的while循环,得到segmentInfos中的一个SegmentInfo,如果它其中的Document数量大于targetMargeDocs就跳出循环,要不就加上这个si中的Document数量。接下来,如果真的到可以合并的数量,那么就合并,不然就跳出循环。

         mergeSegments我们已经见过了,参数中的+1也没什么含义,就是把刚才循环中最后减掉的加回来,我们上一次见它是在flushRamSegments函数中见到的。

         我们看到最后一行,targetMergeDocs乘了mergeFactormergeFactor默认值是10segmentInfos的父类是Vector这也就是为什么中间的循环是倒着写的原因。也就是循环第一次是targetMergeDocsDocument合并为一个segment,第二次看有没有targetMergeDocs * mergeFactorDocument可以合并为一个segment,解释刚才的问题,maxMergeDocs就是控制最多有多少个Document合并为一个segment的参数。

         当时我们在讲flushRamSegments的时候,跳过了两个if现在列出来看一下:

/** Merges all RAM-resident segments. */

private final void flushRamSegments() throws IOException {

    int minSegment = segmentInfos.size() - 1;

    int docCount = 0;

    while (minSegment >= 0

           && (segmentInfos.info(minSegment)).dir == ramDirectory) {

       docCount += segmentInfos.info(minSegment).docCount;

       minSegment--;

    }

    if (minSegment < 0

           || // add one FS segment?

           (docCount + segmentInfos.info(minSegment).docCount) >

                  mergeFactor

           || !(segmentInfos.info(segmentInfos.size() - 1).dir ==

 ramDirectory))

       minSegment++;

    if (minSegment >= segmentInfos.size())

       return; // none to merge

    mergeSegments(minSegment);

}

         我们看到第一个while把那么ramDirectory中的segmentDocument数量加到了一起,第一个if中有判断是否数量已经超过了mergeFactor,那么就为合并做准备减去1(因为前面会减去的)。而如果都是在硬盘上了,那么也加上1(前面不会减的),为了下一步,直接返回。

         而我们在mergeSegments函数中见到了一个重要的类,我们当时没有理睬,它就是SegmentRead类。

SegmentInfo si = segmentInfos.info(i);

IndexReader reader = SegmentReader.get(si);

         get函数列出来看一下:

public static SegmentReader get(SegmentInfo si) throws IOException {

    return get(si.dir, si, null, false, false);

}

public static SegmentReader get(Directory dir, SegmentInfo si,

       SegmentInfos sis, boolean closeDir, boolean ownDir)

       throws IOException {

    SegmentReader instance;

    try {

       instance = (SegmentReader) IMPL.newInstance();

    } catch (Exception e) {

       throw new RuntimeException("cannot load SegmentReader class: "

+ e);

    }

    instance.init(dir, sis, closeDir, ownDir);

    instance.initialize(si);

    return instance;

}

         我们看到这两个函数,还是不太明白怎么初始化了,接着看:

void init(Directory directory, SegmentInfos segmentInfos,

       boolean closeDirectory, boolean directoryOwner) {

    this.directory = directory;

    this.segmentInfos = segmentInfos;

    this.directoryOwner = directoryOwner;

    this.closeDirectory = closeDirectory;

}

    看到这里也没有太大启发,只知道把Directory赋值了,其它的参数也不太明白什么意思。

private void initialize(SegmentInfo si) throws IOException {

    segment = si.name;

 

    // Use compound file directory for some files, if it exists

    Directory cfsDir = directory();

    if (directory().fileExists(segment + ".cfs")) {

       cfsReader = new CompoundFileReader(directory(), segment

+ ".cfs");

        cfsDir = cfsReader;

    }

 

    // No compound file exists - use the multi-file format

    fieldInfos = new FieldInfos(cfsDir, segment + ".fnm");

    fieldsReader = new FieldsReader(cfsDir, segment, fieldInfos);

 

    tis = new TermInfosReader(cfsDir, segment, fieldInfos);

 

    // NOTE: the bitvector is stored using the regular directory, not cfs

    if (hasDeletions(si))

       deletedDocs = new BitVector(directory(), segment + ".del");

 

    // make sure that all index files have been read or are kept open

    // so that if an index update removes them we'll still have them

    freqStream = cfsDir.openInput(segment + ".frq");

    proxStream = cfsDir.openInput(segment + ".prx");

    openNorms(cfsDir);

 

    if (fieldInfos.hasVectors()) { // open term vector files only as needed

       termVectorsReaderOrig = new TermVectorsReader(cfsDir, segment,

              fieldInfos);

    }

}

         这个函数看到了我们想看的东西,但是开头的.cfs文件似乎没有见过,这是因为我们的测试程序中加了setUseCompoundFile(false),如果不加这一条,那么索引就会合起来,形成一个.cfs文件。.cfs暂时不去理会。

         我们将刚才si.dir中的.fnm.fdt.fdx.tis.tii.frq.prx.f都用相应的类对象做好读取的准备。

  评论这张
 
阅读(1062)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017