news 2026/6/1 20:00:21

【Redis从入门到精通】第29篇:RDB持久化——Redis的快照是怎么拍的

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【Redis从入门到精通】第29篇:RDB持久化——Redis的快照是怎么拍的

上一篇【第28篇】数据库通知——Redis的事件订阅机制
下一篇【第30篇】RDB文件格式完全解析——一个快照文件里藏着什么秘密


如果说Redis是内存中的数据库,那RDB就是它的"时间胶囊"——把某一刻的内存状态完整地封存起来,万一哪天Redis挂了,打开胶囊就能还原现场。

引言:为什么需要持久化

Redis是内存数据库,数据都住在内存里。内存是什么?断电就没了。如果你的Redis存的是缓存数据也就罢了,丢就丢了,从数据库再查一遍就好。但如果是业务数据呢?用户积分、订单状态、配置信息——这些丢了可没法跟老板解释。

RDB(Redis Database)就是Redis提供的第一种持久化方案:把内存中的数据以快照的形式保存到磁盘。就像给你的Redis拍了一张全家福,不管之后发生了什么,这张照片永远记录了那个瞬间的状态。

SAVE命令:阻塞型的"危险操作"

SAVE命令是Redis最古老的持久化方式,执行后Redis主进程会阻塞,直到RDB文件生成完毕:

客户端发送 SAVE 命令 │ ▼ ┌────────────────────────────┐ │ Redis主进程被阻塞 │ │ │ │ 遍历所有数据库的所有键 │ │ 逐个写入RDB文件 │ │ │ │ 期间不能处理任何客户端请求 │ ← 所有请求排队等待 │ │ │ RDB文件生成完毕 │ │ 主进程恢复 │ └────────────────────────────┘

SAVE的问题显而易见:如果Redis中有1000万个键,生成RDB可能需要几秒甚至几十秒。这几秒内Redis完全无法响应请求——对于在线服务来说,这无异于自杀。

# 执行SAVE,观察阻塞时间127.0.0.1:6379>SAVE OK(3.52s)# 大数据集可能需要更长时间

踩坑提示生产环境绝对不要使用SAVE命令!它会阻塞主进程,导致所有客户端请求超时。除非你在维护窗口期手动备份,且能容忍短暂的不可用。

BGSAVE命令:后台拍照的正确姿势

BGSAVE(Background Save)才是RDB持久化的正确打开方式。它的核心思想是:fork一个子进程来做脏活,父进程继续服务客户端

BGSAVE的完整流程

┌─────────────────────────────────────────────────────────┐ │ BGSAVE 执行流程 │ │ │ │ 客户端: BGSAVE │ │ │ │ │ ▼ │ │ ┌──────────────┐ │ │ │ 父进程 fork() │ │ │ └──────┬───────┘ │ │ │ │ │ ┌────┴─────────┐ │ │ │ │ │ │ ▼ ▼ │ │ 父进程 子进程 │ │ (继续处理 (开始生成RDB) │ │ 客户端请求) │ │ │ │ │ │ │ │ ┌─────▼──────┐ │ │ │ │ 遍历键空间 │ │ │ │ │ 写入临时 │ │ │ │ │ RDB文件 │ │ │ │ └─────┬──────┘ │ │ │ │ │ │ │ ┌─────▼──────┐ │ │ │ │ 重命名为 │ │ │ │ │ dump.rdb │ │ │ │ └─────┬──────┘ │ │ │ │ │ │ │ ┌─────▼──────┐ │ │ │ │ 通知父进程 │ │ │ │ │ (信号机制) │ │ │ │ └────────────┘ │ │ │ │ │ ▼ │ │ 父进程收到通知 │ │ 更新 lastsave 时间 │ │ RDB完成! │ │ │ └─────────────────────────────────────────────────────────┘

BGSAVE的关键步骤:

  1. 父进程调用fork()系统调用创建子进程
  2. 子进程根据父进程的内存快照生成RDB文件(写到临时文件)
  3. 子进程完成后,将临时文件重命名为dump.rdb
  4. 子进程通过信号通知父进程RDB已完成
  5. 父进程更新lastsave时间戳
# 执行BGSAVE,不阻塞主进程127.0.0.1:6379>BGSAVE Background saving started# 期间可以正常执行其他命令127.0.0.1:6379>GET user:1"Tom"

SAVE vs BGSAVE 对比

维度SAVEBGSAVE
是否阻塞完全阻塞主进程仅fork瞬间短暂阻塞
执行者主进程子进程
客户端可用性不可用可用
适用场景仅维护窗口生产环境标准用法
内存开销无额外开销fork需要额外内存
复杂度高(涉及fork和COW)

fork与写时复制(COW)

BGSAVE之所以能让父进程继续服务,关键在于fork()系统调用和**写时复制(Copy-On-Write, COW)**机制。

