Redis RDB与AOF持久化方式

01 Redis的持久化方式

Redis 提供了不同级别的持久化方式:

  • RDB持久化方式 能够在指定的时间间隔能对数据进行快照存储
  • AOF持久化方式 记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据。AOF命令以redis协议追加保存每次写的操作到文件末尾。另外,Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大

Redis持久化的使用:

  • 不适用持久化:功能类似于memcache,如果只希望数据在服务器运行的时候存在,也可以不使用任何持久化方式。
  • 同时开启两种持久化方式:在这种情况下, 当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整

最重要的事情是了解RDB和AOF持久化方式的不同与差异

  • rdb:基于快照的持久化,速度更快,一般用作备份,主从复制也是依赖于rdb持久化功能
  • aof:以追加的方式记录redis操作日志的文件。可以最大程度的保证redis数据安全,类似于mysql的binlog

02 RDB持久化方式

2.1 使用bgsave命令触发RDB持久化

在默认情况下, Redis 将数据库快照保存在名字为 dump.rdb 的二进制文件中。可以对 Redis 配置文件进行设置, 让它在“ N 秒内数据集至少有 M 个改动”这一条件被满足时, 自动保存一次数据集,并设置快照保存的路径。也可以通过调用 SAVE或者 BGSAVE ,手动让 Redis 进行数据集保存操作。

比如说, save 60 1000 设置会让 Redis 在满足“ 60 秒内有至少有 1000 个键被改动”这一条件时, 自动保存一次数据集,这种持久化方式被称为快照 snapshotting。

bgsave 命令采用异步方式生成快照,Redis会fork出一个子进程进行RDB文件的生成;Redis只有在fork子进程时被阻塞,子进程完成快照生成的同时,Redis可以正常工作。该过程如下:

在这里插入图片描述

上述过程描述为,当 Redis 需要保存 dump.rdb 文件时, 服务器执行以下操作:

  • Redis 调用forks. 同时拥有父进程和子进程。
  • 子进程将数据集写入到一个临时 RDB 文件中。
  • 当子进程完成对新 RDB 文件的写入时,Redis 用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。

这种工作方式使得 Redis 可以从写时复制(copy-on-write)机制中获益。

2.2 RDB持久化方式的优点

  • 数据快照 RDB是一个非常紧凑的文件,它保存了某个时间点得数据集,非常适用于数据集的备份、全量拷贝等场景。比如你可以在每个小时报保存一下过去24小时内的数据,同时每天保存过去30天的数据,这样即使出了问题你也可以根据需求恢复到不同版本的数据集。

  • 灾难恢复:RDB是一个紧凑的单一文件,很方便传送到另一个远端数据中心或者亚马逊的S3(可能加密),非常适用于灾难恢复。

    扫描二维码关注公众号,回复: 13247718 查看本文章
  • 异步处理:RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO操作,所以RDB持久化方式可以最大化redis的性能。

  • 速度较快:与AOF相比,在恢复大的数据集的时候,RDB方式会更快一些。

2.3 RDB持久化方式的缺点

  • 无法做到实时/秒级持久化: 如果你希望在redis意外停止工作(例如电源中断)的情况下丢失的数据最少的话,那么RDB不适合你。虽然可以配置不同的save时间点(例如每隔5分钟并且对数据集有100个写的操作),是Redis要完整的保存整个数据集是一个比较繁重的工作,你通常会每隔5分钟或者更久做一次完整的保存,万一在Redis意外宕机,可能会丢失几分钟的数据。

  • 资源消耗大:RDB 需要经常fork子进程来保存数据集到硬盘上,当数据集比较大的时候,fork的过程是非常耗时的,可能会导致Redis在一些毫秒级内不能响应客户端的请求。如果数据集巨大并且CPU性能不是很好的情况下,这种情况会持续1秒,AOF也需要fork,但是可以通过调节重写日志文件的频率来提高数据集的耐久度。

  • 兼容性差:RDB文件使用特定的二进制格式保存,Redis演进过程中有很多种RDB版本,可能出现格式不兼容的问题。

2.4 RDB持久化实验

1 不使用持久化

如果不进行持久化配置,在关闭Redis后重新启动Redis会导致数据丢失,如下所示

# 查看Redis中所有的数据
172.16.255.101:6379> keys *
1) "myset"
2) "admin:001"
3) "set1"
4) "set2"
5) "admin:002"
6) "mylist"
# 关闭Redis
172.16.255.101:6379> SHUTDOWN
not connected> 
# 重新启动Redis
root@master:/$ redis-server /opt/redis_cluster/redis_6379/conf/redis_6379.conf 
root@master:/$ redis-cli -h 172.16.255.101
172.16.255.101:6379> keys *
(empty array)

