redis持久化

Redis的持久化包括RDB内存快照和AOF写后日志。RDB在特定时间生成数据快照,采用COW技术避免阻塞,但频繁生成会增加磁盘压力。AOF记录修改指令,保证数据不丢失,通过fsync策略平衡性能与可靠性。AOF重写避免文件过大,通过子进程完成。Redis4.0引入混合日志模型,结合RDB与AOF优点,提高重启效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

思维导图

在这里插入图片描述

内存快照

在redis执行 “写指令”过程中,内存数据会一直变化。所谓的内存快照, 指的是 Redis内存中的数据在某一刻的状态。

像拍照一样,通过照片就能把某一刻的瞬间画面完全记录下来。redis与之类似,把某一时刻的数据以文件的形式拍下来,写到磁盘上。这个快照文件就叫RDB文件。 (redis DataBase)

redis通过定时执行RDB内存快照,这样就不必每次执行「写」指令都写磁盘,只需要在执行内存快照的时候写磁盘。既保证了唯快不破,还实现了持久化,宕机快速恢复。

生成RDB策略

Redis 提供了两个指令用于生成 RDB 文件:
save:主线程执行,会阻塞;
bgsave:调用 glibc 的函数fork产生一个子进程

用于写入 RDB 文件,快照持久化完全交给子进程来处理,父进程继续处理客户端请求,生成 RDB 文件的默认配置。

那在对内存数据做「快照」的时候,内存数据还能修改么?也就是写指令能否正常处理?

避免阻塞和 RDB 文件生成期间能处理写操作不是一回事。虽然主线程没有阻塞,到那时为了保证快照的数据的一致性,只能处理读操作,不能修改正在执行快照的数据。

很明显,为了生成 RDB 而暂停写操作,Redis 是不答应的。

Redis 如何实现一边处理写请求,同时生成 RDB 文件呢?

Redis 使用操作系统的多进程写时复制技术 COW(Copy On Write) 来实现快照持久化.

Redis 在持久化时会调用 glibc 的函数fork产生一个子进程,快照持久化完全交给子进程来处理,父进程继续处理客户端请求。

子进程刚刚产生时,它和父进程共享内存里面的代码段和数据段。这时你可以将父子进程想像成一个连体婴儿,共享身体。

这是 Linux 操作系统的机制,为了节约内存资源,所以尽可能让它们共享起来。在进程分离的一瞬间,内存的增长几乎没有明显变化。

bgsave 子进程可以共享主线程的所有内存数据,读取主线程的数据并写入到 RDB 文件。

在执行 SAVE 命令或者BGSAVE命令创建一个新的 RDB 文件时,程序会对数据库中的键进行检查,已过期的键不会被保存到新创建的 RDB 文件中。

当主线程执行写指令修改数据的时候,这个数据就会复制一份副本, bgsave 子进程读取这个副本数据写到 RDB 文件,所以主线程就可以直接修改原来的数据。

这既保证了快照的完整性,也允许主线程同时对数据进行修改,避免了对正常业务的影响。

Redis 会使用 bgsave 对当前内存中的所有数据做快照,这个操作是子进程在后台完成的,这就允许主线程同时可以修改数据。

那可以每秒都执行 RDB 文件么,这样即使发生宕机最多丢失 1 秒的数据。

过于频繁的执行全量数据快照,有两个严重性能开销:

  1. 频繁生成 RDB 文件写入磁盘,磁盘压力过大。会出现上一个 RDB 还未执行完,下一个又开始生成,陷入死循环。

  2. fork 出 bgsave 子进程会阻塞主线程,主线程的内存越大,阻塞时间越长。

RDB的优缺点

优点:
RDB 采用二进制 + 数据压缩的方式写磁盘,文件体积小,数据恢复速度快。

缺点:
快照的恢复速度快,但是生成 RDB 文件频率不好把握。