fork()的原理

fork()是Linux系统调用,它创建一个子进程,子进程是父进程的完整副本——包括内存空间、文件描述符等。但Linux采用了COW优化:

fork() 之后 │ ▼ ┌──────────────────────────────────────────┐ │ 物理内存页 │ │ │ │ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │ │ │ P1 │ │ P2 │ │ P3 │ │ P4 │ │ P5 │ │ │ └─┬──┘ └──┬─┘ └──┬─┘ └──┬─┘ └──┬─┘ │ │ │ │ │ │ │ │ │ ▼ ▼ ▼ ▼ ▼ │ │ ┌─────────────────────────────┐ │ │ │ 共享的页表(只读映射) │ │ │ └──────────────┬──────────────┘ │ │ │ │ │ ┌──────┴──────┐ │ │ │ │ │ │ 父进程页表 子进程页表 │ │ (指向同一 (指向同一 │ │ 物理页) 物理页) │ └──────────────────────────────────────────┘ 当父进程修改某个页时(比如修改P3): │ ▼ ┌──────────────────────────────────────────┐ │ 操作系统复制 P3 → P3' │ │ 父进程页表指向 P3'(可写) │ │ 子进程页表仍指向 P3(只读,不变) │ │ │ │ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │ │ │ P1 │ │ P2 │ │P3' │ │ P4 │ │ P5 │ ← 父进程用 │ └────┘ └────┘ └────┘ └────┘ └────┘ │ │ ┌────┐ │ │ │ P3 │ ← 子进程用(原版) │ │ └────┘ │ └──────────────────────────────────────────┘

COW的核心思想

  1. fork()后,父进程和子进程共享同一块物理内存(页表设为只读)
  2. 当父进程(或子进程)试图修改某个内存页时,触发页错误(page fault)
  3. 操作系统将该页复制一份,修改方使用新副本,未修改方继续使用原页
  4. 这样,fork()本身不需要复制所有内存,只有在修改时才逐页复制

COW对Redis的影响

COW机制意味着:

  • fork()本身很快:只复制页表,不复制实际内存
  • 子进程看到的是fork瞬间的数据快照:因为子进程的页表不会改变
  • 父进程的写操作会触发页复制:每个被修改的页都需要复制一份
  • 内存开销取决于写操作的覆盖范围:如果BGSAVE期间只有10%的页被修改,额外内存开销约为数据量的10%

踩坑提示:如果你的Redis实例占用了30GB内存,BGSAVE期间写操作修改了50%的页,那么额外需要约15GB内存。如果系统没有足够的空闲内存,就会触发OOM Killer或者swap,导致Redis性能急剧下降。建议:Redis服务器预留至少30%-50%的可用内存给COW使用。

fork大内存实例的注意事项

对于大内存Redis实例(比如超过10GB),fork本身也可能成为问题:

  1. fork耗时随内存增长:虽然fork不需要复制内存,但复制页表本身也需要时间。30GB内存的实例fork可能需要数百毫秒。

  2. fork期间主进程短暂阻塞:fork是在主线程中执行的,虽然时间短,但对于延迟敏感的应用(P99延迟<1ms),这可能导致偶尔的延迟毛刺。

  3. 使用THP(Transparent Huge Pages)会加剧问题:THP将4KB页合并为2MB大页,减少了页表条目数,但fork时需要处理的每个页更大,COW的代价更高。

# 检查THP状态cat/sys/kernel/mm/transparent_hugepage/enabled# 建议关闭THP(对Redis更友好)echonever>/sys/kernel/mm/transparent_hugepage/enabled

自动触发RDB的条件

除了手动执行BGSAVE,Redis还可以根据配置自动触发RDB持久化。

save配置参数

redis.conf中,save参数定义了自动触发BGSAVE的条件:

# 默认配置(Redis 7.0+)save36001# 3600秒(1小时)内有至少1次修改save300100# 300秒(5分钟)内有至少100次修改save6010000# 60秒(1分钟)内有至少10000次修改

满足任意一个条件,就会触发BGSAVE。

Redis内部的dirty计数器和lastsave时间戳 │ ▼ ┌──────────────────────────────────────────┐ │ 每次写操作:dirty++ │ │ BGSAVE完成后:dirty = 0, lastsave = now │ │ │ │ serverCron每100ms检查: │ │ ┌──────────────────────────────────┐ │ │ │ if (now - lastsave >= 3600 │ │ │ │ && dirty >= 1) │ │ │ │ → BGSAVE │ │ │ │ │ │ │ │ if (now - lastsave >= 300 │ │ │ │ && dirty >= 100) │ │ │ │ → BGSAVE │ │ │ │ │ │ │ │ if (now - lastsave >= 60 │ │ │ │ && dirty >= 10000) │ │ │ │ → BGSAVE │ │ │ └──────────────────────────────────┘ │ └──────────────────────────────────────────┘

