3、Redis 持久化

1、持久化介绍

Redis 是跑在内存里的,当程序重启或者服务崩溃,数据就会丢失,如果业务场景希望重启之后数据还在,就需要持久化,即把数据保存到可永久保存的存储设备中

Redis 提供两种方式来持久化

  • RDB(Redis Database):记录 Redis 某个时刻的全部数据,这种方式本质就是数据快照,直接保存二进制数据到磁盘,后续通过加载 RDB 文件恢复数据
  • AOF(Append Only File):记录执行的每条命令,重启之后通过重放命令来恢复数据,AOF 本质是记录操作日志,后续通过日志重放恢复数据

RDB 是快照恢复,AOF 是日志恢复,这是两者本质区别,我们甚至都不用去学习他们具体的实现,也能推测出他们如有下差别

  • 体积方面:相同数据量下,RDB 体积更小,因为 RDB 是记录的二进制紧凑型数据
  • 恢复速度:RDB 是数据快照,可以直接加载,而 AOF 文件恢复,相当于重放情况,RDB 显然会更快
  • 数据完整性:AOF 记录了每条日志,RDB 是间隔一段时间记录一次,用 AOF 恢复数据通常会更为完整

用 RDB 还是 AOF

  • 如果业务本身只是缓存数据且并不是一个海量访问,可以不用开持久化
    如果对数据非常重视,可以同时开启 RDB 和 AOF
    注意一点:同时开启的情况下,只会用 AOF 来加载,如果只有 RDB 文件而没有 AOF 文件的话,不会用 RDB 去恢复数据
    如果你自主选择开启 AOF 表明要强一点的一致性,但是 AOF 文件缺失,此时不会去用 RDB,因为可能 RDB 会少很多的数据,此时启动会是一个空库
  • 如果可以接受丢几分钟级别的数据,那么建议只开 RDB
    Redis 官方不建议单独开 AOF:因为如果决定要走数据备份,那么镜像保存始终是数据库领域非常行之有效的解决方案,所以在配置中 RDB 是默认打开的,而 AOF 不是
  • 这里也说下为什么 RDB 是几分钟才做一次持久化
    虽然可以通过 fork 出的子进程来做全量快照,但是如果每一秒一次,会导致很大的性能开销
    可能这一秒的快照都没完成,下一秒又 fork 出一个子进程来做快照
    而 fork 子进程是会导致主线程阻塞的,所以 RDB 的快照触发间隔是比较难确定的,原则上就是不能太短,一般都是几分钟以上

image

2、RDB

2.1、开启 RDB

save 900 1
save 300 10
save 60  10000

这三条配置不是我们增加,是默认就存在的,这就是说 redis 默认已经开启了 RDB 持久化
这里的配置语法是 save interval num,表示每间隔 interval 秒,至少有 num 条写数据操作,写数据操作指增加、删除及更新,就会激活 RDB 持久化

2.2、RDB 文件位置

# The filename where to dump the DB
dbfilename dump.rdb

# The working directory
dir /Users/niuniumart/code/redis

RDB 文件最终会长这个样子,是二进制文件,没有可读性,但是要注意到前面有个 REDIS 字符串作为标记,这个后面讲混合持久化时也会用到
image

2.3、什么时候持久化

1、save              -- 主动持久化:正常关闭时使用阻塞持久化
2、bgsave            -- 后台持久化:定时持久化用的就是它
3、到达持久化配置阈值   -- 定时持久化
4、Redis 正常关闭时进行阻塞持久化

2.4、RDB 写入流程

写时复制

  • Fork 出一个子进程来专门做 RDB 持久化(Fork 会阻塞主进程、当主进程有大量写操作时消耗大量内存)
  • 子进程把数据写入临时 RDB 文件
  • 写完之后,用新 RDB 文件替换旧 RDB 文件

3、AOF

3.1、开启 AOF

appendonly yes

# The name of the append only file (default: "appendonly.aof")
appendfilename "appendonly.aof"

3.2、AOF 写入时机

