哈喽大家好,我是咸鱼
在《一台服务器上部署 Redis 伪集群》这篇文章中,咸鱼在创建 Redis 集群时并没有明确指定哪个 Redis 实例将担任 master,哪个将担任 slave
/usr/local/redis-4.0.9/src/redis-trib.rb create --replicas 1 192.168.149.131:6379 192.168.149.131:26379 192.168.149.131:6380 192.168.149.131:26380 192.168.149.131:6381 192.168.149.131:26381
然而 Redis 却自动完成了主从节点的分配工作
如果大家在多台服务器部署过 Redis 集群的话,比如说在三台机器上部署三主三从的 redis 集群,你会观察到 Redis 自动地将主节点和从节点的部署位置错开
举个例子: master 1 和 slave 3 在同一台机器上; master 2和 slave 1 在同一台机器上; master 3 和 slave 2 在同一台机器上
这是为什么呢?
我们知道老版本的 Redis 集群管理命令是 redis-trib.rb
,新版本则换成了 redis-cli
这两个可执行文件其实是一个用 C 编写的脚本,小伙伴们如果看过这两个文件的源码就会发现原因就在下面这段代码里
/* Return the anti-affinity score, which is a measure of the amount of
* violations of anti-affinity in the current cluster layout, that is, how
* badly the masters and slaves are distributed in the different IP
* addresses so that slaves of the same master are not in the master
* host and are also in different hosts.
*
* The score is calculated as follows:
*
* SAME_AS_MASTER = 10000 * each slave in the same IP of its master.
* SAME_AS_SLAVE = 1 * each slave having the same IP as another slave
of the same master.
* FINAL_SCORE = SAME_AS_MASTER + SAME_AS_SLAVE
*
* So a greater score means a worse anti-affinity level, while zero
* means perfect anti-affinity.
*
* The anti affinity optimization will try to get a score as low as
* possible. Since we do not want to sacrifice the fact that slaves should
* not be in the same host as the master, we assign 10000 times the score
* to this violation, so that we'll optimize for the second factor only
* if it does not impact the first one.
*
* The ipnodes argument is an array of clusterManagerNodeArray, one for
* each IP, while ip_count is the total number of IPs in the configuration.
*
* The function returns the above score, and the list of
* offending slaves can be stored into the 'offending' argument,
* so that the optimizer can try changing the configuration of the
* slaves violating the anti-affinity goals. */
static int clusterManagerGetAntiAffinityScore(clusterManagerNodeArray *ipnodes,
int ip_count, clusterManagerNode ***offending, int *offending_len)
{
...
return score;
}
static void clusterManagerOptimizeAntiAffinity(clusterManagerNodeArray *ipnodes,
int ip_count)
{
...
}
通过注释我们可以得知,clusterManagerGetAntiAffinityScore
函数是用来计算反亲和性得分,这个得分表示了当前 Redis 集群布局中是否符合反亲和性的要求
反亲和性指的是 master 和 slave 不应该在同一台机器上,也不应该在相同的 IP 地址上
那如何计算反亲和性得分呢?
- 如果有多个 slave 与同一个 master 在相同的 IP 地址上,那么对于每个这样的 slave,得分增加 10000
- 如果有多个 slave 在相同的 IP 地址上,但它们彼此之间不是同一个 master,那么对于每个这样的 slave,得分增加 1
- 最终得分是上述两部分得分之和
也就是说,得分越高,亲和性越高;得分越低,反亲和性越高;得分为零表示完全符合反亲和性的要求
获得得分之后,就会对得分高(反亲和性低)的节点进行优化
为了让 Redis 主从之间的反亲和性更高,clusterManagerOptimizeAntiAffinity
函数会对那些反亲和性很低的节点进行优化,它会尝试通过交换从节点的主节点,来改善集群中主从节点分布,从而减少反亲和性低问题
接下来我们分别来看下这两个函数
反亲和性得分计算
static int clusterManagerGetAntiAffinityScore(clusterManagerNodeArray *ipnodes,
int ip_count, clusterManagerNode ***offending, int *offending_len)
{
...
}
可以看到,该函数接受了四个参数:
ipnodes
:一个包含多个clusterManagerNodeArray
结构体的数组,每个结构体表示一个 IP 地址上的节点数组ip_count
:IP 地址的总数offending
:用于存储违反反亲和性规则的节点的指针数组(可选参数)offending_len
:存储offending
数组中节点数量的指针(可选参数)
第一层 for 循环是遍历 ip 地址,第二层循环是遍历每个 IP 地址的节点数组
...
for (i = 0; i < ip_count; i++) {
clusterManagerNodeArray *node_array = &(ipnodes[i]);
dict *related = dictCreate(&clusterManagerDictType);
char *ip = NULL;
for (j = 0; j < node_array->