Redis主从复制、Sentinel、集群总结

558 阅读5分钟

本文记录学习《Redis设计与实现》一书中第三部分关于主从复制、Sentinel、集群的知识总结及理解。

一、主从复制

单节点部署,一旦节点发生故障,就会导致服务不可用,主-从集群就是为了解决这类问题,提高可用性,同时通过主从进行读写分离,可以减少主库的读压力。

主从复制包含全量复制、基于长链接的命令传播、增量复制。

当从节点执行slaveof ip port后,建立主从关系后,会进行第一次全量复制,如下: Redis主从复制.png

1、第一次全量同步,master节点开启bgsave子进程来创建全量RDB,并发送给从节点;

2、从节点接收到主节点的RDB文件,清除本地数据后加载RDB文件;

2、命令传播,全量同步的过程中产生的写命令会缓存在复制缓冲区(replication buffer),主节点将复制缓冲区内的写命令发给从节点执行。

主从节点网络断开-增量复制

在2.8版本之前,主从节点网络断开后会进行全量复制,效率低下。

从2.8开始,网络断开后重新连接,主从节点会进行增量复制。

当主从节点断开连接后,重新连接上后,会根据主从节点的偏移量从repl_backlog_buffer缓冲区获取写命令进行增量同步。

全量复制为什么用RDB,不用AOF?

1、RDB文件内容是经过压缩的二进制数据,而AOF文件记录的是每一次写操作的命令,写操作越多文件会变得很大,其中还包括很多对同一个key的多次冗余操作,使用RDB文件传输相对AOF文件会减少网络消耗;

2、从库通过RDB快照加载数据要快,AOF要逐条执行命令;

二、哨兵机制

哨兵节点是一个特殊的Redis实例,特殊在于普通Redis节点相比,命令集不同,哨兵节点不能执行数据操作命令,可以执行PING,INFO等命令。哨兵集群的作用是用来监控Redis集群,对发生故障的master节点能进行故障转移,保证高可用。

哨兵节点会与所有Redis主-从节点创建两个连接,一个是订阅_sentinel:hello频道;一个是命令连接。

主观下线 vs 客观下线

哨兵会每隔一秒向Redis节点(主、从)发送ping命令,根据节点的响应分为:

  • 有效响应:在规定的时间(通过down-after-milliseconds设置)内,响应+PONG、-LOADING、-MASTERDOWN,即为有效响应
  • 无效响应:除了上面三种响应或在规则的时间内未响应,即为无效响应

如果未无效响应,则会被哨兵标记为主观下线。

当哨兵将一个主节点判断为主观下线后,为了确认该节点是否为客观下线,会向其他监控这一节点的哨兵进行询问,发送is-master-down-by-addr命令,当收到所有的回复中,有大于等于quonum个哨兵判断此主节点为主观下线,即可判断此主节点为客观下线。

Redis哨兵.png

在判定主节点为客观下线后,哨兵集群会选举领头的哨兵(通过Raft算法选举,此处不展开),领头的哨兵随后进行故障转移。

三、集群

Redis节点通过cluster meet命令完成节点间握手,建立连接。建立连接后,节点间会定时互相发送ping/pong命令,交换数据信息(节点信息、状态等)。

每个Redis节点都有一个clusterNode来保存节点信息,当节点加入集群中时会有个clusterState结构来保存集群的信息状态,如下图所示:

Redis集群节点.jpg

节点间通过cluster meet命令,在连接过程中会去更新clusterState中的nodes信息。

集群的整个数据库被分为16384个槽,每个节点负责的槽可以通过cluster addslots来进行指派。在clusterNode结构中属性slots记录节点负责哪些槽:

struct clusterNode {
	unsigned char slots[16384/8];
	int numslots;
}

同时,clusterState结构里会存16384个槽的指派信息,如下:

struct clusterState {
	clusterNode *slots[16384];
}

通过clusterState的slots可以通过O(1)时间找到某个槽对应的节点。

在集群中执行命令

在集群环境下,客户端向某节点发送数据库命令时,会首先计算key属于哪个槽,如果槽对应的指派是当前节点,会执行命令;如果槽对应的指标不是当前节点,会返回MOVED给客户端,并返回槽对应指派的节点信息,后再次执行命令。

计算key属于哪个槽:CRC16(key) & 16383

为什么是16384个槽?

  • 节点之间检测状态,发送的数据里包含节点的分槽信息数组slots[16383/8],如果槽太多会导致每次传输的数据包增大,因为节点间的检测时很频繁的,会影响传输效率,容易造成网络阻塞

  • 一般来说Redis的集群节点数不建议超过1000个,16384个槽位足够用

四、参考