primary shard 和 replica shard
index包含多个shard,每个shard都是一个承载部分的数据最小的工作单元,拥有完整的索引和处理请求的能力。
每一个doc只会存在某一个primary shard以及其对应的replica shard中(replica shard是primary shard的副本,负责容错,以及承担读请求负载)。primary shard 的数量在创建索引的时候就固定了,replica shard的数量可以随时修改
primary shard和replica shard不能和自己的replica shard 放在一个node节点中(为避免节点宕机的时候,primary shard和replica shard数据都都丢失,起不到容错的作用),但是可以和其他的primary shard的replica shard放在同一个node节点中。
es 写数据过程
- 客户端选择一个
coordinating node
( 协调节点)发送请求 coordinating node
对 document 进行路由,将请求转发给对应的 node(有primary shard)。- 实际的 node 上的
primary shard
处理请求,然后将数据同步到replica node
。 coordinating node
如果发现primary node
和所有replica node
数据写入完成,就返回响应结果给客户端。
es 读数据过程
- 客户端发送请求到任意一个 node,成为
coordinate node
。 coordinate node
对doc id
进行哈希路由,将请求转发到对应的 node,使用round-robin
随机轮询算法,在primary shard
以及其所有 replica 中随机选择一个。- 接收请求的 node 返回 document 给
coordinate node
。 coordinate node
返回 document 给客户端。
es 搜索数据过程
- 客户端发送请求到一个
coordinate node
。 - 协调节点将搜索请求转发到所有的 shard 对应的
primary shard
或replica shard
。 - query phase:每个 shard 将自己的搜索结果返回给协调节点,由协调节点进行数据的合并、排序、分页等操作,产出最终结果。
- fetch phase:接着由协调节点根据
doc id
去各个节点上拉取实际的document
数据,最终返回给客户端。
写数据底层原理
数据写入的时候,数据先写入内存 buffer,在 buffer 里的时候数据是搜索不到的,同时将数据写入 translog 日志文件。每隔 1s将数据 refresh 到 os cache,到了 os cache 数据就能被搜索到。每隔 5s,将数据写入 translog 文件,translog 大到一定程度,或者默认每隔 30mins,会触发 commit 操作,将缓冲区的数据都 flush 到 segment file 磁盘文件中。
为什么写入translog 日志文件?
你执行 commit 操作之前,数据要么是停留在 buffer 中,要么是停留在 os cache 中,一旦这台机器死了,内存中的数据就全丢了。所以需要将数据对应的操作写入一个专门的日志文件 translog
中,一旦此时机器宕机,再次重启的时候,es 会自动读取 translog 日志文件中的数据,恢复到内存 buffer 和 os cache 中去。
translog 其实也是先写入 os cache 的,默认每隔 5 秒刷一次到磁盘中去,所以默认情况下,可能有 5 秒的数据会仅仅停留在 buffer 或者 translog 文件的 os cache 中,如果此时机器挂了,会丢失 5 秒钟的数据。但是这样性能比较好,最多丢 5 秒的数据。也可以将 translog 设置成每次写操作必须是直接 fsync
到磁盘,但是性能会差很多。
refresh
如果 buffer 快满了,或者到一定时间,就会将内存 buffer 数据 refresh
到一个新的 segment file
中,但是此时数据不是直接进入 segment file
磁盘文件,而是先进入 os cache
。这个过程就是 refresh
。
每隔 1 秒钟,es 将 buffer 中的数据写入一个新的 segment file
,每秒钟会产生一个新的磁盘文件 segment file
,这个 segment file
中就存储最近 1 秒内 buffer 中写入的数据。(准实时,NRT,
near real-time
)
操作系统里面,磁盘文件其实都有一个东西,叫做 os cache
,即操作系统缓存,就是说数据写入磁盘文件之前,会先进入 os cache
,先进入操作系统级别的一个内存缓存中去。只要 buffer
中的数据被 refresh 操作刷入 os cache
中,这个数据就可以被搜索到了。
commit
当 translog 达到一定长度的时候,就会触发 commit
操作。commit 操作发生第一步,就是将 buffer 中现有数据 refresh
到 os cache
中去,清空 buffer。将一个 commit point
写入磁盘文件,里面标识着这个 commit point
对应的所有 segment file
,同时强行将 os cache
中目前所有的数据都 fsync
到磁盘文件中去。最后清空 现有 translog 日志文件,重启一个 translog,此时 commit 操作完成。
这个 commit 操作叫做 flush
。默认 30 分钟自动执行一次 flush
,但如果 translog 过大,也会触发 flush
。flush 操作就对应着 commit 的全过程,我们可以通过 es api,手动执行 flush 操作,手动将 os cache 中的数据 fsync 强刷到磁盘上去。
删除/更新数据底层原理
删除操作commit 的时候会生成一个 .del
文件,里面将某个 doc 标识为 deleted
状态,那么搜索的时候根据 .del
文件就知道这个 doc 是否被删除了。
buffer 每 refresh 一次,就会产生一个 segment file
,所以默认情况下是 1 秒钟一个 segment file
,这样下来 segment file
会越来越多,此时会定期执行 merge。
每次 merge 的时候,会将多个 segment file
合并成一个,同时这里会将标识为 deleted
的 doc 给物理删除掉,然后将新的 segment file
写入磁盘,这里会写一个 commit point
,标识所有新的 segment file
,然后打开 segment file
供搜索使用,同时删除旧的 segment file
。