1.持久化
1.1.RDB
RDB(Redis Database Backup file)也叫做Redis数据快照。就是把内存中的所有数据记录到磁盘中,当Redis实例故障重启后,从磁盘中读取文件,恢复数据。
快照文件默认保存在当前运行目录。(默认关闭redis会执行一次save操作)
# Redis主进程执行RDB,会阻塞所有命令:
save
# 开启子进程执行RDB,避免主进程受到影响
bgsave
Redis内部有触发RDB的机制,可以在redis.conf
文件中的配置找到
# 900 秒内,如果至少有1个key被修改,则执行bgsave,如果save "" 则表示禁用RDB
save 900 1
RDB的其他在redis.conf文件中的配置
#是否压缩,建议不开启,压缩也会消耗cpu,磁盘不值钱
rdbcompression yes
#RDB文件名
dbfilename dump.db
#文件保存的路径目录
dir ./
RDB方式的bgsave的基本流程
- 触发检查:当执行bgsave命令时,Redis父进程首先检查是否存在正在执行的子进程(如RDB/AOF子进程)。如果存在,则直接返回,避免并发执行导致资源竞争。
- 创建子进:父进程通过fork()系统调用创建子进程。此过程会短暂阻塞父进程(阻塞时间与内存大小相关)。子进程与父进程共享内存数据,通过写时复制(Copy-On-Write, COW)技术保证数据一致性。
- 异步执行快照:子进程独立遍历父进程的内存数据,生成临时RDB文件(默认命名为temp-pid.rdb),写入完成后原子替换旧的RDB文件(如dump.rdb)。
- 父进程在fork()完成后立即恢复响应客户端请求,仅子进程负责持久化操作。
- 完成通知:子进程完成RDB文件写入后,向父进程发送信号。父进程更新统计信息(如rdb_last_save_time、rdb_last_bgsave_status等),记录持久化结果。
写时复制
多个调用者共享同一份资源(如内存或磁盘数据),直到某个调用者尝试修改资源时,系统才会复制一份专用副本供其修改,其他调用者仍访问原始资源。这种延迟复制机制减少了不必要的内存开销和复制时间
RDB的缺点?
- RDB的执行间隔时间长,两次RDB之间写入数据有丢失的风险
- fork子进程、压缩、写出RDB文件都比较耗时
1.2.AOF
AOF全称Append Only File(追加文件)。
Redis处理的每个写命令都会记录到AOF文件,可以看做是命令日志文件。redis故障以后,重新执行一遍aof文件就可以恢复了。
redis.conf配置
#是否开启AOF功能,默认是no
appendonly yes
#AOF文件的名
appendfilename "appendonly.aof"
# 表示每执行一次写命令,立即记录到AOF文件
appendfsync always
#写命令执行完先放入AOF缓存区,然后表示每周1秒将缓存区数据写到AOF文件,是默认方案
appendfsync everysec
#写命令执行先放入AOF缓冲区,有操作系统决定何时将缓冲区内容写回磁盘
appendfsync no
配置项 | 刷盘时机 | 优点 | 缺点 |
---|---|---|---|
Always | 同步刷盘 | 可靠性高,几乎不丢失数据 | 性能影响大 |
everysec | 每秒刷盘 | 性能适中 | 最多丢失1秒数据 |
no | 操作系统控制 | 性能最好 | 可靠性较差,可能丢失大量数据 |
因为是记录命令,AOF文件会比RDB文件大的多。而且AOF会记录对同一个key的多次写操作,但只有最后一次写操作才有意义。通过执行bgrewriteaof
命令,可以让AOF文件执行重写功能,用最少的命令达到相同效果。
比较 | RDB | AOF |
---|---|---|
持久化方式 | 定时对整个内存做快照 | 记录每一次执行的命令 |
数据完整性 | 不完整,两次备份之间会丢失 | 相对完整,即决于刷盘策略 |
文件的大小 | 会有压缩,文件体积小 | 记录命令,文件体积很大 |
宕机恢复速度 | 很快 | 慢 |
数据恢复优先级 | 低,因为数据完整性不如AOF | 高,因为数据完整性更高 |
数据的优先级 | 高,大量CPU和内存消耗 | 低,主要是磁盘IO资源,但是AOF重写时会占用大量CPU和内存资源 |
使用场景 | 可以容忍数分钟的数据丢失,追求更快的启动速度 | 对数据安全性要求较高单常见 |
2.redis主从
单节点Redis的并发能力是有限,要进一步提高Redis的并发能力,需要搭建主从集群,实现读写分离
。
2.1.搭建集群
2.2.数据同步原理
Redis主从同步通过全量
和增量
机制结合,在保证数据一致性的同时兼顾效率。
全量同步用于初始化或数据差异过大时,增量同步则优化了断线恢复的性能。
全量同步
第一次建立主从节点建立连接的时候进行全量同步
:
master如何判断slave是不是第一次来同步数据?这里会用到两个很重要的概念:
Replication ld
:简称replid,是数据集的标记,id一致则说明是同一数据集。每一个master都有唯一的replid,slave则会继承master节点的replidoffset
:偏移量,随着记录在repl_baklog中的数据增多而逐渐增大。slave完成同步时也会记录当前同步的offset。如果slave的offset小于master的offset,说明slave数据落后于master,需要更新。因此slave做数据同步,必须向master声明自己的replication id和offset,master才可以判断到底需要同步哪些数据
所以全量同步的流程可以描述为:
- slave节点请求增量同步
- master节点判断replid,发现不一致,拒绝增量同步
- master将完整内存数据生成RDB,发送RDB到slave
- slave清空本地数据,加载master的RDB
- master将RDB期间的命令记录在repl_baklog,并持续将log中的命令发送给slave
- slave执行接收到的命令,保持与master之间的同步
增量同步
增量同步
用于主从断线重连后恢复同步,避免全量复制
增量同步失败的情况:repl_baklog大小有上限,写满后会覆盖最早的数据。如果slave断开时间过久,导致尚未备份的数据被覆盖
,则无法基于log做增量同步,只能再次全量同步。
优化
全量同步的性能较差,可以从以下几点来优化
- 在master中配置
repl-diskless-sync yes
启用无磁盘复制
,避免全量同步时的磁盘IO(使用网络IO流)。 - Redis单节点上的内存占用不要太大,减少RDB导致的过多磁盘IO
- 适当提高repl_baklog的大小,发现slave宕机时尽快实现故障恢复,尽可能避免全量同步
- 限制一个master上的slave节点数量,如果实在是太多slave,则可以采用主-从-从链式结构,减少master压力
3. 哨兵
slave节点宕机恢复后可以找master节点同步数据,那master节点宕机怎么办?
监控master的状态,如果master宕机,选一个从节点成为新的master
Redis提供了哨兵(Sentinel)机制来实现主从集群的自动故障恢复。
搭建哨兵:www.bilibili.com/video/BV1cr…
Spring的RedisTemplate哨兵模式:www.bilibili.com/video/BV1cr…
3.1 哨兵的作用和原理
哨兵的结构和作用如下:
监控
: Sentinel会不断检查master和slave是否按预期工作故障恢复
︰如果master故障,Sentinel会将一个slave提升为master。当故障实例恢复后也以新的master为主通知
: Sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给Redis的客户端
服务状态监控
Sentinel基于心跳机制监测服务状态,每隔1秒向集群的每个实例发送ping命令:
- 主观下线:如果某sentinel节点发现某实例未在规定时间响应,则认为该实例主观下线。
- 客观下线:若超过指定数量(quorum)的sentinel都认为该实例主观下线,则该实例客观下线(quorum值最好超过Sentinel实例数量的一半)
选举依据
一旦发现master故障,sentinel需要在salve中选择一个作为新的master,选择依据是这样的:
- 首先会判断slave节点与master节点断开时间长短,如果超过指定值(down-after-milliseconds *10)则会排除该slave节点
- 然后判断slave节点的优先级,slave-priority值越小优先级越高,如果是0则永不参与选举
- 如果slave-prority一样,则判断slave节点的offset值,越大说明数据越新,优先级越高
- 最后是判断slave节点的运行id大小,越小优先级越高。
故障迁移步骤
- sentinel给备选的slave1节点发送
slaveof no one
命令,让该节点成为master - sentinel给所有其它slave发送
slaveof 192.168.150.101 7002
命令,让这些slave成为新master(7002)的从节点,开始从新的master上同步数据。 - 最后,sentinel将故障节点标记为slave,当故障节点恢复后会自动成为新的master的slave节点
4. 分片集群
主从和哨兵可以解决高可用、高并发读的问题。但是依然有两个问题没有解决:
- 海量数据存储问题
- 高并发写的问题
使用分片集群可以解决上述问题,分片集群特征:
- 集群中有多个master,每个master保存不同数据
- 每个master都可以有多个slave节点
- master之间通过ping监测彼此健康状态(不需要哨兵了)
- 客户端请求可以访问集群任意节点,最终都会被转发到正确节点
搭建Redis分片集群:www.bilibili.com/video/BV1cr…
Spring RedisTemplate访问分片集群:www.bilibili.com/video/BV1cr…
4.1 散列插槽
Redis会把每一个master节点映射到0~16383共16384个插槽(hash slot)上,查看集群信息时就能看到:
数据key不是与节点绑定,而是与插槽绑定
(节点宕机,可以把插槽分配到其他节点)。redis会根据key的有效部分计算插槽值,分两种情况:
- key中包含"{}",且“{}”中至少包含1个字符,“{}”中的部分是有效部分
- key中不包含“{}”,整个key都是有效部分
例如:key是num,那么就根据num计算,如果是{itcast}/num,则根据itcast计算。计算方式是利用CRC16算法得到一个hash值,然后对16384取余,得到的结果就是slot值,根据slot值分配到相应的插槽。
如何将同一类数据固定的保存在同一个Redis实例?
答:这一类数据使用相同的有效部分,例如key都以{typeld}为前缀
4.2 故障迁移
分片集群有自动的故障迁移功能
但有的时候需要手动故障转移,比如想让节点B替换原来的master节点A作为新的master
利用cluster failover
命令可以手动让集群中的某个master宕机,切换到执行cluster failover
命令的这个slave节点,实现无感知的迁移