Redis 持久化机制详解:RDB & AOF

Redis 持久化:RDB & AOF

Redis 是基于内存的数据库,存在数据丢失的风险。

因此,Redis 提供了三种持久化机制:RDB、AOF 以及 混合持久化。

一、RDB:全量快照

RDB, Redis DataBase,快照。RDB是一个二进制文件。

禁用RDB(默认开启)

arduino 复制代码
# 如果是save "" 则表示禁用RDB
save ""

RDB触发时机

1、自动触发

bash 复制代码
# 代表60秒内,如果至少有10000个key被修改,则执行bgsave命令
save 60 10000 
# 可以设置多个 比如同时存在 save 5 100 满足任意一个条件都会触发

另外,主从同步时也会触发。

当从节点全量复制时,主节点会执行 bgsave 命令,将 RDB 文件发送给从节点。

Redis正常停机(关机)时也会执行一次RDB。但是突然宕机是来不及执行RDB的。

2、主动触发:save

两个命令:savebgsave。save会阻塞Redis,bgsave几乎不会。

bgsave实现原理

开始时会fork主进程得到子进程,子进程共享主进程的内存数据。完成fork后读取内存数据并写入 RDB 文件。

几乎不阻塞

在 fork() 子进程时仍然有短暂的阻塞,不是完全不阻塞。

bgsave过程中的写操作:写时复制

Redis采用copy-on-write技术:

当主进程执行写操作时,则会拷贝一份数据,执行写操作。这样就不会影响bgsave子进程的快照。

写时复制的弊端

写时复制在极端情况下,会占用两倍于Redis内存的物理内存,对于物理内存是一个挑战。

其次,写时复制是会消耗性能的,尤其是开启了「内存大页」的情况。

「内存大页」是指Linux分配一个2MB的内存页,这使得在只修改很少的数据时却不得不复制2MB的大页,因此需要关闭「内存大页机制」

typescript 复制代码
echo never >  /sys/kernel/mm/transparent_hugepage/enabled

