下午业务部门发来消息说搜索引擎的同一个查询语句会出现不同的查询结果,经过反复debug最终发现是因为这个数据所在的集群发生了“脑裂”的现象。

所谓脑裂就是ES集群中的两个节点都认为自己是master,导致原有的集群被分割为两个独立的集群。我们这个ES集群有两台负载,而前段时间机房发生过一次交换机挂掉导致的机柜断网事件,其中一台ES负载就位于这个发生了断网的机柜之中。断网导致两台负载之间的网络连接断开,进而导致两个节点都把自己选举为主节点,等到网络恢复连接之后,本来一个集群就变成了两个集群。

因为搜索引擎有一些uwsgi进程连接到了负载1所在的集群,还有一些则连接到了负载2所在的集群,此时所有的读写操作都是在两个集群上发生的。一段时间之后,这两个集群之中的数据就出现了不一致的情况,此时再查询就会因为查询的集群不一致而导致查询结果不一致的情况,具体表现就是同一个查询语句出现了不同的查询结果。

最终我们向集群中添加了一台新的节点,3个节点能够通过多数派的方式来确定唯一的集群。一般认为成为master的前提是该节点得到 (n ÷ 2) + 1 个选票,具体在这里就是需要 (3 ÷ 2) + 1 = 2 个选票,因此我们把Elasticsearch配置文件 elasticsearch.yml 中的最小选主人数设置为 2

discovery.zen.minimum_master_nodes: 2

如果任意一个节点脱离网络,那么剩下的两个节点会选举得到一个master,而脱离的那个节点因为 1 < 2,所以不能成为master。如果三个节点全部断开网络连接,则三个节点都不能获得足够的选票成为master,整个集群挂掉。

添加了新节点之后,新节点会加入一个旧节点的集群之中。此时把剩下的那个节点重启,之后该节点也会加入那个集群,此时所有节点都组成了一个集群,脑裂现象消失。不过因为此时所有数据都由master确定,所以被重启的那个节点上的数据会发生丢失的现象,这也是脑裂的最大危害:导致数据的混乱或丢失。

不过好在我们所有的数据都是从业务部门的主库中同步过来的,所以这里只需要重新刷新一遍数据即可。

最小主节点数

如果不想因为修改配置文件而重启节点,可以通过如下方式直接修改配置

PUT /_cluster/settings
{
    "persistent" : {
        "discovery.zen.minimum_master_nodes" : 2
    }
}