2 RDB持久化配置

编辑Redis配置文件,添加RDB配置

# 编辑Redis配置文件
vim /opt/redis_cluster/redis_6379/conf/redis_6379.conf
# 添加如下内容
#---------------------- SNAPSHOTTING  -------------------------
# Save the DB on disk:
#   save <seconds> <changes>

save 900 1  #   after 900 sec (15 min) if at least 1 key changed
save 300 10 #   after 300 sec (5 min) if at least 10 keys changed
save 60 10000 #   after 60 sec if at least 10000 keys changed
dbfilename redis_6379.rdb

3 插入数据触发RDB

配置完成后,重启Redis并插入数据

redis-server /opt/redis_cluster/redis_6379/conf/redis_6379.conf 

使用如下shell脚本向Redis插入1000条数据

#!/bin/bash

for i in {
    
    1..1000}
do
	redis-cli -h 172.16.255.101 SET k_${i} v_${i}
done

执行脚本,并查看数据是否插入成功

root@master:/$ bash redis_rdb.sh
root@master:/$ redis-cli -h 172.16.255.101
172.16.255.101:6379> keys *

在这里插入图片描述

查看RDB快照是否创建成功

ll /data/redis_cluster/redis_6379/

在这里插入图片描述

4 验证RDB持久化

关闭Redis,然后重启Redis,查看数据是否成功恢复

172.16.255.101:6379> SHUTDOWN
root@master:/$ redis-server /opt/redis_cluster/redis_6379/conf/redis_6379.conf 
root@master:/$ redis-cli -h 172.16.255.101
172.16.255.101:6379> keys *

2.5 RDB持久化触发时机

除了根据上述配置文件中的条件触发RDB持久化,在关闭Redis时,可能也会触发RDB持久化

配置RDB后,Redis执行SHUTDOWN等于执行了两条命令,即每次执行SHUTDOWN关闭Redis之前,Redis都会自动执行bgsave命令产生一份最新的RDB快照。换句话说,Redis配置RDB后,在不满足配置内容的情况下,执行SHUTDOWN关闭Redis时也会触发RDB持久化

bgsave # 创建RDB快照
kill redis_PID # 关闭Redis 也可以使用 pkill redis_PID;kill -15 redis_PID;shutdown 关闭Redis

SHUTDOWN实验验证

向Redis中插入一条数据,然后使用SHUTDOWM关闭Redis,查看是否生成新的RDB快照

172.16.255.101:6379> set newKey newVal
172.16.255.101:6379> keys * # 查看是否插入新数据成功
172.16.255.101:6379> SHUTDOWN

可以看到在使用SHUTDOWM关闭Redis时,产生了新的RDB快照

在这里插入图片描述

重新启动Redis查看是否发生数据丢失

172.16.255.101:6379> SHUTDOWN
root@master:/$ redis-server /opt/redis_cluster/redis_6379/conf/redis_6379.conf 
root@master:/$ redis-cli -h 172.16.255.101
172.16.255.101:6379> keys *

意外宕机情况

使用kill -9 redis_PID关闭Redis,模拟Redis意外宕机。

向Redis中插入一条数据,然后使用kill -9 redis_PID关闭Redis,查看是否生成新的RDB快照;重新启动Redis查看是否发生数据丢失

172.16.255.101:6379> set newKey newVal
172.16.255.101:6379> keys * # 查看是否插入新数据成功
# kill - 9 redis_PID 杀掉Redis进程
ps -ef|grep redis
kill -9 redis_PID

在这里插入图片描述

可以看到如下图所示,并没有在关闭时产生最新RDB快照,重启Redis查看数据,可以发现新插入的数据丢失了

在这里插入图片描述

结论kill -9关闭进程更加彻底,突然中断;而使用kill/pkill/shutdown获知kill -15都是软关闭进程,即通知进程在执行完当前任务之后关闭,并非戛然而止。软关闭情况下,Redis在关闭之前都会执行bgsave命令产生最新快照保存在磁盘中。

Linux kill、kill-15、kill-9区别

  • kill 等价于 kill -15,这种杀进程的方式会给目标进程一个清理善后工作的机会,完成后关闭
  • kill -9,直接让进程退出,立即关闭,(不要轻易使用这个命令)

03 AOF持久化方式

3.1 只追加操作的文件AOF(Append-only file)

