转-Lucene's MergePolicy

原文https://blog.csdn.net/zteny/article/details/79669977
Lucene’s MergePolicy
Lucene很多特征,在我看来跟LSM-Tree的数据库非常相似,甚至很多问题的解决方式都如出一辙。这里我想跟大家来聊聊Lucene的Segment合并问题,这个问题同样发生LSM-Tree数据库(HBase)。

我们知道我们每次冲刷索引时,Lucene都会生成一个Segment。类似就是每个MemStore的冲刷势必会产生一个HFile的道理是一样一样的。这里我想跟大家分享的是Lucene的合并策略,前面那长长铺垫如有兴趣的同学可以帮忙看一下,否则直接看Lucene的合并策略部分也无妨。

一、Segment很多会怎么样?
我们已经知道所有的索引结构都是树,因为树结构能带来比较高查找效率,同时对磁盘也更友好。我们也知道树型结构跟磁盘一样并不适合做插入写操作,因此不过是Lucene还是HBase都是在内存上先构建完一个棵后才最终落盘的。方便容易理解和描述,(在理想环境下)我们假定在某种极限条件下,使得每个文档都会触发冲刷产生Segment。显然此时每个Segment文件都有且只有一个文档,此我们查找效率被完全降级变成线性查找。
如果是每两个文档一起才会被冲成,那么它的查询效率将会是$O(logN)$。在这种假设下,我们可以理解成查找效率最终会是$O(K)$(K为Segment的数量)。

当然经验告诉我们这是不可能的,因为Segment内进行查询也是需要时间的。当文件越来越大,Lucene在Segment里进行查找时它的IO代价会越来越高。即是需要把索引加载到内存的代价也越来越高了,当然Lucene采用的一个非常先进FST(Finite-State-Transfer,能够有效减少加载代价,这里先不展开介绍)。

那我们结论是,当Segment在一个适当大小时,我们查找效率会很高。所以我们不想让Semgnet文件太多,也不想让Segment文件太大。

二、所以需要合并
基于以上结论,我们希望总能将那些不太大的Segment合并成另一个不太大的Segment以获取更高查找效率。不管Lucene还是HBase(或者是其它LSM Tree结构数据库)都有合并过程,合并主要是有以下两个大作用。

  1. 减少文件的个数提高性能;
  2. 清除无效文档(被标识为删除的文档)。

跟LSM-Tree的数据库一样都有合并的问题,而且功能也都雷同。目的就是为清除无效的数据,同时减少文件数量。

2.1 为什么只有追加
因为磁盘跟数组一样,删除和插入都非常不友好,为了更极致的性能,我们将所有操作变成顺序写。

理论上,Lucene/LSM-Tree并没有更新和删除操作。这些操作都是通过特殊的手段来实现,从磁盘的角度看都是写入。在Lucene/LSM-Tree做一个更新时尽管与原文档长度完全一样,也只能将原文档标记为删除,同时再追加一条新记录。
只因为他们在方面都有显著的特点,即是Append only,顺序写入。

因此他的写性能特别高(线性时间复杂度),但需要消耗额外资源来完成清除和合并的操作。所以说,所有的美好的都是有人代价。在写时拥有非常优秀的表现的同时需要被后期消耗甚至更多的代价来优化读的性能。当然合并就是读优化的一部分,读优化还有其它很多很多内容。

2.2 Lucene索引合并策略
竟然合并势在必行,那我们只能寄希望于每次合并都是最有效率的。那么问题来了,怎么合并怎么才是有效的呢?为此,出生比较早的Lucene有以下实践(据悉HBase也走过相当的路)。

Lucene提供如下三大MergePolicy,后面Solr又扩展了一种叫SortingMergePolicy。

LogMergePolicy
LogByteSizeMergePolicy
LogDocMergePolicy
TieredMergePolicy
UpgradeIndexMergePolicy
TieredMergePolicy在Lucene4.0成为Lucene的默认合并策略。

老实说最后一种(UpgradedIndexMergePolicy)我也不了解它。也没用过,也鲜有人提及。

2.2.1 LogMergePolicy
这里先来介绍一下LogMergePolicy吧,这种合并策略非常好理解,她提供一种定长的合并方式。我们只介绍LogByteSizeMergePolicy,因为LogDocMergePolicy与LogByteSizePolicy的原理一样。LogByteSizeMergePolicy描述了合并条件的上水位和下水位,默认的上水位是2Gb。即是捡出一堆不超出上限的Segmnet进行合并,且将这一堆Segment分成若干层,默认上限是2GB。

在LogMergePolicy的实现上,总能感觉到作者在设计这个策略的时候,已经是假设Segment文件大小基本符合梯形分布。

实际上LogMergePolicy的实现有些晦涩难懂,不过不用着急,都坐下听我说。如果Segment文件的大小总能大体符合梯形分布的,且从左到右按从大到小排列的。然后在这个系列中找到最大的Segment,并把其大小记为max,然后把max减去一个跨度记为bottom。同时从右向左遍历找到第一个Segment文件大小小于等于bottom的Segment,并把其下标记为upto。然后把从左开始位置start=0,到upto之间的Segment作为第一层级。然后把start=upto+1,又一次从最右边开始找到bottom所有Segment,这个区间称为第二层级。以此类推至将所有Segment都划分完成。

