3.4.2.20.Refresh/flush
创作人:章海怒
审稿人:欧阳楚才
简而言之,_refresh 用于使新文档可见以进行搜索。 而 _flush 用于将内存中的 Segment 段保留在硬盘上。_flush 不会影响 Elasticsearch 中文档的可见性,因为搜索是在内存中
Segment 进行的,而 _refresh 则会影响其可见性。两个操作都与 ES 对数据的存储有着千丝万缕的联系,下面我们就从数据的存储开始仔细梳理其中的区别。
Elasticsearch 中的 refresh
当我们把一条数据写入到 Elasticsearch 中后,它并不能马上被用于搜索。新增的索引必须写入到 Segment 后才能被搜索到,因此我们把数据写入到内存缓冲区之后并不能被搜索到。新增了一条记录时,Elasticsearch 会把数据写到 translog 和 in-memory buffer (内存缓存区)中,如下图所示:
在此期间,该文档不能被搜索,但是我们还是可以通过 ID 使用 GET 来获得该文档。如果希望该文档能立刻被搜索,需要手动调用 refresh 操作。在 Elasticsearch 中,默认情况下 _refresh 操作设置为每秒执行一次。 在此操作期间,内存中缓冲区的内容将复制到内存中新创建的 Segment 中,如下图所示。 _refresh 接口触发的结果就是新数据可用于搜索。
这个 refresh 的时间间隔可以由 index 设置中 index.refresh_interval 来定义。只有在 buffer的内容写入到 Segement 后,这个被写入的文档才变为可以搜索的文档。通常 buffer 里的内容被写入到 Segment 里去有三种触发方式:
l 由索引中的设置所指定的 refresh_interval 启动的周期性的 refresh。在默认的情况下为1s。这使对索引的最近更改可见以进行搜索。 也可以设置为 -1 以禁用刷新。在 Elasticsearch 7.0 发布之后,如果未明确设置此设置,则至少在 index.search.idle.after 秒之后仍未看到搜索流量的分片在收到搜索请求之前将不会接收后台刷新。 命中空闲分片的搜索将等待下一次后台刷新(在1秒内)。 此行为旨在在不执行搜索时在默认情况下自动优化批量索引。 为了退出此行为,应将显式值 1s 设置为刷新间隔。
l 在导入文档时强制调用 refresh,例如:PUT twitter/_doc/1?refresh=true
l 当 In Memory Buffer 满了,在默认的情况下为 node Heap 的 10%
这个过程会产生一个叫 Lucene flush 的操作,也会生产一个 segment。执行完 refresh 后的结果如下:这个 refresh 的时间间隔可以由 index 设置中 index.refresh_interval 来定义。只有在 buffer的内容写入到 Segement 后,这个被写入的文档才变为可以搜索的文档。通常 buffer 里的内容被写入到 Segment 里去有三种触发方式:
l 由索引中的设置所指定的 refresh_interval 启动的周期性的 refresh。在默认的情况下为1s。这使对索引的最近更改可见以进行搜索。 也可以设置为 -1 以禁用刷新。在 Elasticsearch 7.0 发布之后,如果未明确设置此设置,则至少在 index.search.idle.after 秒之后仍未看到搜索流量的分片在收到搜索请求之前将不会接收后台刷新。 命中空闲分片的搜索将等待下一次后台刷新(在1秒内)。 此行为旨在在不执行搜索时在默认情况下自动优化批量索引。 为了退出此行为,应将显式值 1s 设置为刷新间隔。
l 在导入文档时强制调用 refresh,例如:PUT twitter/_doc/1?refresh=true
l 当 In Memory Buffer 满了,在默认的情况下为 node Heap 的 10%
这个过程会产生一个叫 Lucene flush 的操作,也会生产一个 segment。执行完 refresh 后的结果如下:
我们可以看出来,相比上一个状态,In-meomory buffer 中的数据被清空,但是是 Translog 里还是有东西的。
refresh 的开销比较大,我在自己环境上测试 10W 条记录的场景下refresh一次大概要14ms,因此在批量构建索引时可以把 refresh 间隔设置成-1来临时关闭 refresh, 等到索引都提交完成之后再打开 refresh, 可以通过如下接口修改这个参数:
PUT /my-index-000002/_settings {"index": {"refresh_interval":"-1"}}
另外当你在批量创建索引时,可以考虑把副本数设置成0,因为 document 从主分片(primary shard)复制到从分片(replica shard)时,从分片也要执行相同的分析、索引和合并过程,这样的开销比较大,会影响批量创建的速率,你可以在构建索引之后再开启副本,这样只需要把数据从主分片拷贝到从分片:
PUT /my-index-000002/_settings {"index": {"number_of_replicas":0}}
执行完批量索引之后,再把刷新间隔与副本数改回来:
PUT /my-index-000002/_settings {"index": {"refresh_interval":"1s"}}
你还可以强制执行一次refresh以及索引分段的合并:
POST /my-index-000002/_refresh POST /my-index-000002/_forcemerge?max_num_segments=5
Translog 及持久化存储
但是,translog 如何解决持久性问题? 每个 Shard 中都存在一个 translog,这意味着每个
translog 会同时占用磁盘与内存。 translog 是同步且安全的,因此即使对于尚未 Commit 的文档,translog 也可以保证写入数据的持久性。 如果发生了异常,translog 也可以将未落盘但是已进入 translog 的数据恢复。 Translog 写事务日志入磁盘同样也是在一个固定周期内向磁盘 commit 或在成功完成请求(索引,批量,删除或更新)后。
《Elastic Stack 实战手册》——三、产品能力——3.4.入门篇——3.4.2.Elasticsearch基础应用——3.4.2.20.Refresh/flush(中) https://developer.aliyun.com/article/1229331