RDB快照功能并不是非常耐久(durable): 如果 Redis 因为某些原因而造成故障停机, 那么服务器将丢失最近写入、且仍未保存到快照中的那些数据。 从 1.1 版本开始, Redis 增加了一种完全耐久的持久化方式: AOF 持久化。

在配置文件中配置AOF持久化方式

appendonly yes # 打开aof日志功能
appendfsync always # 每个命令都立即同步到AOF
appendfsync everysec # 每秒写一次
appendfsync no # 写入工作是否较给操作系统
appendfilename appendonly.aof # 操作日志保存文件

开启AOF持久化后, 每当 Redis 执行一个改变数据集的命令时(比如 SET), 这个命令就会被追加到 AOF 文件的末尾。这样的话,当 Redis 重新启时,程序就可以通过重新执行 AOF 文件中的命令来达到重建数据集的目的。

AOF重写机制运行流程

AOF 重写和 RDB 创建快照一样,都巧妙地利用了写时复制机制:

  • Redis 执行 fork() ,现在同时拥有父进程和子进程。
  • 子进程开始将新 AOF 文件的内容写入到临时文件。
  • 对于所有新执行的写入命令,父进程一边将它们累积到一个内存缓存中,一边将这些改动追加到现有 AOF 文件的末尾,这样即使在重写的中途发生停机,现有的 AOF 文件也还是安全的。
  • 当子进程完成重写工作时,它给父进程发送一个信号,父进程在接收到信号之后,将内存缓存中的所有数据追加到新 AOF 文件的末尾。
  • 最后 Redis 原子地用新文件替换旧文件,之后所有命令都会直接追加到新 AOF 文件的末尾。

在这里插入图片描述

3.2 AOF 持久化方式优点

  • 耐久性好:使用AOF 会让你的Redis更加耐久: 你可以使用不同的fsync策略:无fsync,每秒fsync,每次写的时候fsync.使用默认的每秒fsync策略,Redis的性能依然很好(fsync是由后台线程进行处理的,主线程会尽力处理客户端请求),一旦出现故障,你最多丢失1秒的数据.

  • 易修复:AOF文件是一个只进行追加的日志文件,所以不需要写入seek,即使由于某些原因(磁盘空间已满,写的过程中宕机等等)未执行完整的写入命令,你也可使用redis-check-aof工具修复这些问题.

  • 重写安全:Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。 整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。

  • 可读性好:AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。 导出(export) AOF 文件也非常简单: 举个例子, 如果你不小心执行了 FLUSHALL 命令, 但只要 AOF 文件未被重写, 那么只要停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就可以将数据集恢复到 FLUSHALL 执行之前的状态。

3.3 AOF 持久化方式缺点

  • AOF文件体积大:对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。

  • 数据恢复速度较慢:根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB 。 在一般情况下, 每秒 fsync 的性能依然非常高, 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。 不过在处理巨大的写入载入时,RDB 可以提供更有保证的最大延迟时间(latency)。

3.4 AOF 日志重写

因为 AOF 的运作方式是不断地将命令追加到文件的末尾, 所以随着写入命令的不断增加, AOF 文件的体积也会变得越来越大。例如, 如果你对一个计数器调用了 100 次 INCR , 那么仅仅是为了保存这个计数器的当前值, AOF 文件就需要使用 100 条记录(entry)。然而在实际上, 只使用一条 SET 命令已经足以保存计数器的当前值了, 其余 99 条记录实际上都是多余的。

为了处理这种情况, Redis 支持一种有趣的特性: 可以在不打断服务客户端的情况下, 对 AOF 文件进行重建(rebuild)。执行 BGREWRITEAOF 命令, Redis 将生成一个新的 AOF 文件, 这个文件包含重建当前数据集所需的最少命令。Redis 2.2 需要自己手动执行 BGREWRITEAOF 命令; Redis 2.4 则可以自动触发 AOF 重写。

3.5 AOF文件损坏处理

服务器可能在程序正在对 AOF 文件进行写入时停机, 如果停机造成了 AOF 文件出错(corrupt), 那么 Redis 在重启时会拒绝载入这个 AOF 文件, 从而确保数据的一致性不会被破坏。当发生这种情况时, 可以用以下方法来修复出错的 AOF 文件:

  • 为现有的 AOF 文件创建一个备份。
  • 使用 Redis 附带的 redis-check-aof 程序,对原来的 AOF 文件进行修复,$ redis-check-aof –fix
  • 使用 diff -u 对比修复后的 AOF 文件和原始 AOF 文件的备份,查看两个文件之间的不同之处。
  • 重启 Redis 服务器,等待服务器载入修复后的 AOF 文件,并进行数据恢复。

