持久策略(面试重点)
注意事项
Redis是基于内存的数据库,内存是断电即失的,因此在生产环境必须进行持久化设置(除非你明确用于纯缓存场景)
正常关闭redis-server(例如 systemctl stop | restart)时会自动执行一次保存(除非未开启对应的保存策略)
数据总是应该进行备份
数据恢复前总是应该先关闭服务
保存相关命令
# 返回最近一次 Redis 成功将数据保存到磁盘上的时间
127.0.0.1:6379> LASTSAVE
(integer) 1747522611
# 即使未开启aof
BGREWRITEAOF 异步执行一个 AOF(AppendOnly File) 文件重写操作
# 即使未开启rdb
BGSAVE 在后台异步保存当前数据库的数据到磁盘
SAVE 同步保存数据到硬盘
# 保证不存在aof或rdb文件
[root@cloudhost redis]# ls
[root@cloudhost redis]#
[root@cloudhost redis]# redis-cli
# 确定未开启aof和rdb
127.0.0.1:6379> CONFIG GET appendonly
1) "appendonly"
2) "no"
127.0.0.1:6379> CONFIG GET save
1) "save"
2) ""
127.0.0.1:6379> FLUSHALL
OK
127.0.0.1:6379> SET k1 v1
OK
127.0.0.1:6379> SET k2 v2
OK
127.0.0.1:6379> SET k3 v3
OK
# 前台保存
127.0.0.1:6379> SAVE
OK
127.0.0.1:6379> QUIT
# 查看发现保存rdb
[root@cloudhost redis]# ls
dump.rdb
# 删除rdb保证不存在
[root@cloudhost redis]# rm dump.rdb
rm: remove regular file ‘dump.rdb’? y
[root@cloudhost redis]# ls
[root@cloudhost redis]#
[root@cloudhost redis]# redis-cli
127.0.0.1:6379> DBSIZE
(integer) 3
# 后台保存
127.0.0.1:6379> BGSAVE
Background saving started
127.0.0.1:6379> QUIT
# 查看发现保存rdb
[root@cloudhost redis]# ls
dump.rdb
# 删除rdb保证不存在
[root@cloudhost redis]# rm dump.rdb
rm: remove regular file ‘dump.rdb’? y
[root@cloudhost redis]# ls
[root@cloudhost redis]#
[root@cloudhost redis]# redis-cli
127.0.0.1:6379> DBSIZE
(integer) 3
# 后台重写
127.0.0.1:6379> BGREWRITEAOF
Background append only file rewriting started
127.0.0.1:6379> QUIT
# 查看发现保存aof
[root@cloudhost redis]# ls
appendonly.aof
RDB
原理
redis-server启动时载入rdb文件恢复原始数据库中的数据
当触发持久化规则(即配置文件中的save设置)时,redis-server会创建一个子进程,子进程将数据写入到临时的rdb文件中,写入完成时再将上次持久化的文件(保存位置和文件名称在配置文件中的dir和dbfilename设置)替换掉
该过程主进程不进行IO操作,因此不影响数据库的读写性能,此外rdb的载入恢复速度也很快,缺点是最后一次持久化前的数据(丢失的数量参考其持久化规则)可能会丢失:断电,崩溃,手动杀死进程(正常关闭服务则不会)
测试
# 设置持久化规则为60秒内修改5次
# 注意:测试后应恢复原样,实际生产中应至少设置一个 save xx 1
# 使用 save "" 可以取消rdb自动备份,但正常关闭服务时仍然会触发,手动保存也可以正常进行
# 如果要彻底关闭rdb,则注释掉所有SAVE配置即可
# save 900 1
# save 300 10
# save 60 10000
save 60 5
# 测试持久化规则是否生效
# 清除原rdb文件(其实是备份)
[root@cloudhost ~]# cd /var/lib/redis/
[root@cloudhost redis]# ls
dump.rdb
[root@cloudhost redis]# mv dump.rdb dump.rdb_bark
[root@cloudhost redis]# ls
dump.rdb_bark
# 开始进行数据库写入操作,此处的操作只要是涉及数据修改的即可
[root@cloudhost redis]# redis-cli
127.0.0.1:6379> SELECT 11
OK
127.0.0.1:6379[11]> SET key1 val1
OK
127.0.0.1:6379[11]> SET key2 val2
OK
127.0.0.1:6379[11]> SET key3 val3
OK
127.0.0.1:6379[11]> SET key4 val4
OK
127.0.0.1:6379[11]> QUIT
# 此时不满足60秒写入5次,因此尚未生成rdb文件
[root@cloudhost redis]# ls
dump.rdb_bark
# 再进行一次数据库写入操作
[root@cloudhost redis]# redis-cli
127.0.0.1:6379> SELECT 12
OK
127.0.0.1:6379[12]> SET key5 val5
OK
127.0.0.1:6379[12]> QUIT
# 满足持久化规则,因此生成rdb文件
[root@cloudhost redis]# ls
dump.rdb dump.rdb_bark
# 进一步测试数据丢失
# 写入4条数据,未触发持久化规则
[root@cloudhost redis]# redis-cli
127.0.0.1:6379> SELECT 13
OK
127.0.0.1:6379[13]> DBSIZE
(integer) 0
127.0.0.1:6379[13]> SET k1 v1
OK
127.0.0.1:6379[13]> SET k2 v2
OK
127.0.0.1:6379[13]> SET k3 v3
OK
127.0.0.1:6379[13]> SET k4 v4
OK
127.0.0.1:6379[13]> QUIT
# 强制杀死redis-server(注意必须强制杀死进程或断电等极端条件才行)
# 在客户端内执行 DEBUG SEGFAULT 命令也可以
[root@cloudhost redis]# ps -ef | grep redis
redis 5230 1 0 04:02 ? 00:00:00 /usr/bin/redis-server 127.0.0.1:6379
root 5305 1564 0 04:04 pts/1 00:00:00 grep --color=auto redis
[root@cloudhost redis]# kill -9 5230
[root@cloudhost redis]# systemctl status redis
● redis.service - Redis persistent key-value database
Loaded: loaded (/usr/lib/systemd/system/redis.service; enabled; vendor preset: disabled)
Drop-In: /etc/systemd/system/redis.service.d
└─limit.conf
Active: failed (Result: signal) since Sun 2025-05-18 04:04:41 CST; 5s ago
Process: 5054 ExecStop=/usr/libexec/redis-shutdown (code=exited, status=1/FAILURE)
Process: 5230 ExecStart=/usr/bin/redis-server /etc/redis.conf --supervised systemd (code=killed, signal=KILL)
Main PID: 5230 (code=killed, signal=KILL)
May 18 04:02:41 cloudhost systemd[1]: Starting Redis persistent key-value database...
May 18 04:02:42 cloudhost systemd[1]: Started Redis persistent key-value database.
May 18 04:04:41 cloudhost systemd[1]: redis.service: main process exited, code=killed, status=9/KILL
May 18 04:04:41 cloudhost systemd[1]: Unit redis.service entered failed state.
May 18 04:04:41 cloudhost systemd[1]: redis.service failed.
[root@cloudhost redis]# systemctl start redis
[root@cloudhost redis]# systemctl status redis
● redis.service - Redis persistent key-value database
Loaded: loaded (/usr/lib/systemd/system/redis.service; enabled; vendor preset: disabled)
Drop-In: /etc/systemd/system/redis.service.d
└─limit.conf
Active: active (running) since Sun 2025-05-18 04:05:05 CST; 2s ago
Process: 5054 ExecStop=/usr/libexec/redis-shutdown (code=exited, status=1/FAILURE)
Main PID: 5359 (redis-server)
Tasks: 3
Memory: 4.2M
CGroup: /system.slice/redis.service
└─5359 /usr/bin/redis-server 127.0.0.1:6379
May 18 04:05:05 cloudhost systemd[1]: Starting Redis persistent key-value database...
May 18 04:05:05 cloudhost systemd[1]: Started Redis persistent key-value database.
# 查看数据库,发现数据不存在
[root@cloudhost redis]# redis-cli
127.0.0.1:6379> SELECT 13
OK
127.0.0.1:6379[13]> DBSIZE
(integer) 0
# 恢复配置
save 900 1
save 300 10
save 60 10000
# 必须先停止服务再恢复原数据库,顺序相反会造成恢复的文件被重启服务时保存的文件覆盖!!!
# 停止服务
[root@cloudhost redis]# systemctl stop redis
[root@cloudhost redis]# systemctl status redis
● redis.service - Redis persistent key-value database
Loaded: loaded (/usr/lib/systemd/system/redis.service; enabled; vendor preset: disabled)
Drop-In: /etc/systemd/system/redis.service.d
└─limit.conf
Active: inactive (dead) since Sun 2025-05-18 06:54:27 CST; 9s ago
Process: 14055 ExecStop=/usr/libexec/redis-shutdown (code=exited, status=0/SUCCESS)
Process: 14018 ExecStart=/usr/bin/redis-server /etc/redis.conf --supervised systemd (code=exited, status=0/SUCCESS)
Main PID: 14018 (code=exited, status=0/SUCCESS)
May 18 06:53:54 cloudhost systemd[1]: Starting Redis persistent key-value database...
May 18 06:53:54 cloudhost systemd[1]: Started Redis persistent key-value database.
May 18 06:54:27 cloudhost systemd[1]: Stopping Redis persistent key-value database...
May 18 06:54:27 cloudhost systemd[1]: Stopped Redis persistent key-value database.
# 恢复原数据库
[root@cloudhost redis]# mv dump.rdb_bark dump.rdb
mv: overwrite ‘dump.rdb’? y
AOF
原理
redis-server启动时把aof文件的命令从头到尾执行一遍,将写入操作转换成对应的协议格式并写入aof缓冲区中
当触发持久化规则(即配置文件中的appendfsync设置)时,主进程将缓冲区的aof记录追加到临时的aof文件中
当触发日志重写规则时,redis-server会创建一个子进程,同时继续将写入操作转换成对应的协议格式并写入aof重写缓冲区中,子进程利用内存中的数据库进行完整的命令重构(注意:直接基于内存中的数据库,而不是旧的aof文件,这依赖于操作系统的写时复制技术),进而将命令集写入到临时的aof文件中,写入完毕后通知主进程,主进程再把重写缓冲区的aof记录写入到这个临时的aof文件中,最后主进程再用该文件替换掉原文件
以上过程基本不影响主进程的正常工作,数据一致性很强,在每秒写入一次的持久化规则下,数据丢失的可能性和数量都大大减少,并且重写前的命令可以直接进行显式的撤回(例如误操作删库,只要重写未发生都可以停止redis进行恢复),缺点是启动慢(毕竟要执行一遍aof文件的命令)以及aof文件体积大于rdb
另外,不必过多担心aof文件的损坏,损坏的文件redis启动时会报错,并且官方提供了恢复工具,该工具会截掉从错误命令起后面的所有命令
测试
# 备份数据库
[root@cloudhost ~]# cd /var/lib/redis/
[root@cloudhost redis]# ls
dump.rdb
[root@cloudhost redis]# mv dump.rdb dump.rdb_bark
[root@cloudhost redis]# ls
dump.rdb_bark
[root@cloudhost redis]# redis-cli
# 清空数据库(一定保证前面备份了数据库)
# 开启aof持久化并进行数据写入操作
127.0.0.1:6379> SELECT 14
OK
127.0.0.1:6379[14]> FLUSHALL
OK
127.0.0.1:6379[14]> CONFIG GET appendonly
1) "appendonly"
2) "no"
127.0.0.1:6379[14]> CONFIG SET appendonly yes
OK
127.0.0.1:6379[14]> CONFIG GET appendonly
1) "appendonly"
2) "yes"
# 不要忘了把临时配置持久化到配置文件
127.0.0.1:6379[14]> CONFIG REWRITE
OK
127.0.0.1:6379[14]> SET k1 v1
OK
127.0.0.1:6379[14]> SET k2 v2
OK
127.0.0.1:6379[14]> SET k3 v3
OK
127.0.0.1:6379[14]> QUIT
# 满足持久化规则(超过了1秒),因此生成rdb文件
# 为什么也生成了新的rdb,因为两者可以并存,且并未关闭rdb持久化
[root@cloudhost redis]# ls
appendonly.aof dump.rdb dump.rdb_bark
# 查看aof文件,不难看出本质就是记录了每个写入操作,此外也包含数据库切换操作
[root@cloudhost redis]# cat appendonly.aof
*2 # 命令的参数总个数为2
$6 # 参数1的字节长度
SELECT # 参数的值1
$2 # 参数2的字节长度
14 # 参数的值2
*3
$3
SET
$2
k1
$2
v1
*3
$3
SET
$2
k2
$2
v2
*3
$3
SET
$2
k3
$2
v3
# 继续追加数据
[root@cloudhost redis]# redis-cli
127.0.0.1:6379> SELECT 14
OK
127.0.0.1:6379[14]> SELECT 15
OK
127.0.0.1:6379[15]> SET counter 0
OK
127.0.0.1:6379[15]> INCR counter
(integer) 1
127.0.0.1:6379[15]> INCR counter
(integer) 2
127.0.0.1:6379[15]> INCR counter
(integer) 3
127.0.0.1:6379[15]> QUIT
# 查看aof文件,发现重复的INCR操作并未合并,而切换数据库不进行写入操作,该切换操作也不记录
[root@cloudhost redis]# cat appendonly.aof
*2
$6
SELECT
$2
14
*3
$3
SET
$2
k1
$2
v1
*3
$3
SET
$2
k2
$2
v2
*3
$3
SET
$2
k3
$2
v3
*2
$6
SELECT
$2
15
*3
$3
SET
$7
counter
$1
0
*2
$4
INCR
$7
counter
*2
$4
INCR
$7
counter
*2
$4
INCR
$7
counter
# 对aof文件进行手动修改,修改后如下
[root@cloudhost redis]# cat appendonly.aof
*2
$6
SELECT
$2
14
*3
$3
SET
$2
k1
$2
v1
*3
$3
SET
$2
k2
$2
v2
*3
$3
SET
$2
k3
$2
v3
# 此处删除了切换数据库的操作
*3
$3
SET
$7
counter
$1
0
*2
$4
INCR
$7
counter
*2
$4
INCR
$7
count # 此处写了错误的键名
*2
$4
INCR
$7
counter
# 重启服务发现失败
[root@cloudhost redis]# systemctl restart redis
[root@cloudhost redis]# systemctl status redis
● redis.service - Redis persistent key-value database
Loaded: loaded (/usr/lib/systemd/system/redis.service; enabled; vendor preset: disabled)
Drop-In: /etc/systemd/system/redis.service.d
└─limit.conf
Active: failed (Result: exit-code) since Sun 2025-05-18 06:18:06 CST; 1min 29s ago
Process: 12053 ExecStop=/usr/libexec/redis-shutdown (code=exited, status=0/SUCCESS)
Process: 12069 ExecStart=/usr/bin/redis-server /etc/redis.conf --supervised systemd (code=exited, status=1/FAILURE)
Main PID: 12069 (code=exited, status=1/FAILURE)
May 18 06:18:06 cloudhost systemd[1]: Starting Redis persistent key-value database...
May 18 06:18:06 cloudhost systemd[1]: Started Redis persistent key-value database.
May 18 06:18:06 cloudhost systemd[1]: redis.service: main process exited, code=exited, status=1/FAILURE
May 18 06:18:06 cloudhost systemd[1]: Unit redis.service entered failed state.
May 18 06:18:06 cloudhost systemd[1]: redis.service failed.
# 修复文件后再次启动,成功
[root@cloudhost redis]# redis-check-aof --fix appendonly.aof
0x bd: Expected \r\n, got: 2a32
AOF analyzed: size=223, ok_up_to=171, diff=52
This will shrink the AOF from 223 bytes, with 52 bytes, to 171 bytes
Continue? [y/N]: y
Successfully truncated AOF
[root@cloudhost redis]# systemctl start redis
[root@cloudhost redis]# systemctl status redis
● redis.service - Redis persistent key-value database
Loaded: loaded (/usr/lib/systemd/system/redis.service; enabled; vendor preset: disabled)
Drop-In: /etc/systemd/system/redis.service.d
└─limit.conf
Active: active (running) since Sun 2025-05-18 06:23:25 CST; 5s ago
Process: 12053 ExecStop=/usr/libexec/redis-shutdown (code=exited, status=0/SUCCESS)
Main PID: 12358 (redis-server)
Tasks: 3
Memory: 4.2M
CGroup: /system.slice/redis.service
└─12358 /usr/bin/redis-server 127.0.0.1:6379
May 18 06:23:25 cloudhost systemd[1]: Starting Redis persistent key-value database...
May 18 06:23:25 cloudhost systemd[1]: Started Redis persistent key-value database.
# 查看aof文件发现counter位于14号数据库,
# 从错误count的命令起之后的内容被截断
# 可至数据库验证结果
[root@cloudhost redis]# cat appendonly.aof
*2
$6
SELECT
$2
14
*3
$3
SET
$2
k1
$2
v1
*3
$3
SET
$2
k2
$2
v2
*3
$3
SET
$2
k3
$2
v3
*3
$3
SET
$7
counter
$1
0
*2
$4
INCR
$7
counter
# 关闭aof持久化
[root@cloudhost redis]# redis-cli
127.0.0.1:6379> CONFIG SET appendonly no
OK
127.0.0.1:6379> CONFIG REWRITE
OK
# 必须先停止服务再恢复原数据库,顺序相反会造成恢复的文件被重启服务时保存的文件覆盖!!!
# 停止服务
[root@cloudhost redis]# systemctl stop redis
[root@cloudhost redis]# systemctl status redis
● redis.service - Redis persistent key-value database
Loaded: loaded (/usr/lib/systemd/system/redis.service; enabled; vendor preset: disabled)
Drop-In: /etc/systemd/system/redis.service.d
└─limit.conf
Active: inactive (dead) since Sun 2025-05-18 06:54:27 CST; 9s ago
Process: 14055 ExecStop=/usr/libexec/redis-shutdown (code=exited, status=0/SUCCESS)
Process: 14018 ExecStart=/usr/bin/redis-server /etc/redis.conf --supervised systemd (code=exited, status=0/SUCCESS)
Main PID: 14018 (code=exited, status=0/SUCCESS)
May 18 06:53:54 cloudhost systemd[1]: Starting Redis persistent key-value database...
May 18 06:53:54 cloudhost systemd[1]: Started Redis persistent key-value database.
May 18 06:54:27 cloudhost systemd[1]: Stopping Redis persistent key-value database...
May 18 06:54:27 cloudhost systemd[1]: Stopped Redis persistent key-value database.
# 恢复原数据库
[root@cloudhost redis]# rm appendonly.aof
rm: remove regular file ‘appendonly.aof’? y
[root@cloudhost redis]# mv dump.rdb_bark dump.rdb
mv: overwrite ‘dump.rdb’? y
#redis#此专栏由于更新观看不便,不会保持及时更新,最新更新见计算机合集专栏https://www.nowcoder.com/creation/manager/columnDetail/04yp33