redis 3 - 持久化
一、概述
(本质是Redis是个有状态服务,需要考虑状态不丢失)
Redis是内存数据库。一旦断电,内存中的数据都会丢失。因此Redis需要对数据进行持久化,防止数据丢失。
Redis有两种持久化技术,分别是AOF和RDB。
二、AOF
Append Only File. 即把所有的写操作以追加的方式记录在文件里。
Redis在重启的时候,通过回放文件中的操作,就可以恢复数据。
在Redis中AOF功能默认是关闭的,可以通过修改redis.conf
配置文件中的参数开启。
1 |
|
Redis先执行写命令,再将命令记录到AOF日志中,有两个好处:
- 避免额外的检查开销。
- 先执行写操作再记录日志,只有当命令执行成功时,才将命令记录下来。这样可以保证记录在AOF日志里的命令都是可执行的、正确的。
- 不会阻塞当前写操作的执行
- 因为先执行写操作。
也有两个风险:
- 如果当Redis没有把命令写入到磁盘时,服务器宕机了,数据就会有丢失的风险
- 虽然不阻塞当前写操作,但是可能阻塞下一个写操作。
- 如果写入硬盘时,硬盘的IO压力大,就会导致写入的速度比较慢,阻塞后面的命令执行。
这两个风险都和「AOF日志写回硬盘的时机」相关。
三种写回策略
Redis写入AOP日志的过程,如图所示,具体分为三步:
- Redis执行完成命令后,将该命令写到应用程序缓冲区
- 执行write命令,将该命令从应用程序缓冲区写入内核缓冲区
- 执行fsync命令,将该命令从内核缓冲区写入硬盘
「AOF日志写回硬盘的时机」其实就是指的第三步的时机。
在redis.conf
配置文件中,appendfsync
配置项有三种可选参数
- Always
- 每次写命令执行完成后,同步执行fysnc函数
- Everysec
- 一秒后创建一个异步任务执行fysnc函数
- No
- 永不执行fsync函数,由操作系统控制写回时机。
这三种策略其实是可靠性和性能的权衡。
写回策略 | 写回时机 | 数据可靠性 | 性能 |
---|---|---|---|
Always | 同步写回 | 高 | 低 |
Everysec | 每秒异步写回 | 中,宕机会丢失一秒内数据 | 适中 |
No | 操作系统控制写回 | 低,宕机会丢很多数据 | 高 |
AOF重写机制
AOF日志是一个文件,随着写操作命令越来越多,文件会变得越来越大。
会造成一个问题:Redis重启后,回放命令的过程比较长,数据恢复时间久。
Redis为了避免AOF文件越来越大,提供了AOF重写机制。
当AOF文件大小超过设定的阈值,就会启动「AOF重写」,压缩AOF文件。
「AOF重写」的执行逻辑是:
读取当前数据库所有键值对,将每个键值对转换成一条命令记录到新的AOF文件,记录完成后,用新的AOF文件替换现有的AOF文件。
优点在于:只需要记录结果,相当于把对同一个数据键的多次操作合并了。
AOF后台重写
AOF重写时,需要读取数据库里所有键值对,将每个键值对都转换成命令记录下来。
这个过程非常耗时,所以不能放在主进程里。
所以,Redis重写AOF过程是由后台子进程bgrewriteaof
来完成的。
这样的话,子进程进行AOF重写期间,主进程可以继续处理命令,避免阻塞主进程。
具体操作是:
- 主进程fork调用系统生成子进程,操作系统把主线程的「页表」复制一份给子进程。
- 这个页表记录虚拟地址到物理地址的映射关系,而不会复制物理内存。
- 在不修改内存数据的情况下,子进程和父进程是共享了物理内存数据的。
- 父进程修改内存数据时,会系统中断进入「写时复制」流程
- 发生写操作的时候,复制物理内存,将父子进程对应的物理内存分离。
- 只会复制修改的物理内存,没修改的还是和子进程共享。
- 子进程用复制的一份数据重写完成AOF文件后,会给父进程发送一个信号,父进程收到信号后,调用信号处理函数。完成AOF文件的替换。
在这个流程中有三个阶段会阻塞父进程
- 创建子进程时,需要复制父进程的页表。阻塞时间和页表大小有关
- 创建子进程后,如果父进程修改了共享的数据,会发生「写时复制」,复制物理内存。内存越大阻塞时间越长。
- 信号处理函数执行时。
上述描述只关注了「存量数据」,即页表复制完成那一时刻的数据。
对于增量数据,Redis提供了一个重写缓冲区,在信号处理函数中将重写缓冲区里的内容追加到新的AOF文件中。
所以在AOF重写的过程中,对于写操作,Redis会将它写入两个缓冲区。
三、RDB
RDB也是将数据持久化到日志文件中,但是和AOF不同,它记录的是二进制数据。是某一时刻的全量数据快照。
在Redis恢复数据时,RDB的效率更高一些,因为直接将RDB文件读入内存即可,不需要像AOF那样回放操作。
RDB的快照是全量快照,所以执行快照是一个比较重的操作,如果频率太频繁,可能会对Redis性能产生影响。
这就决定了它执行地不能太频繁,而这样,在宕机时就会丢失更多数据。
Redis提供了两个命令来生成RDB文件,分别是save
和bgsave
。区别在于是否在主线程执行。
可以通过配置文件选项来实现每隔一段时间自动执行一次bgsave
命令,默认提供以下配置
1 |
|
只要满足任意一个条件,就会执行一次bgsave
命令。它们的意思分别是:
- 900秒内,对数据库进行了至少1次修改
- 300秒内,对数据库进行了至少10次修改
- 60秒内,对数据库进行了至少10000次修改
RDB与AOF结合
这里发现,AOF重写时其实也是记录下当时全量数据的快照。
所以用RDB快照的方式代替AOF重写,就能获得这两种持久化方式的优点。
Redis4.0提供了这种混合使用的方法。
在redis.conf
中将配置项设置为yes
1 |
|
四、总结
Redis是一种内存数据库,而内存在断电时就会丢失数据,因此需要对数据进行持久化。
Redis支持两种持久化方式,分别是
- AOF,记录下所有写操作,在重启时回放写操作来恢复数据。
- RDB,备份数据的二进制文件,在重启时直接读取文件来恢复数据。
AOF是以记录增量操作的方式记录数据,所以记录频次相对较高,优点是丢失数据比较少。
但是这样就会记录许多无用操作(对一个键的多次操作,我们其实只关心结果),这样文件的大小就会很快地膨胀,很影响数据恢复的效率;
Redis提供了AOF重写机制,在AOF文件大小超过阈值时,将它进行重写,来压缩文件大小。(重写逻辑是将当前所有键值对转换成AOF文件格式)
RDB是以全量快照的方式记录数据,记录频次比较低,宕机时会丢失更多数据。
但是重启时RDB格式恢复比较快,因为它记录的是二进制数据,并且没有重复数据。
这里发现,AOF重写其实逻辑和RDB是一样的(都是记录当前快照)
Redis高版本提供了两种技术结合使用的方式,AOF负责记录增量数据,AOF重写使用RDB替代。
这样既能在对性能影响比较小的情况下丢失比较少的数据(AOF的优点),也可以拥有比较快的数据恢复速度(RDB的优点)。