jasper的技术小窝

关注DevOps、运维监控、Python、Golang、开源、大数据、web开发、互联网

elasticsearch源码分析之Merge

作者:jasper | 分类:ElasticSearch | 标签:   | 阅读 4052 次 | 发布:2015-12-06 10:06 p.m.

上一篇中说道数据被flush到磁盘之后,其实是一个一个小的segment,这样的segment每秒都在产生,大量的segment会占用大量的资源,而且每次search的时候会去遍历所有的segment,从而导致性能低下,ES为了优化,在后面运行任务将这种小的segment归并为较大的,这个过程就叫做“merge”,这篇文章就来探究探究merge是怎么工作的。

概述

将小的segment合并为大的segment的过程,直接用官方的图来说明即是:

merge开始状态:

merge结束状态:

上面也说了merge其实就是在后台运行的线程,那么这些线程的配置是怎么样的,跑起来后由干了些啥呢,下面且细细道来。

merge的配置

配置的加载都在MergeSchedulerConfig这个类中,我们看看都有哪些:

  • MAX_THREAD_COUNT:同时工作的最大线程数,因为每个merge线程都是独立运行的,当最大线程数达到时,剩下的merge任务将会一直等待,直到有空余的线程出来;该参数由index.merge.scheduler.max_thread_count配置,默认值为Math.max(1, Math.min(4, Runtime.getRuntime().availableProcessors() / 2)),官方也说了,该参数在SSD下测试工作很好,对于普通磁盘,建议减小到1;
  • MAX_MERGE_COUNT:同时运行的最大的merge的数量,由index.merge.scheduler.max_merge_count配置,默认值为MAX_THREAD_COUNT+5;
  • AUTO_THROTTLE:是否要自动限流,由index.merge.scheduler.auto_throttle配置,默认是true;
  • NOTIFY_ON_MERGE_FAILURE:index.merge.scheduler.notify_on_failure

merge的策略

merge的策略有很多,这里就挑几个常用的介绍一下:

  • index.merge.policy.floor_segment:小于这个大小的 segment,优先被归并,默认值是2MB;
  • index.merge.policy.max_merge_at_once:一次最多合并多少个segment,默认是10;
  • index.merge.policy.max_merge_at_once_explicit:optimize时,一次最多合并多少segment,默认是30;
  • index.merge.policy.max_merged_segment:大于这个值的segment不参与合并,默认为5GB;
  • index.merge.enabled:是否开启merge,默认当然是true了,注意:虽然有这一项配置,但是在生产环境中千万别设为false,不然可能会有很严重的问题;

运行merge

merge的运行在ElasticsearchConcurrentMergeScheduler中完成:

1、运行beforeMerge,里面会获取本次要merge的segment的数目,如果大于配置的segment则需要限流:

indexingService.throttlingActivated();
activateThrottling();

其实,原理就是使用锁lockReference,锁住引用;

2、运行merge,这一段逻辑其实是Lucene的IndexWriter中的,获取merge的策略mergePolicy,由MergePolicy来决定选择哪些段应该参与合并,将选择出的段合并成新段的过程,这一步由MergeScheduler来执行,主要由下面三部分组成:

mergeInit(merge);
mergeMiddle(merge, mergePolicy);
mergeFinish(merge);

其实大体过程就是将小的segment合并为大的,等较大的该segment刷到磁盘后,commit文件做出相应变更,改成新的大segment。等检索请求都从小segment 转到大segment上以后,删除没用的小segment 具体里面的就涉及到Lucene的细节了,感兴趣的可以参考大神的对Lucene合并的解析:http://www.cnblogs.com/forfuture1978/archive/2010/03/06/1679501.html

3、运行afterMerge,里面会获取本次要merge的segment的数目,如果小于配置的segment则需要限流:

indexingService.throttlingDeactivated();
deactivateThrottling();

其实,原理就是释放锁;

总结

merge操作是一个很消耗性能的过程,特别是对于磁盘的IO,因此对于merge我们希望其最好不要影响到其他任务,所以要合理地配置merge,特别地,indices.store.throttle.max_bytes_per_sec配置可以限制merge每秒的大小。另外,对于老的数据(建议只针对不再写入的索引,因为这个很耗资源,对于正在写入的操作,可能会大大降低索引速度),可以使用optimize接口(2.1之后改为了_forcemerge接口,该接口其实就是不受merge policy的限制)将index手动merge成相应数量的segment数。


转载请注明出处:http://www.opscoder.info/es_merge.html

其他分类: