redis持久化

redis持久化概念

为了保证速度快,数据肯定还是得存在内存中。但是为了数据持久,数据还要想办法存储到硬盘上。

Redis决定,内存存数据,硬盘中也存一份数据,这两份数据理论上是完全相同的,但实际上有小概率存在差异(取决于我们具体是怎样进行持久化的)。

写入 :当要插入一个新的数据时,就需要把数据同时写入内存和硬盘。(说是两边都写,但实际上具体怎么写入硬盘还有不同的策略------还是可以保证整体的效率足够高的)

读取:当查询某个数据时,直接从内存读取。

硬盘的数据只是在redis重启时,用来恢复内存中的数据的。

Redis实现持久化的时候,具体是按照什么样的策略来实现的?

  1. RDB (Redis DataBase)定期备份的策略

  2. AOF (Append Only FIle)实时备份的策略

RDB策略

RDB定期的把我们Redis中的数据,都写入硬盘,生成一个快照

定期具体来说,又有两种方式:

  1. 手动触发

    程序员通过redis客户端,执行特定的命令,来触发生成快照。

  2. 自动触发

    在redis配置文件中,设置一下,让redis定期自动触发。

手动触发

⼿动触发分别对应 save 和 bgsave 命令:

  • save :执行save的时候,redis就会全力以赴的进行"快照生成"操作,此时就会阻塞redis的其他客户端的命令(一般不建议使用save

  • bgsavebg(background),放到后台执行 ,不会影响redis服务器处理其他客户端的请求和命令。

    redis服务器是单线程模型 ,如何并发执行?此处redis使用的是多进程 的方式,来完成并发编程,来完成bgsave的实现。

    1.判断当前是否已经存在其他正在工作的子进程。

    比如现在已经有一个子进程正在执行bgsave,此时就直接把当前的bgsave返回

    2.如果没有其他的工作子进程,就通过fork这样的系统调用 ,创建出一个子进程来

    复制出来的这个子进程的内存中的数据和父进程是一样的。接下来安排子进程去执行"持久化"操作,也就相当于把父进程本体这里的数据给持久化了。

    3.子进程负责写文件,生成快照的过程。父进程继续接收客户端的请求,继续正常提供服务。

    4.子进程生成RDB文件

    5.子进程完成整体的持久化过程之后,就会通知父进程,父进程就会更新一些统计信息,子进程就可以结束销毁了。

bgsave执行流程

子进程 :创建子进程,子进程完成持久化。
文件替换 :持久化会把数据写入到新的文件中,然后使用新的文件替换旧的文件。

当执行生成rdb镜像操作时,此时就会把要生成的快照数据先保存到一个临时文件中,当这个快照生成完毕之后,再删除之前的rdb文件,把新生成的临时的rdb文件名字改成刚才的文件名 ------dump.rdb

就是说,每次生成的rdb镜像文件是不同的,而不是说在原有文件的基础上进行修改的。

save执行流程

若直接使用save命令,此时不会触发子进程&文件替换 逻辑,直接在当前进程 中,向刚才同一个文件中写入数据

关于rdb镜像文件

redis生成的rdb文件,是存放在redis的工作目录中的。这个目录在redis的配置文件中可以进行修改。

bash 复制代码
#配置文件中的选项

# Note that you must specify a directory here, not a file name
dir /var/lib/redis
# The filename where to dump the DB
dbfilename dump.rdb

rdb镜像文件的路径

bash 复制代码
root@VM-8-5-ubuntu:~# ll /var/lib/redis/
total 12
drwxr-x---  2 redis redis 4096 Feb 11 02:09 ./
drwxr-xr-x 62 root  root  4096 Feb  8 00:01 ../
-rw-rw----  1 redis redis   89 Feb 11 02:09 dump.rdb  # rdb机制产生的镜像文件

redis服务器默认就是开启了rdb的。

二进制文件,把内存中的数据,以压缩形式,保存到这个二进制文件中。

后续redis服务器重新启动,就会尝试加载这个rdb文件,如果人为修改dump.rdb文件导致格式错误,就会加载数据失败。哪怕我们不主动去修改它,也可能会出现一些意外问题,一旦通过一些操作(比如:网络传输)引起这个文件被破坏,此时redis服务器就无法正常启动。

如何解决?redis提供了rdb文件的检测工具

rdb文件的检测工具

bash 复制代码
root@VM-8-5-ubuntu:~# ll /usr/bin/redis-check-rdb 
-rwxr-xr-x 1 root root 2624600 Oct 13 23:22 redis-check-rdb*   #redis提供的rdb文件的检测工具

在redis 5.0版本中,检测工具和redis服务器本质上是同一个可执行程序 ,可以在运行时加入不同选项 ,从而使用不同功能。

用法:redis-check-rdb rdb文件

bash 复制代码
root@VM-8-5-ubuntu:~# redis-check-rdb dump.rdb
[offset 0] Checking RDB file dump.rdb

redis日志

当redis服务器挂了的时候,可以看看redis日志,了解发生了什么

bash 复制代码
root@VM-8-5-ubuntu:~# ll /var/log/redis/
total 16
drwxr-s---  2 redis adm    4096 Feb  8 00:01 ./
drwxr-xr-x 13 root  syslog 4096 Feb  8 00:01 ../
-rw-rw----  1 redis adm    6031 Feb 11 23:54 redis-server.log  #日志文件

例子:手动执行save & bgsave 触发一次生成快照

bash 复制代码
127.0.0.1:6379> bgsave
Background saving started

数据比较少的情况下,执行bgsave瞬间完成,立即查看dump.rdb文件是有结果的。

数据比较多的情况下,执行bgsave就有可能需要消耗一定的时间。

通过上述操作,就可以看到redis服务器重启时,加载了rdb文件的内容,恢复了内存中之前的状态。

自动触发

自动触发的配置

bash 复制代码
# Save the DB to disk.
#
# save <seconds> <changes> [<seconds> <changes> ...]
#
# Redis will save the DB if the given number of seconds elapsed and it
# surpassed the given number of write operations against the DB.
#
# Snapshotting can be completely disabled with a single empty string argument
# as in following example:
#
# save ""
#
# Unless specified otherwise, by default Redis will save the DB:
#   * After 3600 seconds (an hour) if at least 1 change was performed
#   * After 300 seconds (5 minutes) if at least 100 changes were performed
#   * After 60 seconds if at least 10000 changes were performed
#
# You can set these explicitly by uncommenting the following line.
#
save 3600 1 300 100 60 10000

# 或者如下配置
# save 3600 1
# save 300 100
# save 60 10000

save 执行M时间内 且 修改N次------就会生成一次快照。

修改的基本原则:生成一次rdb快照的成本是比较高的,不能让这个操作执行的太频繁。

正式因为rdb生成的不能太频繁,这就导致快照里的数据和当前实时的数据会有所偏差。

bash 复制代码
save "" #关闭自动生成快照

例子:插入新的key,不手动执行bgsave,重新启动redis服务器。

没有执行bgsave,结果新插入的key在重启之后仍然存在。


结论 :redis生成快照的操作,不仅仅是手动执行命令才触发,也可以自动触发

  1. 通过修改配置文件中save
  2. 通过shutdown命令(redis中的一个命令)关闭redis服务器,也会触发。(正常重启)
  3. redis进行主从复制的时候,主节点也会生成rdb快照,然后把rdb快照文件内容传输给从节点。

注意

如果是正常流程重新启动 reids服务器,此时redis服务器会在退出的时候,自动触发生成rdb操作

但如果是异常重启(kill -9 或者 服务器掉电)此时redis服务器来不及生成rdb,内存中尚未保存到快照中的数据,就会随着重启而丢失。

AOF策略

类似与mysql的bin log,会把用户的每个操作都记录到文件中。

当redis重新启动的时候,就会读取这个aof文件的内容,重新恢复数据。

开启AOF策略

/etc/redis/redis.conf中修改配置文件,使redis支持AOF策略(rdb策略是redis默认开启的)

bash 复制代码
appendonly no    #yes:开启aof策略

# The name of the append only file
appendfilename "appendonly.aof"   # aof 文件名
bash 复制代码
root@VM-8-5-ubuntu:/var/lib/redis# ll
total 16
drwxr-x---  3 redis redis 4096 Feb 12 00:59 ./
drwxr-xr-x 62 root  root  4096 Feb  8 00:01 ../
drwxr-x---  2 redis redis 4096 Feb 12 00:59 appendonly.aof
-rw-rw----  1 redis redis   89 Feb 12 00:59 dump.rdb

AOF是一个文本文件。

每次进行操作,都会被记录到文本文件中。

如下是AOF文件的内容(例子)

bash 复制代码
# appendonly.aof 内容举例
*2
$6
SELECT
$1
0
*3
$3
SET
$4
name
$5
Alice
*3
$3
SET
$3
age
$2
25
*5
$4
MSET
$4
city
$5
Beijing
$6
gender
$1
F
*4
$5
SETEX
$8
vericode
$2
60
$6
887766

通过一些特殊的符号作为分割符,来对命令的细节做出区分。

引入AOF策略后,需要实时备份,是否影响到了redis的性能?实际上,是没有的。

  1. AOF是每次把新的操作写入原有文件的末尾,属于顺序写入。(硬盘上读写数据,顺序读写的速度是比较快的,而随机访问是比较慢的)
  2. AOF机制并非是直接让工作线程把数据写入硬盘,而是先写入一个内存中的缓冲区,积累一波后,再统一写入硬盘中。

注:在高版本的redisaof文件有所变化

bash 复制代码
root@VM-8-5-ubuntu:/var/lib/redis# ll 
total 16 
drwxr-x---  3 redis redis 4096 Feb 12 00:59 ./ 
drwxr-xr-x 62 root  root  4096 Feb 12:00:01 ../ 
drwxr-x---  2 redis redis 4096 Feb 12 00:59 appendonlydir/
-rw-rw----  1 redis redis   89 Feb 12 00:59 dump.rdb

root@VM-8-5-ubuntu:/var/lib/redis/appendonlydir# ll 
total 16
drwxr-x--- 2 redis redis 4096 Feb 12 00:59 ./ 
drwxr-x--- 3 redis redis 4096 Feb 12 00:59 ../
-rw-rw---- 1 redis redis   89 Feb 12 00:59 appendonly.aof.1.base.rdb
-rw-r----- 1 redis redis    0 Feb 12 00:59 appendonly.aof.1.incr.aof
-rw-r----- 1 redis redis   88 Feb 12 00:59 appendonly.aof.manifest 

AOF缓冲区刷新策略

如果把数据写入缓冲区 里,本质还是在内存中 啊,这个时候,突然进程挂了,怎么办?缓冲区数据是不是丢了?

是的,缓冲区中没有来的及写入硬盘的数据是会丢的。

保持数据完整性(实时更新)与redis性能是矛盾的,需要做出一些取舍。

redis给出一些选项,让程序员根据实际情况决定怎么取舍------这就是缓冲区的刷新策略

  • 刷新频率越高,性能影响就越大,同时数据可靠性就越高。
  • 刷新频率越低,性能影响就越小,同时数据可靠性就越低。

redis提供了多种AOF缓冲区同步⽂件策略,由参数 appendfsync 控制

可配置值 说明 比较
always 命令写入 aof_buf 后调用 fsync 同步,完成后返回 频率最高,数据可靠性最高,性能最低
everysec 命令写入 aof_buf 后只执行 write 操作,不进行 fsync。每秒由同步线程进行 fsync。 频率低一点,数据可靠性降低,性能提高
no 命令写入 aof_buf 后只执行 write 操作,由 OS 控制 fsync 频率。 频率最低,数据可靠性最低,性能最高
bash 复制代码
#配置文件中默认是everysec

# appendfsync always
appendfsync everysec
# appendfsync no

AOF重写机制

随着AOF文件持续增长,体积越来越大,会影响到redis下次的启动时间。(redis启动的时候要读取AOF文件的内容)

AOF重写机制的核心是压缩 AOF 文件体积 ,解决因 "追加式写入" 导致的文件膨胀问题,节省磁盘空间。
次要但关键的价值是提升 Redis 重启时的数据恢复速度,保证服务可用性。

实际上上述的aof文件中,有一些内容是冗余的。

例子:
lpush key 111 + lpush key 222 + lpush key 333lpush key 111 222 333
set key 111 + set key 222 + set key 333set key 333
set key 111 + del key → 什么都不做

因此,redis 存在一个机制,能够针对 aof 文件进行整理操作。这个整理就是能够剔除其中冗余操作,合并一些操作,达到给 aof 文件进行瘦身的操作。

AOF重写过程可以手动触发和自动触发:

  • ⼿动触发:调⽤ bgrewriteaof 命令。
  • ⾃动触发:根据 auto-aof-rewrite-min-size 和 auto-aof-rewrite-percentage 参数确定⾃动触发时
    机。
    • auto-aof-rewrite-min-size:表⽰触发重写时 AOF 的最⼩⽂件⼤⼩,默认为 64MB。
    • auto-aof-rewrite-percentage:代表当前 AOF 占⽤⼤⼩相⽐较上次重写时增加的⽐例。

AOF重写流程

fork创建子进程

  • 父进程仍然负责接收请求
  • 子进程负责针对aof文件进行重写

注意:重写的时候,不关心aof文件中原来有什么内容,只是关心内存中最终的数据

子进程只需要把内存中当前的数据获取出来,以AOF格式写入到一个新的AOF文件中 ------重写的核心原理

内存中的数据的状态,就已经相当于是把AOF文件结果整理后的模样了。

此处子进程写数据的过程,非常类似于RDB生成一个镜像快照。
唯一区别

  • RDB是按照二进制的方式生成的
  • AOF重写,则是按照AOF中要求的文本格式生成的

共同点 :都是为了把当前内存中的所有数据状态记录到文件中

问题:如果在执行bgrewriteaof的时候,当前redis已经正在进行aof重写了,会咋样?

答:不会再次aof重写,直接返回了。

问题:如果在执行bgrewriteaof的时候,发现当前redis在生成rdb文件的快照,会咋样?

答:aof重写操作会进行等待,等rdb快照生成完毕之后,在进行aof重写操作。

问题:最后新的AOF文件要替代旧的AOF文件,那父进程在fork后继续写旧的AOF文件是否还有意义?

答:有意义,这是 Redis 为了保证 AOF 重写过程中在极端情况下数据不丢失 的核心设计。

例子:再重写期间,子进程还在重写新的aof文件 ,服务器突然断电,新的aof文件是不完整的文件,只能依靠父进程写入的旧的aof文件,若父进程在重写期间没有在旧aof文件中写入,那将丢失重写期间所有的数据。

突然挂机的具体场景

场景1 :父进程 fork 后,继续写旧 AOF 文件(Redis 实际实现)

假设执行 bgrewriteaof 的完整时间线:
0 秒 :触发bgrewriteaof,父进程 fork 子进程,此时内存数据是 100 条命令,旧 AOF 文件包含这 100 条。
10 秒 :子进程还在重写临时 AOF 文件(刚写到 80 条),父进程处理了 20 条新命令,这些命令同时写入aof_buf(刷入旧 AOF)和aof_rewrite_buf,旧 AOF 文件现在有 120 条命令,aof_rewrite_buf缓存了 20 条。
11 秒:Redis 服务器突然断电挂机。

恢复过程

重启 Redis 时,因为重写还没完成,临时 AOF 文件不会被使用,Redis 会加载旧 AOF 文件(包含 120 条命令),最终仅丢失 "11 秒挂机瞬间可能还没刷盘的极少量命令"(由appendfsync策略决定,如 everysec 最多丢 1 秒数据),核心数据 120 条全部恢复。

场景 2 :父进程 fork 后,停止写旧 AOF 文件(反例)

同样的时间线:
0 秒 :fork 子进程,旧 AOF 文件有 100 条命令。
10 秒 :子进程还在重写(写到 80 条),父进程处理 20 条新命令,只写入aof_rewrite_buf,旧 AOF 文件仍停留在 100 条。
11 秒:服务器断电挂机。

恢复过程

  • 临时 AOF 文件只写到 80 条(不完整),且没合并aof_rewrite_buf的 20 条,无法用;
  • 旧 AOF 文件只有 100 条,丢失了 10 秒内的 20 条新命令;
  • 最终 Redis 只能恢复 100 条命令,20 条新数据永久丢失。

AOF文件与RDB文件同时存在的情况

当redis上同时存在aof文件和rdb快照时,此时以谁为主?

**以aof为主,rdb就直接被忽略了。**因为aof比rdb数据更全啊。

混合持久化

AOF本来是按照文本的方式来写入文件的,但是文本的方式写文件,后续加载成本是比较高的。

redis就引入了"混合持久化"的方式,结合了rdb与aof的特点。

  • 按照aof的方式,每个请求/操作,都记录到文件。
  • 在触发aof重写后,会把当前内存的状态按照rdb的二进制格式写入到新的aof文件中。
  • 后续在进行的操作,仍然是按照aof文本的方式追加到文件后面。

配置文件中的选项

bash 复制代码
# Redis can create append-only base files in either RDB or AOF formats. Using
# the RDB format is always faster and more efficient, and disabling it is only
# supported for backward compatibility purposes.
aof-use-rdb-preamble yes   #yes表示开启混合持久化

问题:同一个AOF文件里既存二进制又存文本,为什么不冲突?

这其实是Redis对文件格式的巧妙设计------混合持久化的AOF文件是"分段式"的,不同段有明确的格式标识,Redis能精准识别并解析

混合持久化的AOF文件结构

混合持久化的AOF文件并不是"混乱地混合二进制和文本",而是有严格的格式划分,整体结构如下:

复制代码
[AOF文件头部标识] + [RDB二进制数据段] + [AOF文本命令段]

第三步的文本追加,是追加在RDB二进制段的后面,且两段之间有明确的分隔标记,Redis读取时能清晰区分,完全不冲突。

AOF文件的实际内容示例(简化版):

复制代码
# 以下是RDB二进制数据段(不可读的二进制)
52 45 44 49 53 30 30 31 31 ...(二进制字节)
# 以下是Redis内置的分隔标记
[AOF段开始标记]
# 以下是文本格式的AOF命令(可读)
*3\r\n$3\r\nSET\r\n$1\r\na\r\n$3\r\n123\r\n
*3\r\n$3\r\nHSET\r\n$4\r\nuser\r\n$4\r\nname\r\n$8\r\nzhangsan\r\n

当Redis重启恢复数据时,会按"先RDB、后AOF"的顺序解析:

  1. 先读取文件开头的RDB二进制段,快速加载全量基础数据(RDB的优势:加载速度快);
  2. 遇到分隔标记后,切换到AOF解析模式,逐条执行后面的文本命令,恢复重写后的增量数据(AOF的优势:数据完整性高)。

这样做的优势:

  • RDB二进制段:负责全量数据,加载速度极快(解决纯AOF恢复慢的问题);
  • AOF文本段:负责增量数据,记录每一条操作(解决纯RDB数据丢失多的问题);

举个例子

假设你操作Redis的流程:

  1. 写入SET a 1SET b 2INCR a(此时内存中a=2,b=2);
  2. 触发混合持久化重写,子进程把a=2、b=2以RDB二进制写入临时AOF文件;
  3. 接着你又写入SET c 3HSET user age 20
  4. 这两条新命令以文本形式追加到临时AOF文件的RDB段后面;
  5. 重写完成,临时文件替换旧AOF文件。

最终AOF文件内容:

复制代码
[RDB二进制:记录a=2、b=2]
[分隔标记]
SET c 3(文本)
HSET user age 20(文本)

Redis重启时:

  • 先通过RDB段快速加载a=2、b=2;
  • 再执行后面的文本命令,得到c=3、user:age=20;
  • 最终恢复所有数据,既快又完整。

总结

  1. 混合持久化的AOF文件是"分段式"结构,RDB二进制段在前、AOF文本段在后,有明确的分隔标记,Redis能精准识别,完全不冲突;
  2. 文本追加,是追加在RDB段的后面,而非混合在RDB段中;
  3. 这种设计结合了RDB"加载快"和AOF"数据全"的优势,是对纯AOF、纯RDB的最优折中。
相关推荐
jghhh014 小时前
LT喷泉码编解码的MATLAB实现
数据库·算法·matlab
PD我是你的真爱粉4 小时前
MySQL8新特性
数据库·mysql
shalou29015 小时前
Spring 核心技术解析【纯干货版】- Ⅶ:Spring 切面编程模块 Spring-Instrument 模块精讲
前端·数据库·spring
eWidget5 小时前
政务电子证照系统重构:如何解决跨模态数据的“一致性”与“安全合规”难题?
数据库·mongodb·kingbase·数据库平替用金仓·金仓数据库·文档数据库
芝士爱知识a6 小时前
【FinTech前沿】AlphaGBM:重塑期权交易的智能分析引擎——从原理到实践
数据结构·数据库·人工智能·alphagbm·期权
AC赳赳老秦6 小时前
2026主权AI趋势:DeepSeek搭建企业自有可控AI环境,保障数据安全实战
大数据·数据库·人工智能·python·科技·rabbitmq·deepseek
仍然.6 小时前
MYSQL---事务
数据库·mysql
king_harry6 小时前
openGauss 6.0 主备集群备份与恢复实战指南:基于 gs_probackup
数据库·opengauss·gs_probackup
ruxshui6 小时前
MySQL备份核心指南
数据库·mysql