Elasticsearch 中的近实时搜索与持久化

近实时搜索(Near-real-Time Search)

一条记录从调用 Index API 到最终持久化,会经历如下过程:

  1. index 阶段,先在 document buffer 积聚(此时还不是 searchable ),然后 Index API 请求被响应成功.
  2. refresh 阶段,被动(Refresh API)或主动(每隔1s一次)将 document buffer 中的文档生成 segment(此时 searchable),这一过程称为refresh,此时 segment 存在于 file system cache

    周期性refresh只针对最近30秒收到过搜索过请求的索引: By default, Elasticsearch periodically refreshes indices every second, but only on indices that have received one search request or more in the last 30 seconds.

  3. segment merge, 由于 segment 的生成很频繁,导致搜索时要遍历过多小的 segment 影响性能,所以ES会定期地将多个小的segment合并为一个大的.
  4. flush 阶段,被动(调用Flush API)或主动(取决于配置index.translog.durability)将 file system cache 中的 segment 刷盘,此时真正的持久化.

“Near-real-Time Search”的意思是指在“第一步完成”到“第二步完成之前”这个时间段中,无法通过 Search API 查到 Index API 插入的记录, 因为这时记录还没被索引到 lucene segment 中.

段合并(segment merge)

InnoDB 中表的索引是可变的,CRUD 都作用于索引页,页空间不足以容纳记录时产生页分裂,页中有效利用空间不足MERGE_THRESHOLD(默认 50%)时做页合并.

ES shard 是一个 Lucene index,由若干个segment组成,segment是不可变的,所以增删改在refresh时总是会产生新的 segment,而refresh默认1s一次,这会导致lucene index 的segment过多. 另一方面,查询的时候需要遍历segment,过多的小segment会导致性能降低.

基于以上原因,ES会周期性的做段合并,在合并完成后,新的段被打开,旧的段被删除. 合并操作由后台线程执行,线程数量可通过index.merge.scheduler.max_thread_count设置.

持久化(Translog)

Translog的产生背景

在 InnoDB 中,如果直接持久化缓冲页,会带来大量随机 IO (因为事务造成的修改通常发生在多个不连续的页),并且即使只改了页中一个字节,也要产生 16KB 的IO. 对此问题,InnoDB的解决方案是 redolog,其有体积小,且连续IO写入的特点,在保证 Durability 的同时,也尽可能提升了性能.

ES 中,持久化增删改需要执行一次“Lucene commit”,这个操作用官方的形容是: ”which is a relatively expensive operation“,可以合理推测,ES 面临着 InnoDB 一样的问题————持久化与性能之间的矛盾,类似地,ES也引入了类似redolog的概念————tanslog.

Translog的回收策略

InnoDB 中用一个 ring buffer 来存储redolog,随着增删改操作的执行,redolog 会越积越多,因此在适当的时候也需要将脏页刷盘,这样就可以回收相应的redolog,这一过程称为checkpoint,它让ring buffer的头指针向后移动,也就释放了空间,ring就这样“转动”起来了.

ES 中,可通过 index.translog.flush_threshold_size 控制,当translog积累多少后,执行“lucene commit”将 file system cache中的segment刷盘,以回收相应的translog. 这个过程也类似 InnoDB 中的 checkpoint.

Durability 的几种级别

人们对性能的追求是不会停止的

  • 有时,性能大于持久性,允许丢失最近的数据
  • 有时,持久性大于性能,只要一个操作被“acknowledged”,那么之后即使crash,数据也不能丢失.

    对于MySql,”acknowledged“意味着commit操作返回成功,对于ES,这意味着Index/Update/Delete API返回了成功.

对此,InnoDB 与 ES 都给出了对应的配置,让用户根据需要定义合适的持久化策略

InnoDB中,提供了 innodb_flush_log_at_trx_commit选项,可选值如下:

  • 0,异步持久化redolog,acknowledged前不立即刷盘redolog,发生crash会丢失最近数据
  • 1(默认值),同步持久化redolog,acknowledged前刷盘redolog,
  • 2,同步写到 file system cache,如果只是mysql进程挂了,那么cache数据会由OS负责刷盘; 如果OS也挂了,那么数据就没了.

在ES中,提供了 index.translog.durability 选项,可选值如下:

  • request(默认值),同步持久化,acknowledged之前,translog已经被持久化
  • async,异步持久化,后台线程定期持久化(取决于index.translog.sync_interval),crash会丢失最近数据.

基于Translog的实时CRUD

InnoDB 对页的修改直接作用于buffer pool中的缓冲页(没有document buffer),后续的读写也是优先先走缓冲页,所以任何写操作都是实时可见的.

ES 用 translog 被用来提供实时 CRUD ,对于 GET API、Update API、Delete API,共同特点是都提供了文档 ID,ES 首先通过 ID 去检查 translog 中有无最近的变更(由此可以合理推测,ES 的 translog 在数据组织上是用文档 ID 索引的),这使得增删改操作对之后基于文档 ID 的操作来说是立即可见的.

而对于 Search API,由于其依赖 lucene segment 去实现各种复杂的索引方式,所以只能等 document buffer 被 refresh 到 segment. 因此 Search API 是 “near-real-time” 的.

参考

おすすめ

転載: juejin.im/post/7035142136173428767