51、Redis持久化:RDB快照、AOF日志、混合持久化、C语言配置持久化策略

Redis 跑得飞快,但数据全在内存里。一旦断电或进程崩溃,所有数据就没了。这可不是闹着玩的。我在项目中见过有人把 Redis 当缓存用,丢了就丢了,无所谓。但更多时候,Redis 里存的是会话数据、计数、甚至业务状态——丢了就得赔钱。

所以,持久化是 Redis 生产环境里的必修课。今天咱们就把 RDB、AOF、混合持久化这三板斧讲透,最后再聊聊怎么用 C 代码去配置这些策略。

Redis 持久化体系 Redis 持久化 RDB 快照 全量数据快照,二进制紧凑 AOF 日志 追加写操作命令,可秒级恢复 混合持久化 RDB + AOF 增量,兼顾速度与安全 优点:文件小,恢复快 缺点:可能丢数据 优点:数据更安全 缺点:文件大,恢复慢 优点:两者兼顾 缺点:兼容性略差 C 语言通过 hiredis + redisCommand 配置持久化策略

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