踩坑提示:如果不想使用RDB持久化,可以注释掉所有save行,或者设置save ""。但请注意,这并不意味着数据不会丢失——只是不会自动保存到磁盘。

禁用RDB的配置

# 方式一:redis.conf中save""# 方式二:运行时配置CONFIG SET save""

其他触发RDB的时机

除了手动和自动触发,还有几种场景也会产生RDB:

触发场景说明
DEBUG RELOAD调试命令,重新加载Redis时会先保存RDB
FLUSHALL清空所有数据库时,如果配置了save,会先触发RDB(RDB文件为空)
主从复制主服务器执行全量同步时,会生成RDB发送给从服务器
SHUTDOWNRedis正常关闭时,如果配置了save,会执行BGSAVE(等子进程完成后再退出)
主从全量同步中的RDB生成 │ ▼ ┌──────────┐ BGSAVE ┌──────────┐ │ 主服务器 │ ──────────► │ 子进程 │ │ │ │ 生成RDB │ │ │ └────┬─────┘ │ │ │ │ │ ┌───────────────┘ │ │ │ 发送RDB文件 │ │ ▼ │ │ ┌──────────┐ │ │ │ 从服务器 │ │ │ │ 接收RDB │ │ │ │ 载入数据 │ │ │ └──────────┘ └──────────┘

踩坑提示FLUSHALL命令会生成一个空的RDB文件,覆盖之前的RDB备份!如果你不小心执行了FLUSHALL,而此时自动save已经触发,那之前的数据就再也找不回来了。建议在执行FLUSHALL之前先备份dump.rdb文件。

RDB文件的位置和命名

RDB文件的存储位置由两个配置参数控制:

# redis.confdir/var/lib/redis# RDB文件存放目录dbfilename dump.rdb# RDB文件名
# 查看当前配置127.0.0.1:6379>CONFIG GETdir1)"dir"2)"/var/lib/redis"127.0.0.1:6379>CONFIG GET dbfilename1)"dbfilename"2)"dump.rdb"

踩坑提示dir参数也影响AOF文件的存储位置。建议将RDB和AOF文件放在有足够磁盘空间的目录,并确保Redis进程有写入权限。生产环境建议放在独立的磁盘或SSD上,避免和日志等其他IO密集型操作争抢磁盘带宽。

RDB文件校验

Redis提供了redis-check-rdb工具来检查RDB文件的完整性:

# 检查RDB文件redis-check-rdb /var/lib/redis/dump.rdb# 输出示例:[offset0]Checking RDBfile/var/lib/redis/dump.rdb[offset26]AUX FIELD redis-ver='7.0.12'[offset40]AUX FIELD redis-bits='64'[offset13443]Selecting DB ID0[offset13567]CHECKSUM: OK

如果RDB文件损坏,你可以尝试修复(但可能丢失部分数据):

# 注意:修复操作可能丢失损坏部分之后的所有数据# 修复前请先备份原始RDB文件!cpdump.rdb dump.rdb.bak redis-check-rdb--fix/var/lib/redis/dump.rdb

LASTSAVE命令

LASTSAVE命令返回Redis最后一次成功保存RDB的Unix时间戳:

127.0.0.1:6379>LASTSAVE(integer)1687700123# 转换为可读时间# 在bash中:date-d@1687700123# Mon Jun 26 14:28:43 UTC 2023

这个命令在监控中很有用——如果你设置了自动save,可以定期检查LASTSAVE是否在合理时间范围内更新:

# 监控脚本:检查RDB是否正常保存#!/bin/bashLASTSAVE=$(redis-cli LASTSAVE)NOW=$(date+%s)DIFF=$((NOW-LASTSAVE))if[$DIFF-gt3600];thenecho"WARNING: RDB not saved for${DIFF}s"fi

RDB的优缺点

优点

  1. 恢复速度快:RDB是紧凑的二进制文件,恢复时直接载入内存,速度远快于AOF重放。对于大数据集,RDB恢复可能只需要AOF恢复时间的1/10甚至更快。

  2. 文件紧凑:RDB文件经过压缩,体积通常比AOF小很多。

  3. 对性能影响小:BGSAVE由子进程完成,父进程正常服务。fork+COW的额外开销可控。

  4. 适合备份:RDB文件是一个完整的时间点快照,非常适合定期备份到S3等远程存储。

缺点

  1. 数据丢失风险:RDB是定时快照,两次快照之间的数据修改在Redis崩溃后会丢失。最坏情况下,可能丢失最近一个save周期内的所有数据。
