image.png

ES写数据

  • primary shard接收到写入数据请求时,会先把数据写入内存buffer中,如果只写内存buffer,机器宕机了,数据就会丢失,所以同时还会把数据写入translog文件中去。
  • 需要注意的是,此时客户端来搜索,是搜索不到这条数据的。
  • 如果内存buffer满了,或者es会定时把内存buffer中的数据刷入到磁盘文件中去,默认间隔时间是1秒,所以每秒钟会产生一个segment file文件。当然,如果内存buffer中没有数据,就不会产生一个空的segment file文件了
  • 现在操作系统写文件不是直接进入磁盘文件的,是先写入os cache中。所以es执行refresh操作时,是先把内存buffer中的数据刷到os cache,进入操作系统级别的一个内存缓冲中去。
  • 只要内存buffer中的数据被刷入os cache中,客户端执行搜索就可以搜索到了。所以为什么称es是近实时(NRT)的,就是这个原因,你向es中写入一条数据,一般1秒之后才会刷入os cache,才可以搜索到。
  • 你也可以通过调用es的restful api或者Java api,手动执行一次refresh操作,手动将内存buffer中的数据刷入os cache中,让数据立马可以被搜索到。
  • 数据刷入os cache中之后,内存buffer中的数据就会被清空,不会占据很大内存空间,但是translog日志文件是怎么处理的呢?
  • 当translog日志文件大到一定程度的时候,就会执行commit操作。
  • commit操作的第一步,就是把buffer中的数据refresh到os cache中去,清空buffer。然后将一个commit point写入磁盘文件,里面标识着这个commit point 对应的所有segment file。同时强行将os cache中目前所有的数据都fsync到磁盘文件中去。最后清空现有translog日志文件,重启一个 translog,此时 commit 操作完成。
  • 那么translog日志文件的作用是什么呢?
  • 在执行commit操作之前,数据要么是停留在buffer中,要么是在os cache 中,无论是buffer还是os cache都是内存,一旦这台机器宕机了,内存中的数据就全丢了。所以需要将数据对应的操作写入一个专门的日志文件translog中,一旦此时机器宕机,再次重启的时候,es 会自动读取translog 日志文件中的数据,恢复到内存 buffer 和 os cache 中去。
  • translog其实也是先写入os cache的,默认每隔5秒刷一次到磁盘中去,所以默认情况下,可能有5秒的数据会仅仅停留在buffer或者translog文件的os cache 中,如果此时机器挂了,会丢失5秒钟的数据。但是这样性能比较好,最多丢5秒的数据。也可以将translog设置成每次写操作必须是直接fsync到磁盘,但是性能会差很多。
  • 这个commit操作叫做flush。默认30分钟自动执行一次flush,但如果 translog过大,也会触发flush。flush操作就对应着commit的全过程,我们可以通过es api,手动执行flush操作,手动将os cache中的数据fsync强刷到磁盘上去。

    ES读取数据

  • 写请求是写入primary shard,然后同步给所有的replica shard;读请求可以从primary shard或replica shard读取,采用的是随机轮询算法

    通过doc id来进行读取

  • 客户端发送请求到任意一个node,成为协调节点(coordinate node)。

  • 协调节点对doc id进行哈希路由,将请求转发到对应的node,此时会使用round-robin随机轮询算法,在primary shard以及其所有replica中随机选择一个,让读请求负载均衡。
  • 接收请求的node返回document给协调节点。
  • 协调节点返回document给客户端。

    查询

  • 客户端发送请求到一个协调节点(coordinate node)。

  • 协调节点将搜索请求转发到所有的shard对应的primary shard 或replica shard。
  • 查询阶段(query phase):每个 shard 将自己的搜索结果(其实就是一些 doc id)返回给协调节点,由协调节点进行数据的合并、排序、分页等操作,生成最终结果。
  • 返回阶段(fetch phase):接着由协调节点根据doc id去各个节点上拉取实际的document 数据,最终返回给客户端

    删除/更新

  • 如果是删除操作,commit 的时候会生成一个 .del 文件,里面将某个 doc 标识为 deleted 状态,那么搜索的时候根据 .del 文件就知道这个doc是否被删除了。

  • 如果是更新操作,就是将原来的doc 标识为deleted状态,然后新写入一条数据。
  • 内存buffer每refresh一次,就会产生一个segment file,所以默认情况下1秒产生一个segment file,这样下来segment file 会越来越多,此时es会定期执行merge操作。
  • 每次merge的时候,会将多个segment file合并成一个,同时这里会将标识为deleted的doc给物理删除掉,然后将新的segment file写入磁盘,这里会写一个commit point,标识所有新的segment file,然后打开segment file供搜索使用,同时删除旧的segmentment file