然后根据MergeFactor(合并因子,默认为10)把一个层级拆分成多个合并单元,拆分合并单元的规则是根据start到upto之间的Segment的个数size做如下处理:

  1. size < 10,start=upto+1,即是放弃整个Level
  2. Size = 10*N,拆分N个合并单元
  3. size = 10N + m,把前10N拆分N个合并单元,把最后m个Semgent扔掉

值得注意是如果这个合并单元里有一个Segment越界或者正处于合并状态或待合并状态的,这个合并单元会被放弃。

说一千道一万,我觉得都是废话。下面用一句话来概括一下这个合并策略:

只能与相邻的Segment进行合并,不管邻居们是高大还是矮小,它们最后总会合并在一起。

原本以为,按Segment编码排序从左向右选取一定量的Segment进行合并,同时还引入了一个很复杂的分层算法。讲道理的话,Segment按文件大小跟Segment编号成反比。若用一个柱体来表示文档的大小话,柱体最终应该总能形成是一个梯形分布。然而事实上事与愿违,他最终分布往往是不规则的。即是理想状态下,segment文件是按梯形分布的。然而现实非常现实却是那么的残酷,总是能出现小文档跟大文档发生合并。

那么在这个范围内的索引都会被拉进来发生合并,而且在极端是条件下,几兆的文件在跟上千兆的文件在合并。这相当于什么呢,相当于一个1/2Gb的文件在移来移去。这是非常耗资源的过程,而且带来的效益无乎没有。

小文档跟大文档合并会怎么样?
首先是Lucene/LSM-Tree都是Append Only的,所以当出现合并的时候,实际上是把原Segment读出来重新写入磁盘变成一个新Segment。

好了,开始“如果”之路。
如果我有两个Segment,一个有10万文档,文件大小为2Gb;另一个有1个文档,文件大小为1Kb。现在来了,此时是不是可以理解为把大文件重新写一遍。如果你的Lucene总是了发生这样的事的话,那你的Lucene的Merge队列将会被阻塞,你的系统十分耗机器的资源,估计也正处于崩溃的边缘了。

到这里我们可以清晰的知道,LogMergePolicy的设计是有缺陷的MergePolicy。

2.2.2 TieredMergePolicy
既然文档合并是一个不可规避的问题,那我们只能让每次合并有更大效益。

实际上LogMergePolicy分层就是为了解决合并单元的Segment大小差异过于悬殊的问题,只是说没有解决好而已。因此才能引入TieredMergePolicy一个全新合并策略。经过以上的描述其实,不难知道应该怎么避免这个问题。其最终目的是非常清楚的,就是宁可少发生一些合并,也不要将大文件跟小文件合并。

TieredMergePolicy在每一次捡起时,都会先做一次排序,按文件大小排序。因此TieredMergerPolicy已经打破了必须与相邻的Segment才能合并的规则,从全局的眼界去挑选更合适的,大小更接近的Segment进行合并。

TieredMergePolicy的实现是挺复杂,也很难懂的。早期Lucene的代码着实有些天秀的感觉,常常会被代码组织得非常晦涩。往后的代码,也能看到很多难懂的代码被重构了,可读性越来越高。

最后介绍一下TieredMergePolicy的实现粗节,

  1. Segment先按从大到小排序
  2. 统计并过滤大于预设最大Segment的一半的Segment
  3. 过滤掉正处于合并状态,或者已经在待合并状态的Segment
  4. 如果一层里的合并后的总大小超过设定的最大值(默认5Gb),该层被扔掉
  5. 对每层侯选集进行打分,依据是合并后的文件大小以及删除率

TieredMergePolicy总能避免选中大文件出来进行合并,这些大文件一般都是以为更高的删除率时,或者系统比较空闲的时候会被合并。由些可见TieredMergePolicy总选择合并代价最低,且结果往往能捡出性价比更高的Segment进行合并。

三、结语
TieredMergePolicy如果没有记错应该是大神Mike带来,出生于3.x并在4.0时升级为默认的MergePolicy。据大神Mike表示,TieredMergePolicy带来8x的性能提升。

虽然说MergePolicy问题,近期并没有在Lucene的issue中提及了。我也是偶然整理自己的笔记的时候发这个topic,因此整理整理发出来与大家分享。

Es中指导意义:
lucene合并 ,segment文档数合并LogDocMergePolicy,segment大小合并LogByteSizeMergePolicy TieredMergePolicy ,默认使用TieredMergePolicy。需要你自己调参数 包括复合文件 mergeFactor mergeSize mergeDocs 参数很多需要创建writer的时候自己调参。。。慢慢调吧

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,277评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,689评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,624评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,356评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,402评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,292评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,135评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,992评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,429评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,636评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,785评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,492评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,092评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,723评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,858评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,891评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,713评论 2 354

推荐阅读更多精彩内容