6.1 RDB快照:fork与写时复制


文档摘要

6.1 RDB快照:fork与写时复制 本节摘要:RDB 是某一时刻内存数据的二进制快照文件。生成方式是 BGSAVE:主进程 fork 出子进程遍历内存写文件,期间靠操作系统的写时复制机制保证主进程照常写入而不影响快照一致性。恢复快、文件紧凑,代价是两次快照之间的数据会丢。 快照怎么拍 配置里通常写的是自动触发规则: 规则是"窗口加变化量"的组合,低峰少拍、高峰多拍,全量刷盘永远只在数据真的变了之后发生。 save 规则的取舍做个对比实验就明白。背景:一台存会话的实例,业务方能接受的丢失上限是十五分钟;两种配置摆在一起: 方案二把丢失窗口压进五分钟,代价是快照频率升高——每次 fork 在大实例上都是一次主线程停顿加一次内存上浮。会话数据丢五分钟无感的业务,方案一更划算;

6.1 RDB快照:fork与写时复制

本节摘要:RDB 是某一时刻内存数据的二进制快照文件。生成方式是 BGSAVE:主进程 fork 出子进程遍历内存写文件,期间靠操作系统的写时复制机制保证主进程照常写入而不影响快照一致性。恢复快、文件紧凑,代价是两次快照之间的数据会丢。

快照怎么拍

> BGSAVE # 后台快照,立即返回 Background saving started > LASTSAVE # 最近一次成功快照时间戳 > SAVE # 前台快照,阻塞一切——只该在灾难恢复时用 > DEBUG OBJECT user:1001 # 带 lru 字段的不进快照

配置里通常写的是自动触发规则:

save 3600 1 # 1小时内至少1次修改 save 300 100 # 5分钟内至少100次修改 save 60 10000 # 1分钟内至少10000次修改

规则是"窗口加变化量"的组合,低峰少拍、高峰多拍,全量刷盘永远只在数据真的变了之后发生。

save 规则的取舍做个对比实验就明白。背景:一台存会话的实例,业务方能接受的丢失上限是十五分钟;两种配置摆在一起:

# 方案一:窗口稀,fork少,单次丢失大 save 900 1 # 方案二:窗口密,fork勤,单次丢失小 save 300 10 save 60 1000

方案二把丢失窗口压进五分钟,代价是快照频率升高——每次 fork 在大实例上都是一次主线程停顿加一次内存上浮。会话数据丢五分钟无感的业务,方案一更划算;写敏感、丢不起的,方案二再配第 6.2 的 AOF 兜底。配置没有标准答案,只有"丢失容忍与停顿容忍"的换算表。

fork 与写时复制:一图看懂

快照最大的难题是"拍照时人还在动"。解法全在操作系统层:fork 的一瞬间,子进程拿到的是父进程内存页的映射(此刻起就是快照的"底片");之后父进程修改任何一页,内核先把旧页复制一份再让父进程改——旧页留在子进程眼里,快照自然冻结在 fork 那一刻

fork 与写时复制

fork 与写时复制

由此推出两条运维纪律。内存余量:极端写负载下页被大量复制,内存占用可接近翻倍,宿主机要留出这份余量,否则 fork 失败或触发系统级杀进程。fork 本身的耗时:页表越大 fork 越慢,几十 GB 的实例 fork 可达数百毫秒——INFO 里latest_fork_usec 这个指标要盯着。

把快照期间的健康检查落成一次实操。背景:一台 8GB 的实例每天凌晨自动快照,想确认它没在快照时拖累业务;操作与读数:

> BGSAVE Background saving started > INFO persistence rdb_bgsave_in_progress:1 # 正在进行,结束后回0 rdb_last_save_time:1724301000 # 上次成功完成的时刻 rdb_last_bgsave_status:ok # 上次结果,fail要立刻追查磁盘 rdb_last_bgsave_time_sec:24 # 上次耗时24秒,正常量级 > INFO stats latest_fork_usec:87300 # 最近一次fork耗时87毫秒

解读:24 秒的写盘发生在子进程,主线程无感;真正的主线程代价是那 87 毫秒的 fork——经验阈值是百毫秒级内无伤,超过几百毫秒且业务延迟敏感,就得考虑缩减实例体积或调大宿主机内存余量。rdb_last_bgsave_status 一旦出现 fail,第一怀疑对象是磁盘满与权限,快照失败不会自愈,每次都是 fail 直到修好。变式:把 BGSAVE 安排在 INFO stats 采样之后跑,前后对比 instantaneous_op_per_sec 与延迟曲线,可以量化"快照期间到底慢没慢",给"要不要优化"提供数据而不是感觉。

RDB 文件长什么样

二进制格式,按类型紧凑编码:SDS 长度前缀、listpack 逐条落盘、跳表按层展开。不存命令、只存结果,所以一个键无论被改过多少次,快照里只占它当前大小。10GB 内存常常压出 2 至 3GB 的文件,传输备份都轻。

文件本身还带校验:开头魔数识别格式、尾部的校验和检测损坏。搬运备份文件前用自带工具验一遍完整性,是恢复演练前的标准动作:

redis-check-rdb dump.rdb # 输出逐段报出键数与校验结果,最后一行 RDB file was saved with checksum disabled: NO, ok

恢复也快:加载就是"按格式逐键还原结构",没有命令解析与重放,一个几 GB 的 RDB 通常几十秒内加载完,实例重新对外服务。

加载期间的表现也要心里有数:服务进程起来但处于 LOADING 状态,所有写命令被拒,读命令视配置而定(多数版本同样不可用)。所以"重启后服务多久可用"等于"RDB 加载多久",容量评审时这一项要明写。两个排错点常客:其一,启动报找不到 RDB 或 RDB 校验失败——检查 dir 与 dbfilename 配置是否指向备份路径,搬运文件时用校验工具先过一遍;其二,MISCONF 报错出现在快照失败后——服务端发现持久化连续失败会拒绝写入自保,处置顺序是修磁盘、手动 BGSAVE 验证、再恢复写入,这个"故障即熔断"的行为救过很多数据。

⚠️ 常见坑:save 规则写得太稀(例如只留 save 3600 1),深夜一次写入之后一整天没再快照,第二天断电丢 23 小时数据。缓存可以接受,存储型实例必须上 AOF(下一节)或缩短窗口。

该用 RDB 的场景

  • 纯缓存实例:丢了回源重建,RDB 只为加速冷启动,够用且省事
  • 容灾备份:紧凑文件方便跨机房定时归档,甚至传对象存储
  • 主从全量同步:第 7 章会看到,复制初始化传输的正是 RDB 格式的数据

一句常被问到的收尾:快照期间实例还能写吗?能,这正是写时复制的价值——主线程照常服务,被改到的页才复制,快照永远定格在 fork 那一刻。真正的限制只有两条:fork 瞬间的停顿,与快照期间的内存上浮,两者的量化方法前面的运维纪律里都给了。

本节要点回顾

  • BGSAVE 是 fork 加子进程遍历写盘,主线程只在 fork 瞬间有停顿
  • 写时复制让快照冻结在 fork 一刻,代价是高峰期内存峰值上升
  • save 规则是窗口加变化量的组合,按业务容忍度配置
  • 二进制结果集:文件小、恢复快,但丢两次快照之间的所有写入
  • 盯两个指标:latest_fork_usec 与快照期间内存涨幅

作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U