深度解密:Redis RDB 持久化策略------滑动窗口还是累积计数?

在 Redis 的持久化配置中,save <seconds> <changes> 是最基础也是最容易被误读的参数。
最近有同学提出了一个非常经典的场景推演:
配置
save 60 1000(60秒内改动1000次)。
- t=0s: 开始
- t=30s: 累计 100 次修改
- t=60s: 累计 200 次修改
- t=70s: 累计 1000 次修改
- t=120s: 累计 2000 次修改
问:Redis 到底会在 t=70s 还是 t=120s 触发
bgsave?
很多 AI 模型(甚至一些技术博客)会给出一个看似高级的"滑动窗口"解释,认为 Redis 会计算"过去60秒内的增量",从而得出 t=120s 才触发的错误结论。
这是一个典型的过度设计式误解。 今天我们就从 Redis 源码逻辑出发,还原 RDB 触发的真实机制。
一、 核心结论:拒绝"滑动窗口"
直接给出结论:在 t=70s 时,Redis 会触发 bgsave。
Redis 的 RDB 触发机制非常简单粗暴,它不维护任何复杂的"时间窗口"或"历史记录",它只维护两个全局变量。
二、 源码级原理解析
在 Redis 内部(server.c),检查是否需要执行 RDB 保存的逻辑(serverCron 函数中)极其精简。
为了高性能,Redis 维护了两个核心状态:
server.dirty:一个简单的计数器。记录了自上一次成功保存 RDB 之后,所有对数据库进行修改的指令次数(incr, set, del 等)。server.lastsave:一个时间戳。记录了上一次成功保存 RDB 的时间。
Redis 的判断伪代码:
c
// 遍历用户配置的所有 save 规则 (例如 save 60 1000, save 300 10)
for (j = 0; j < server.saveparamslen; j++) {
struct saveparam *sp = server.saveparams+j;
// 核心判断逻辑:逻辑与 (AND)
// 条件1:当前脏数据计数器 >= 配置的次数
// 条件2:(当前时间 - 上次保存时间) >= 配置的时间
if (server.dirty >= sp->changes &&
(server.unixtime - server.lastsave) > sp->seconds)
{
// 两个条件同时满足,触发保存!
rdbSaveBackground(server.rdb_filename,rsi);
break;
}
}
关键点 : server.dirty 是一个只增不减的累加值(直到发生保存才清零)。它不关心这 1000 次修改是刚才发生的,还是 59 秒前发生的。只要总量够了,它就达标。
三、 场景复盘:为什么是 t=70s?
让我们用 Redis 的真实逻辑重新推演一遍:
t=0s
server.lastsave= 0server.dirty= 0
t=60s
- 状态 :累计修改 200 次。此时
server.dirty= 200。 - 判断 :
- 时间差:
60 - 0 = 60s(满足>= 60) - 修改量:
200 < 1000(不满足)
- 时间差:
- 结果:❌ 不触发。
- 注意 :这 200 次修改依然保留在
server.dirty中,不会因为时间过了而被"滑出窗口"。
t=70s
- 状态 :累计修改 1000 次。此时
server.dirty= 1000。 - 判断 :
- 时间差:
70 - 0 = 70s(满足>= 60) - 修改量:
1000 >= 1000(满足)
- 时间差:
- 结果 :✅ 触发 bgsave!
触发后的连锁反应 (t=70s 之后)
一旦触发保存,Redis 会执行以下重置操作:
server.lastsave更新为 70。server.dirty清零(重置为 0)。
t=120s
- 状态:总修改量达到 2000。
- 增量计算:由于 t=70s 时计数器清零了,t=70s 到 t=120s 期间新增了 1000 次修改。
- 此时
server.dirty= 1000。 - 判断 :
- 时间差:
120 - 70 = 50s。 - 配置要求:60s。
50 < 60(不满足)
- 时间差:
- 结果:❌ 不触发。Redis 会等到 t=130s (70+60) 时才会再次触发。
四、 总结
Redis 的 save 配置逻辑可以总结为一句话: "距离上次保存够久了(时间条件),且这期间攒的脏数据够多了(数量条件),就干活。"
不要被"滑动窗口"等复杂概念误导。在高性能中间件的设计中,简单往往意味着高效。Redis 不会为了持久化判断去维护复杂的历史时间线,一个时间戳加一个计数器,足矣。