你真的了解 Redis 的持久化机制吗?
2022 年 6 月的某一天,办公区的气氛格外的压抑,此时的小编吹着空调双眼微眯的站在同事胖虎身后,看着他满头大汗的将数据库的数据导入到 Redis。
经过小编的一番询问才知道,原来是他们生产 Redis 今天莫名其妙的挂了,而且因为是新项目,一会要给领导演示用,生产环境 Redis 就只部署了一个节点,这一挂不要紧,缓存进去的数据怎么办...
恰巧这个时候,小编听见老板办公室里传出了一声大喊,“胖虎,来给我演示一下项目”,五分钟后,满脸通红的胖虎从老板办公室里出来了。
这时小编突然想到,Redis 不是有持久化机制吗? 在和胖虎的交谈中发现,他竟然不知道 Redis 的持久化机制,小编就默默地装了个 13。
Redis 持久化
Redis 的持久化机制有两种,一种是快照(RDB),另一种是 AOF 日志。RDB 是一次全量备份,AOF 日志是连续的增量备份。快照是内存数据的二进制序列化形式,在存储上非常紧凑,而 AOF 日志记录的是内存数据修改的指令记录文本。
RDB 快照
RDB 快照持久化是 Redis 默认开启的,它会根据配置策略将内存数据保存在一个名为 dmp.rdb 的二进制文件中。
在 redis.conf 配置文件中,可以配置 RDB 的持久化策略,系统默认是有三个保存策略(三个同时生效),如下:
save 900 1 900 秒内有 1 个键发生改变
save 300 10 30 秒内有 10 个键发生改变
save 60 10000 60 秒内有 10000 个键发生改变
我们也可以根据我们自己生产环境的具体情况进行配置。如果我们要关闭 RDB 快照,直接将配置文件中上述三个 save 注释掉即可。
写时复制机制(COW)
Redis 提供了 save 命令进行快照生成,但是 save 命令要阻塞主进程(执行客户端命令的线程),当需要生成快照的内存特别大时(几百个 G),需要的时间也会很长,甚至十几秒,所以这个时候如果阻塞主进程,会导致整个服务不可用,就得不偿失了。
针对这种情况,Redis 借助操作系统提供的写时复制技术,提供了 bgsave 命令,basave 命令可以在主进程的基础上,fork 一个子进程,子进程会共享主进程的代码段和数据段,相当于是在后台生成快照。
在 bgsave 子进程写入 RDB 数据时,如果主进程对这些数据也都是读操作,那么,主进程和 bgsave 子进程相互不影响。但是,如果主进程要修改一块数据,这块数据就会被复制一份,生成该数据的副本。然后,bgsave 子进程会把这个副本数据写入 RDB,而在这个过程中,主进程仍然可以直接修改原来的数据。
【文章福利】另外小编还整理了一些 C/C++后台开发教学视频,相关面试题,后台学习路线图免费分享,需要的可以自行添加:Q群:720209036 点击加入~ 群文件共享
小编强力推荐 C++后台开发免费学习地址:C/C++Linux服务器开发高级架构师/C++后台开发架构师
演示
bgsave 会进行后台操作,不阻塞流程。 保存后会生成一个 dump.rdb 文件。因为二进制文件打开也是乱码,所以我们就不打开了。
优点
Redis 宕机后数据恢复快
二进制文件体积小
缺点
持久化策略可能会导致在宕机时数据丢失量较多
AOF(append-only file)
AOF 持久化可以通过 redis.conf 文件中的 appendonly 参数控制是否开启。
appendonly no 默认情况下是 no, 将 no 改为 yes 即可开启 AOF 持久化方式,Redis 会将修改的每一条指令先记录到系统缓存,默认每隔一秒钟就把缓存刷新到 appendonly.aof 磁盘文件中 当 Redis 重新启动时, 程序就可以通过重新执行 AOF 文件中的命令来达到重建数据集的目的。
演示
小编直接修改 redis.conf 文件中的 appendonly 参数,然后重启 redis,set 了几个键值对,等了好几秒,发现当前目录下并没有生成 appendonly.aof 文件。
后来通过直接在 redis-cli 执行 config set appendonly yes 命令,当前目录下才生成 appendonly.aof 文件。
接下来我们打开 appendonly.aof 文件就能看到我们之前写入的键值对。
这是一种 resp 协议格式数据,星号后面的数字代表命令有多少个参数,$号后面的数字代表这个参数有几个字
AOF 持久化策略
appendfsync always:每次有新命令追加到 AOF 文件时就执行一次 fsync ,非常慢,也非常安全,不建议用,本身 Redis 就是借助的内存快的优势,要是每次操作都访问磁盘这个优势就没了。
appendfsync everysec:每秒 fsync 一次,足够快,并且在故障时只会丢失 1 秒钟的数据,AOF 默认会走在这个配置。
appendfsync no:从不 fsync ,将数据交给操作系统来处理。更快,也更不安全的选择,发生宕机时,丢失的数据最多。
AOF 重写
假设我们执行了两个命令 set key1 1 与 set key1 2,在开启 AOF 持久化的情况下,这两个命令都会被记录到 AOF 文件中,但是我们在宕机后重启恢复数据的时候是不是就只需要执行 set key1 2 就可以了,之前的 set key1 1 在恢复数据的时候忽略掉也没有任何影响,并且因为 AOF 文件随着时间的推移,体积会越来越大,重写 AOF 就显得尤为重要。
手动触发后台重写,redis 客户端执行命令:bgrewriteaof
Redis 提供了两个配置参数控制 AOF 自动重写频率(redis.conf)。
auto‐aof‐rewrite‐min‐size 64mb //aof 文件至少要达到 64M 才会自动重写,文件太小恢复速度本来就 很快
auto‐aof‐rewrite‐percentage 100 //aof 文件自上一次重写后文件大小增长了 100%则再次触发重写
触发重写时 Redis 会通过 fork 主进程,生成一个子进程去做,这么做的原因是因为重写一般意味着要有大量的 I/O 操作,非常耗时,如果用主进程去重写会导致主进程长时间的阻塞,影响 Redis 执行客户端命令,甚至导致服务长时间不可用,但是子进程去重写就意味着主进程仍然可以接收、处理客户端指令,就会导致重写后的 AOF 文件和服务器当前的数据库状态不一致。
例如:子进程 fork 主进程时,内存中只有 key1 这个键,但是当开始重写后,客户端又多 set 了 key2、key3,这就导致了重写的 AOF 文件和数据库不一致。
所以 Redis 在 AOF 重写时加了一个 AOF 重写缓冲区,当有新命令执行的时候,Redis 会将命令发送到 AOF 缓冲区和 AOF 重写缓冲区。
执行完重写任务后,子进程会向主进程发送一个信号,主进程收到信号后会执行下面两个操作(以下两个操作会阻塞主进程):
将 AOF 重写缓冲区中的所有内容写入到新的 AOF 文件中,保证新 AOF 文件保存的数据库状态和服务器当前状态一致。
对新的 AOF 文件进行改名,原子的覆盖现有 AOF 文件,完成新旧文件的替换
虽然只有上面两个操作会阻塞 Redis 主进程,但是很显然它已经将重写对性能的影响降到了最低。但是如果你想再对其进行优化,这里有两个解决方案。
服务低峰期定时重写
使用 ssd,提升持久化效率
注意:AOF 在重写时,为了避免执行命令时造成的客户端缓冲区溢出,重写程序在处理列表、哈希表、集合、有序集合这四种可能会有多个元素的键时,会先检查键所包含的元素数量,如果元素的数量超过了 redis.h/REDIS_AOF_REWRITe_ITeMS_PER_CMD 常量的值(默认 64 个元素),那么重写程序将会使用多条命令来记录键的值,而不是单单只用一条命令。
优点
数据完整性要高于 RDB,默认情况下,最多丢失 1 秒钟的数据。
缺点
Redis 宕机后数据恢复慢
文件体积大
混合持久化(sence redis4.0)
Redis 宕机恢复时,为了提高数据的完整程度,我们通常使用 AOF 日志进行恢复,但是使用 AOF 日志性能相对 RDB 来说要慢很多,这样在 Redis 实例很大的情况下,启动需要花费很长的时间。 Redis4.0 为了解决这个问题,推出了混合持久化,说白了,混合持久化就是结合了 RDB 与 AOF 两种持久化方式的优势。
启混合模式后,AOF 在重写时会将 AOF 文件中的数据以 RDB 文件的二进制格式写入当前 AOF 文件中,之后的新的写操作继续以 AOF 文件的格式进行追加。当 redis 宕机重启的时候,加载 AOF 文件进行恢复数据:先加载 RDB 的部分再加载剩余的 AOF 内容,因此重启效率大幅得到提升。
通过修改 redis.conf 文件中的以下配置开启混合持久化(必须先开启 AOF 持久化):
aof‐use‐rdb‐preamble yes
总结
RDB 文件会保存 Redis 中所有的键值对数据
save 生成 RDB 文件会阻塞主进程
bgsave 命令后台生成 RDB,不会阻塞主进程
RDB 文件体积小,保存的是二进制数据
RDB 通过配置策略执行,可能会丢失部分数据
RDB 文件在 Redis 宕机后恢复快
AOF 文件保存的是客户端执行的命令
AOF 文件体积大,恢复慢,但是默认配置下数据比 RDB 要全很多
Redis 宕机重启后默认是用 AOF 方式进行恢复
AOF 文件中的命令是以 Resp 协议格式保存的
命令会先保存到缓冲区,再定期同步到 AOF 文件
AOF 会根据配置策略自动的对 AOF 文件进行重写,以降低文件体积
AOF 重写时会 fork 一个子进程去执行,同时会有一个重写缓冲区,用来保存重写时主进程修改的键
AOF 重写时,最后会生成一个新的 AOF 文件,覆盖原有的文件
将重写缓冲区的内容写入到 AOF 与替换新旧 AOF 文件时会阻塞主进程
混合持久化是 RDB 与 AOF 的优势结合所产生的
混合持久化本质还是用的 AOF 文件
混合持久化使用的前提是开启 AOF 持久化
混合持久化在重写 AOF 文件时会将数据直接写成 RDB 的二进制格式,之后新的命令还是以 AOF 文件 Resp 协议格式进行保存
附
fsync
为了提高文件的写入效率,linux 系统在做持久化时,会调用 write 函数,先将要写入文件的数据保存在一个内存缓冲区里就会直接返回,等到缓冲区被填满,或者超过了指定的时限(一般是 30 秒)后,才会将缓冲区的数据写入到磁盘。
这种做法虽然提高了写入效率,但是如果发生宕机,就有可能会导致缓冲区中还未写入磁盘的数据丢失。
为此,linux 提供了 fsync 函数,它会强制并阻塞等待系统将数据写入到磁盘,从而保证数据的安全性
子进程
为什么 Redis 会 fork 一个子进程而不用子线程,是因为子进程可以携带主进程的数据副本,可以避免在不使用锁的情况下,保证数据安全。
注:子进程在重写期间,主进程还是会接收并处理客户端命令,会导致子进程与主进程数据不一致。
参考资料
推荐一个零声教育 C/C++后台开发的免费公开课程,个人觉得老师讲得不错,分享给大家:C/C++后台开发高级架构师,内容包括Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,立即学习
评论