Elasticsearch 一致性

版权声明:作者:jiankunking 出处:http://blog.csdn.net/jiankunking 本文版权归作者和CSDN共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。 https://blog.csdn.net/xunzaosiyecao/article/details/84666839

突然想起一个问题,ES如何保证写入后,当前节点宕掉,不会发生数据丢失?

一、动态更新的 Lucene 索引

以在线动态服务的层面看,要做到实时更新条件下数据的可用和可靠,就需要在倒排索引的基础上,再做一系列更高级的处理。

其实总结一下 Lucene 的处理办法,很简单,就是一句话:新收到的数据写到新的索引文件里。

Lucene 把每次生成的倒排索引,叫做一个段(segment)。然后另外使用一个commit 文件,记录索引内所有的 segment。而生成 segment 的数据来源,则是内存中的 buffer。 也就是说,动态更新过程如下:

  1. 当前索引有 3 个 segment 可用。索引状态如图 2-1:
    在这里插入图片描述
  2. 新接收的数据进入内存 buffer。索引状态如图 2-2;
    在这里插入图片描述
  3. 内存 buffer 刷到磁盘,生成一个新的 segment,commit 文件同步更新。索引状态如图 2-3。
    在这里插入图片描述

二、利用磁盘缓存实现的准实时检索

既然涉及到磁盘,那么一个不可避免的问题就来了:磁盘太慢了!对我们要求实时性很高的服务来说,这种处理还不够。所以,在第 3 步的处理中,还有一个中间状态:

  1. 内存 buffer 生成一个新的 segment,刷到文件系统缓存中,Lucene 即可检索这个新 segment。索引状态如图 2-4。
    在这里插入图片描述

  2. 文件系统缓存真正同步到磁盘上,commit 文件更新。达到图 2-3 中的状态。这一步刷到文件系统缓存的步骤,在 Elasticsearch 中,是默认设置为 1 秒间隔的,对于大多数应用来说,几乎就相当于是实时可搜索了。Elasticsearch 也提供了单独的 /_refresh 接口,用户如果对 1 秒间隔还不满意的,可以主动调用该接口来保证搜索可见。

注:5.0 中还提供了一个新的请求参数: ?refresh=wait_for ,可以在写入数据后不强制刷新但一直等到刷新才返回。

不过对于 Elastic Stack 的日志场景来说,恰恰相反,我们并不需要如此高的实时性,而是需要更快的写入性能。所以,一般来说,我们反而会通过 /_settings接口或者定制 template 的方式,加大 refresh_interval 参数:

# curl -XPOST http://127.0.0.1:9200/logstash-2015.06.21/_setting
s -d'
{ "refresh_interval": "10s" }
'

如果是导入历史数据的场合,那甚至可以先完全关闭掉:

# curl -XPUT http://127.0.0.1:9200/logstash-2015.05.01 -d'
{
"settings" : {
"refresh_interval": "-1"
}
}'

在导入完成以后,修改回来或者手动调用一次即可:

# curl -XPOST http://127.0.0.1:9200/logstash-2015.05.01/_refresh

三、translog 提供的磁盘同步控制

既然 refresh 只是写到文件系统缓存,那么第 4 步写到实际磁盘又是有什么来控制的?如果这期间发生主机错误、硬件故障等异常情况,数据会不会丢失?

这里,其实有另一个机制来控制。Elasticsearch 在把数据写入到内存 buffer 的同时,其实还另外记录了一个 translog 日志。 也就是说,第 2 步并不是图 2-2 的状态,而是像图 2-5 这样:
在这里插入图片描述

在第 3 和第 4 步,refresh 发生的时候,translog 日志文件依然保持原样,如图 2-6:
在这里插入图片描述
也就是说,如果在这期间发生异常,Elasticsearch 会从 commit 位置开始,恢复整个 translog 文件中的记录,保证数据一致性。

等到真正把 segment 刷到磁盘,且 commit 文件进行更新的时候, translog 文件才清空。这一步,叫做 flush。同样,Elasticsearch 也提供了 /_flush 接口。

对于 flush 操作,Elasticsearch 默认设置为:每 30 分钟主动进行一次 flush,或者
当 translog 文件大小大于 512MB (老版本是 200MB)时,主动进行一次 flush。这两个行为,可以分别通过 index.translog.flush_threshold_period 和
index.translog.flush_threshold_size 参数修改。

如果对这两种控制方式都不满意,Elasticsearch 还可以通过
index.translog.flush_threshold_ops 参数,控制每收到多少条数据后 flush一次。

四、translog 的一致性

索引数据的一致性通过 translog 保证。那么 translog 文件自己呢?

默认情况下,Elasticsearch 每 5 秒,或每次请求操作结束前,会强制刷新 translog日志到磁盘上。

后者是 Elasticsearch 2.0 新加入的特性。为了保证不丢数据,每次 index、bulk、delete、update 完成的时候,一定触发刷新 translog 到磁盘上,才给请求返回 200OK。 这个改变在提高数据安全性的同时当然也降低了一点性能。

如果你不在意这点可能性,还是希望性能优先,可以在 index template 里设置如下参数:

{
"index.translog.durability": "async"
}

五、副本一致性

作为分布式系统,数据副本可算是一个标配。ES 数据写入流程,自然也涉及到副本。在有副本配置的情况下,数据从发向 ES 节点,到接到 ES 节点响应返回,流向如下:
在这里插入图片描述
1、 客户端请求发送给 Node 1 节点,注意图中 Node 1 是 Master 节点,实际完全可以不是。
2、Node 1 用数据的 _id 取余计算得到应该讲数据存储到 shard 0 上。通过cluster state 信息发现 shard 0 的主分片已经分配到了 Node 3 上。Node 1 转发请求数据给 Node 3。
3、 Node 3 完成请求数据的索引过程,存入主分片 0。然后并行转发数据给分配有shard 0 的副本分片的 Node 1 和 Node 2。当收到任一节点汇报副本分片数据写入成功,Node 3 即返回给初始的接收节点 Node 1,宣布数据写入成功。Node 1 返回成功响应给客户端。

这个过程中,有几个参数可以用来控制或变更其行为:

  • wait_for_active_shards 上面示例中,2 个副本分片只要有 1 个成功,就可以返回给客户端了。这点也是有配置项的。其默认值的计算来源如下:
int( (primary + number_of_replicas) / 2 ) + 1

根据需要,也可以将参数设置为 one,表示仅写完主分片就返回,等同于 async;还可以设置为 all,表示等所有副本分片都写完才能返回。

  • timeout 如果集群出现异常,有些分片当前不可用,ES 默认会等待 1 分钟看分片能否恢复。可以使用 ?timeout=30s 参数来缩短这个等待时间。
    副本配置和分片配置不一样,是可以随时调整的。有些较大的索引,甚至可以在做forcemerge 前,先把副本全部取消掉,等 optimize 完后,再重新开启副本,节约单个 segment 的重复归并消耗。
# curl -XPUT http://127.0.0.1:9200/logstash-mweibo-2015.05.02/_s
ettings -d '{
"index": { "number_of_replicas" : 0 }
}'

本文整理自:《elk-stack-guide-cn》

个人微信公众号:
这里写图片描述

作者:jiankunking 出处:http://blog.csdn.net/jiankunking

猜你喜欢

转载自blog.csdn.net/xunzaosiyecao/article/details/84666839