RDB 全称 Redis Database Backup file(Redis 数据备份文件),也被叫做 Redis 数据快照。简单来说就是把内存中的所有数据都记录到磁盘中。当 Redis 实例故障重启后,从磁盘读取快照文件,恢复数据。
快照文件称为 RDB 文件,默认是保存在当前运行目录。
Redis 停机时会执行一次 RDB。
Redis 内部有触发 RDB 的机制,可以在 redis.conf 文件中找到,格式如下:
RDB 的其它配置也可以在 redis.conf 文件中设置:
bgsave 开始时会 fork 主进程得到子进程,子进程共享主进程的内存数据。完成 fork 后读取内存数据并写入 RDB 文件。 fork 采用的是 copy-on-write 技术:
(1)当主进程执行读操作时,访问共享内存;
(2)当主进程执行写操作时,则会拷贝一份数据,执行写操作。
AOF 全称为 Append Only File(追加文件)。Redis 处理的每一个写命令都会记录在 AOF 文件,可以看做是命令日志文件。
AOF 默认是关闭的,需要修改 redis.conf 配置文件来开启 AOF:
AOF 的命令记录的频率也可以通过 redis.conf 文件来配:
因为是记录命令,AOF 文件会比 RDB 文件大的多。而且 AOF 会记录对同一个 key 的多次写操作,但只有最后一次写操作才有意义。通过执行 bgrewriteaof 命令,可以让 AOF 文件执行重写功能,用最少的命令达到相同效果。
Redis 也会在触发阈值时自动去重写 AOF 文件。阈值也可以在 redis.conf 中配置:

