Redis 主从复制:主从节点之间如何同步数据?
单机 Redis 存在单点风险问题,也就是说,如果唯一的一个 Redis 节点宕机的话,就会导致大量的请求直接被打到数据库,严重的情况下,数据库很可能会直接被干宕机了。
这个时候,保障 Redis 服务的高可用就成为了我们不得不面对的问题。
如何保证 Redis 服务高可用? 最简单的一种办法就是基于 主从复制 搭建一个 Redis 集群,master(主节点)主要负责处理写请求,slave(从节点)主要负责处理读请求。
如果 master 宕机的话,从 slave 中选出一台作为 master 即可实现故障转移(Failover)。
是不是有点类似于 MySQL 的读写分离?这其实就是一种典型的多副本/备份的思想,经常被用在高可用架构上。
什么是主从复制?
简单来说,主从复制 就是将一台 Redis 主节点的数据复制到其他的 Redis 从节点中,尽最大可能保证 Redis 主节点和从节点的数据是一致的。主从复制是 Redis 高可用的基石,我们后面介绍到的 Redis Sentinel 以及 Redis Cluster 都依赖于主从复制。
主从复制这种方案不仅保障了 Redis 服务的高可用,还实现了读写分离,提高了系统的并发量,尤其是读并发量。
主从复制这种方案基于 Redis replication(默认使用异步复制),开发者可以通过 replicaof
(Redis 5.0 之前是 slaveof 命令)命 令来配置各个 Redis 节点的主从关系。
# 在指定 redis 节点上执行 replicaof 命令就可以让其成为 master 的 slave
# ip port 是 master 的
replicaof ip port
配置完成之后,主从节点之间的数据同步会自动进行,不需要人为插手。
至于具体要配置多少 slave 节点,主要取决于项目的读吞吐量,因为 slave 节点分担的是读请求,写请求由 master 节点负责。
主从复制下从节点会主动删除过期数据吗?
类似的问题:
- 主从复制下会读取到过期的数据吗?
- 主从复制下如何避免读取到过期的数据?
这是一个常见的问题,面试中也经常会问到。
我们知道,Redis 中常用的过期数据的删除策略就两个(重要!自己造缓存轮子的时候需要格外考虑的东西):
- 惰性删除 :只会在取出 key 的时候才对数据进行过期检查。这样对 CPU 最友好,但是可能会造成太多过期 key 没有被删除。
- 定期删除 : 每隔一段时间抽取一批 key 执行删除过期 key 操作。并且,Redis 底层会通过限制删除操作执行的时长和频率来减少删除操作对 CPU 时间的影响。
定期删除对内存更加友好,惰性删除对 CPU 更加友好。两者各有千秋,所以 Redis 采用的是 定期删除+惰性/懒汉式删除 。
那客户端读取从节点会读取到过期数据么? 答案是:有可能,但要看具体的情况。
- Redis 3.2 版本之前,客户端读从库并不会判断数据是否过期,有可能返回过期数据。Redis 3.2 版本及之后,客户端读从库会先判断数据是否过期,如果读取的数据已经过期了,从库虽然不会删除,但是会返回空值,这就避免了客户端读到过期数据。
- 采用
EXPIRE
或者PEXPIRE
设置过期时间的话,表示的是从执行这个命令开始往后 TTL 时间过期。如果从节点同步执行命令因为网络等原因延迟的话,客户端就可能会读取到过期的数据(如下图所示,客户端在 T3-T4 之间读取到的就是过期数据)。这种情况可以考虑使用EXPIREAT
和PEXPIREAT
,这两个命令语义和效果和EXPIRE
或者PEXPIRE
类似,但不是指定 TTL(生存时间)的秒数/毫秒数,而是使用绝对的 Unix 时间戳(自 1970 年 1 月 1 日以来的秒数)。由于设置的是时间点,主从节点的时钟需要保持一致。
主从节点之间如何同步数据?
类似的问题:
- 主从复制的原理是什么?
- master 节点的数据是如何同步给 slave 节点的?
- 复制积压缓冲区的有什么用?
这个问题其实还挺复杂的,Redis 主从复制经历了多次改进,每一次的改进都解决了上一个版本的一些痛点。想要彻底搞懂的话,我们需要耐心地看看 Redis 主从复制的演进历程,这里主要分为下面 3 个阶段:
- Redis 2.8 之前的 SYNC 方案
- Redis 2.8 PSYNC 方案
- Redis 4.0 PSYNC2.0 方案
为了让大家更好理解,我这里已经是经我所能使用大白话讲解了,并且,还配有图解。
需要注意:每一个版本的方案基本都是持续优化改进得到的。