Py学习  »  Elasticsearch

Elasticsearch基础概念之基本原理

逍遥白亦 • 5 年前 • 498 次点击  

ES基本原理

名词解释

In-memory buffer: ES内存缓冲区,新建的document写入的地方

document:索引和搜索的主要数据载体,对应写入到ES中的一个doc

Segment: Lucene里面的一个数据集概念,前边提到的document最终就是存放在这里边,一个Segment包含多个document

Refresh:将In-memory buffer中的数据生成一个segment并放入系统缓存区,同时清空In-memory buffer

Translog: 新建的document也会写入到这个里边,记录这些日志,并用于回放

Flush:会先执行一次refresh操作,之后将系统缓存区中的一个一个Segment拷贝到磁盘中,之后清空translog

Refresh过程

新建的document会先写进ES内存缓冲区中,但是这个区域是不可见的,不能被搜索到,所以Refresh的过程就是产生一个segment放入系统缓存区,这部分区域是可读的,可以被搜索到。在默认情况下ES每隔1秒钟,会自动Refresh,保证数据可见。

Refersh的执行条件:

  1. 手动触发,通过GET查询以及index、bulk写入时指定refresh
  2. 自动触发,配置refresh_interval
  3. In-memory buffer满了

可以更改这个配置

改为10秒

POST /index/_settings
{“refresh_interval”: “10s”}

改为不自动更新

POST /index/_settings
{“refresh_interval”: “-1″}

Segment合并

由于每一次refresh都会新建一个Segment,那么随着refresh次数越来越多,segment文件也会越来越多,而每一次查询最终都会检索segment文件,每一个segment都会占用操作系统的CPU、文件句柄和内存资源,而且,在查询的时候,需要在每个segment上都执行一次查询,这样是很消耗性能的。

为了解决这个问题,es会自动定期的将多个小segment合并为一个大的segment,这次合并是真正意义上的物理删除。

当新合并后的segment完全写入磁盘之后,es就会自动删除掉那些零碎的segment,之后的查询都在新合并的segment上执行。Segment的合并会消耗大量的IO和cpu资源,这会影响查询性能。

下面是几个常用配置项

  1. index.merge.policy.floor_segment:该属性用于阻止碎片段的频繁刷新。小于或者等于该设定值的段将考虑被合并。默认值为2M。
  2. index.merge.policy.max_merge_at_once:该属性指定了索引过程中同一时刻用于合并的段的最大数量,默认为10。如果将值设置得更大,一次合并操作将合并更多的段,同时合并过程也需要更多的I/O资源。
  3. index.merge.policy.max_merged_segment:默认值为5GB,该属性指定了索引过程中单个段的最大容量。这个值是一个近似值,因为合并操作中,段的大小等于待合并段的总大小减去各个段中删除文档的大小。
  4. index.merge.policy.segments_per_tier:该属性指定了每层段的数量。较小的值带来较少的段。这意味着更多的合并操作,和更低索引性能。默认值为10,其值应该不低于index.merge.policy.max_merge_at_once属性值,否则就会使合并次数过多,引起性能问题。

Translog与Flush

由于Refresh操作将数据放入到内存中,而内存中的不保险,所以就会把每一条document新建和更新记录得translog中,这样如果出现意外情况,可以从translog中找到这部分数据,并进行恢复。然后会通过Flush操作,将这部分数据内存中的数据,写入磁盘里,也为了避免translog太大,同时情空该部分数据。

Flush的具体流程:

  1. 将系统缓存区里的一个一个segment写入到磁盘中
  2. 执行一次refresh操作
  3. 清空translog日志

具体源码如下:

// Only flush if (1) Lucene has uncommitted docs, or (2) forced by caller, or (3) the
// newly created commit points to a different translog generation (can free translog)
if (indexWriter.hasUncommittedChanges() || force || shouldPeriodicallyFlush()) {
    ensureCanFlush();
        try {
            translog.rollGeneration();
            logger.trace("starting commit for flush; commitTranslog=true");
            //将系统缓存区里的一个一个segment写入到磁盘中
            commitIndexWriter(indexWriter, translog, null);
            logger.trace("finished commit for flush");
            //执行一次refresh操作
            refresh("version_table_flush", SearcherScope.INTERNAL);
            //清空translog日志
            translog.trimUnreferencedReaders();
        } catch (AlreadyClosedException e) {
            throw e;
        } catch (Exception e) {
            throw new FlushFailedEngineException(shardId, e);
        }
         refreshLastCommittedSegmentInfos();

}

该段源码很好解释了执行flush的三个内部条件

  1. Lucene中有未提交的文档
  2. 强制调用
  3. 定期执行

ES默认配置中,translog的配置同flush进行绑定,每一次index、bulk这些写入更新操作,都会先flush然后返回200,但是这种配置会牺牲很大的性能,可以采用异步刷新的方式,并配置每隔几秒进行刷新,来提高写入性能。

注意:该操作要先关闭索引,才能执行

PUT /index/_settings
{
    "index.translog.durability": "async",
    "index.translog.sync_interval": "5s"
}

Flush执行条件:

  1. 默认配置下(index.translog.flush_threshold_period),每30分钟进行一次
  2. 当translog的大小超过设定值(index.translog.flush_threshold_size),默认是512MB
  3. 多少时间间隔内(index.translog.interval:多少时间间隔内会检查一次translog,来进行一次flush操作。es会随机的在这个值到这个值的2倍大小之间进行一次操作,默认是5s。)会检查一次translog,来进行一次flush操作。es会随机的在这个值到这个值的2倍大小之间进行一次操作,默认是5s。

近实时搜索与实时搜索

看过上边的Refresh操作,可以得知,在默认情况下,每隔一秒进行一次refresh操作,写入的docment才可以搜索出来,这是准实时的原因。但是ES也提供了实时搜索的功能。下面就是两种实时搜索的操作。

  1. GET查询
  2. 写入时同步执行refresh操作再返回200

这两种操作的原理都是手动执行refresh操作,保证数据从In-memory buffer写入到系统缓存区,但是这种操作,相比每秒或者定时刷新refresh,在性能上的开销是很大的,相当于每一次请求就会执行一次refresh操作,生成一个新的segment,完全没有用到In-memory buffer这个缓冲区。

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/103935