目录
使用Redis缓存的时候,我们经常需要对内存中的数据进行持久化,也就是将内存中的数据写入到硬盘中。大部分原因是为了之后重用数据(比如重启机器、机器故障之后恢复数据),或者是为了做数据同步(比如 Redis 集群的主从节点通过 RDB 文件同步数据)。
RDB持久化
1.什么是 RDB 持久化
RDB持久化又称为RDB快照,所谓快照,就是某一时刻的数据拷贝。
RDB快照是全量快照,也就是把内存中的「所有数据」都记录到磁盘中。RDB,就是一份全量数据文件,恢复的时候,直接加载到内存中即可,所以RDB数据恢复速度很快。
Redis 提供了两个命令来生成 RDB 快照文件,分别是 save
和 bgsave
,他们的区别就在于是否在「主线程」里执行:
save
: 同步保存操作,会阻塞 Redis 主线程;bgsave
: fork 出一个子进程,子进程执行,不会阻塞 Redis 主线程,默认选项。
快照持久化是 Redis 默认采用的持久化方式,可以通过配置文件的选项来实现每隔一段时间自动执行一次 bgsave 命令,在 redis.conf
配置文件中默认有此下配置
save 900 1
save 300 10
save 60 10000
这里的save其实是指bgsave命令,以第一行配置为例,表示如果900秒内至少有1个key被修改就执行bgsave。
2.写时复制
前文提到,执行 bgsave 过程中,由于是交给子进程来构建 RDB 文件,那么主进程是否能够对数据进行修改呢?
答案是可以的,这里就用到了写时复制技术(Copy-on-write)。
执行 bgsave 命令的时候,会通过 fork()
一个子进程,此时子进程和父进程是共享同一片内存数据的,创建子进程的时候,会复制父进程的页表,但是页表指向的实际物理内存是同一个,操作系统会把这一块内存空间标记为只读(read-only)。只有当内存数据被修改时(写操作),才会真正拷贝物理内存,这就是写时复制。
写时复制的好处在于减少创建子进程时的性能损耗,子进程创建时只需要复制页表而不是进行物理内存的拷贝,加快了子进程的创建速度。
3.RDB优缺点
优点:
- RDB是一个紧凑压缩的二进制文件,代表Redis在某一个时间点上的数据快照,非常适合用于备份、全量复制等场景。
- RDB是Redis数据的内存快照,数据恢复速度较快,相比于AOF的命令重放有着更高的性能。
缺点:
- RDB方式无法做到实时或秒级持久化。因为持久化过程是通过fork子进程后由子进程完成的,子进程的内存只是在fork操作那一时刻父进程的数据快照。而fork操作是一个耗时操作,无法做到实时性。
- RDB持久化过程中的fork操作,会导致内存占用加倍,而且父进程数据越多,fork过程越长。
- RDB文件有文件格式要求,不同版本的Redis会对文件格式进行调整,存在老版本无法兼容新版本的问题。
AOF持久化
1.什么是AOF持久化
AOF(Append Only File) 持久化功能,是指保存写操作命令到日志的持久化方式,注意只有写操作命令,因为记录读操作对于持久化而言没有意义。
默认情况下 Redis 没有开启 AOF(append only file)方式的持久化,可以通过 appendonly
参数开启:
//redis.conf
appendonly yes
2.AOF的执行流程
1.命令追加(append):所有的写命令会追加到 AOF 缓冲区中。
2.文件写入(write):将 AOF 缓冲区的数据写入到 AOF 文件中。这一步需要调用
write
函数(系统调用),write
将数据写入到了系统内核缓冲区之后直接返回了(延迟写),此时并没有同步到磁盘。3.文件同步(fsync):AOF 缓冲区根据对应的写回策略向硬盘做同步操作。这一步需要调用
fsync
函数(系统调用),fsync
针对单个文件操作,对其进行强制硬盘同步,fsync
将阻塞直到写入磁盘完成后返回,保证了数据持久化。4.文件重写(rewrite):随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩的目的。
5.重启加载(load):当 Redis 重启时,可以加载 AOF 文件进行数据恢复。
3.三种写回策略
redis.conf配置文件中的 appendfsync
配置项可以有以下 3 种参数可填:
- Always 每次写操作命令执行完后,同步将 AOF 日志数据写回硬盘;
- Everysec 每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,然后每隔一秒将缓冲区里的内容写回到硬盘;
- No 不由 Redis 控制写回硬盘的时机,转交给操作系统控制写回的时机,也就是每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,再由操作系统决定何时将缓冲区内容写回硬盘。
3种写回策略本质上是在控制 fsync()
函数的调用时机,对于Always,在每次写入内核缓冲区(Page Cache)后,立刻调用fsync()
,而Everysec是创建一个异步任务来执行 fsync() 函数,No就是不主动调用fsync() 函数,让操作系统决定。
这 3 种写回策略都无法能完美解决「主进程阻塞」和「减少数据丢失」的问题,原因如下:
- Always 策略的话,可以最大程度保证数据不丢失,但是由于它每执行一条写操作命令就同步将 AOF 内容写回硬盘,所以是不可避免会影响主进程的性能;
- No 策略的话,是交由操作系统来决定何时将 AOF 日志内容写回硬盘,相比于 Always 策略性能较好,但是操作系统写回硬盘的时机是不可预知的,如果 AOF 日志内容没有写回硬盘,一旦服务器宕机,就会丢失不定数量的数据。
- Everysec 策略的话,是上述两种方式的折中,避免了 Always 策略的性能开销,也比 No 策略更能避免数据丢失,当然如果上一秒的写操作命令日志没有写回到硬盘,发生了宕机,这一秒内的数据自然也会丢失。
4.AOF重写
由于AOF记录写操作命令,随着写操作命令增多,AOF文件会越来越大,当 AOF文件 变得太大时,Redis 能够在后台自动重写 AOF 产生一个新的 AOF 文件,这个新的 AOF 文件和原有的 AOF 文件所保存的数据库状态一样,但体积更小。
AOF 重写机制是在重写时,读取当前数据库中的所有键值对,然后将每一个键值对用一条命令记录到「新的 AOF 文件」,等到全部记录完后,就将新的 AOF 文件替换掉现有的 AOF 文件。
例如原来AOF文件保存有"set key breeze"和"set key yes"两个命令,执行重写后原有的第一个命令就没有必要记录了,只要根据对于key的新值来记录命令即可。
为了避免对 Redis 正常处理命令请求造成影响,Redis 将 AOF 重写程序放到子进程里执行。这里AOF后台重写也采用了写时复制copy-on-write,和RDB类似。
5.AOF优缺点
优点:
- 数据丢失较少,数据安全性相对较高。根据使用的fsync策略(fsync是同步内存中redis所有已经修改的文件到存储设备),默认是appendfsync everysec,即每秒执行一次fsync,在这种配置下,redis仍然可以保持良好的性能,并且就算发生故障停机,最多指挥丢失一秒钟的数据(fsync会在后台线程执行,所以主线程可以继续努力的处理命令请求)
- redis可以在aof文件体积变得过大时,自动的在后台对aof进行重写,重写后的新aof文件包含了恢复当前数据集所需要的最小命令集合。整个重写操作时绝对的,因此redis在创建新aof文件的过程中,append模式不断的将修改数据追加到现有的aof文件里面,即使重写过程中发生停机,现有的aof文件也不会丢失。而一旦新aof文件创建完毕,redis就会从旧aof文件切换到新aof文件,并开始对新aof文件进行追加操作。
缺点:
- 由于会记录重复写操作命令,aof文件的大小往往要大于rdb格式的文件
- aof在恢复大数据集时的速度比rdb的恢复速度要慢