频率过低宕机丢失的数据就会比较多;频率太快,又会消耗额外开销。

AOF写后日志

AOF 日志存储的是 Redis 服务器的顺序指令序列,AOF 日志只记录对内存进行修改的指令记录。

假设 AOF 日志记录了自 Redis 实例创建以来所有的修改性指令序列,那么就可以通过对一个空的 Redis 实例顺序执行所有的指令,也就是「重放」,来恢复 Redis 当前实例的内存数据结构的状态。

写前与写后日志对比

写前日志(Write Ahead Log, WAL): 在实际写数据之前,将修改的数据写到日志文件中,故障恢复得以保证。
比如 MySQL Innodb 存储引擎 中的 redo log(重做日志)便是记录修改的数据日志,在实际修改数据前先记录修改日志在执行修改数据。

写后日志: 先执行「写」指令请求,将数据写入内存,再记录日志。

为什么 Redis 使用写后日志这种方式呢?

写后日志避免了额外的检查开销,不需要对执行的命令进行语法检查。如果使用写前日志的话,就需要先检查语法是否有误,否则日志记录了错误的命令,在使用日志恢复的时候就会出错。
另外,写后才记录日志,不会阻塞当前的「写」指令执行。

那有了 AOF 就万无一失了么?

AOF 避免了当前命令的阻塞,但是可能会给下一个命令带来阻塞的风险。AOF 日志是主线程执行,将日志写入磁盘过程中,如果磁盘压力大就会导致写磁盘很慢,导致后续的「写」指令阻塞。

写回策略

为了提高文件的写入效率,当用户调用 write 函数,将一些数据写入到文件的时候,操作系统通常会将写入数据暂时保存在一个内存缓冲区里面,等到缓冲区的空间被填满、或者超过了指定的时限之后,才真正地将缓冲区中的数据写入到磁盘里面。

这种做法虽然提高了效率,但也为写入数据带来了安全问题,因为如果计算机发生停机,那么保存在内存缓冲区里面的写入数据将会丢失。

为此,系统提供了fsync和fdatasync两个同步函数,它们可以强制让操作系统立即将缓冲区中的数据写入到硬盘里面,从而确保写入数据的安全性。

Redis 提供的 AOF 配置项appendfsync写回策略直接决定 AOF 持久化功能的效率和安全性。

有三种策略:

1) always:同步写回,写指令执行完毕立马将 aof_buf缓冲区中的内容刷写到 AOF 文件。

2)everysec:每秒写回,写指令执行完,日志只会写到 AOF 文件缓冲区,每隔一秒就把缓冲区内容同步到磁盘。

3) no: 操作系统控制,写执行执行完毕,把日志写到 AOF 文件内存缓冲区,由操作系统决定何时刷写到磁盘。

如何选择策略?

可以根据系统对高性能和高可靠性的要求,来选择写回策略。

总结一下:想要获得高性能,就选择 No 策略;如果想要得到高可靠性保证,就选择 Always 策略;如果允许数据有一点丢失,又希望性能别受太大影响的话,那么就选择 Everysec 策略。

AOF 日志文件过大着怎么办?

AOF 写前日志,记录的是每个「写」指令操作。

不会像 RDB 全量快照导致性能损耗,但是执行速度没有 RDB 快,同时日志文件过大也会造成性能问题,对于唯快不破的 Redis 这个真男人来说,绝对不能忍受日志过大导致的问题。

所以,Redis 设计了一个杀手锏「AOF 重写机制」,Redis 提供了 bgrewriteaof指令用于对 AOF 日志进行瘦身。

其原理就是开辟一个子进程对内存进行遍历转换成一系列 Redis 的操作指令,序列化到一个新的 AOF 日志文件中。

序列化完毕后再将操作期间发生的增量 AOF 日志追加到这个新的 AOF 日志文件中,追加完毕后就立即替代旧的 AOF 日志文件了,瘦身工作就完成了。

