深入浅出 Redis 持久化
一、Redis 持久化总览
持久化,顾名思义就是将内存中的数据存储到磁盘中。由于Redis 的读写都是在内存中,所以读写性能比较高,但是呢,内存中的数据会随着服务器的重启而丢失,为了保证数据不丢失,我们需要将内存中的数据存储到磁盘,以便 Redis 重启时能够从磁盘中恢复原有的数据,而整个过程就叫做 Redis 持久化。
Redis 持久化的方式:
- 快照方式(RDB, Redis DataBase)将某一个时刻的内存数据,以二进制的方式写入磁盘;
- 文件追加方式(AOF, Append Only File),记录所有的操作命令,并以文本的形式追加到文件中;
- 混合持久化方式,Redis 4.0 之后新增的方式,混合持久化是结合了 RDB 和 AOF 的优点,在写入的时候,先把当前的数据以 RDB 的形式写入文件的开头,再将后续的操作命令以 AOF 的格式存入文件,这样既能保证 Redis 重启时的速度,又能减低数据丢失的风险。
- 虚拟内存(VM)
- DISKSTORE
本文的重点是 RDB 持久化、AOF 持久化和混合持久化方式,其他两种持久化不做要求
二、RDB 持久化
简介:RDB(Redis DataBase)是将某一个时刻的内存快照(Snapshot),以二进制的方式写入磁盘的过程。
RDB 的持久化触发方式有两类:一类是手动触发,另一类是自动触发。
1、手动触发
两个操作:
save
和bgsave
,它们主要区别体现在:是否阻塞 Redis 主线程的执行。save命令:在客户端中执行
save
命令,就会触发 Redis 的持久化,但同时也是使 Redis 处于阻塞状态,直到 RDB 持久化完成,才会响应其他客户端发来的命令,所以在生产环境一定要慎用bgsave命令:bgsave(background save)既后台保存的意思, 它和
save
命令最大的区别就是bgsave
会 fork() 一个子进程来执行持久化,整个过程中只有在 fork() 子进程时有短暂的阻塞,当子进程被创建之后,Redis 的主进程就可以响应其他客户端的请求了具体流程:
- redis客户端执行bgsave命令或者自动触发bgsave命令;
- 主进程判断当前是否已经存在正在执行的子进程,如果存在,那么主进程直接返回;
- 如果不存在正在执行的子进程,那么就fork一个新的子进程进行持久化数据,fork过程是阻塞的,fork操作完成后主进程即可执行其他操作;
- 子进程先将数据写入到临时的rdb文件中,待快照数据写入完成后再原子替换旧的rdb文件;
- 同时发送信号给主进程,通知主进程rdb持久化完成,主进程更新相关的统计信息
2、自动触发
- save m n
ave m n
是指在 m 秒内,如果有 n 个键发生改变,则自动触发持久化。 参数 m 和 n 可以在 Redis 的配置文件中找到,例如,save 60 1
则表明在 60 秒内,至少有一个键发生改变,就会触发 RDB 持久化。 自动触发持久化,本质是 Redis 通过判断,如果满足设置的触发条件,自动执行一次 bgsave
命令。 注意:当设置多个 save m n 命令时,满足任意一个条件都会触发持久化
flushall
flushall
命令用于清空 Redis 数据库,在生产环境下一定慎用,当 Redis 执行了flushall
命令之后,则会触发自动持久化,把 RDB 文件清空主从同步触发
在 Redis 主从复制中,当从节点执行全量复制操作时,主节点会执行
bgsave
命令,并将 RDB 文件发送给从节点,该过程会自动触发 Redis 持久化。默认情况下执行shutdown命令时,如果没有开启aof持久化,那么也会触发bgsave操作
redis.conf中配置RDB:
快照周期:内存快照虽然可以通过技术人员手动执行SAVE或BGSAVE命令来进行,但生产环境下多数情况都会设置其周期性执行条件。
# RDB 保存的条件
save 900 1
save 300 10
save 60 10000
# bgsave 失败之后,是否停止持久化数据到磁盘,yes 表示停止持久化,no 表示忽略错误继续写文件。
stop-writes-on-bgsave-error yes
# RDB 文件压缩
rdbcompression yes
# 写入文件和读取文件时是否开启 RDB 文件检查,检查是否有无损坏,如果在启动是检查发现损坏,则停止启动。
rdbchecksum yes
# RDB 文件名
dbfilename dump.rdb
# RDB 文件目录
dir ./
① save 参数 它是用来配置触发 RDB 持久化条件的参数,满足保存条件时将会把数据持久化到硬盘。 默认配置说明如下:
- save 900 1:表示 900 秒内如果至少有 1 个 key 值变化,则把数据持久化到硬盘;
- save 300 10:表示 300 秒内如果至少有 10 个 key 值变化,则把数据持久化到硬盘;
- save 60 10000:表示 60 秒内如果至少有 10000 个 key 值变化,则把数据持久化到硬盘。
② rdbcompression 参数它的默认值是 yes
表示开启 RDB 文件压缩,Redis 会采用 LZF 算法进行压缩。如果不想消耗 CPU 性能来进行文件压缩的话,可以设置为关闭此功能,这样的缺点是需要更多的磁盘空间来保存文件
③ rdbchecksum 参数 它的默认值为 yes
表示写入文件和读取文件时是否开启 RDB 文件检查,检查是否有无损坏,如果在启动是检查发现损坏,则停止启动。
3、RDB 优缺点
优点:
- RDB 的内容为二进制的数据,占用内存更小,更紧凑,更适合做为备份文件;
- RDB 对灾难恢复非常有用,它是一个紧凑的文件,可以更快的传输到远程服务器进行 Redis 服务恢复;
- RDB 可以更大程度的提高 Redis 的运行速度,因为每次持久化时 Redis 主进程都会 fork() 一个子进程,进行数据持久化到磁盘,Redis 主进程并不会执行磁盘 I/O 等操作;
- 与 AOF 格式的文件相比,RDB 文件可以更快的重启。
缺点:
- 因为 RDB 只能保存某个时间间隔的数据,如果中途 Redis 服务被意外终止了,则会丢失一段时间内的 Redis 数据;
- RDB 需要经常 fork() 才能使用子进程将其持久化在磁盘上。如果数据集很大,fork() 可能很耗时,并且如果数据集很大且 CPU 性能不佳,则可能导致 Redis 停止为客户端服务几毫秒甚至一秒钟。
4、 小结
通过本文我们可以得知,RDB 持久化分为手动触发和自动触发两种方式,它的优点是存储文件小,Redis 启动 时恢复数据比较快,缺点是有丢失数据的风险。RDB 文件的恢复也很简单,只需要把 RDB 文件放到 Redis 的根目录,在 Redis 启动时就会自动加载并恢复数据。
三、AOF 持久化
上面我们说过 RDB 的持久化有一定的时间间隔,如果这段时间内 Redis 服务被终止了,就会丢失最新的数据,于是出现了 AOF 持久化
AOF(Append Only File)即附加到文件,顾名思义 AOF 可以把 Redis 每个键值对操作都记录到文件(appendonly.aof)中。
开启 AOF 持久化:
通过命令行的方式
config set appendonly yes
优缺点:命令行启动优点是无需重启 Redis 服务,缺点是如果 Redis 服务重启,则之前使用命令行设置的配置就会失效。
通过修改配置文件的方式(redis.conf)
Redis 的配置文件在它的根路径下的 redis.conf 文件中,只需要在配置文件中设置
appendonly yes
即可,默认appendonly no
表示关闭 AOF 持久化# appendonly参数开启AOF持久化 appendonly no # 表示关闭 AOF 持久化 # AOF持久化的文件名,默认是appendonly.aof appendfilename "appendonly.aof" # AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的 dir ./ # 同步策略 # appendfsync always # 同步写回 appendfsync everysec # 每秒写回 # appendfsync no # 操作系统控制的写回 # aof重写期间是否同步 no-appendfsync-on-rewrite no # 重写触发配置 auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb # 加载aof出错如何处理 aof-load-truncated yes # 文件重写策略 aof-rewrite-incremental-fsync yes
优缺点:缺点是每次修改配置文件之后重启 Redis 才会生效,优点是之后重启 Redis 服务器修改的配置文件都是有效的
小贴士:因为每次写入磁盘都会对 Redis 的性能造成一定的影响,所以要根据用户的实际情况设置相应的策略,一般设置每秒写入一次磁盘的频率就可以满足大部分的使用场景了。
1、手动触发
在客户端执行 bgrewriteaof
命令即可
2、自动触发
满足 AOF 设置的策略和满足 AOF 重写都会自动触发 AOF 持久化
满足 AOF 设置的策略:
- always,同步写回:每条 Redis 操作命令都会写入磁盘,最多丢失一条数据;
- everysec,每秒写回:每秒钟写入一次磁盘,最多丢失一秒的数据;
- no,操作系统控制的写回:不设置写入磁盘的规则,根据当前操作系统来决定何时写入磁盘,Linux 默认 30s 写入一次数据至磁盘。
优缺点:
配置项 | 写回时机 | 优点 | 缺点 |
---|---|---|---|
Always | 同步写回 | 可靠性高,数据基本不丢失 | 每个写命令都要落盘,性能影响比较大 |
Everysec | 每秒写回 | 性能适中 | 宕机时丢失1秒内的数据 |
No | 操作系统控制的写回 | 性能好 | 宕机时丢失数据较多 |
3、AOF 重写
AOF 是通过记录 Redis 的执行命令来持久化(保存)数据的,所以随着时间的流逝 AOF 文件会越来越多,这样不仅增加了服务器的存储压力,也会造成 Redis 重启速度变慢,为了解决这个问题 Redis 提供了 AOF 重写的功能。
那到底啥是 AOF 重写呢?AOF 重写指的是它会直接读取 Redis 服务器当前的状态,并压缩保存为 AOF 文件。这样可能比较晦涩,我们画一个图看看
从图中我们可以看出,在重写前 AOF 记录了 6 条命令,在重写后 AOF 只记录了 1 条命令,使得 AOF 文件的体积更小,这样对内存更加友好
小贴士:AOF 重写是一个有歧义的名字,该功能是通过读取数据中的键值对来实现的,程序无需对现有的 AOF 文件进行任何读入、分析或写入操作
AOF 重写触发条件:
auto-aof-rewrite-min-size
:表示运行AOF重写时文件的最小大小,默认为64MB。auto-aof-rewrite-percentage
:AOF 文件重写的大小比例,默认值是 100,表示 100%,也就是只有当前 AOF 文件,比最后一次(上次)的 AOF 文件大一倍时,才会启动 AOF 文件重写。
小贴士:只有同时满足 auto-aof-rewrite-min-size 和 auto-aof-rewrite-percentage 设置的条件,才会触发 AOF 文件重写。
注意:使用 bgrewriteaof
命令,可以自动触发 AOF 文件重写。
AOF 重写流程:
在执行 bgrewriteaof
命令时,Redis 服务器会维护一个 AOF 重写缓存区,该缓冲区会在子进程创建新 AOF 文件期间,记录服务器执行的所有的命令。当子进程完成创建新 AOF 文件的工作之后,服务器会将重写缓冲区中的所有内容追加到新 AOF 文件的末尾,使得新旧两个 AOF 文件所保存的数据库状态一致。最后,服务器用新的 AOF 文件替换旧的 AOF 文件,以此来完成 AOF 文件重写操作
旧的 AOF 文件会正常被服务器(父进程)写入,到时候重写之后的 AOF 文件直接覆盖 旧的 AOF 文件
AOF重写会阻塞吗?
AOF重写过程是由后台进程bgrewriteaof
来完成的。主线程fork出后台的bgrewriteaof
子进程,fork会把主线程的内存拷贝一份给bgrewriteaof
子进程,这里面就包含了数据库的最新数据。然后,bgrewriteaof
子进程就可以在不影响主线程的情况下,逐一把拷贝的数据写成操作,记入重写日志。所以aof在重写时,在fork进程时是会阻塞住主线程的。
持久化文件加载规则
- 如果只开启了 AOF 持久化,Redis 启动时只会加载 AOF 文件(appendonly.aof),进行数据恢复;
- 如果只开启了 RDB 持久化,Redis 启动时只会加载 RDB 文件(dump.rdb),进行数据恢复;
- 如果同时开启了 RDB 和 AOF 持久化,Redis 启动时只会加载 AOF 文件(appendonly.aof),进行数据恢复。
4、AOF 优缺点
AOF 优点:
- AOF 持久化保存的数据更加完整,AOF 提供了三种保存策略:每次操作保存、每秒钟保存一次、跟随系统的持久化策略保存,其中每秒保存一次,从数据的安全性和性能两方面考虑是一个不错的选择,也是 AOF 默认的策略,即使发生了意外情况,最多只会丢失 1s 钟的数据;
- AOF 采用的是命令追加的写入方式,所以不会出现文件损坏的问题,即使由于某些意外原因,导致了最后操作的持久化数据写入了一半,也可以通过 redis-check-aof 工具轻松的修复;
- AOF 持久化文件,非常容易理解和解析,它是把所有 Redis 键值操作命令,以文件的方式存入了磁盘。即使不小心使用
flushall
命令删除了所有键值信息,只要使用 AOF 文件,删除最后的flushall
命令,重启 Redis 即可恢复之前误删的数据。
AOF 缺点:
- 对于相同的数据集来说,AOF 文件要大于 RDB 文件;
- 在 Redis 负载比较高的情况下,RDB 比 AOF 性能更好;
- RDB 使用快照的形式来持久化整个 Redis 数据,而 AOF 只是将每次执行的命令追加到 AOF 文件中,因此从理论上说,RDB 比 AOF 更健壮。
5、 小结
AOF 保存数据更加完整,它可以记录每次 Redis 的键值变化,或者是选择每秒保存一次数据。AOF 的持久化文件更加易读,但相比与二进制的 RDB 来说,所占的存储空间也越大,为了解决这个问题,AOF 提供自动化重写机制,最大程度的减少了 AOF 占用空间大的问题。同时 AOF 也提供了很方便的异常文件恢复命令: redis-check-aof --fix
,为使用 AOF 提供了很好的保障。
四、混合持久化
RDB 和 AOF 持久化各有利弊,RDB 可能会导致一定时间内的数据丢失,而 AOF 由于文件较大则会影响 Redis 的启动速度,为了能同时使用 RDB 和 AOF 各种的优点,Redis 4.0 之后新增了混合持久化的方式。
在开启混合持久化的情况下,AOF 重写时会把 Redis 的持久化数据,以 RDB 的格式写入到 AOF 文件的开头,之后的数据再以 AOF 的格式化追加的文件的末尾。
如图所示:
1、开启持久化
使用
config get aof-use-rdb-preamble
命令可以查看 Redis 是否开启了混合持久化小贴士:Redis 5.0 默认是开启的。
通过命令行开启
使用命令 config set aof-use-rdb-preamble yes
开启
注意:命令行设置配置的缺点是重启 Redis 服务之后,设置的配置就会失效。
通过修改 Redis 配置文件开启
把配置文件 redis.conf 文件中的 aof-use-rdb-preamble no
改为 aof-use-rdb-preamble yes
数据恢复
混合持久化的数据恢复和 AOF 持久化过程是一样的,只需要把 appendonly.aof 放到 Redis 的根目录,在 Redis 启动时,只要开启了 AOF 持久化,Redis 就会自动加载并恢复数据
混合持久化的加载流程
- 判断是否开启 AOF 持久化,开启继续执行后续流程,未开启执行加载 RDB 文件的流程;
- 判断 appendonly.aof 文件是否存在,文件存在则执行后续流程;
- 判断 AOF 文件开头是 RDB 的格式, 先加载 RDB 内容再加载剩余的 AOF 内容;
- 判断 AOF 文件开头不是 RDB 的格式,直接以 AOF 格式加载整个文件。
流程图如下:
这样可能比较晦涩,我们举个例子吧:
如下图所示,T1 和 T2 时刻的修改,用 AOF 日志记录,等到第二次做全量快照时,就可以清空 AOF 日志,因为此时的修改都已经记录到快照中了,恢复时就不再用日志了。
这个方法既能享受到 RDB 文件快速恢复的好处,又能享受到 AOF 只记录操作命令的简单优势, 实际环境中用的很多。
2、优缺点
混合持久化优点:
- 混合持久化结合了 RDB 和 AOF 持久化的优点,开头为 RDB 的格式,使得 Redis 可以更快的启动,同时结合 AOF 的优点,有减低了大量数据丢失的风险。
混合持久化缺点:
- AOF 文件中添加了 RDB 格式的内容,使得 AOF 文件的可读性变得很差;
- 兼容性差,如果开启混合持久化,那么此混合持久化 AOF 文件,就不能用在 Redis 4.0 之前版本了。
巨人的肩膀:
https://www.pdai.tech/md/db/nosql-redis/db-redis-x-rdb-aof.html