Redis 主从架构数据同步
Redis 提供了主从模式,通过主从复制,将数据冗余一份复制到其他 Redis 服务器。
面试官:主从之间数据如何保证一致性?
为了保证副本数据的一致性,主从架构采用了读写分离的方式。
- 读操作:主、从库都可以执行;
- 写操作:主库先执行,之后将写操作同步到从库;
面试官:主从复制还有其他作用么?
- 故障恢复:当主节点宕机,其他节点依然可以提供服务;
- 负载均衡:Master 节点提供写服务,Slave 节点提供读服务,分担压力;
- 高可用基石:是哨兵和 cluster 实施的基础,是高可用的基石。
面试官:主从复制如何实现的?
同步分为三种情况:
- 第一次主从库全量复制;
- 主从正常运行期间的同步;
- 主从库间网络断开重连同步。
面试官:第一次同步怎么实现?
主从库第一次复制过程大体可以分为 3 个阶段:连接建立阶段(即准备阶段)、主库同步数据到从库阶段、发送同步期间新写命令到从库阶段;
- 建立连接:从库会和主库建立连接,从库执行 replicaof 并发送 psync 命令并告诉主库即将进行同步,主库确认回复后,主从库间就开始同步了。
- 主库同步数据给从库:master 执行
bgsave
命令生成 RDB 文件,并将文件发送给从库,同时主库为每一个 slave 开辟一块 replication buffer 缓冲区记录从生成 RDB 文件开始收到的所有写命令。从库保存 RDB 并清空数据库再加载 RDB 数据到内存中。
- 发送 RDB 之后接收到的新写命令到从库:在生成 RDB 文件之后的写操作并没有记录到刚刚的 RDB 文件中,为了保证主从库数据的一致性,所以主库会在内存中使用一个叫 replication buffer 记录 RDB 文件生成后的所有写操作。并将里面的数据发送到 slave。
面试官:主从库间的网络断了咋办?断开后要重新全量复制么?
在 Redis 2.8 之前,如果主从库在命令传播时出现了网络闪断,那么,从库就会和主库重新进行一次全量复制,开销非常大。
从 Redis 2.8 开始,网络断了之后,主从库会采用增量复制的方式继续同步。
增量复制:用于网络中断等情况后的复制,只将中断期间主节点执行的写命令发送给从节点,与全量复制相比更加高效。
断开重连增量复制的实现奥秘就是 repl_backlog_buffer
缓冲区,不管在什么时候 master 都会将写指令操作记录在 repl_backlog_buffer
中,因为内存有限, repl_backlog_buffer
是一个定长的环形数组,如果数组内容满了,就会从头开始覆盖前面的内容。
master 使用 master_repl_offset
记录自己写到的位置偏移量,slave 则使用 slave_repl_offset
记录已经读取到的偏移量。
当主从断开重连后,slave 会先发送 psync 命令给 master,同时将自己的 runID
,slave_repl_offset
发送给 master。
master 只需要把 master_repl_offset
与 slave_repl_offset
之间的命令同步给从库即可。
增量复制执行流程如下图:
面试官:那完成全量同步后,正常运行过程中如何同步数据呢?
当主从库完成了全量复制,它们之间就会一直维护一个网络连接,主库会通过这个连接将后续陆续收到的命令操作再同步给从库,这个过程也称为基于长连接的命令传播,使用长连接的目的就是避免频繁建立连接导致的开销。
哨兵原理连环问
面试官:可以呀,知道这么多,你知道 哨兵集群原理么?
哨兵是 Redis 的一种运行模式,它专注于对 Redis 实例(主节点、从节点)运行状态的监控,并能够在主节点发生故障时通过一系列的机制实现选主及主从切换,实现故障转移,确保整个 Redis 系统的可用性。
他的架构图如下:
Redis 哨兵具备的能力有如下几个:
- 监控:持续监控 master 、slave 是否处于预期工作状态。
- 自动切换主库:当 Master 运行故障,哨兵启动自动故障恢复流程:从 slave 中选择一台作为新 master。
- 通知:让 slave 执行 replicaof ,与新的 master 同步;并且通知客户端与新 master 建立连接。
面试官:哨兵之间是如何知道彼此的?
哨兵与 master 建立通信,利用 master 提供发布/订阅机制发布自己的信息,比如身高体重、是否单身、IP、端口……
master 有一个 __sentinel__:hello
的专用通道,用于哨兵之间发布和订阅消息。这就好比是 __sentinel__:hello
微信群,哨兵利用 master 建立的微信群发布自己的消息,同时关注其他哨兵发布的消息。
面试官:哨兵之间虽然建立连接了,但是还需要和 slave 建立连接,不然没法监控他们呀,如何知道 slave 并监控他们的?
关键还是利用 master 来实现,哨兵向 master 发送 INFO
命令, master 掌门自然是知道自己门下所有的 salve 小弟的。所以 master 接收到命令后,便将 slave 列表告诉哨兵。
哨兵根据 master 响应的 slave 名单信息与每一个 salve 建立连接,并且根据这个连接持续监控哨兵。
Cluster 集群连环炮
面试官:除了哨兵以外,还有其他的高可用手段么?
有 Cluster 集群实现高可用,哨兵集群监控的 Redis 集群是主从架构,无法很想拓展。使用 Redis Cluster 集群,主要解决了大数据量存储导致的各种慢问题,同时也便于横向拓展。
在面向百万、千万级别的用户规模时,横向扩展的 Redis 切片集群会是一个非常好的选择。
面试官:什么是 Cluster 集群?
Redis 集群是一种分布式数据库方案,集群通过分片(sharding)来进行数据管理(「分治思想」的一种实践),并提供复制和故障转移功能。
将数据划分为 16384 的 slots,每个节点负责一部分槽位。槽位的信息存储于每个节点中。
它是去中心化的,如图所示,该集群有三个 Redis 节点组成,每个节点负责整个集群的一部分数据,每个节点负责的数据多少可能不一样。
三个节点相互连接组成一个对等的集群,它们之间通过 Gossip
协议相互交互集群信息,最后每个节点都保存着其他节点的 slots 分配情况。
面试官:哈希槽又是如何映射到 Redis 实例上呢?
- 根据键值对的 key,使用 CRC16 算法,计算出一个 16 bit 的值;
- 将 16 bit 的值对 16384 执行取模,得到 0 ~ 16383 的数表示 key 对应的哈希槽。
- 根据该槽信息定位到对应的实例。
键值对数据、哈希槽、Redis 实例之间的映射关系如下:
面试官:Cluster 如何实现故障转移?
Redis 集群节点采用 Gossip
协议来广播自己的状态以及自己对整个集群认知的改变。比如一个节点发现某个节点失联了 (PFail),它会将这条信息向整个集群广播,其它节点也就可以收到这点失联信息。
如果一个节点收到了某个节点失联的数量 (PFail Count) 已经达到了集群的大多数,就可以标记该节点为确定下线状态 (Fail),然后向整个集群广播,强迫其它节点也接收该节点已经下线的事实,并立即对该失联节点进行主从切换。
面试官:客户端又怎么确定访问的数据到底分布在哪个实例上呢?
Redis 实例会将自己的哈希槽信息通过 Gossip 协议发送给集群中其他的实例,实现了哈希槽分配信息的扩散。
这样,集群中的每个实例都有所有哈希槽与实例之间的映射关系信息。
当客户端连接任何一个实例,实例就将哈希槽与实例的映射关系响应给客户端,客户端就会将哈希槽与实例映射信息缓存在本地。
当客户端请求时,会计算出键所对应的哈希槽,再通过本地缓存的哈希槽实例映射信息定位到数据所在实例上,再将请求发送给对应的实例。
面试官:什么是 Redis 重定向机制?
哈希槽与实例之间的映射关系由于新增实例或者负载均衡重新分配导致改变了,客户端将请求发送到实例上,这个实例没有相应的数据,该 Redis 实例会告诉客户端将请求发送到其他的实例上。
Redis 通过 MOVED 错误和 ASK 错误告诉客户端。
MOVED
MOVED 错误(负载均衡,数据已经迁移到其他实例上):当客户端将一个键值对操作请求发送给某个实例,而这个键所在的槽并非由自己负责的时候,该实例会返回一个 MOVED 错误指引转向正在负责该槽的节点。
同时,客户端还会更新本地缓存,将该 slot 与 Redis 实例对应关系更新正确。
ASK
如果某个 slot 的数据比较多,部分迁移到新实例,还有一部分没有迁移。
如果请求的 key 在当前节点找到就直接执行命令,否则时候就需要 ASK 错误响应了。
槽部分迁移未完成的情况下,如果需要访问的 key 所在 Slot 正在从 实例 1 迁移到 实例 2(如果 key 已经不在实例 1),实例 1 会返回客户端一条 ASK 报错信息:客户端请求的 key 所在的哈希槽正在迁移到实例 2 上,你先给实例 2 发送一个 ASKING 命令,接着发发送操作命令。
比如客户端请求定位到 key = 「公众号:码哥字节」的槽 16330 在实例 172.17.18.1 上,节点 1 如果找得到就直接执行命令,否则响应 ASK 错误信息,并指引客户端转向正在迁移的目标节点 172.17.18.2。
注意:ASK 错误指令并不会更新客户端缓存的哈希槽分配信息。
未完待续
本篇主要将 Redis 核心内容过了一遍,涉及到数据结构、内存模型、 IO 模型、持久化 RDB 和AOF 、主从复制原理、哨兵原理、cluster 原理。
「面霸」系列会分为几篇,分别从核心原理、高可用、实战、如何避坑等方面全方位拿下 Redis。