时间轴: │ ▼ 12:00 RDB保存 ← 此时数据完整保存 │ ▼ 12:05 写入key A 12:10 写入key B 12:15 写入key C │ ▼ 12:18 Redis崩溃! ← key A/B/C 丢失! │ (不在RDB中) ▼ 重启后从RDB恢复 ← 只能恢复到12:00的状态
  1. fork大内存实例的开销:如前所述,大内存实例的fork可能需要较长时间,且COW需要额外内存。

  2. 不适合实时性要求高的场景:如果你的业务要求每秒级的数据安全,RDB显然不够。

RDB优缺点与AOF的初步对比

维度RDBAOF
持久化方式快照(全量)日志追加(增量)
数据安全可能丢失一个周期的数据最多丢失1秒(默认配置)
恢复速度快(直接载入)慢(重放所有写命令)
文件大小紧凑较大(重写后可缩小)
系统开销fork时的短暂阻塞每次写入都追加AOF
适用场景备份、灾备、可容忍少量丢失数据安全要求高

生产配置建议

# redis.conf 生产环境推荐配置# 1. 开启BGSAVE,设置合理的save阈值save9001# 15分钟内有1次修改save30010# 5分钟内有10次修改save6010000# 1分钟内有10000次修改# 2. RDB文件名(建议包含端口号,避免多实例冲突)dbfilename dump-6379.rdb# 3. 存储目录(使用独立磁盘)dir/data/redis/rdb# 4. 启用压缩(对List/Set/Hash等大对象使用LZF压缩)rdbcompressionyes# 5. 启用RDB文件校验rdbchecksumyes# 6. 关闭THP# 在系统层面执行:# echo never > /sys/kernel/mm/transparent_hugepage/enabled# 7. 确保系统有足够内存# 建议可用内存 >= Redis最大内存 * 1.5

踩坑提示:如果你的Redis实例同时开启了RDB和AOF,Redis重启时会优先使用AOF来恢复数据(因为AOF的数据更完整)。所以即使配置了RDB,如果AOF也是开启的,RDB更多是作为备份手段存在。

总结

RDB持久化是Redis最基础的持久化方案:

  1. SAVE是阻塞型的危险操作,生产禁用;BGSAVE通过fork子进程实现后台保存
  2. fork + COW机制让子进程获得内存快照的同时,父进程可以继续服务
  3. 自动触发条件通过save参数配置,满足任意一个即触发BGSAVE
  4. RDB的优点是恢复速度快、文件紧凑;缺点是有数据丢失风险
  5. 大内存实例需要关注fork耗时和COW内存开销

了解了RDB是怎么拍的快照,下一篇我们就来拆解这张"照片"本身——RDB文件的格式里到底藏着什么秘密?


上一篇【第28篇】数据库通知——Redis的事件订阅机制
下一篇【第30篇】RDB文件格式完全解析——一个快照文件里藏着什么秘密


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/1 19:58:38

B站缓存视频解锁指南:m4s-converter让离线视频重获新生

B站缓存视频解锁指南&#xff1a;m4s-converter让离线视频重获新生 【免费下载链接】m4s-converter 一个跨平台小工具&#xff0c;将bilibili缓存的m4s格式音视频文件合并成mp4 项目地址: https://gitcode.com/gh_mirrors/m4/m4s-converter 你是否曾经遇到过这样的困扰&…

作者头像 李华
网站建设 2026/6/1 19:57:27

Linux 组调度的层次化负载传播:子组负载的向上聚合

简介在现代 Linux 系统中&#xff0c;cgroup 配合内核组调度&#xff08;Group Scheduling&#xff09;已经成为容器虚拟化、云服务器、嵌入式系统、服务器算力隔离的底层基石。传统单任务调度仅能对进程做独立调度与负载统计&#xff0c;当大量进程归属于同一业务、同一租户、…

作者头像 李华
网站建设 2026/6/1 19:57:22

2026房地产三维动画制作公司推荐榜:五家实力派服务商深度评测

在数字化展示浪潮的推动下&#xff0c;房地产三维动画和建筑可视化技术已成为楼盘营销、招商展示和项目汇报的核心工具。通过UE5引擎、CIM系统和VR交互等技术的深度融合&#xff0c;三维动画能够将项目规划、建筑细节、园林景观和室内空间等信息整合为沉浸式的视觉体验&#xf…

作者头像 李华
网站建设 2026/6/1 19:57:20

便宜token

反正大家都要用ai 各种编程模型 几个便宜 官网1/5价格

作者头像 李华
网站建设 2026/6/1 19:56:40

猫抓浏览器插件:视频资源嗅探下载的终极解决方案

猫抓浏览器插件&#xff1a;视频资源嗅探下载的终极解决方案 【免费下载链接】cat-catch 猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 还在为无法下载网页视频而烦恼吗&…

作者头像 李华