参考:javaguide
前文
Redis是一种内存数据库,在服务器断电或是重启之后,内存中的数据都会被抹除掉,因此Redis的数据也被一同抹除掉了。但是在某些场景下,我们也需要对Redis中的数据进行持久化,即存储到磁盘,比如重启机器后仍然需要使用之前的数据、服务器崩溃之后的重启需要恢复数据、主从同步等。所以,Redis推出了几种持久化方案:
- RDB(Redis Database Backup,也称作快照持久化)
- AOF(Append Only File,只追加文件)
- RDB和AOF的混合持久化(Redis4.0+支持)
RDB持久化
RDB持久化指的是创建Redis程序当前时间点上的内存数据快照,并且以二进制数据形式存储在磁盘中。 RDB持久化是Redis默认采用的持久化方式,在默认的redis.conf配置文件中有以下配置:
yml
save 900 1 #在900秒之后,如果至少有1个key发生变化,Redis就会自动触发bgsave命令创建快照。
save 300 10 #在300秒之后,如果至少有10个key发生变化,Redis就会自动触发bgsave命令创建快照。
save 60 10000 #在60秒之后,如果至少有10000个key发生变化,Redis就会自动触发bgsave命令创建快照。
RDB持久化会阻塞主线程吗?
Redis提供了两个命令执行RDB持久化:
save:在主线程执行RDB持久化,会阻塞后续命令的执行bgsave:Redis的默认选项,执行后会fork出一个子进程,在子进程执行RDB持久化,不会阻塞主线程。
AOF持久化
AOF持久化是这样实现的:每执行一条更改Redis中的数据的命令,就将该命令写入到AOF文件中。
AOF的基本工作流程?

