原文地址: http://java.dzone.com/news/merge-policy-internals-solr
上周,一个同事问了我一个关于solr的段合并的简单问题. 在讨论这个问题了几分钟后, 我意识到关于这个问题还有许多精细的地方值得注意, 所以我开始阅读源代码, 然后发现了一些很有意思的事情, 这也是我在这个文章将要总结的东西 .
首先, 什么是合并策略(merge policy) ?
MergePolicy是一个抽象类, 它负责挑选出哪些段需要进行合并. 这个类会产生一个MergeSpecification, MergeSpecification这个类包含了一组OneMerge对象. 每一个OneMerge对象代表一个合并(merge)动作, 这个合并动作是由即将被合并成一个大段的多个小段定义的.
当索引发生变化的时候,IndexWriter类就会调用MergePolicy去获取一个MergeSpecification. 接着, 它又调用MergeScheduler, MergeSheduler负责决定什么时候去执行合并动作. MergeScheduler主要有两个实现类: ConcurrentMergeScheduler--对于每个合并都使用单独的线程去执行, SerialMergeScheduler--在当前的线程中串行地执行所有的合并.最后,当真正执行合并的时候, IndexWriter完成部分工作, 把其他的部分工作交给SegmentMerger.
所以,如果我们想要知道什么时候多个段将要被合并, 为什么一个段会被合并而另一个不会, 以及其他的类似事情, 我们应该好好看下MergePolicy.
MergePolicy有多个实现,我主要注意到的是其中一个(LogByteSizeMergePolicy),因为它是Solr的默认实现(译者注:在最新版的solr中,默认实现是TieredMergePolicy),而且我相信它应该是被大多数人所使用的.MergePolicy定义了三个抽象方法来构造一个MergeSpecification
- findMerges方法, 只要索引发生改变,这个方法就会被调用.这篇文章中,我会讲到这个方法
- findMergesForOptimize,只要optimize动作执行的时候,这个方法就会被调用
- findMergesToExpungeDeletes,只要擦除删除(expunge deletes)动作执行的时候,这个方法就会被调用
- mergeFactor:这个参数决定了许多事情,比如多少个段会被合并到一个新段中,在一个组(level)中最多能放入多少个段,以及每个组(level)的跨度.可以在solrconfig.xml中设置.
- minMergeSize:所有小于这个参数值的段都属于同一个组(level).这个值是固定的.
- maxMergeSize:所有大于这个参数值的段都不会被合并.这个值也是固定的.
- maxMergeDocs:所有包含大于这个参数值的document的段都不会被合并.可以在solrconfig.xml中设置这个参数.
这种情况下有多少个组(level) ? 让我们看下, 最大的段大小 为200MB, 因此, levelMinSize就为35MB. 最新的大于levelMinSize的段是x,所以第一个组(leve)就包含x和所有比x老的组. 这就意味着只有一个组!
挑选哪些段合并
在决定了组(levels)后, MergePolicy就将挑选要合并的段.为了完成这个, 它会分别分析每个组(level). 如果一个组(level)中的段个数小于mergeFactor,那么它就会跳过这个组(level).否则,每个组就会被合并成一个新的段.如果一个组中至少一个段的大小大于maxMergeFactor,或者包含多于maxMergeDocs个document,那么这个组就会被跳过.
回到前面的第二个例子,这儿只有一个组,合并的结果将是:
minMergeSize and maxMergeSize
本文中,我已经提到过合并过程中起作用的这两个参数,它们的值在Lucene的源代码中是被写死的,它们的值分别是:
- minMergeSize:1.6MB.这意味着任何小于1.6MB的段都会被包含在最后一个组(level)里
- maxMergeSize:2GB.这意味着任何大于2GB的段永远都不会被合并.