1、什么是脑裂?

让我们看一个有两个节点的elasticsearch集群的简单情况。节点1在启动时被选举为主节点并保存主分片(在下面的schema里标记为0P),而节点2保存副本分片(0R)。


es1.jpg

现在,如果在两个节点之间的通讯中断了,会发生什么?由于网络问题或只是因为其中一个节点无响应(例如stop-the-world垃圾回收),这是有可能发生的。

es2.jpg

两个节点都相信对方已经挂了。节点1不需要做什么,因为它本来就被选举为主节点。但是节点2会自动选举它自己为主节点,因为它相信集群的一部分没有主节点了。在elasticsearch集群,是由主节点来决定将分片平均的分布到节点上的。节点2保存的是副本分片,但它相信主节点不可用了,所以它会自动提升复制节点为主节点。

es3.jpg

现在我们的集群在一个不一致的状态了。打在节点1上的索引请求会将索引数据分配在主节点,同时打在节点2的请求会将索引数据放在分片上。在这种情况下,分片的两份数据分开了,如果不做一个全量的重索引很难对它们进行重排序。在更坏的情况下,一个对集群无感知的索引客户端(例如,使用REST接口的),这个问题非常透明难以发现,无论哪个节点被命中索引请求仍然在每次都会成功完成。问题只有在搜索数据时才会被隐约发现:取决于搜索请求命中了哪个节点,结果都会不同。

2、如何避免脑裂问题

elasticsearch的默认配置足够可用,但是elasticsearch项目组不可能知道你的特定场景里的所有细节。这就是为什么某些配置参数需要改成适合你的需求的原因。本文里所有提到的参数都可以在你elasticsearch安装地址的config目录中的elasticsearch.yml中更改。

要预防脑裂问题,我们需要看的一个参数就是 discovery.zen.minimum_master_nodes。这个参数决定了在选主过程中需要有多少个节点通信。缺省配置是1。一个基本的原则是这里需要设置成 N/2+1,N是集群中节点的数量。例如在一个三节点的集群中, minimum_master_nodes 应该被设为 3/2 + 1 = 2(四舍五入)。

让我们想象下之前的情况,如果我们把 discovery.zen.minimum_master_nodes 设置成 2(2/2 + 1)。当两个节点的通信失败了,节点1会失去它的主状态,同时节点2也不会被选举为主。没有一个节点会接受索引或搜索的请求,让所有的客户端马上发现这个问题。而且没有一个分片会处于不一致的状态。

我们可以调的另一个参数是 discovery.zen.ping.timeout。它的默认值是3秒,并且它用来决定一个节点在假设集群中的另一个节点响应失败的情况时等待多久。在一个慢速网络中将这个值调的大一点是个不错的主意。这个参数不止适用于高网络延迟,还能在一个节点超载响应很慢时起作用。

3、两节点集群

如果你觉得(或直觉上)在一个两节点的集群中把 minimum_master_nodes 参数设成2是错的,那就对了。在这种情况下如果一个节点挂了,那整个集群就都挂了。尽管这种方式杜绝了脑裂的可能性,但这使elasticsearch另一个好特性 - 用复制分片来构建高可用性,失效了。

如果你初次使用elasticsearch,建议配置一个3节点集群。这样你可以设置 minimum_master_nodes为2,减少了脑裂的可能性,但仍然保持了高可用的优点:你可以承受一个节点失效但集群还是正常运行的。

但是,如果已经运行了一个两节点elasticsearch集群怎么办?可以选择为了保持高可用而忍受脑裂的可能性,或者选择为了防止脑裂而选择高可用性。为了避免这种妥协,最好的选择是给集群添加一个节点。这听起来很极端,但并不是。对于每一个elasticsearch节点你可以设置 node.data 参数来选择这个节点是否需要保存数据。缺省值是 “true”,意思是默认每个elasticsearch节点同时也会作为一个数据节点。

在一个两节点集群,你可以添加一个新节点并把 node.data 参数设置为 “false”。这样这个节点不会保存任何分片,但它仍然可以被选为主(默认行为)。因为这个节点是一个无数据节点,所以它可以放在一台便宜服务器上。现在你就有了一个三节点的集群,可以安全的把 minimum_master_nodes 设置为2,避免脑裂而且仍然可以丢失一个节点并且不会丢失数据。

4、结论

脑裂问题很难被彻底解决,在elasticsearch的问题列表里仍然存在,在一个极端情况下正确设置了minimum_master_nodes 的参数时仍然产生了脑裂问题。elasticsearch项目组正在致力于开发一个选主算法的更好的实现,但远水不解近渴,你需要知道这个潜在的问题,并提前做好预防准备。

如何尽快发现这个脑裂问题很重要。一个比较简单的检测问题的方式是,做一个对/_nodes下每个节点终端响应的定期检查。这个终端返回一个所有集群节点状态的短报告。如果有两个节点报告了不同的集群列表,那么这是一个产生脑裂状况的明显标志。

标签: none

添加新评论