1. 为什么要持久化
- Redis是内从数据库,宕机后数据会丢失;
- Redis重启后,为了快速恢复数据,提供了持久化机制;
- Redis有两种持久化方式:RDB和AOF,这也是Redis无畏宕机与快速恢复数据的杀手锏。
注意:Redis持久化不保证数据的完整性。
当Redis用作DB时,DB数据要完整,所以一定要有一个完成的数据源(文件、MySQL),在系统重启时,从这个完整的数据源中将数据load到Redis中。
2. RDB 快照,让宕机快速恢复
2.1 RDB(Redis DataBase)
RDB(Redis DataBase)是Redis默认的持久化方式。Redis将内存数据库快照保存在名字为 dump.rdb 的二进制文件中。
在做数据恢复时,直接将 RDB 文件中的数据读入内存完成恢复。
2.2 触发快照的方式
- 符合自定义配置的快照规则
在 redis.conf 中配置:N 秒内数据集至少有 M 个改动,这一条件被满足时,自动保存一次数据集
# save 3600 1
# save 300 100
# 60 秒内有至少有 1000 个键被改动这一条件时, 自动保存一次数据集
save 60 10000
- 命令显示触发,执行 save 或者 bgsave 命令
还可以手动执行命令生成RDB快照,进入 Redis 客户端执行命令 save 或 bgsave 可以生成 dump.rdb 文件, 每次命令执行都会将所有 Redis 内存快照到一个新的rdb文件里,并覆盖原有rdb快照文件。
bgsave的写时复制(COW)机制:
Redis 借助操作系统提供的写时复制技术(Copy-On-Write, COW),在生成快照的同时,依然可以正常 处理写命令。简单来说,bgsave 子进程是由主线程 fork 生成的,可以共享主线程的所有内存数据。 bgsave 子进程运行后,开始读取主线程的内存数据,并把它们写入 RDB 文件。此时,如果主线程对这些 数据也都是读操作,那么,主线程和 bgsave 子进程相互不影响。但是,如果主线程要修改一块数据,那么,这块数据就会被复制一份,生成该数据的副本。然后,bgsave 子进程会把这个副本数据写入 RDB 文件,而在这个过程中,主线程仍然可以直接修改原来的数据。
save与bgsave对比:
配置自动生成rdb文件后台使用的是bgsave方式。
- 执行 flushall 命令
执行 flushall 命令,也会产生 dump.rdb 文件,但里面是空的,无意义。
- 执行主从复制操作 (第一次)
2.3 RDB持久化流程
-
Redis父进程首先判断:当前是否在执行save,或bgsave/bgrewriteaof(aof文件重写命令)的子进程,如果在执行则bgsave命令直接返回。
-
父进程执行fork(调用OS函数复制主进程)操作创建子进程,这个过程中父进程是阻塞的,Redis不能执行来自客户端的任何命令。
-
父进程fork后,bgsave命令返回”Background saving started”信息并不再阻塞父进程,并可以响应其他命令。
-
子进程创建RDB文件,根据父进程内存快照生成临时快照文件,完成后对原有文件进行原子替换。(RDB始终完整)
-
子进程发送信号给父进程表示完成,父进程更新统计信息。
-
父进程fork子进程后,继续工作。
2.4 RDB的优缺点
2.4.1 优点
- 适合大规模数据恢复
- 对数据完整性和一致性要求不高更适合使用
- RDB 采⽤⼆进制 + 数据压缩的⽅式写磁盘,⽂件体积⼩,数据恢复速度快
2.4.2 缺点
-
Fork的时候,内存中的数据会被克隆一份,大致2倍的膨胀,需要考虑;
-
虽然Redis在fork的时候使用了写时拷贝技术,但是如果数据庞大时还是比较消耗性能;
-
不保证数据完整性,在备份周期在一定间隔时间做一次备份,所以如果 Redis 意外 down 的话,会丢失最后一次快照以后更改的所有数据。
3. AOF(append-only file),避免宕机数据丢失
3.1 AOF是什么
快照功能并不是非常耐久(durable): 如果 Redis 因为某些原因而造成故障停机, 那么服务器将丢失 最近写入、且仍未保存到快照中的那些数据。从 1.1 版本开始, Redis 增加了一种完全耐久的持久化方 式: AOF 持久化,将修改的每一条指令记录进文件 appendonly.aof 中(先写入 os cache,每隔一段时间 fsync 到磁盘)。
以日志的形式来记录每个写操作(增量保存),将 Redis 执行过的所有写指令记录下来(读操作不记录),只允追加文件但不可改写文件,Redis 启动之初会读取该文件重新构造数据,换言之,Redis 重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
3.2 AOF持久化实现
配置 redis.conf
# 可以通过修改redis.conf配置文件中的appendonly参数开启
appendonly yes
# AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的。
dir /path
# 默认的文件名是appendonly.aof,可以通过appendfilename参数修改
appendfilename "appendonly.aof"
3.3 AOF持久化流程
- 命令写入(append)
客户端的请求写命令会被 append 追加到 aof_buf 缓冲区内
- 文件同步(sync)
AOF 缓冲区会根据 AOF 持久化策略 [always, everysec, no] 将操作 sync 同步到磁盘的 AOF 文件中
- 文件重写(rewrite)
AOF 文件大小超过重写策略或手动重写时,会对 AOF 文件进行重写(rewrite),压缩 AOF 文件容量
- 重启加载(load)
Redis 服务器重启时,会重新 load 加载 AOF 文件中的写操作达到数据恢复的目的
3.4 AOF原理
3.4.1 AOF是如何实现的
AOF文件中存储的是redis的命令,同步命令到 AOF 文件的整个过程可以分为三个阶段:
命令传播: Redis 将执行完的命令、命令的参数、命令的参数个数等信息发送到 AOF 程序中。
缓存追加: AOF 程序根据接收到的命令数据,将命令转换为网络通讯协议的格式,然后将协议内容追加到服务器的 AOF 缓存中。
文件写入和保存: AOF 缓存中的内容被写入到 AOF 文件末尾,如果设定的 AOF 保存条件被满足的话,fsync 函数或者 fdatasync 函数会被调用,将写入的内容真正地保存到磁盘中。
命令传播
当一个 Redis 客户端需要执行命令时, 它通过网络连接, 将协议文本发送给 Redis 服务器。服务器在接到客户端的请求之后, 它会根据协议文本的内容, 选择适当的命令函数, 并将各个参数从字符串文本转换为 Redis 字符串对象( StringObject )。每当命令函数成功执行之后, 命令参数都会被传播到AOF 程序。
缓存追加
当命令被传播到 AOF 程序之后, 程序会根据命令以及命令的参数, 将命令从字符串对象转换回原来的协议文本。协议文本生成之后, 它会被追加到 redis.h/redisServer 结构的 aof_buf 末尾。
redisServer 结构维持着Redis 服务器的状态, aof_buf 域则保存着所有等待写入到 AOF 文件的协议文本(RESP)。
文件写入和保存
每当服务器常规任务函数被执行、 或者事件处理器被执行时, aof.c/flushAppendOnlyFile 函数都会被调用, 这个函数执行以下两个工作:
WRITE:根据条件,将 aof_buf 中的缓存写入到 AOF 文件。
SAVE:根据条件,调用 fsync 或 fdatasync 函数,将 AOF 文件保存到磁盘中。
3.4.2 AOF日志记录的内容有哪些?
当 Redis 接受到 “set key value” 命令将数据写到内存后,Redis 会按照如下格式写⼊ AOF ⽂件。
这是一种resp协议格式数据,星号后面的数字代表命令有多少个参数,$号后面的数字代表这个参数有几个字符。
- *3:命令有3个部分
- $3:后面紧跟具体的指令,指令的字长
3.4.3 AOF保存模式 - 三种写入策略
可以在 redis.config 中配置 Redis 多久将数据 fsync 到磁盘一次:
# If unsure, use "everysec".
# 每次有新命令追加到 AOF 文件时就执行一次 fsync ,非常慢,也非常安全。
# appendfsync always
# 每秒 fsync 一次,足够快,并且在故障时只会丢失 1 秒钟的数据。
appendfsync everysec
# 从不 fsync ,将数据交给操作系统来处理。更快,也更不安全的选择:
# appendfsync no
推荐(并且也是默认)的措施为每秒 fsync 一次, 这种 fsync 策略可以兼顾速度和安全性。
always 策略在实践中非常慢,但它支持组提交,因此如果有多个并行的写入操作,Redis 将尝试执行单个 fsync 操作。
3.5 AOF重写
3.5.1 AOF重写机制介绍
随着命令不断写入AOF,文件会越来越大,为了解决这个问题,Redis引入了AOF重写机制压缩文件体积。AOF文件重写是把Redis进程内的数据转化为写命令同步到新AOF文件的过程。
3.5.2 AOF重写机制原理
重写后的AOF文件为什么可以变小?有如下原因:
-
进程内已经超时的数据不再写文件。
-
旧的AOF文件含有无效命令,比如,我们调用INCR test命令100次,文件中就必须保存全部的100条命令,但其实99条都是多余的。因为要恢复数据库的状态其实文件中保存一条SET test 100就够了。
-
多条写命令可以合并为一个,如:lpush list a、lpush list b、 lpush list c 可以转化为:lpush list a b c。
为了防止合并的数据过大造成客户端缓冲区溢出,对于list、set、hash、zset等类型,以64个元素为界拆分为多条。
-
AOF 重写实现:
-
Redis 7.0 中 Multi Part AOF 实现:
3.5.3 触发 AOF 重写方式
- Redis 客户端手动触发
进入redis客户端执行命令 bgrewriteaof
重写AOF
- 配置 redis.config
# aof 文件自上一次重写后文件大小增长了100%,则再次触发重写机制,比如说,上次AOF文件容量为100M,这次文件已经到200M了,则自动触发重写机制
auto-aof-rewrite-percentage 100
# aof 文件至少要达到64M才会触发自动重写,文件太小,恢复速度很快,重写意义不大
auto-aof-rewrite-min-size 64mb
3.6 AOF 的优缺点
优点
具有更高的数据安全性,AOF 默认同步策略为每秒同步一次,因此即使 Redis 服务出现问题,那么最多也只会丢失最近一秒内修改的数据。AOF 文件采用追加的形式进行保存,因此即使在写入过程中出现问题,也不会破坏日志文件中已经存在的内容;如果当我们的写入过程只进行了一半就出现了问题,那么在下一次 Redis 启动之前,我们可以通过 redis-check-aof 工具来帮助我们解决数据一致性的问题当日志文件过大时,我们可以通过重写机制来对 AOF 文件进行重写AOF 文件有序地保存了对数据库执行的所有写入操作,格式十分清晰,易于理解
缺点
-
数据的完整性和一致性更高相同数量的数据集而言,AOF文件通常要大于RDB文件。RDB在恢复大数据集时的速度比 AOF 的恢复速度要快。
-
同步策略的不同,AOF在运行效率上往往会慢于RDB。总之,每秒同步策略的效率是比较高的,同步禁用策略的效率和RDB一样高效。当我们采用同步禁用 (appendfsync no) 策略时,其效率和 RDB 相同
-
数据的完整性和一致性更高因为AOF记录的内容多,文件会越来越大,数据恢复也会越来越慢。
-
对于相同数量的数据集而言,AOF文件通常要大于RDB文件。RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
4. RDB 和 AOF 对比
-
官方推荐2个都启用
-
内存数据库:使用 rdb+aof - 数据不容易丢
-
用作缓存服务器,对数据不敏感:使用rdb - 性能高
-
不建议只使用 aof - 性能差
-
在数据还原时:有rdb+aof,则还原aof,因为RDB会造成文件的丢失,AOF相对数据要完整。只有rdb,则还原rdb。
-
追求高性能:都不开,redis宕机则从数据源恢复
5. Redis 4.0 混合持久化
重启 Redis 时,我们很少使⽤ rdb 来恢复内存状态,因为会丢失⼤量数据。我们通常使⽤ AOF ⽇志重放,但是重放 AOF ⽇志性能相对 rdb 来说要慢很多,这样在 Redis 实例很⼤的情况下,启动需要花费很⻓的时间。
Redis 4.0 为了解决这个问题,带来了⼀个新的持久化选项——混合持久化。将 rdb ⽂件的内容和增量的 AOF ⽇志⽂件存在⼀起。这⾥的 AOF ⽇志不再是全量的⽇志,⽽是⾃持久化开始到持久化结束的这段时间发⽣的增量 AOF ⽇志,通常这部分 AOF ⽇志很⼩。
在 redis.conf 文件可以开启混合持久化(必须先开启aof)
# aof‐use‐rdb‐preamble yes
如果开启了混合持久化,AOF在重写时,不再是单纯将内存数据转换为RESP命令写入AOF文件,而是将 重写这一刻之前的内存做RDB快照处理,并且将RDB快照内容和增量的AOF修改内存数据的命令存在一 起,都写入新的AOF文件,新的文件一开始不叫appendonly.aof,等到重写完新的AOF文件才会进行改 名,覆盖原有的AOF文件,完成新旧两个AOF文件的替换。
于是在 Redis 重启的时候,可以先加载 RDB 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,因此重启效率大幅得到提升。
混合持久化AOF文件结构如下