RDB的缺点

  1. RDB执行间隔时间长,两次RDB之间写入数据有丢失的风险(及两者中间宕机了,那么中间的数据都没有了。
  2. fork子进程、压缩、写出RDB文件都比较耗时。如果数据集很大,fork带来的阻塞可能会长达一秒。

二、AOF:增量操作

AOF, Append Only File,文件追加

AOF会把 Redis 每个键值对操作都记录到文件(appendonly.aof)中。

开启AOF(默认关闭)

bash 复制代码
appendonly yes  # 是否开启AOF功能,默认是no

AOF触发时机

Redis 是先执行命令,把数据写入内存,然后才记录日志

1、自动触发

这里涉及到一个AOF内存缓冲区,在 appendfsync everysec 策略下是先写入缓冲区,再一次性写入AOF文件

bash 复制代码
# 写命令执行完先放入AOF缓冲区(内存操作),然后表示每隔1秒将缓冲区数据写到AOF文件(磁盘IO),是默认方案 
appendfsync everysec
# 还有no 和 always策略
# no交给OS来管理 , Linux默认 30s 写入一次数据至磁盘
# always 每次都直接写入文件

2、主动触发:bgrewriteaof

执行bgrewriteaof命令

写入AOF文件是谁负责

策略不同,负责人不同

  • 如果是appendfsync always,是Redis主进程
  • 如果是appendfsync everysec,是一个异步任务
  • 如果是appendfsync no,是交给操作系统管

everysec 和 always 会发起 fsync 系统调用

no会发起 write 系统调用

另外,bgrewriteaof主动触发,是fork一个子进程进行。

AOF文件膨胀问题

AOF是对操作的追加写,只会变大不会变小,因此会膨胀,带来如下问题:

  • Redis重启要遍历整个AOF文件,无效操作会降低启动速度
  • 文件无限膨胀,影响写入速度,并且会超过OS对单个文件的大小限制

要解决这个问题,我们需要重写AOF文件使得它变小。

AOF文件重写:解决AOF文件膨胀问题

AOF文件重写的本质

本质就是读取整个Redis数据库,读到一个键值对,然后以set k v的形式作为命令写入AOF文件

是不是和RDB很像,但是比RDB占据的空间更大

子进程重写

AOF文件很大,重写操作很重,不能占用主进程导致阻塞,因此bgrewriteaof 时,主线程 fork 出后台的 bgrewriteaof 子进程来进行重写。

AOF文件重写触发时机

1、自动重写

arduino 复制代码
# AOF文件比上次文件 增长超过多少百分比则触发重写,默认为 100
auto-aof-rewrite-percentage 100
​
# AOF文件体积最小多大以上才触发重写,默认为 64mb
auto-aof-rewrite-min-size 64mb
  • AOF文件大小翻倍,重写
  • AOF文件大小至少auto-aof-rewrite-min-size才会触发重写
  • 以上两个条件需要同时满足才会自动重写

因为是记录命令,AOF文件会比RDB文件大的多。而且AOF会记录对同一个key的多次写操作,但只有最后一次写操作才有意义,因此需要重写AOF文件,但会占用大量CPU和内存资源

2、手动重写:bgrewriteaof

bgrewriteaof不仅会持久化AOF,也会重写AOF。

重写过程中的写操作

重写过程中,有两个日志,一个旧的大AOF日志,一个新的正在重写的AOF日志。

此时,写操作会同时写入两个日志的AOF缓冲区

AOF重写的风险:阻塞fsync

AOF 重写会对磁盘进行大量 IO 操作,同时,fsync 又需要等到数据写到磁盘后才能返回,所以,当 AOF 重写的压力比较大时,就会导致 fsync 被阻塞。

fsync 阻塞还会进一步导致主线程阻塞。

当主线程让子线程 fsync 时,发现上一次 fsync 还没有执行完,主线程也会阻塞。

解决方案

对于always/everysec的刷盘机制,只有一种方案:直接不fsync

yaml 复制代码
# 表示在 AOF 重写时,不进行 fsync 操作 默认 no
no-appendfsync-on-rewrite yes

此时,Redis把写命令写到内存后,不调用后台线程进行 fsync 操作,直接返回。

缺点是:此时实例发生宕机,就会导致数据丢失。

AOF文件异常处理

手动修复

使用 AOF 修复工具,检测出现的问题,在命令行中输入 redis-check-aof 命令,它会跳转到出现问题的命令行,这个时候可以尝试手动修复此文件;

自动修复

如果无法手动修复,我们可以使用 redis-check-aof --fix 自动修复 AOF 异常文件,不过执行此命令,可能会导致异常部分至文件末尾的数据全部被丢弃

AOF的缺点

AOF 文件通常更大,负载比较高的情况下,RDB 比 AOF 性能更好;

注意:同时开启RDB 和 AOF,Redis 启动时只会加载 AOF 文件

三、混合持久化

混合下的AOF文件重写

在开启混合持久化的情况下,AOF 重写时会把 Redis 的持久化数据,以 RDB 的格式写入到 AOF 文件的开头,(bgsave过程中的操作以及)之后操作的数据再以 AOF 的格式,追加在文件的末尾。

开启混合持久化

bash 复制代码
aof-use-rdb-preamble yes
# 还需要开启 AOF 和 RDB

混合持久化的Redis启动流程

一般情况下,会先加载AOF文件中的RDB部分,再加载AOF部分

混合持久化的优点

兼具AOF 与 RDB的优点

混合 vs AOF

因为涉及RDB,比AOF数据恢复速度更快

文件大小比纯AOF文件更小

混合 vs RDB

丢失数据的风险比RDB更低;

Redis持久化实战

一般需要持久化的情况,就用混合持久化即可。

主从架构下,从节点不需要持久化。

《Redis 核心技术与实战》给出的建议是:

  • 数据不能丢失时,内存快照和 AOF 的混合使用是一个很好的选择;
  • 如果允许分钟级别的数据丢失,可以只使用 RDB;
  • 如果只用 AOF,优先使用 everysec 的配置选项,因为它在可靠性和性能之间取了一个平衡。

参考文档

《Redis 核心技术与实战》

《Redis 核心原理与实战》

相关推荐
Themberfue12 分钟前
基础算法之双指针--Java实现(下)--LeetCode题解:有效三角形的个数-查找总价格为目标值的两个商品-三数之和-四数之和
java·开发语言·学习·算法·leetcode·双指针
深山夕照深秋雨mo21 分钟前
在Java中操作Redis
java·开发语言·redis
努力的布布27 分钟前
SpringMVC源码-AbstractHandlerMethodMapping处理器映射器将@Controller修饰类方法存储到处理器映射器
java·后端·spring
xujinwei_gingko27 分钟前
Spring MVC 常用注解
java·spring·mvc
PacosonSWJTU31 分钟前
spring揭秘25-springmvc03-其他组件(文件上传+拦截器+处理器适配器+异常统一处理)
java·后端·springmvc
PacosonSWJTU33 分钟前
spring揭秘26-springmvc06-springmvc注解驱动的web应用
java·spring·springmvc
记得开心一点嘛40 分钟前
在Java项目中如何使用Scala实现尾递归优化来解决爆栈问题
开发语言·后端·scala
银氨溶液43 分钟前
MySql数据引擎InnoDB引起的锁问题
数据库·mysql·面试·求职
原野心存1 小时前
java基础进阶——继承、多态、异常捕获(2)
java·java基础知识·java代码审计
进阶的架构师1 小时前
互联网Java工程师面试题及答案整理(2024年最新版)
java·开发语言