Redis(七)高可用
AOF 和 RDB,这两个持久化技术保证了即使在服务器重启的情况下也不会丢失数据(或少量损失)。
不过,由于数据都是存储在一台服务器上,会出现单点故障。解决的最好办法是将数据备份到其他服务器上,让这些服务器也可以对外提供服务,这样即使有一台服务器出现了故障,其他服务器依然可以继续提供服务。
多台服务器要保存同一份数据,这些服务器之间的数据如何保持一致性?
主从复制
主从服务器之间采用的是「读写分离」的方式。
主服务器可以进行读写操作,当发生写操作时自动将写操作同步给从服务器,而从服务器一般是只读。
第一次同步
第一阶段:建立链接、协商同步
第一次同步,主服务器还不知道从服务器,从服务器就会给主服务器发送 psync
命令,表示要进行数据同步。psync 命令包含两个参数,分别是主服务器的 runID 和复制进度 offset。
主服务器收到 psync 命令后,会用 FULLRESYNC
作为响应命令返回给对方。FULLRESYNC 响应命令的意图是采用全量复制的方式,也就是主服务器会把所有的数据都同步给从服务器。
所以,第一阶段的工作时为了全量复制做准备。
第二阶段:主服务器同步数据给从服务器
主服务器会执行 bgsave 命令来生成 RDB 文件,然后把文件发送给从服务器。
主服务器生成 RDB 文件到从服务器加载完RDB文件这期间的写操作命令并没有记录到刚刚生成的 RDB 文件中,这时主从服务器间的数据就不一致了。那么为了保证主从服务器的数据一致性,主服务器将收到的写操作命令,写入到 replication buffer 缓冲区里
从服务器收到 RDB 文件后,会先清空当前的数据,然后载入 RDB 文件
第三阶段:主服务器发送新写操作命令给从服务器
主服务器将 replication buffer 缓冲区里所记录的写操作命令发送给从服务器,以同步主服务器生成RDB快照之后的操作
基于长连接的命令传播
主从服务器在完成第一次同步后,双方之间就会维护一个 TCP 连接,后续主服务器可以通过这个连接继续将写操作命令传播给从服务器,这个过程被称为基于长连接的命令传播
分摊主服务器的压力
主服务器是可以有多个从服务器的,如果从服务器数量非常多,而且都与主服务器进行全量同步的话,主服务器压力过大。
从服务器可以有自己的从服务器,主服务器生成 RDB 和传输 RDB 的压力可以分摊到充当经理角色的从服务器。
增量复制
网络断开又恢复后,从主从服务器会采用增量复制的方式继续同步。
主从复制优点
- 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离;
- Slave 同样可以接受其它 Slaves 的连接和同步请求,这样可以有效的分载 Master 的同步压力(分摊主服务器压力);
- Master Server 和 Slave Server 是以非阻塞的方式完成数据同步。所以在 Master-Slave 同步期间,客户端仍然可以提交查询或修改请求;
- 网络断开又恢复后,可以增量复制。
主从复制缺点
- Redis不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复(也就是要人工介入);
- 主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性;
- 如果多个 Slave 断线了,需要重启的时候,尽量不要在同一时间段进行重启。因为只要 Slave 启动,就会发送sync 请求和主机全量同步,当多个 Slave 重启的时候,可能会导致 Master IO 剧增从而宕机。
- Redis 较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。
主服务器如何知道要将哪些增量数据发送给从服务器?
网络断开从服务器重新上线之后,会发送自己的复制偏移量slave_repl_offset到主服务器,主服务器根据自己的 master_repl_offset 和 slave_repl_offset 偏移量之间的差距判断要执行的操作:
- 如果从服务器要读的数据在repl_backlog_buffer中,则采用增量复制;
- 如果不在,采用全量复制。
repl_backlog_buffer,是一个「环形」缓冲区,用于主从服务器断连后,从中找到差异的数据;
replication offset,标记上面那个缓冲区的同步进度,主从服务器都有各自的偏移量,主服务器使用 master_repl_offset 来记录自己「写」到的位置,从服务器使用 slave_repl_offset 来记录自己「读」到的位置。
如何避免主从数据的不一致?
让主从节点处于同一机房,降低网络延迟;
或者由外部程序监控主从复制进度:先计算得出主从服务之间的复制进度差(master_repl_offset - slave_repl_offset),如果复制进度差大于程序设定的阈值,让客户端不再在此节点读取数据,减小数据不一致的情况对业务的影响。为了避免出现客户端和所有从节点都不能连接的情况,需要把复制进度差值的阈值设置得大一些。
主从架构中过期key如何处理?
主节点处理一个过期的key之后就会发送一条删除命令给从服务器,从节点收到命令后进行删除。
主从模式是同步复制还是异步复制?
异步。因为主节点收到写命令之后,先写到内部的缓冲区,然后再异步发送给从节点。
主从切换如何减少数据丢失?
主从切换过程中,产生数据丢失的情况有两种:
- 异步复制同步丢失
- 集群产生脑裂数据丢失
异步复制同步丢失
是什么:主节点还没来得及同步给从节点时发生了断电,那么主节点内存中的数据会丢失
解决方案
配置一个阈值,一旦所有的从节点数据复制和同步的延迟都超过了阈值,主节点就会拒绝接收任何请求。(这样就能将 master 和 slave 数据差控制在范围内)
对于客户端,发现主节点不可写后,可以采取降级措施。将数据暂时写入本地缓存和磁盘中,在一段时间后重新写入主节点来保证数据不丢失,也可以将数据写入消息队列,等主节点恢复正常,再隔一段时间去消费消息队列中的数据,让将数据重新写入主节点。
集群产生脑裂数据丢失
是什么:如果主节点的网络突然发生了问题与所有的从节点都失联了,但此时的主节点和客户端的网络是正常的,客户端不知道集群内部已经出现了问题,还在向这个失联的主节点写数据,此时这些数据被主节点缓存到了缓冲区里。哨兵也发现主节点失联了,就会在从节点中选举出一个leader作为主节点,会导致集群有两个主节点。
网络恢复后哨兵因为之前已经选举出一个新主节点了,它就会把旧主节点降级,然后从旧主节点会向新主节点请求数据同步,因为第一次同步是全量同步的方式,旧主节点会清空掉自己本地的数据。客户端在过程之前写入的数据就会丢失了。所以脑裂会导致集群数据的丢失。
总结一句话就是:由于网络问题,集群节点之间失去联系。主从数据不同步;
重新平衡选举,产生两个主服务。
等网络恢复,旧主节点会降级为从节点,再与新主节点进行同步复制的时候,由于从节点会清空自己的缓冲区,所以导致之前客户端写入的数据丢失了。
解决方案
当主节点发现从节点下线或者通信超时的总数量小于阈值时,那么禁止主节点进行写数据,直接把错误返回给客户端。
哨兵模式(Sentinel)
第一种主从同步/复制的模式,当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式。
哨兵是一个独立的进程
哨兵模式的作用/工作原理
- 监控:master 是否宕机
- 每个Sentinel进程以每秒钟一次的频率向整个集群中的 Master ,Slave 以及其他Sentinel进程发送一个 PING 命令
- 一个master一段时间没回sentinel,被这个sentinel标记为主观下线。足够多的sentinel标记master为主观下线就变成客观下线
- 选主:master 宕机后,将 slave 切换成 master
- 首先确定master下线的sentinel成为候选者,其他sentinel投票决定leader来进行主从切换;
- 挑选出一个从节点(网络好,优先级高,数据完整);
- 让已下线主节点属下的所有「从节点」修改复制目标,修改为复制「新主节点」。
- 通知:通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机
- 将新主节点的 IP 地址和信息,通过「发布者/订阅者机制」通知给客户端;
- 继续监视旧主节点,当这个旧主节点重新上线时,将它设置为新主节点的从节点。
哨兵模式优点:
- 哨兵模式是基于主从模式的,所有主从的优点,哨兵模式都具有。
- 主从可以自动切换,系统更健壮,可用性更高(可以看作自动版的主从复制)。
哨兵模式缺点:
- Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。
Cluster 集群模式
Redis-Cluster采用无中心结构,每个节点都保存数据,节点之间互相连接从而知道整个集群状态。
数据分片(Sharding):Cluster将数据分散存储在多个节点上,每个节点负责一部分数据。数据分片基于哈希槽(Slot)实现,共有16384个槽,每个键通过CRC16算法映射到特定槽,槽再分配给集群中的节点。
Redis Cluster工作机制
相当于多个主从结构
主节点(Master):负责处理槽对应的读写请求,维护数据副本,并同步给从节点。
从节点(Slave):复制主节点数据,提供读服务,主节点故障时可晋升为主节点。
数据路由:
客户端路由:客户端使用MOVED重定向响应或ASK转向请求,自动将请求转发至正确的主节点。
节点间路由:节点通过集群拓扑信息,将不属于本节点的请求转发至目标节点。
故障检测与自动故障转移:
- 心跳检测:节点间定期发送PING/PONG消息,检测对方是否存活。
- 主观下线与客观下线:类似Sentinel,节点根据心跳超时判断主观下线,多数节点同意则标记客观下线。
- 故障转移:故障节点的从节点竞选成为新主节点,其他节点更新槽映射与配置,客户端自动重定向。
数据同步与增量复制:
- 全量同步:新节点加入或从节点晋升为主节点时,通过RDB快照进行全量同步。
- 增量复制:主从节点间通过PSYNC命令进行增量数据同步,减少网络开销。
Cluster优点:
- 多主多从,去中心化:从节点作为备用,复制主节点,不做读写操作,不提供服务
- 支持动态扩容节点:这是我认为算是Rerdis Cluster最大的优点之一;
- 节点之间相互通信,相互选举,不再依赖sentinel:准确来说是主节点之间相互“监督”,保证及时故障转移。
Cluster缺点:
不支持处理多个key:因为数据分散在多个节点,在数据量大高并发的情况下会影响性能;
Redis切片集群的工作原理?
切片集群会采用哈希槽来进行数据和节点的映射,一个切片集群一共有16384个槽位,每个存储数据的key会经过运算映射到16384个槽位中,映射关系如下:
- 由key通过CRC16算法计算出一个16bit的数字
- 根据上面计算得到的数字对16384取模来确定对应的哈希槽
哈希槽和Redis节点是如何对应的?
主要有平均分配和手动分配两种方式。
平均分配是集群创建时,Redis自动将哈希槽平均分配到集群节点上;
手动分配是使用命令指定每个节点上面的哈希槽数目,使用手动分配时要把16384个槽位给分完,否则集群不会正常工作。
参考:
认识Redis集群——Redis Cluster - JJian - 博客园 (cnblogs.com)
深度图解Redis Cluster - 知乎 (zhihu.com)
[[Redis] 你了解 Redis 的三种集群模式吗? - 个人文章 - SegmentFault 思否](https://segmentfault.com/a/1190000022808576)
【Redis深度解析】揭秘Cluster(集群):原理、机制与实战优化_redis cluster 集群分片原理-CSDN博客