跳到主要内容

持久化

RDB:内存快照​

把某一时刻的全部数据以二进制压缩格式写成一个文件。

两个命令:

  • SAVE —— 主线程执行,阻塞所有请求,生产禁用
  • BGSAVE —— fork 子进程写盘,主线程继续服务(默认行为,save 配置触发的也是 BGSAVE)

fork 与写时复制(COW):fork() 不复制物理内存,父子进程共享同一份内存页并标记为只读。子进程遍历内存写 RDB 期间,如果主线程要修改某页,触发缺页异常,内核复制那一页给主线程改,子进程仍看到旧页。所以 RDB 是一致的快照。

:::warning fork 的两个代价

  1. fork 本身会阻塞主线程 —— 虽然不复制数据,但要复制页表。内存越大页表越大,10GB 实例的 fork 可能阻塞几十到上百毫秒。用 INFO stats 的 latest_fork_usec 观测。
  2. 可能导致内存翻倍 —— 极端情况下(持久化期间所有页都被写过)需要两倍内存。所以实例内存不宜超过物理内存的一半。 :::

优缺点:文件紧凑、恢复快(直接加载到内存),适合做备份和全量同步;但快照之间的数据会丢,频率高了 fork 开销受不了。

AOF:追加命令日志​

把每条写命令追加到文件末尾(Redis 协议格式,可读)。

写回策略 appendfsync:

值行为崩溃丢失性能
always每条命令都 fsync最多一条命令最差
everysec(默认)每秒 fsync 一次最多 1 秒折中
no交给操作系统决定不可控最好

关键细节:命令是先执行后记录(和 MySQL 的 WAL 相反)。好处是不会记录错误命令,且无需校验语法;坏处是执行完还没写盘就崩溃,这条命令就丢了,且 AOF 不能用于回滚。

AOF 重写:文件会无限膨胀(对同一个 key 自增 100 次就 100 条记录),所以需要重写 —— fork 子进程,根据当前内存数据生成最小命令集(100 次自增压成一条 SET)。

重写期间主线程的新写入会同时进AOF 缓冲区和AOF 重写缓冲区,重写完成后把重写缓冲区的内容追加到新文件末尾,再原子替换旧文件。这保证了重写期间数据不丢。

触发条件:auto-aof-rewrite-percentage 100(比上次重写后大一倍)且 auto-aof-rewrite-min-size 64mb。

混合持久化​

Redis 4.0 引入 aof-use-rdb-preamble yes(7.0 起默认开启并改为 multi-part AOF):AOF 重写时,把当前数据以 RDB 格式写在文件开头,之后的增量命令以 AOF 格式追加。

这是生产推荐方案,因为它同时拿到了两者的优点:RDB 部分让恢复快,AOF 部分让数据丢失窗口小到 1 秒。

怎么选​

场景方案
纯缓存,数据可重建关闭持久化,或只开 RDB 低频备份
不能丢数据的业务数据混合持久化 + everysec
需要定期全量备份RDB(文件小,便于传输到对象存储)
对一致性要求极高AOF always,但要接受性能损失,且仍不保证分布式一致

恢复优先级:同时开启时,Redis 启动优先加载 AOF(因为通常更完整)。

:::tip 高频追问 「Redis 持久化能保证不丢数据吗?」答案是不能。即使 AOF always,主库 fsync 完成后、传给从库前宕机仍会丢;主从切换时未同步的数据也会丢。Redis 的定位是高性能缓存与数据结构服务,不能当强一致数据库用,需要强一致就上 MySQL 或引入 WAIT 命令配合业务补偿。 :::

大 key 对持久化的影响​

一个 1GB 的 key 会让 RDB 写入和 AOF 重写都变慢,fork 后的 COW 内存开销也更大。排查手段:

redis-cli --bigkeys # 采样扫描各类型最大 key
redis-cli --memkeys # 按内存占用排(4.0+)
redis-cli MEMORY USAGE <key> # 单个 key 的精确占用

拆分方案:Hash 按 field hash 分片成多个 key(user:info:{id % 100});List 按时间或序号分段;String 大对象考虑不放 Redis,改放对象存储只缓存 URL。