高可用
主从复制
Why?
单点故障无法提供服务,硬盘故障数据丢失。
How?
数据备份,并且保证一致性:主从复制
- 读写分离:写入主,读取从
建立过程:
-
建立链接、协商同步;
- 发送:psync:主服务器runID、offset
- 回复:FULLRESYNC:主服务器的 runID、主服务器目前的复制进度 offset
-
主服务器同步数据给从服务器;
- 主 bgsave 生成 RDB 文件,发送给从服务器。
- 从收到 RDB 文件后,会先清空当前的数据,然后载入 RDB 文件
-
主服务器发送新写操作命令给从服务器。
- 主将 replication buffer 缓冲区写操作命令发送给从
- 从执行主的命令,的数据就一致了
命令传播
Why?
后期主数据的变更需要及时同步到从
How?
双方之间就会维护一个长 TCP 连接。
后续主通过这个连接发送命令给从。
分摊主服务器的压力
Why?
每次从加入都要bgsave,fork创建子进程、传递RDB文件消耗资源
How?
主服务器生成 RDB 和传输 RDB 的压力可以分摊到充当经理角色的从服务器:
-
在「从服务器」上执行下面这条命令,使其作为目标服务器的从服务器:
replicaof <目标服务器的IP> 6379
- 目标服务器是「从服务器」,该目标服务器就会成为「经理」的角色
增量复制
Why?
主从TCP连接网络问题断开后,数据不一致。回复连接如何恢复一致性?
How?
- Redis 2.8 之前:重新进行一次全量复制,性能消耗大
- Redis 2.8 后:增量复制,只把网络断开期间主接收到的写操作命令,同步给从。
步骤:
-
从发送 psync 命令给主,offset 参数不是 -1;
-
主返回 CONTINUE 告诉从增量复制同步数据;
-
主服务将主从断线期间,所执行的写命令发送给从,从执行这些命令。
-
repl_backlog_buffe:环形缓冲区,1M
-
replication offset:标记缓冲区的同步进度
- master_repl_offset:主写到的位置
- slave_repl_offset:从读到的位置。
主的写入速度远超于从读取速度,缓冲区的数据会被覆盖,只能再采用全量同步。
-
为了避免主频繁全量同步,调大 repl_backlog_buffer 缓冲区大小
repl-backlog-size 10mb
高频问题
怎么判断 Redis 某个节点是否正常工作?
通过互相的 ping-pong 心态检测机制,如果有一半以上的节点去 ping 一个节点的时候没有 pong 回应,集群就会认为这个节点挂掉了,会断开与这个节点的连接。
-
主默认每 10 秒对从发送 ping,repl-ping-slave-period 控制发送频率。
-
从每 1 秒发送 replconf ack{offset} 命令,给主上报自身当前的复制偏移量,目的是为了:
- 实时监测主从节点网络状态;
- 上报自身复制偏移量检查复制数据是否丢失, 如丢失, 再从主复制缓冲区中拉取丢失数据。
过期key如何处理?
主模拟一条del命令发送给从
Redis 是同步复制还是异步复制?
主每次收到写命令之后,先写到内部的缓冲区,然后异步发送给从节点。
主从复制中两个 Buffer(replication buffer 、repl backlog buffer)有什么区别?
-
repl backlog buffer:增量复制阶段,一主只分配一个;
- 满了覆盖起始位置数据
-
replication buffer:全量复制阶段和增量复制阶段都会出现
- 主会给每个新连接的从,分配一个;
- 满了,连接断开删除缓存,从重新连接,重新全量复制。
如何应对主从数据不一致?
Why?
- 主从间的命令复制是异步的,无法实现强一致性
How?
-
尽量保证主从节点间的网络连接状况良好,避免主从节点在不同的机房。
-
可以开发一个外部程序来监控主从节点间的复制进度。
- INFO replication 命令查看主接收写命令的进度信息(master_repl_offset)和从复制写命令的进度信息(slave_repl_offset)
- 可以开发一个监控程序,先 INFO replication ,master_repl_offset – slave_repl_offset 得到从和主间的复制进度差值。
- 如果某个从的进度差值大于预设的阈值,让从失效防止客户端读取。为了避免出现客户端和所有从都不能连接的情况,需要把复制进度差值的阈值设置得大一些。
主从切换如何减少数据丢失?
主从切换数据丢失的情况:
-
异步复制同步丢失
- min-slaves-max-lag:一旦所有从节点数据复制和同步的延迟都超过这个值,主拒绝服务。防止主宕机导致丢失过多数据。
- 客户端发现 master 不可写后,降级措施,将数据暂时写入本地缓存和磁盘中,等 master 恢复后重新写入,也可以将数据写入 kafka 消息队列。
-
集群产生脑裂数据丢失
-
脑裂:主网络异常,哨兵找不到,新选了主,形成双主。
-
主网络恢复只能变成从,重新全量同步新主,原来的新数据丢失
-
减少脑裂的数据丢的方案
-
当主发现从下线的数量太多,或网络延迟太大,禁止写操作
- min-slaves-to-write x:主必须要有至少 x 个从节点连接
- min-slaves-max-lag x:主从数据复制和同步的延迟不能超过 x 秒
主从如何做到故障自动切换?
- 需要人工处理
- 哨兵
哨兵
Why?
主节点出问题,人工切换太沙雕了。
How?
Redis 2.8 版本提供的哨兵(Sentinel)机制:实现主从节点故障转移。监测、选主、通知。
监控
哨兵每1s对主发PING,没有在down-after-milliseconds内收到主的响应,主被判断下线。
- 主观下线:哨兵网络问题观察不到主,但主其实没问题
- 客观下线:主确实有问题
Why?
为避免单哨兵导致主观下线的错误判断,需要多个(>=3)哨兵对主进行监控
How?
一个哨兵发现主下线,所有哨兵进行投票,超过quorum个赞同就确定主下客观线。
-
三个哨兵quorum设置为2(1/2+1)
哨兵leader选举
Why?
由哪个哨兵进行主从故障转移?
How?
- 候选者:发现主下线的哨兵
- 投票:每个哨兵只投一票,候选者可投自己
- 归票:半数以上的赞成票 and 大于等于哨兵配置文件中的 quorum 值
如果未满足条件
故障转移
过程:
-
从里挑选出一个转换为主。
-
去除网络不好的从:down-after-milliseconds断链超过10次
-
选主:优先级排序,优先级相同按复制进度排序、再相同以ID小优先
- slave-priority:从的优先级,高配置节点可以优先级高
- slave_repl_offset:复制进度
-
向这个选出来的从发送 SLAVEOF no one 命令,转换为主。哨兵 leader 每秒向被升级的从发送 INFO 命令判断当前是否升级完成。
-
-
所有从修改复制目标为复制「新主节点」;
- 新主升级完成,哨兵leader向所有从发送 SLAVEOF 命令转移新主
-
将新主节点的 IP 地址和信息,通过「发布者/订阅者机制」通知给客户端;
- 哨兵向 +switch-master 频道发布新主节点的 IP 地址和端口的消息,客户端用新主的 IP 地址和端口通信
- 哨兵提供的消息订阅频道:
-
继续监视旧主,当重新上线时,设置新主的从;
哨兵集群
哨兵节点之间通过 Redis 的发布者/订阅者机制来相互通信
-
主节点:sentinel:hello 频道
- 哨兵把自己IP端口发送到__sentinel__:hello,其他订阅的哨兵就可以感知,并与之建立连接
-
哨兵通过向主发送INFO得知从节点,并建立连接
切片集群
Why?
数据量太大,单机内存不够用。
How?
Redis Cluster:
-
数据到哈希槽映射(16384 个哈希槽)
- 键值对的 key,按照 CRC16 算法计算一个 16 bit 值。
- 16bit 值对 16384 取模,得到 0~16383 范围内的模数,每个模数代表一个相应编号的哈希槽。
-
哈希槽到实例映射
-
平均分配: cluster create 命令创建 Redis 集群时,自动把所有哈希槽平均分布到集群节点上。
-
手动分配: cluster meet 命令手动建立节点间的连接,组成集群,再使用 cluster addslots 命令,指定每个节点上的哈希槽个数。
-