51、Redis持久化:RDB快照、AOF日志、混合持久化、C语言配置持久化策略
Redis 跑得飞快,但数据全在内存里。一旦断电或进程崩溃,所有数据就没了。这可不是闹着玩的。我在项目中见过有人把 Redis 当缓存用,丢了就丢了,无所谓。但更多时候,Redis 里存的是会话数据、计数、甚至业务状态——丢了就得赔钱。
所以,持久化是 Redis 生产环境里的必修课。今天咱们就把 RDB、AOF、混合持久化这三板斧讲透,最后再聊聊怎么用 C 代码去配置这些策略。
RDB 快照:简单粗暴,但别太依赖
RDB 就是给 Redis 内存里的数据拍一张「照片」。到点了,或者触发了条件,Redis fork 一个子进程,把全量数据 dump 到磁盘上的 .rdb 文件里。
我个人习惯把 RDB 当作「保底方案」。为什么?因为它文件小、恢复快。你想想看,一个 10GB 的 RDB 文件,加载回内存也就几十秒。但代价是什么?丢数据。默认配置下,如果 5 分钟内没有 100 次写入,RDB 根本不会触发。万一在这期间宕机,这 5 分钟的数据就全没了。
核心配置项:
save 900 1—— 900 秒内至少 1 次修改,触发快照save 300 10—— 300 秒内至少 10 次修改save 60 10000—— 60 秒内至少 10000 次修改dbfilename dump.rdb—— 快照文件名dir /var/lib/redis—— 文件存放目录
我在项目中遇到过一个问题:某次高峰期,Redis 频繁触发 RDB 快照,fork 子进程导致主进程卡顿了几十毫秒。嗯,这里要注意——RDB 虽然快,但 fork 操作在内存大的时候会消耗 CPU 和内存。别把 save 条件设得太激进。
AOF 日志:每条命令都记下来
AOF 的思路完全不同。它不拍照片,而是写日记。每一条写命令,都会被追加到 .aof 文件末尾。你执行了 SET key value,它就记一行。你执行了 INCR counter,它再记一行。
说白了,AOF 就是「重放日志」。Redis 重启时,把 AOF 文件里的命令从头到尾执行一遍,数据就恢复了。
AOF 有三种刷盘策略:
| 策略 | 行为 | 安全性 | 性能 |
|---|---|---|---|
| appendfsync always | 每次写命令都刷盘 | 最安全,最多丢一条 | 最慢 |
| appendfsync everysec | 每秒刷一次 | 最多丢 1 秒数据 | 折中 |
| appendfsync no | 由操作系统决定 | 可能丢大量数据 | 最快 |
我建议生产环境用 everysec。always 太慢,no 太危险。everysec 是 Redis 官方推荐的,也是我踩过坑之后的选择。曾经有一次我把 always 开起来,结果写入 QPS 直接掉了 30%,吓得我赶紧改回来。
小技巧:AOF 文件会越来越大,需要定期执行 BGREWRITEAOF 进行重写。Redis 会自动触发,你也可以手动调用。重写时 Redis 会 fork 子进程,把内存中的数据重新生成一份最小的 AOF 文件。
混合持久化:我现在的首选
RDB 恢复快但丢数据,AOF 安全但恢复慢。能不能两个都要?Redis 4.0 之后引入了混合持久化,说白了就是:RDB 做全量快照,AOF 只记录增量。
开启方式很简单:
# redis.conf 中设置
aof-use-rdb-preamble yes
混合持久化生成的 .aof 文件,前半段是 RDB 格式的二进制数据,后半段是 AOF 格式的增量命令。Redis 重启时,先加载 RDB 部分(快),再重放 AOF 部分(补全增量)。
我个人现在所有新项目都默认开混合持久化。为什么?因为它在恢复速度和数据安全性之间找到了最好的平衡点。你想想看,如果只用 RDB,万一宕机可能丢几分钟数据;如果只用 AOF,恢复一个 50GB 的 AOF 文件可能要等半小时。混合模式呢?RDB 部分几秒加载完,AOF 增量部分也就几百 MB,重放很快。
注意:混合持久化要求 Redis 版本 >= 4.0。如果你的 Redis 还是 3.x,赶紧升级吧。另外,混合持久化生成的 AOF 文件不能被旧版本 Redis 识别,迁移时要注意版本兼容。
C 语言配置持久化策略
好了,理论讲完了。咱们来点实际的——怎么用 C 代码去配置 Redis 的持久化策略?
Redis 提供了 CONFIG SET 命令,可以在运行时动态修改配置。我们用 hiredis 客户端库来操作。
先看一个完整的例子:
#include <stdio.h>
#include <stdlib.h>
#include <hiredis/hiredis.h>
int main() {
redisContext *c = redisConnect("127.0.0.1", 6379);
if (c == NULL || c->err) {
if (c) {
printf("连接错误: %s\n", c->errstr);
redisFree(c);
} else {
printf("无法分配 redis 上下文\n");
}
return 1;
}
redisReply *reply;
// 1. 关闭 RDB(如果之前开着)
reply = redisCommand(c, "CONFIG SET save \"\"");
printf("关闭 RDB: %s\n", reply->str);
freeReplyObject(reply);
// 2. 开启 AOF,每秒刷盘
reply = redisCommand(c, "CONFIG SET appendonly yes");
printf("开启 AOF: %s\n", reply->str);
freeReplyObject(reply);
reply = redisCommand(c, "CONFIG SET appendfsync everysec");
printf("设置刷盘策略: %s\n", reply->str);
freeReplyObject(reply);
// 3. 开启混合持久化
reply = redisCommand(c, "CONFIG SET aof-use-rdb-preamble yes");
printf("开启混合持久化: %s\n", reply->str);
freeReplyObject(reply);
// 4. 手动触发一次 BGREWRITEAOF,让配置生效
reply = redisCommand(c, "BGREWRITEAOF");
printf("触发 AOF 重写: %s\n", reply->str);
freeReplyObject(reply);
// 5. 验证配置
reply = redisCommand(c, "CONFIG GET appendonly");
printf("appendonly = %s\n", reply->element[1]->str);
freeReplyObject(reply);
reply = redisCommand(c, "CONFIG GET aof-use-rdb-preamble");
printf("aof-use-rdb-preamble = %s\n", reply->element[1]->str);
freeReplyObject(reply);
redisFree(c);
return 0;
}
这段代码做了几件事:
- 先关闭 RDB,因为我们打算用混合持久化,不需要单独的 RDB 触发
- 开启 AOF,设置 everysec 刷盘
- 开启混合持久化(aof-use-rdb-preamble yes)
- 手动触发 BGREWRITEAOF,让 Redis 把当前内存数据以 RDB 格式写入 AOF 文件头部
- 最后验证配置是否生效
我曾经在线上环境用这套代码做动态切换。当时的情况是:业务方发现 Redis 重启后恢复太慢,我过去一看,好家伙,AOF 文件 80GB,纯文本格式,恢复要 40 分钟。我直接在运行时切到混合持久化,触发一次重写,AOF 文件从 80GB 缩到 12GB,重启时间降到 2 分钟。嗯,这就是 C 语言操作 Redis 配置的威力。
注意:CONFIG SET 修改的配置是临时的,重启 Redis 后会丢失。如果你想让配置永久生效,记得同时修改 redis.conf 文件。或者用 CONFIG REWRITE 命令把当前运行时配置写入配置文件。
最后说一句:持久化策略没有银弹。如果你的 Redis 只做缓存,RDB 就够了。如果数据不能丢,上混合持久化。如果对恢复时间有极致要求,那就 RDB + 主从复制。选哪种,取决于你的业务场景。我个人的经验是:混合持久化 + everysec 刷盘,适合 90% 的生产场景。
公众号:蓝海资料掘金营,微信deep3321