1.为什么Redis需要持久化
Redis作为内存数据库,如果出现宕机的话那么数据就会丢失。在没有设置持久化的情况下,Redis的数据只能通过脚本进行初始化,因此为了不需要脚本进行初始化,我们就需要Redis的持久化来进行自身的数据恢复
2.RDB
RDB持久化是把当前进程数据生成快照保存到硬盘的过程。
2.1 触发模式
2.1.1 手动触发
Redis的save和bgsave命令会触发RDB,save因为严重的性能问题已废弃
- save命令:堵塞当前服务器,直到RDB过程完成为止。
- bgsave命令:redis进程执行fork操作创建子程序,RDB持久化过程由子程序负责。
2.1.2 被动触发
Redis内部还存在自动触发RDB的持久化的机制:
- 在redis.conf中配置通过数据改变触发RDB的配置项save m n来触发bgsave
- 当从节点初始化的时候,主节点进行全量复制的时候会触发bgsave
- 执行debug reload命令重新加载Redis时,会触发save
- 默认情况下执行shutdown命令时,如果没有开启AOF持久化功能则自动触发save
2.2 bgsave流程
- 执行bgsave命令,Redis父进程判断当前是否存在正在执行的子进程,如RDB/AOF子程序,如果存在bgsave命令直接返回。
- 父亲执行fork操作创建子进程,fork操作过程中父进程会堵塞。
- 父进程fork后,bgsave命令返回”Background saving started”信息并不再阻塞父进程,并可以响应其他命令。
- 子进程创建RDB文件,根据父进程内存快照生成临时快照文件,完成后对原有文件进行原子替换。
- 子进程发送信号给父进程表示完成,父进程更新统计信息。
2.3 RDB文件压缩
Redis通过LZF算法来减小持久化文件的大少,它会远远小于对应的内存大小
3.AOF
AOF以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中的命令达到恢复数据的目的。
AOF是默认不开启的,通过配置
# 可以通过修改redis.conf配置文件中的appendonly参数开启
appendonly yes
# AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的。
dir ./
# 默认的文件名是appendonly.aof,可以通过appendfilename参数修改
appendfilename appendonly.aof
3.1 AOF工作流程
- 所有写入命令会追加到aof_buf中。
- aof_buf根据对应的策略向硬盘做同步同步操作。
- 随着AOF文件越来越大,需要定期对AOF文件进行重写,达到压缩的目的。(AOF在没有重写之前是一个全量的记录)
- 当Redis服务器重启时,加载AOF文件进行重写。
3.1.1 命令写入
AOF直接以Redis的序列化协议RESP的内容,作为保存内容,原因为:
- 文本协议具有很好的兼容性。
- 避免开销。
- 文本协议具有可读性,方便直接修改和处理。
3.1.2 文件同步
为了解决直接写入硬盘照成的性能瓶颈,AOF使用了aof_buf缓冲区,并且因为使用了缓冲区,我们可以选择自己的同步文件策略来确定性能和数据安全之间的平衡。
目前AOF缓冲区同步文件支持以下三种策略
- always 每执行一秒钟保存一次
每次执行命令都会往aof_buf都写入AOF文件,并且立刻fsync保存到磁盘中。
- everysec 每一秒钟保存一次
每次执行命令都会往aof_buf都写入AOF文件,每一秒fork子线程fsync保存到磁盘中。
- no 不保存
每次执行命令都会往aof_buf都写入AOF文件,fsync保存到磁盘会在以下情况下执行:
Redis 被关闭
AOF 功能被关闭
系统的写缓存被刷新(可能是缓存已经被写满,或者定期保存操作被执行)
模式 | aof_buf写入文件 | fsync保存到磁盘 | 停机时丢失数据量 |
always | 阻塞 | 阻塞 | 最多只丢失一个命令数据 |
everysec | 阻塞 | 不阻塞 | 一般情况下不超过两秒钟数据 |
no | 阻塞 | 阻塞 | 最后一次fsync保存到磁盘的数据 |
3.1.3 重写机制
随着不断写入AOF,文件会越来越大,AOF区别于RDB,引入AOF重写机制压缩文件体积。
重写的AOF文件变小的原因:
- 进程内超时的数据不再写入文件
- 重写使用数据生成对应的命令,只保留最终数据的命令,例如del a,set b 111,set b 222(中的set b 111)则不需要。
- 多条写入命令进行合并,为了防止单条命令过大,以64个元素为界拆分多条
1.执行AOF重写请求
如果当前进程正在执行AOF重写,请求不执行
如果当前进程正在执行bgsave操作,重写命令延迟到bgsave完成之后再执行
2.父进程执行fork创建子进程
3.1 主进程fork操作完成后,继续响应其他命令。所有修改命令依然写入AOF缓存区根据同步策略同步到硬盘中,保证原有AOF机制的正确性
3.2 由于fork操作运行写时复制技术,子程序只能共享fork操作时的内存数据,由于父进程依然响应命令,Redis使用aof_rewrite_buf(AOF重写缓冲区)保存这部分新数据,防止新AOF文件生成期间丢失这部分数据。
4.子进程根据内存快照,按照命令合并规则写入到新的AOF文件,每次批量写入硬盘数据量由配置aof-rewrite-incremental-fsync控制,默认为32MB,防止单次刷盘数据过大,导致硬盘堵塞
5.1 子进程发送信号给父进程表示完成,父进程更新统计信息
5.2 父进程把AOF重写缓冲区的数据写入到新的AOF文件(此时最终AOF、新AOF文件、Redis内存中数据三者状态完全一致)
5.3 使用新AOF文件替换老文件,完成AOF重写
3.1.4 重启加载
4.混合持久化
RDB和AOF各有优点,Redis4.0开始支持rdb和aof的混合持久化,当混合持久化打开,aof重写的时候直接把rdb的内容写到aof文件开头。
在加载时,首先会识别AOF文件是否以REDIS字符串开头,如果是就按RDB加载,加载完RDB后继续按AOF格式加载剩余部分。
5.RDB与AOF的对比
-
RDB存储的是数据快照,采用LZF压缩存储。AOF存储操作命令,通过重写压缩存储。
-
RDB加载快于AOF。
-
RDB由于是对内存中所有数据进行快照,因此只有AOF适合作为实时出持久化。
6.应用场景
通过上文可知RDB或者AOF当他们fork子程序会影响服务器性能,这实际与追求高性能的想法相违背,因此笔者目前的项目缓存出现宕机的时候,使用数据源进行恢复。
但是在某一些特殊的数据则不妨可以使用RDB,这就是数据字典,它的内容不会多,而且更改频率较低。
当Redis数据量过大,又不得不使用持久化的时候,则选择AOF,因为数据量大的时候,RDB会堵塞服务器。
参考:SHUTDOWN [SAVE|NOSAVE] — Redis 命令参考
书籍:《Redis开发与维护》