重写过程

和 AOF 日志由主线程写回不同,重写过程是由后台子进程 bgrewriteaof 来完成的,这也是为了避免阻塞主线程,导致数据库性能下降。

总的来说,一共出现 两个日志,一次拷内存数据拷贝,分别是旧的 AOF 日志和新的 AOF 重写日志和 Redis 数据拷贝。

Redis 会将重写过程中的接收到的「写」指令操作同时记录到旧的 AOF 缓冲区和 AOF 重写缓冲区,这样重写日志也保存最新的操作。等到拷贝数据的所有操作记录重写完成后,重写缓冲区记录的最新操作也会写到新的 AOF 文件中。

每次 AOF 重写时,Redis 会先执行一个内存拷贝,用于遍历数据生成重写记录;使用两个日志保证在重写过程中,新写入的数据不会丢失,并且保持数据一致性。

AOF 重写也有一个重写日志,为什么它不共享使用 AOF 本身的日志呢?


这个问题问得好,有以下两个原因:

  1. 一个原因是父子进程写同一个文件必然会产生竞争问题,控制竞争就意味着会影响父进程的性能。
  2. 如果 AOF 重写过程中失败了,那么原本的 AOF 文件相当于被污染了,无法做恢复使用。所以 Redis AOF 重写一个新文件,重写失败的话,直接删除这个文件就好了,不会对原先的 AOF 文件产生影响。等重写完成之后,直接替换旧文件即可。

AOF的优缺点

优点:执行成功才记录日志,避免了指令语法检查开销。同时,不会阻塞当前「写」指令。

缺点:由于 AOF 记录的是一个个指令内容,具体格式请看上面的日志格式。故障恢复的时候需要执行每一个指令,如果日志文件太大,整个恢复过程就会非常缓慢。

另外文件系统对文件大小也有限制,不能保存过大文件,文件变大,追加效率也会变低。

Redis 4.0 混合日志模型

重启 Redis 时,我们很少使用 rdb 来恢复内存状态,因为会丢失大量数据。我们通常使用 AOF 日志重放,但是重放 AOF 日志性能相对 rdb 来说要慢很多,这样在 Redis 实例很大的情况下,启动需要花费很长的时间。

Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化。将 rdb 文件的内容和增量的 AOF 日志文件存在一起。这里的 AOF 日志不再是全量的日志,而是自持久化开始到持久化结束的这段时间发生的增量 AOF 日志,通常这部分 AOF 日志很小。

于是在 Redis 重启的时候,可以先加载 rdb 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重启效率因此大幅得到提升。

所以 RDB 内存快照以稍微慢一点的频率执行,在两次 RDB 快照期间使用 AOF 日志记录期间发生的所有「写」操作。
这样快照就不用频繁的执行,同时由于 AOF 只需要记录两次快照之间发生的「写」指令,不需要记录所有的操作,避免出现文件过大的情况。

总结

Redis 设计了 bgsave 和写时复制,尽可能避免执行快照期间对读写指令的影响,频繁快照会给磁盘带来压力以及 fork 阻塞主线程。

Redis 设计了两大杀手锏实现了宕机快速恢复,数据不丢失。
避免日志过大,提供了 AOF 重写机制,根据数据库的数据最新状态,生成数据的写操作作为新日志,并且通过后台完成不阻塞主线程。

综合 AOF 和 RDB 在 Redis 4.0 提供了新的持久化策略,混合日志模型。

在 Redis 重启的时候,可以先加载 rdb 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重启效率因此大幅得到提升。

最后,关于 AOF 和 RDB 的选择问题,

1)数据不能丢失时,内存快照和 AOF 的混合使用是一个很好的选择;

2)如果允许分钟级别的数据丢失,可以只使用 RDB;

3)如果只用 AOF,优先使用 everysec 的配置选项,因为它在可靠性和性能之间取了一个平衡。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值