3.6 AOF持久化实验

AOF持久化验证

先按照3.1节中的配置方式,为Redis配置AOF持久化方式,此时Redis同时配置了RDB和AOF两种持久化方式

清空Redis原先的数据,使用数据插入脚本插入1000条数据,查看数据插入结果与持久化情况

bash redis_rdb.sh
# 查看插入数据结果
redis-cli -h 172.16.255.101
keys * 
# 关闭Redis
SHUTDOWN

可以看到Redis数据目录中,同时存在AOF文件和RDB快照

在这里插入图片描述

重新启动Redis,并查看数据,可以看到数据被恢复

root@master:/home/wang$ redis-server /opt/redis_cluster/redis_6379/conf/redis_6379.conf 
root@master:/home/wang$ redis-cli -h 172.16.255.101
172.16.255.101:6379> keys * 

AOF与RDB持久化关系

Redis同时使用AOF和RDB两种持久化方式,Redis进行数据恢复时优先使用AOF方式

基于上述AOF持久化验证实验,验证Redis进行数据恢复时优先使用AOF方式

(a)备份旧RDB快照,然后向Redis插入一条数据,让AOF文件添加插入命令。关闭Redis后,在数据目录中用旧RDB快照替换新RDB快照,检验Redis数据恢复是否会受到影响。

# 备份旧RDB快照
root@master:/$ cp /data/redis_cluster/redis_6379/redis_6379.rdb   /data/redis_cluster/redis_6379/redis_6379.rdb.bak
# 插入新数据
172.16.255.101:6379> set newkey newval
172.16.255.101:6379>keys *
172.16.255.101:6379>SHUTDOWN

在这里插入图片描述

# 用旧RDB快照替换新RDB快照
cp /data/redis_cluster/redis_6379/redis_6379.rdb.bak   /data/redis_cluster/redis_6379/redis_6379.rdb
# 重启Redis,查看恢复数据
root@master:/home/wang$ redis-server /opt/redis_cluster/redis_6379/conf/redis_6379.conf 
root@master:/home/wang$ redis-cli -h 172.16.255.101
172.16.255.101:6379> keys * 

可以看到数据并没有丢失,所以旧RDB快照对同时使用AOF持久化的Redis数据恢复没有影响

在这里插入图片描述

(b)备份旧AOF文件,然后向Redis插入一条数据,产生新的AOF文件和RDB快照。关闭Redis后,在数据目录中用旧AOF文件替换新AOF文件,检验Redis数据恢复是否会受到影响。

# 备份旧AOF文件
root@master:/$ cp /data/redis_cluster/redis_6379/appendonly.aof   /data/redis_cluster/redis_6379/appendonly.aof.bak
# 插入新数据
172.16.255.101:6379> set newkey2 newval2
172.16.255.101:6379>keys *
172.16.255.101:6379>SHUTDOWN

在这里插入图片描述

在这里插入图片描述

# 用旧AOF文件替换新AOF文件
cp /data/redis_cluster/redis_6379/appendonly.aof.bak  /data/redis_cluster/redis_6379/appendonly.aof
# 重启Redis,查看恢复数据
root@master:/home/wang$ redis-server /opt/redis_cluster/redis_6379/conf/redis_6379.conf 
root@master:/home/wang$ redis-cli -h 172.16.255.101
172.16.255.101:6379> keys * 

在这里插入图片描述

在这里插入图片描述

可以看到数据发生了丢失,所以同时使用AOF和RDB持久化的Redis,在数据恢复时优先使用AOF方式恢复

4 AOF和RDB之间的相互作用

在版本号大于等于 2.4 的 Redis 中, BGSAVE 执行的过程中, 不可以执行 BGREWRITEAOF 。 反过来说, 在 BGREWRITEAOF 执行的过程中, 也不可以执行 BGSAVE。这可以防止两个 Redis 后台进程同时对磁盘进行大量的 I/O 操作。

如果 BGSAVE 正在执行, 并且用户显示地调用 BGREWRITEAOF 命令, 那么服务器将向用户回复一个 OK 状态, 并告知用户, BGREWRITEAOF 已经被预定执行: 一旦 BGSAVE 执行完毕, BGREWRITEAOF 就会正式开始。 当 Redis 启动时, 如果 RDB 持久化和 AOF 持久化都被打开了, 那么程序会优先使用 AOF 文件来恢复数据集, 因为 AOF 文件所保存的数据通常是最完整的。

参考资料

Redis官方文档

猜你喜欢

转载自blog.csdn.net/qq_41773806/article/details/120347182