redis持久化
持久化的意义
如果没有持久化的话,redis 遇到灾难性故障的时候,就会丢失所有的数据
通过持久化将数据存在磁盘上,然后可以定期同步和备份这些文件到云存储服务上去,那么就可以保证数据不丢失
redis 集群架构是用来支撑海量数据、高并发、高可用,持久化主要是做灾难恢复、数据恢复,也可以归类到高可用的一个环节里面去
持久化机制:RDB
对 redis 中的数据执行周期性的持久化,如下图
持久化机制:AOF
AOF机制对每条写入命令作为日志,以append-only的模式写入一个日志文件中,在redis重启的时候,可以通过回放AOF日志中的写入指令来重新构建整个数据集
为了保证性能,会先写入 os cache 中,然后定期强制执行 fsync 操作将数据刷入磁盘
它的原理:
- 因为每台单机 redis 的数据量是受内存限制的,所以 aof 文件不会无限增长
- 且当数据超过内存限制的时候,会自动使用 LRU 算法将一部分数据淘汰掉
- AOF 存放的是每条写入命令,所以会不断膨胀,当达到一定时候,会做 rewrite 操作
- rewrite 操作:基于当时 redis 内存中的数据,重新构造一个更小的 aof 文件,然后删除旧的 aof 文件
rewrite操作示意图如上, aof 不断被追加,内存中数据有最大限制会自动淘汰,当 aof 中的数据大于内存中数据时,就会执行 rewrite 操作,生成新的 aof 文件
AOF 机制对每条写入命令作为日志,以 append-only 的模式写入一个日志文件中,在 redis 重启的时候,可以通过回放 AOF 日志中的写入指令来重新构建整个数据集
RDB和AOF对比
RDB 持久化机制的优点
- 适合做冷备 RDB 会生成多个数据文件,每个数据文件都代表了某一个时刻中 redis 的完整数据,可以将这种完整的数据文件发送到一些远程的安全存储云上,以预定好的备份策略来定期备份 redis 中的数据。 AOF 只有一个文件,可以通过脚本定时去 copy 一份文件出来
- 性能影响小 能让 redis 对外提供的读写服务不受影响,因为 redis 主进程只需要 fork 一个子进程,让子进程执行磁盘 IO 操作来进行 RDB 持久化即可
- 数据恢复快 相对于 AOF 持久化机制来说,直接基于 RDB 数据文件来重启和恢复 redis 进程,更加快速。 因为 AOF,存放的指令日志,做数据恢复的时候,其实是要回放和执行所有的指令日志,来恢复出来内存中的所有数据的 RDB 就是一份数据文件,恢复的时候,直接加载到内存中即可
RDB 持久化机制的缺点
- 在故障时,数据丢得多 一般来说,RDB 数据快照文件,都是每隔 5 分钟,或者更长时间生成一次,一旦 redis 进程宕机,那么会丢失最近 5 分钟的数据(因为在内存中还未来得及导出到磁盘) 这个问题也是 rdb 最大的缺点,就是不适合做第一优先的恢复方案,如果你依赖 RDB 做第一优先恢复方案,会导致数据丢失的比较多
- 如果数据文件特别大,可能会有性能影响 RDB 每次在 fork 子进程来执行 RDB 快照数据文件生成的时候,如果数据文件特别大,可能会导致对客户端提供的服务暂停数毫秒,或者甚至数秒。一般不要让 RDB 的间隔太长,否则每次生成的 RDB 文件太大了,对 redis 本身的性能可能会有影响的
AOF 持久化机制的优点
- 在故障时,数据丢得少 一般 AOF 会每隔 1 秒,通过一个后台线程执行一次 fsync 操作,保证 os cache 中的数据写入磁盘中,最多丢失 1 秒钟的数据
- AOF 文件写入性能高 AOF 日志文件以 append-only 模式写入,所以没有任何磁盘寻址的开销,写入性能非常高,而且文件不容易破损,即使文件尾部破损,也很容易修复(官方提供了一个修复工具)
- rewrite 操作对 redis 主线程影响较小 AOF 日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写。因为在 rewrite log 的时候,会对其中的数据进行压缩,创建出一份需要恢复数据的最小日志出来。再创建新日志文件的时候,老的日志文件还是照常写入。当新的 merge 后的日志文件 ready 的时候,再交换新老日志文件即可。
- AOF 文件内容比较容易阅读 适合做灾难性的误删除的紧急恢复。比如不小心用 flushall 命令清空了所有数据,只要这个时候后台 rewrite 还没有发生,那么就可以立即拷贝 AOF 文件,将最后一条 flushall 命令给删了,然后再将该 AOF 文件放回去,就可以通过恢复机制,自动恢复所有数据
AOF 持久化机制的缺点
- 日志文件稍大 对于同一份数据来说,AOF 日志文件通常比 RDB 数据快照文件更大
- 性能稍低 AOF 开启后,支持的写 QPS 会比 RDB 支持的写 QPS 低,因为 AOF 一般会配置成每秒 fsync 一次日志文件
- 发生过 BUG 以前 AOF 发生过 bug,通过 AOF 记录的日志,数据恢复没有恢复一模一样的数据出来。基于命令日志 /merge/ 回放的方式,比基于 RDB 每次持久化一份完整的数据快照文件的方式,更加脆弱一些,容易有 bug。 AOF 为了避免 rewrite 过程导致的 bug,因此每次 rewrite 并不是基于旧的指令日志进行 merge 的,而是基于当时内存中的数据进行指令的重新构建,这样健壮性会好很多。
- 数据恢复较慢 不适合做冷备,数据恢复基于指令稍慢
持久化方式选择
- 不要仅仅使用 RDB,那样会导致丢失很多数据
- 也不要仅仅使用 AOF,那样有两个问题 第一,没有 RDB 做冷备,出现故障时恢复速度会比较慢; 第二,RDB 每次简单粗暴生成数据快照,更加健壮,可以避免 AOF 这种复杂的备份和恢复机制的 bug
- 综合使用 AOF 和 RDB 两种持久化机制 用 AOF 来保证数据不丢失,作为数据恢复的第一选择; 用 RDB 来做不同程度的冷备,在 AOF 文件都丢失或损坏不可用的时候,还可以使用 RDB 来进行快速的数据恢复
持久化配置
RDB配置
如何配置 RDB 持久化机制
/etc/redis/6379.conf
代码语言:javascript复制save 900 1
save 300 10
save 60 10000
以上内容是原始默认的配置,该功能叫做 SNAPSHOTTING(快照)
save <seconds> <changes>
:当 n 秒后有 n 个 key 发生改变,就做一次快照备份
也可以手动调用save或者bgsave命令,同步或异步执行rdb快照生成
save可以设置多个snapshotting检查点,默认设置了 3 个检查点,每到一个检查点,就会去check一下,是否有指定的key数量发生了变更,如果有,就生成一个新的dump.rdb文件
RDB 持久化机制的工作流程
- redis 根据配置自己尝试去生成 rdb 快照文件
- fork 一个子进程出来
- 子进程尝试将数据 dump 到临时的 rdb 快照文件中
- 完成 rdb 快照文件的生成之后,就替换之前的旧的快照文件
每次生成一个新的快照,都会覆盖之前的老快照,所以只会有一个 dump.rdb
基于 RDB 持久化机制的数据恢复实验
思路:
- 保存几条数据
- 关闭 redis
- 重启 redis
- 检查数据是否还在
下面使用命令来实验
代码语言:javascript复制redis-cli
set k1 11
set k2 22
set k3 33
exit
redis-cli shutdown
# 启动 redis
cd /etc/init.d/
./redis_6379 start
# 进入 cli 查看数据是否还存在
redis-cli
get k1
get k2
实验证明数据还是存在的。因为是 redis 自带的停止工具,是一种安全的退出模式,会将内存中的数据立即生成一份 rdb 快照文件,该文件存储在 /var/redis/6379/dump.rdb 中
下面再来测试 2 种非安全的退出模式:
第一种,写入几条数据,然后直接 kill 掉 redis 进程,启动后会发现数据丢失了
第二种
- 手动配置一个 save 5 1
- 写入几条数据,等待 5 秒钟,会发现自动进行了一次 dump rdb 快照 可通过查看 dump.rdb 文件更新时间确定
- kill -9 redis 进程
- 启动 redis 查看数据 因为有 save 生效,所以数据都在;
TIP 在非正常退出 redis 的时候,再次启动会报错 [root@eshop-cache01 init.d]# ./redis_6379 start /var/run/redis_6379.pid exists, process is already running or crashed 由此可以看出来,当 redis 启动的时候回生成一个 pid 文件,如果该文件存在则不能再次启动 这里只能先删除该 pid 文件后,才能启动 redis 了
AOF配置
如何配置 AOF 持久化机制
AOF 持久化默认是关闭的,RDB 是默认开启的
打开需要修改/etc/redis/6379.conf 中的 APPEND ONLY MODE 配置区:
打开 AOF 持久化机制之后,redis 每次接收到一条写命令,先写入 os cache 的,然后每隔一定时间再 fsync 一下,就会写入磁盘的日志文件中
appendonly yes
启用后 appendfsync 属性开始生效,有三个策略可选
- no:不主动执行fsync redis 负责将数据写入 os cache 就不管了,后面 os 会根据策略将数据刷入磁盘,不可控
- always:每次写入一条数据就执行一次 fsync 每次写入一条数据,立即将这个数据对应的写日志 fsync 到磁盘上去,性能非常差,吞吐量很低,但能确保说 redis 里的数据一条都不丢
- everysec:每隔一秒执行一次 fsync 每秒将 os cache 中的数据 fsync 到磁盘,最常用的生产环境配置,性能很高,QPS上万
AOF 持久化的数据恢复实验
- 先仅仅打开 RDB,写入一些数据,然后 kill -9 杀掉 redis 进程,接着重启 redis,发现数据没了,因为 RDB 快照还没生成
- 打开AOF的开关,启用 AOF 持久化
- 写入一些数据,观察 AOF 文件中的日志内容(它们先被写入 os cache 的,1 秒后才 fsync 到磁盘中的)
- kill -9 杀掉 redis 进程,重新启动 redis 进程,发现数据被恢复回来了,就是从 AOF 文件中恢复回来的 redis 进程启动的时候,直接就会从 appendonly.aof 中加载所有的日志,把内存中的数据恢复回来
原理:/var/redis/6379 该路径下,是之前配置的路径,文件内容如下,操作是写入了一条 set mykey1 123k 命令。
代码语言:javascript复制[root@eshop-cache01 6379]# cat appendonly.aof
*2
$6
SELECT
$1
0
*3
$3
set
$6
mykey1
$4
123k
AOF rewrite
redis 中的数据其实有限的,很多数据可能会自动过期,可能会被用户删除,可能会被 redis 用缓存清除的算法清理掉,总之 redis 中的数据会不断淘汰掉旧的,就一部分常用的数据会被自动保留在 redis 内存中
所以可能很多之前的已经被清理掉的数据,对应的写日志还停留在 AOF 中,AOF 日志文件就一个,会不断的膨胀到很大
所以 AOF 会自动在后台每隔一定时间做 rewrite 操作,比如日志里已经存放了针对 100w 数据的写日志了; redis 内存中只剩下 10 万; 基于内存中当前的 10 万数据构建一套最新的日志,到 AOF 中; 覆盖之前的老日志; 确保 AOF 日志文件不会过大,保持跟 redis 内存数据量一致
redis 2.4 之前,还需要手动,开发一些脚本 crontab 定时通过 BGREWRITEAOF 命令去执行 AOF rewrite,但是 redis 2.4 之后,会自动进行 rewrite 操作
aof rewrite 有两个重要的配置参数
/etc/redis/6379.conf
代码语言:javascript复制auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
上面的配置意思是: 当 aof 日志超过 64 m ,且增长的比例超过了之前的100%,比如之前是 60 m,那么当文件增长到 120 m 的时候,就会触发 rewrite 操作
- auto-aof-rewrite-percentage: 增长百分比,比上一次增长多少内容的时候就会触发 rewrite 操作
- auto-aof-rewrite-min-size:rewrite 操作的最小文件大小,超过该大小才会执行 rewrite 操作
rewrite 流程
- redis fork 一个子进程
- 子进程基于当前内存中的数据,构建日志,开始往一个新的临时的 AOF 文件中写入日志
- redis 主进程,接收到 client 新的写操作之后,在内存中写入日志,同时新的日志也继续写入旧的 AOF 文件
- 子进程写完新的日志文件之后,redis 主进程将内存中的新日志再次追加到新的 AOF 文件中
- 用新的日志文件替换掉旧的日志文件
下图对上面文字描述的演示
AOF 破损文件的修复
如果 redis 在 append 数据到 AOF 文件时,机器宕机了,可能会导致 AOF 文件破损
可以用 redis-check-aof --fix 命令来修复破损的 AOF 文件(该命令在 redis 安装目录下)
代码语言:javascript复制redis-check-aof --fix xxx.aof
可以手动以破坏,然后执行修复:
- 将 aof 文件删除后两行数据
- 然后使用 redis-check-aof 修复
- 查看被修复的文件
修复的原理就是删除掉破损的数据,将有缺失的一整条命令删除掉,保留完整的其他命令。
AOF 和 RDB 同时工作
- 他们的自动执行是互斥的
- 如果 RDB 在执行 snapshotting,此时用户手动执行 BGREWRITEAOF 命令,那么等 RDB 快照生成之后,才会去执行 AOF rewrite
- 同时有 RDB snapshot 文件和 AOF 日志文件,那么 redis 重启的时候,会优先使用 AOF 进行数据恢复,因为其中的日志更完整
在有 rdb 的 dump 和 aof 的 appendonly 的同时,rdb 里也有部分数据,aof 里也有部分数据,这个时候其实会发现,rdb 的数据不会恢复到内存中
- 设置 rdb 5 秒保存一次,写入两条数据,等待 rdb 数据持久化后停止 redis 进程
- 这个时候 rdb 和 aof 中的数据都是完整的
- 我们模拟让 aof 破损,然后 fix,有一条数据会被 fix 删除
- 再次用 fix 的 aof 文件去重启 redis,发现数据只剩下一条了
同时存在的时候,会优先使用 aof 文件恢复数据。
数据恢复完全是依赖于底层的磁盘的持久化的,如果 rdb 和 aof 上都没有数据,那就没了
数据备份和恢复方案
企业级的数据备份方案
RDB 非常适合做冷备,每次生成之后,就不会再有修改了
数据备份方案:写 crontab 定时调度脚本去做数据备份
- 小时级:每小时都 copy 一份 rdb 的备份,到一个目录中去,仅仅保留最近 48 小时的备份
- 日级:每天都保留一份当日的 rdb 的备份,到一个目录中去,仅仅保留最近 1 个月的备份
- 每天晚上将当前服务器上所有的数据备份,发送一份到远程的云服务上去
每次 copy 备份的时候,都把太旧的备份给删了
这里在 /usr/local/redis 目录下完成这个备份实验
按小时级备份
在 copy/redis_rdb_copy_daily.sh
授权:chmod 777 redis_rdb_copy_hourly.sh
代码语言:javascript复制#!/bin/sh
# 生成小时级的文件夹名称 2021022021
cur_date=`date %Y%m%d%k`
# 以防万一,先删除,再创建目录
rm -rf /usr/local/redis/snapshotting/$cur_date
# -p 可以创建多级目录
mkdir -p /usr/local/redis/snapshotting/$cur_date
cp /var/redis/6379/dump.rdb /usr/local/redis/snapshotting/$cur_date
# 删除48 小时前的目录 2021021821
del_date=`date -d -48hour %Y%m%d%k`
rm -rf /usr/local/redis/snapshotting/$del_date
代码语言:javascript复制# 该命令打开的是一个列表,有多条调度任务就一行一个
crontab -e
# 每小时/每天/每月/每周 第0分钟,也就是每小时执行一次该脚本
0 * * * * sh /usr/local/redis/copy/redis_rdb_copy_hourly.sh
按天备份
与前面的脚本一样,只是时间表达式不一样
在 copy/redis_rdb_copy_daily.sh
代码语言:javascript复制#!/bin/sh
# 生成文件夹名称 20190320
cur_date=`date %Y%m%d`
# 以防万一,先删除,再创建目录
rm -rf /usr/local/redis/snapshotting/$cur_date
# -p 可以创建多级目录
mkdir -p /usr/local/redis/snapshotting/$cur_date
cp /var/redis/6379/dump.rdb /usr/local/redis/snapshotting/$cur_date
# 删除一个月前的文件夹
del_date=`date -d -1month %Y%m%d`
rm -rf /usr/local/redis/snapshotting/$del_date
代码语言:javascript复制# 该命令打开的是一个列表,有多条调度任务就一行一个
crontab -e
代码语言:javascript复制# 每小时/每天/每月/每周,也就是每小时执行一次该脚本
0 * * * * sh /usr/local/redis/copy/redis_rdb_copy_hourly.sh
# 每天0点0分执行一次
0 0 * * * sh /usr/local/redis/copy/redis_rdb_copy_daily.sh
数据恢复方案
这里讲解几个场景下的数据恢复方案
1、如果是 redis 进程挂掉
那么重启 redis 进程即可,直接基于 AOF 日志文件恢复数据
2、如果是 redis 进程所在机器挂掉
那么重启机器后,尝试重启 redis 进程,尝试直接基于 AOF 日志文件进行数据恢复
因为AOF append-only是顺序写入,如果 AOF 文件破损,那么用 redis-check-aof fix修复
3、如果 redis 当前最新的 AOF 和 RDB 文件出现了丢失/损坏
那么可以尝试基于该机器上当前的某个最新的 RDB 数据副本进行数据恢复
4、如果当前最新的 AOF 和 RDB 文件都出现了丢失/损坏到无法恢复
模拟数据恢复-错误的做法:停止 redis之后,先删除 appendonly.aof,然后将我们的 dump.rdb 拷贝过去,然后再重启 redis,这个时候其实不会恢复 dump.rdb 的数据,因为我们开启了 aof,当 aof 不存在的时候,也不会主动去用 dump.rdb 去恢复数据
正确的做法:停止 redis,关闭 aof,拷贝 rdb 备份,重启 redis,确认数据恢复, 直接在命令行热修改 redis 配置,打开 aof,这个 redis 就会将内存中的数据对应的日志,写入 aof 文件中
热修改命令:redis config set appendonly yes
切记不要停止 redis ,修改配置文件为 yes ,再启动 redis。因为这个时候 aof 文件没有生成的话,数据就又会没有的
5、如果当前机器上的所有 RDB 文件全部损坏
那么从远程的云服务上拉取最新的 RDB 快照回来恢复数据
6、如果是发现有重大的数据错误,比如某个小时上线的程序一下子将数据全部污染了,数据全错了 那么可以选择某个更早的时间点,对数据进行恢复
举个例子,12 点上线了代码,发现代码有 bug,导致代码生成的所有的缓存数据,写入 redis,全部错了,那么你应该找到一份 11 点的 rdb 的冷备,然后按照上面的步骤,去恢复到 11 点的数据,就可以了
参考:
https://zq99299.github.io/note-book/cache-pdp/redis/011.html
https://zq99299.github.io/note-book/cache-pdp/redis/012.html
https://zq99299.github.io/note-book/cache-pdp/redis/013.html