执行请求时,每条日志都会写入到 AOF,会带来一定的性能损耗,Redis 的三种刷盘策略

  • appendfsync always:每次请求都刷入 AOF,非常慢、非常安全
  • appendfsync everysec:每秒刷一次盘,足够快了、但是在崩溃场景下你可能会丢失 1 秒的数据
  • appendfsync no:不主动刷盘,让操作系统刷,一般情况 Linux 会每 30 秒刷一次盘,这种策略对性能的影响最小,但是如果发生崩溃,可能会丢失相对比较多的数据

Redis 建议是方案二,也就是每秒刷一次盘,这种方式下速度也足够快了,同时崩溃时损失的数据只有 1s,这在大多数场景都是可以接受的
我们要根据实际业务来选择,比如就是做简单的缓存,并且不存在什么超级热点缓存,那么丢失 30 秒也不是什么大事,这时候如果追求性能可以选择方案 3
方案一说实话倒是很少有场景会使用,因为 Redis 本身是无法做到完全不丢数据,Redis 的定位就不是完全可靠,通常也就没必要损耗大量性能去追求立刻刷盘
image

3.3、AOF 写入流程

  • 将数据写入 AOF 缓存中,这个缓存名字是 aof_buf,其实就是一个 sds 数据
  • aof_buf 数据刷入磁盘缓冲区(使用 write 函数来将数据写入操作系统缓冲区)
  • 调用系统的 flush 函数刷盘(将操作系统缓冲区的数据刷入硬盘)

3.4、AOF 重写

AOF 是不断写入的,如此下去 AOF 就会不断膨胀,针对这个问题,Redis 采用了重写的方式来解决

  • Redis 可以在 AOF 文件体积变得过大时,自动地在后台 Fork 一个子进程,专门对 AOF 进行重写
    说白了就是:针对相同 Key 的操作进行合并,比如同一个 Key 的 set 操作,那就是后面覆盖前面
  • 在重写过程中,Redis 不但将新的操作记录在原有的 AOF 缓冲区,而且还会记录在 AOF 重写缓冲区
    一旦新 AOF 文件创建完毕,Redis 就会将重写缓冲区内容,追加到新的 AOF 文件,再用新 AOF 文件替换原来的 AOF 文件
  • Fork + 两处缓存 + AOF 文件替换
    Fork 子进程完成 AOF 重写,如果有新的操作,主进程将写入 "AOF 缓存" 和 "AOF 重写缓存"
    写入 AOF 缓存:如果 AOF 重写过程中发生宕机,原来的 AOF 日志也是完整的,可用于恢复
    写入 AOF 重写缓存:保证新的 AOF 文件不会丢失最新的写入操作,AOF 重写缓存由主进程通过管道传输给子进程

AOF 达到多大会重写,这也是配置决定,默认如下,同时满足这两个条件则重写
超过 64M 的情况下,相比上次重写时的数据大一倍,则触发重写,在周期函数来检查和触发的

# 相比上次重写时候数据增长 100%
auto-aof-rewrite-percentage 100

# 超过 64mb
auto-aof-rewrite-min-size 64mb

image

4、AOF 优化

AOF 的不足:体积大、加载速度慢、AOF 重写影响性能

4.1、混合持久化

混合部署实际发生在 AOF 重写阶段

  • 将当前状态保存为 RDB 二进制内容,写入新的 AOF 文件
  • 再将重写缓冲区的内容追加到新的 AOF 文件
  • 最后替代原有的 AOF 文件
  • 此时的 AOF 文件,就不再单纯的是日志数据,而是二进制数据 + 日志数据的混合体,所以叫混合持久化

打开 redis 配置文件 redis.conf:aof-use-rdb-preamble
5.0 之后默认是打开的,所以 5.0 之后只要 AOF 配置开启,默认就是混合持久化
混合特久化的 AOF 文件里开头有 REDIS 这个标记,加载时候通过这个标记来进行判断
image

4.2、AOF 重写优化

image

manifest 是文件清单,描述了当前有效的 BASEAOF、INCRAOF 是哪个,之前旧的文件标记为 HISTORYAOF,它们会被 Redis 异步删除掉

image

posted @ 2023-10-06 16:50  lidongdongdong~  阅读(11)  评论(0编辑  收藏  举报