假设有 A、B 两个 Redis 实例,如何让 B 作为 A 的 slave 节点?
在 B 节点执行命令:slaveof A的IP A的port
master 如何判断 slave 是不是第一次来同步数据?这里会用到两个很重要的概念:
(1)Replication Id:简称replid,是数据集的标记,id 一致则说明是同一数据集。每一个master 都有唯一的replid,slave 则会继承 master 节点的 replid
(2)offset:偏移量,随着记录在 repl_baklog 中的数据增多而逐渐增大。slave 完成同步时也会记录当前同步的 offset。如果 slave 的 offset 小于 master 的offset,说明 slave 数据落后于master,需要更新。
(3)因此 slave 做数据同步,必须向 master 声明自己的 replication id 和 offset,master 才可以判断到底需要同步哪些数据
简述全量同步的流程
(1)slave 节点请求增量同步
(2)master 节点判断 replid,发现不一致,拒绝增量同步
(3)master 将完整内存数据生成RDB,发送 RDB 到slave
(4)slave清空本地数据,加载master的RDB
(5)master 将 RDB 期间的命令记录在 repl_baklog,并持续将log中的命令发送给slave (6)slave 执行接收到的命令,保持与 master 之间的同步
主从第一次同步是全量同步,但如果 slave 重启后同步,则执行增量同步
repl_baklog(环形数组) 大小有上限,写满后会覆盖最早的数据。如果 slave 断开时间过久,导致尚未备份的数据被覆盖,则无法基于 log 做增量同步,只能再次全量同步。
可以从以下几个方面来优化 Redis 主从就集群:
(1)在 master 中配置 repl-diskless-sync yes 启用无磁盘复制,避免全量同步时的磁盘IO。
(2)Redis 单节点上的内存占用不要太大,减少 RDB 导致的过多磁盘 IO
(3)适当提高 repl_baklog 的大小,发现 slave 宕机时尽快实现故障恢复,尽可能避免全量同步
(4)限制一个 master 上的 slave 节点数量,如果实在是太多 slave,则可以采用主-从-从链式结构,减少 master 压力
Redis 提供了哨兵(Sentinel)机制来实现主从集群的自动故障恢复。哨兵的结构和作用如下:
(1)监控:Sentinel 会不断检查您的 master 和 slave 是否按预期工作
(2)自动故障恢复:如果 master 故障,Sentinel 会将一个 slave 提升为 master。当故障实例恢复后也以新的 master 为主
(3)通知:Sentinel 充当 Redis 客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给 Redis 的客户端
Sentinel 基于心跳机制监测服务状态,每隔1秒向集群的每个实例发送 ping 命令:
(1)主观下线:如果某sentinel节点发现某实例未在规定时间响应,则认为该实例主观下线。
(2)客观下线:若超过指定数量(quorum)的 sentinel 都认为该实例主观下线,则该实例客观下线。quorum 值最好超过 Sentinel 实例数量的一半。
一旦发现 master 故障,sentinel 需要在 salve 中选择一个作为新的master,选择依据是这样的:
(1)首先会判断 slave 节点与 master 节点断开时间长短,如果超过指定值(down-after-milliseconds * 10)则会排除该 slave 节点
(2)然后判断 slave 节点的 slave-priority 值,越小优先级越高,如果是0则永不参与选举
(3)如果 slave-prority 一样,则判断 slave 节点的 offset 值,越大说明数据越新,优先级越高
(4)最后是判断 slave 节点的运行 id 大小,越小优先级越高。
当选中了其中一个 slave 为新的 master 后(例如 slave1),故障的转移的步骤如下:
(1)sentinel 给备选的 slave1 节点发送 slaveof no one 命令,让该节点成为 master
(2)sentinel 给所有其它 slave 发送 slaveof 192.168.150.101 7002 命令,让这些 slave 成为新 master 的从节点,开始从新的 master 上同步数据。
(3)最后,sentinel 将故障节点标记为 slave,当故障节点恢复后会自动成为新的 master 的slave 节点
在 Sentinel 集群监管下的 Redis 主从集群,其节点会因为自动故障转移而发生变化,Redis 的客户端必须感知这种变化,及时更新连接信息。Spring 的 RedisTemplate 底层利用 lettuce 实现了节点的感知和自动切换。
(1)在pom文件中引入redis的starter依赖:
(2) 然后在配置文件application.yml中指定sentinel相关信息:
(3)配置主从读写分离
这里的ReadFrom是配置Redis的读取策略,是一个枚举,包括下面选择:
(1)MASTER:从主节点读取
(2)MASTER_PREFERRED:优先从master节点读取,master不可用才读取replica
(3)REPLICA:从slave(replica)节点读取
(4)REPLICA _PREFERRED:优先从slave(replica)节点读取,所有的slave都不可用才读取master
主从和哨兵可以解决高可用、高并发读的问题。但是依然有两个问题没有解决:
(1)海量数据存储问题
(2)高并发写的问题
使用分片集群可以解决上述问题,分片集群特征:
(1)集群中有多个 master,每个 master 保存不同数据
(2)每个 master 都可以有多个 slave 节点
(3)master 之间通过 ping 监测彼此健康状态
(4)客户端请求可以访问集群任意节点,最终都会被转发到正确节点
Redis 会把每一个 master 节点映射到 0~16383 共16384个插槽(hash slot)上,查看集群信息时就能看到:
数据 key 不是与节点绑定,而是与插槽绑定。redis 会根据 key 的有效部分计算插槽值,分两种情况:
(1)key 中包含"{}",且“{}”中至少包含1个字符,“{}”中的部分是有效部分
(2)key 中不包含“{}”,整个key都是有效部分
例如:key 是 num,那么就根据 num 计算,如果是 {itcast}num,则根据 itcast 计算。计算方式是利用 CRC16 算法得到一个 hash 值,然后对 16384 取余,得到的结果就是 slot 值。
(1)将16384个插槽分配到不同的实例
(2)根据key的有效部分计算哈希值,对16384取余
(3)余数作为插槽,寻找插槽所在实例即可
这一类数据使用相同的有效部分,例如key都以{typeId}为前缀
redis-cli --cluster 提供了很多操作集群的命令,可以通过下面方式查看:
比如,添加节点的命令:
(1)首先是该实例与其它实例失去连接
(2)然后是疑似宕机:
(3)最后是确定下线,自动提升一个slave为新的master:
利用cluster failover命令可以手动让集群中的某个master宕机,切换到执行cluster failover命令的这个slave节点,实现无感知的数据迁移。其流程如下:
手动的Failover支持三种不同模式:
(1)缺省(推荐):默认的流程,如图1~6歩
(2)force:省略了对 offset 的一致性校验
(3)takeover:直接执行第5歩,忽略数据一致性、忽略 master 状态和其它 master 的意见
RedisTemplate底层同样基于lettuce实现了分片集群的支持,而使用的步骤与哨兵模式基本一致:
(1)引入redis的starter依赖
(2)配置分片集群地址
(3)配置读写分离
与哨兵模式相比,其中只有分片集群的配置方式略有差异,如下: