在运维 redis 集群的过程当中,经常会发现集群中节点数量和理论数量对不上,尤其是在发生迁移或扩缩容的时候。原因是集群进行节点变动的时候,一些本该从集群中剔除的节点由于种种原因没有完全被剔除掉,进而一直残留在集群中。
那么为什么会出现这种情况呢?这就需要重redis cluster剔除节点的机制说起了。
首先来说说redis cluster剔除节点时所进行的操作,Redis Cluster 采用无中心的集群模式,集群中所有节点通过互相交换消息来维持一致性。当有新节点需要加入集群时,只需要将它与集群中的一个节点建立联系即可,通过集群间节点互相交换消息所有节点都会互相认识。所以当需要剔除节点的时候,需要向所有节点发送 cluster forget 命令。
在这里,细心的同学可能就发现了,向集群所有节点发送cluster forget命令需要一段时间,而在这段时间内已经接收到 cluster forget 命令的节点与没有接收的节点会发生信息交换,进而导致 cluster forget 命令失效。
那么为了应对这个问题 ,Redis也设计了一个黑名单机制。当节点接收到 cluster forget 命令后,不仅会将被踢节点从自身的节点列表中移除,还会将被剔除的节点添加入到自身的黑名单中。当与其它节点进行消息交换的时候,节点会忽略掉黑名单内的节点。所以通过向所有节点发送 cluster forget 命令就能顺利地剔除节点。
但是黑名单内的节点不应该永远存在于黑名单中,那样会导致被踢掉的节点不能再次加入到集群中,同时也可能导致不可预期的内存膨胀问题。所以黑名单是需要有时效性的,Redis 设置的时间为一分钟。
redis cluster 剔除节点机制到这里就说完了,看起来完全没问题是吧,那么怎么还会出现文章开头说的集群中残留失效节点的情况呢?诚然,上述的redis cluster剔除节点的原理已经考虑的比较全面了,关键问题就出在黑名单失效性这里,redis默认设置了1分钟,那么如果在一分钟内没有完成整个集群剔除节点的操作呢?
当剔除节点的时候,在一分钟内没能向所有节点发出 cluster forget 命令,会导致剔除失败,尤其在集群规模较大的时候会经常发生。
解决方案:
1、一次性剔除多个节点时,可以开启多进程针对每个要剔除的节点发送 cluster forget 命令
2、要剔除的节点数量较少时,每次只forget一个节点
清理脚本:
#!/bin/bash for i in `cat test` do host=`echo $i | awk -F ':' '{print $1}'` port=`echo $i | awk -F ':' '{print $2}'` # 获取指定单节点的node id。主要用于集群节点过多时,forget多个节点时,花费时间过长,导致已被删除的节点又从其他节点同步过来,导致forget失败 # del_nodeids=$(redis-cli -h $host -p $port cluster nodes|grep -E 'handshake|fail'| grep '127.0.0.1:6379' | awk '{print $1}') del_nodeids=$(redis-cli -h $host -p $port cluster nodes|grep -E 'handshake|fail' | awk '{print $1}') if [ -n "$del_nodeids" ];then for nodeid in ${del_nodeids[@]}; do echo $host $port $nodeid redis-cli -h $host -p $port cluster forget $nodeid done fi done