- 将写命令追加到AOF缓冲区中。(append)
- 将AOF缓冲区的数据写入到系统内核缓冲区,并返回。(write)
- 根据配置文件中配置的
appendfsync策略,在不同时机将系统内核缓冲区中的数据写入磁盘。(fsync) 当AOF文件超过一定的大小之后,会对AOF文件进行重写,压缩其大小。 当Redis重启时,就可以加载AOF文件,并且逐条执行其中的命令,完成数据恢复。
AOF的fsync策略有哪些?
- appendfync always:主线程调用write之后,立即调用fsync,主线程会阻塞,直到fsync命令将数据完全同步到磁盘之后才会返回。这种方式最安全,理论上不会有数据丢失,但是性能很差,因为每次写操作都会因为AOF阻塞主线程。
- appendfync everysec:主线程调用write之后立即返回,由后台线程每秒执行一次fsync。这种方式基本不影响主线程的性能,但是在Redis宕机或是服务器崩溃时,最多会丢失最近一秒内写入的数据。
- appendfync no :主线程调用write之后立即返回,让操作系统决定何时调用fsync。例如Linux系统一般为30秒执行一次。这种方式性能最好,但是数据安全性最差,宕机时丢失的数据量无法估量,取决于操作系统。 为了兼顾性能和数据安全,可以考虑 appendfync everysec。
AOF为什么是在执行完写命令之后才写AOF文件?
- 不会阻塞写命令的执行
- 写命令执行成功说明语法没问题,直接写AOF文件即可,无需进行命令检查,避免了命令语法检查的开销。 当然这样也会带来一些风险,比如执行完写命令之后立即就宕机了,还没来得及写AOF文件,当前的数据就丢失了;因为将命令写到AOF缓冲区的动作是在主线程完成的,所以可能会阻塞后续命令的执行。
AOF重写是什么?
当AOF文件太大时,Redis会在后台自动重写AOF文件,新的AOF文件和原有的AOF文件保存的数据一致,但体积更小。 AOF的重写实际上是一个不太准确的名字,因为重写并不是基于旧的AOF文件,而是直接读取内存中的数据来实现的。 AOF文件重写期间,Redis会维护一个AOF重写缓冲区,该缓冲区记录在子进程创建新AOF文件期间执行的所有写命令(在Redis7.0之前,这些写命令也会写入旧的AOF文件,如果这个期间的命令很多,此时会使用大量内存,重写期间到达的所有写入命令都会写入磁盘两次)。当子进程完成创建新的AOF文件之后,Redis会将重写缓冲区中的所有内容追加到新的AOF文件的末尾,使得新的AOF文件保存的数据库状态与现有的数据库状态一致。最后,用新的AOF文件替换旧的AOF文件,整个重写流程就结束了。 我们可以使用bgrewriteaof命令手动执行AOF重写,也可以设置下面两个配置项,让程序自动触发AOF重写:
auto-aof-rewrite-min-size:AOF文件至少要达到这个参数的大小,才会考虑重写。auto-aof-rewrite-percentage:当前AOF相比于上一次重写时的AOF大小增长的百分比超过这个参数,才会考虑重写。默认值是100,也就是说当前AOF大小是上一次重写的AOF文件大小的两倍时,才会考虑重写。
Redis每次执行写命令之后,都会检查是否同时满足以上两个条件(1、当前AOF文件大小 > auto-aof-rewrite-min-size 2、当前AOF文件大小 > 上次重写后大小 × (1 + auto-aof-rewrite-percentage / 100)),若满足则自动触发AOF重写。
Redis7.0之后,AOF重写机制得到了改进,实现了Multi-part AOF 机制(非重点等待补充...)。
RDB和AOF的混合持久化是什么?AOF校验机制是怎样的?
Redis的混合持久化的机制是这样的:在AOF文件重写时,先读取内存数据生成RDB快照文件,并将此写到AOF文件的开头,随后将重写期间的写命令写入到AOF文件末尾。 混合持久化的好处是可以结合RDB和AOF的优点,缺点是AOF文件中的RDB部分可读性差。 关于校验机制,分为两种情况:
- 在AOF模式下,Redis重启加载AOF文件时会逐条解析AOF文件中的命令,如果解析过程中发现语法错误,则会终止加载并报错。
- 在混合持久化模式下,Redis会使用AOF文件开头的RDB快照部分计算出CRC64校验和,并放在RDB部分之后、AOF部分之间。当Redis重启加载AOF文件时,会先使用AOF文件开头的RDB快照部分计算出CRC64校验和,并与文件中的校验和进行比较,如果不相等,则拒绝启动。如果检验通过,会逐条解析AOF文件中的命令,如果解析过程中发现语法错误,则会终止加载后续命令,并报告错误,但是此时Redis已经成功加载了RDB快照部分的数据并且启动了。
如何选择RDB和AOF?
RDB比AOF好的地方:
- RDB文件是经过压缩的二进制数据,文件体积小,并且大多数情况下比AOF文件体积更小。
- 使用RDB文件恢复数据时,直接解析还原数据即可,不需要像AOF一样逐行读取并执行命令,速度更快。
AOF比RDB好的地方:
- 在Redis版本演进中有多个不同版本的RDB,存在老版本的Redis不兼容新版本的RDB格式的问题。
- AOF的格式相对来说是易读的,可以进行人工分析,或是直接修改AOF文件来解决一些问题。比如如果不小心执行了
FLUSHALL命令将数据清空之后,只要AOF文件还没有被重写,在AOF文件中删除掉该命令,并使用其重启即可恢复数据。 - AOF的数据安全性更好,并且更轻量 。因为使用
appendfync everysec就能保证至多只有最近一秒的数据丢失,并且保证性能几乎没有损耗,同时因为仅仅只是需要追加命令到AOF文件,较为轻量。虽然可以调整配置提高生成RDB快照的频率,以保证数据安全性,但是一般来说是不会在生产环境使用,因为生成RDB文件的过程相对来说比较消耗CPU资源和内存资源,严重的时候甚至会使得Redis服务宕机。
综上:
- 如果能容许丢失小部分数据,可以选择RDB
- 不建议单独使用AOF,因为使用RDB快照可以更快重启,并且可以做数据库备份,同时还可以解决AOF引擎错误。
- 如果对保存数据安全性较高,推荐同时开启RDB和AOF持久化,或者开启混合持久化。