走进Redis - 数据持久化
Redis 的数据持久化机制
Redis 的持久化主要有两大机制:AOF(Append Only File) 日志和 RDB 快照。
AOF
AOF 记录 Redis 的操作日志,它是在主线程中执行的。AOF 和数据库的写前日志(Write Ahead Log, WAL)不同,WAL 是在执行命令前先把修改的数据写到日志文件中,而 AOF 是在命令执行成功之后再记录日志,这样有两个好处:
- 可以减少额外的检查命令是否正确的开销。
- 因为是在命令执行后记录,所以不会阻塞当前的写操作。
AOF 日志的格式
AOF 中的命令是以文本形式记录的,以 “set testkey testvalue” 为例:Redis 会先以 *3 来表示当前命令有三个部分,每部分会先以 $+数字 来表示长度,然后跟上具体的命令、键或值。例如:
潜在风险
- 可能在命令执行之后,写入 AOF 之前机器宕机,那么相应数据就有丢失的风险。
- 上面说到 AOF 是在主线程执行的,如果磁盘写入很慢,那么就有可能阻塞下一个命令的执行。
写回策略
根据上述的潜在风险,Redis 提供了三种写回策略供用户评估选择,可以在 AOF 配置项 appendfsync 配置:
配置项 | 写回时间 | 优点 | 缺点 |
---|---|---|---|
Always | 同步写回 | 可靠性高,数据基本不丢失 | 每个写命令都要落盘,性能影响较大 |
Everysec | 每秒写回 | 性能适中 | 宕机时丢失1秒内的数据 |
No | 操作系统控制的写回 | 性能好 | 宕机时丢失数据较多 |
AOF 重写机制
背景
当 Redis 接收的命令越来越多,那么 AOF 文件就会越来越大,AOF 文件过大带来性能问题:
- 文件系统本身对文件大小有限制,无法保存过大的文件;
- 文件过大时,追加命令记录效率会变低;
- 如果发生宕机,AOF 中的记录要一个个回放恢复数据,文件过大时会导致这个过程非常缓慢,影响 Redis 的正常使用。
重写机制
重写机制是指 Redis 会根据当前的数据状态创建一个新的 AOF 文件,即读取当前 Redis 中的所有的记录,将其一一写入新的 AOF 文件中。
为什么重写机制可以缩小 AOF 文件的大小呢?正常使用过程中,使用者可能会对一个 key 进行多次操作,而这些操作都会被记录到 AOF 中,而重写机制会忽略这些过程,只将最后的结果记录到 AOF 中,多条记录合并成了一条。
重写过程和 AOF 日志不同,它是由后台子进程 bgrewriteaof 来完成的,这样可以避免阻塞主线程导致的性能下降。
重写过程的主要流程如下:
- 每次执行重写时,主线程会 fork 一个 bgrewiteaof 子进程,因为 fork 的特性,同时会把主进程的内存拷贝一份给子进程。
- 因为此时主线程没有被阻塞,AOF 日志还会正常执行。
- 主线程的操作还会被记录到重写缓冲区中,等拷贝数据重写完成后,重写缓冲区的日志也会被写入新的 AOF 文件,都完成后,新的 AOF 文件会替代就文件。
💡 其实,重写机制是会阻塞主线程的,这发生在 fork 子进程的时候,因为这时候需要拷贝主线程的数据结构,包括内存页。这个过程会消耗大量的 CPU 资源,并且主线程是阻塞的,阻塞的时间取决于整个实例的内存大小。同时,因为 Linux 的 fork 是写时赋值,当主线程接收请求,操作了一个已经存在的数据,父进程会重新为该数据分配内存,如果操作的是一个 bigkey,重新申请内存的时间就会变长,从而产生阻塞的风险。Linux 上分配内存是以内存页为单位的,默认大小为 4k,当开启了大页机制(Big Huge 页面大小 2M)时,阻塞的概率会大大提高,所以,Redis 机器上最好关闭 Huge Page 机制。
RDB
RDB 是 Redis 提供的一种内存快照,用来记录一段时间内 Redis 中的数据。和 AOF 相比,RDB 记录的是数据而不是操作,所以可以直接将 RDB 文件读入内存恢复数据。
快照机制
Redis 提供了两个命令来生成 RDB 文件:
- save:在主线程执行,会导致阻塞;
- bgsave:在子进程执行,避免阻塞,是默认的选择。
bgsave 和 AOF 的重写机制一样,是 fork 一个子进程来执行的,而 fork 之后的内存是写时拷贝的,当主线程接收到修改数据的命令时,会拷贝一份数据进行修改。这样既不会影响 bgsave 对旧的数据做快照,也不会影响主线程对数据进行修改。
优化
快照的意义在于备份数据库的数据,当两次备份中服务器发生了宕机,那么就可能会导致数据丢失,这时就会要求两次快照之间的时间间隔足够短。如果在短时间内执行全量快照可能会带来两方面的开销:
- 频繁将全量数据写入磁盘,会给磁盘带来很大压力,多个快照竞争优先的磁盘带宽,前一个快照还没做完,后一个就开始了,容易造成恶性循环;
- 和 bgrewiteaof 一样,fork 子进程可能会有阻塞主线程的风险,频繁执行全量快照会将这个风险放大。
一个解决方式是增量快照,即记录两次快照间的数据变化,下一次做快照只针对这些变化的数据做记录。在内存中记录数据变化是现实的,会造成大量的内存开销。Redis 的解决方案是混用 AOF 日志和 RDB,由 AOF 来记录两次内存快照间的数据变化。这样,快照不需要频繁执行,避免了频繁 fork 对主线程的影响,而 AOF 也只用记录两次快照间的数据,不会出现文件过大的情况,避免重写开销。
颇有两全其美的感觉。
总结
- 数据不能丢失时,内存快照和 AOF 的混合使用是一个很好的选择;
- 如果允许分钟级的数据丢失,可以只是用 RDB。
- 如果只用 AOF,优先使用 everysec 的配置选项。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· 2 本地部署DeepSeek模型构建本地知识库+联网搜索详细步骤