文章目录
- 1 :peach:RDB:peach:
- 1.1 :apple:触发机制:apple:
- 1.1.1 :lemon:手动触发:lemon:
- 1.1.2 :lemon:自动触发:lemon:
- 1.2 :apple:RDB 文件的处理:apple:
- 1.3 :apple:RDB 的优缺点:apple:
- 2 :peach:AOF:peach:
- 2.1 :apple:使用 AOF:apple:
- 2.1.2 :lemon:命令写入:lemon:
- 2.1.3 :lemon:文件同步:lemon:
- 2.1.4 :lemon:重写机制:lemon:
- 3 :peach:回顾:peach:
Redis ⽀持RDB
(Redis DataBase)和AOF
(Append Only File)两种持久化机制,持久化功能有效地避免因进程退出造成数据丢失问题,当下次重启时利⽤之前持久化的⽂件即可实现数据恢复。
1 🍑RDB🍑
RDB 持久化是把当前进程数据生成快照保存到硬盘的过程,触发 RDB 持久化过程分为⼿动触发和⾃动触发。
注意:RDB文件默认是自动开启的。
1.1 🍎触发机制🍎
1.1.1 🍋手动触发🍋
⼿动触发分别对应 save
和 bgsave
命令:
-
save
命令:阻塞当前 Redis 服务器,直到 RDB 过程完成为⽌,对于内存⽐较⼤的实例造成⻓时间阻塞,基本不采⽤。 -
bgsave
命令:Redis 进程执⾏fork
操作创建⼦进程,RDB 持久化过程由⼦进程负责,完成后⾃动结束。阻塞只发⽣在 fork 阶段,⼀般时间很短。
我们可以先来试验下:
我们首先在/var.lib/redis
目录下找到rdb文件然后打开:
发现里面的数据是二进制的,当我们向里面插入一些数据时:
再次观察该rdb文件中内容:
我们发现内容与最初时一模一样,这也符合我们的预期,因为我们还没有手动触发,也没有达到自动触发的条件,当我们使用bgsave
命令后再观察:
此时该rdb文件中的数据才发生改变,并且我们能够明显观察出插入了一些数据,尽管有些数据看不懂。
由于我们插入数据非常少所以rdb文件生成快照保存在硬盘的速度非常快,那么bgsave
命令的运作流程究竟是怎样的呢?下面一张图可以总结:
- 执⾏ bgsave 命令,Redis ⽗进程判断当前是否存在其他正在执⾏的⼦进程,如 RDB/AOF ⼦进程,如果存在 bgsave 命令直接返回。
- ⽗进程执⾏ fork 创建⼦进程,fork 过程中⽗进程会阻塞,通过 info stats 命令查看latest_fork_usec 选项,可以获取最近⼀次 fork 操作的耗时,单位为微秒。
- ⽗进程 fork 完成后,bgsave 命令返回 “Background saving started” 信息并不再阻塞⽗进程,可以继续响应其他命令。
- ⼦进程创建 RDB ⽂件,根据⽗进程内存⽣成临时快照⽂件,完成后对原有⽂件进⾏原⼦替换。执⾏ lastsave 命令可以获取最后⼀次⽣成 RDB 的时间,对应 info 统计的 rdb_last_save_time 选项。
- 进程发送信号给⽗进程表⽰完成,⽗进程更新统计信息。
但是现在有一个问题,假如我们此时停止了redis-cli,并没有进行bgsave,还会保存新插入的数据吗?我们来验证下:我们插入k4 和 k5 并不执行bgsave,然后退出客户端:
重新启动时我们发现数据还在,说明在我们退出客户端的时候Redis会自动替换RDB文件。但假如不是正常退出而是异常退出呢?我们先插入一些数据,然后使用kill命令干掉redis服务器:
在centos上我们使用service重新启动会出错,要使用systemctl命令:
此时我们登录redis-cli发现数据没有了:
另外要注意我们使用flushall
命令时也会自动替换RDB文件,只是该文件数据为空。
Redis 内部的所有涉及 RDB 的操作都采⽤类似 bgsave 的⽅式。
1.1.2 🍋自动触发🍋
除了⼿动触发之外,Redis 运⾏⾃动触发 RDB 持久化机制,这个触发机制才是在实战中有价值的。
- 使⽤
save
配置。如 “save m n” 表⽰ m 秒内数据集发⽣了 n 次修改,⾃动 RDB 持久化。 - 从节点进⾏全量复制操作时,主节点⾃动进⾏ RDB 持久化,随后将 RDB ⽂件内容发送给从结点。(这个我们后面再说)
- 执⾏
shutdown
命令关闭 Redis 时,执⾏ RDB 持久化。
首先我们先找到Redis的配置文件:/etc/redis.conf
打开配置文件找到下面内容:
在配置文件中我们可以自行设置自动触发的条件,可以结合着业务场景来灵活的进行设置,不过要注意在设置成功后要通过service redis-server restart
(centos上可能要使用systemctl restart redis
才行)命令来重新启动。
其实在这里大家或许就发现了RDB的一些弊端:不能实时的更新快照文件,而想要实时更新的话就要使用AOF,但是有人或许会问我们设置配置文件时替换快照文件的时间短一点儿不就好了吗?
但是别忘了,fork生成子进程是存在着一定的代价的,如果短时间内产生大量的替换的话效率会非常低下。
此外在该配置文件中在存在着生成RDB文件的默认路径(用户可以自己指定生成路径):
我们进入到该目录中:
会发现在该路径下存在一个默认的rdb文件,这个文件就是使用rdb机制生成的镜像文件,里面的格式是二进制的,用户不要手动修改该文件,一但把该文件的格式给该坏了那么重新启动Redis时就可能因为格式不对而造成数据加载失败甚至服务器直接起不来。为了避免上述情况的发生,Redis还提供了一个检测工具专门检测rdb文件的格式:
使用redis-check-rdb
工具可以有效的检测rdb文件的格式是否有误。
如果我们想要查看日志的话可以在/var/log/redis
目录下进行查看。
1.2 🍎RDB 文件的处理🍎
保存:RDB ⽂件保存再 dir 配置指定的⽬录(默认 /var/lib/redis/
)下,⽂件名通过 dbfilename配置(默认dump.rdb)指定。可以通过执⾏ config set dir {newDir}
和 config set dbfilename{newFilename}
运⾏期间动态执⾏,当下次运⾏时 RDB ⽂件会保存到新⽬录。
压缩:Redis 默认采⽤ LZF
算法对⽣成的 RDB ⽂件做压缩处理,压缩后的⽂件远远⼩于内存⼤⼩,默认开启,可以通过参数 config set rdbcompression {yes|no}
动态修改。
校验:如果 Redis 启动时加载到损坏的 RDB ⽂件会拒绝启动。这时可以使⽤ Redis 提供的 redischeck-dump ⼯具检测 RDB ⽂件并获取对应的错误报告。
1.3 🍎RDB 的优缺点🍎
- RDB 是⼀个紧凑压缩的⼆进制⽂件,代表 Redis 在某个时间点上的数据快照。⾮常适⽤于备份,全量复制等场景。⽐如每 6 ⼩时执⾏ bgsave 备份,并把 RDB ⽂件复制到远程机器或者⽂件系统中(如 hdfs)⽤于灾备。
- Redis 加载 RDB 恢复数据远远快于 AOF 的⽅式。
- RDB ⽅式数据没办法做到实时持久化 / 秒级持久化。因为 bgsave 每次运⾏都要执⾏ fork 创建⼦进程,属于重量级操作,频繁执⾏成本过⾼。
- RDB ⽂件使⽤特定⼆进制格式保存,Redis 版本演进过程中有多个 RDB 版本,兼容性可能有⻛险。
2 🍑AOF🍑
AOF(Append Only File)持久化:以独立日志的⽅式记录每次写命令,重启时再重新执⾏ AOF⽂件中的命令达到恢复数据的⽬的。AOF 的主要作⽤是解决了数据持久化的实时性,⽬前已经是Redis 持久化的主流⽅式。理解掌握好 AOF 持久化机制对我们兼顾数据安全性和性能⾮常有帮助。
2.1 🍎使用 AOF🍎
开启 AOF 功能需要设置配置:appendonly yes
,默认不开启。AOF ⽂件名通过appendfilename 配置(默认是 appendonly.aof)设置。保存⽬录同 RDB 持久化⽅式⼀致,通过 dir配置指定。AOF 的⼯作流程操作:命令写⼊(append)、⽂件同步(sync)、⽂件重写(rewrite)、重启加载(load)
- 所有的写⼊命令会追加到 aof_buf(缓冲区)中。
- AOF 缓冲区根据对应的策略向硬盘做同步操作。
- 随着 AOF ⽂件越来越⼤,需要定期对 AOF ⽂件进⾏重写,达到压缩的⽬的。
- 当 Redis 服务器启动时,可以加载 AOF ⽂件进⾏数据恢复。
AOF文件的默认路径与RDB文件一致:
2.1.2 🍋命令写入🍋
AOF 命令写⼊的内容直接是文本协议格式。例如 set k1 hello 与 set k2 world 命令,在 AOF 缓冲区会追加如下
⽂本:
此处遵守 Redis 格式协议,Redis 选择⽂本协议可能的原因:⽂本协议具备较好的兼容性,实现简单,具备可读性。
AOF 过程中为什么需要 aof_buf 这个缓冲区?Redis 使⽤单线程响应命令,如果每次写 AOF ⽂件都直接同步硬盘,性能从内存的读写变成 IO 读写,必然会下降。先写⼊缓冲区可以有效减少 IO 次数,同时,Redis 还可以提供多种缓冲区同步策略,让⽤⼾根据⾃⼰的需求做出合理的平衡。
2.1.3 🍋文件同步🍋
Redis 提供了多种 AOF 缓冲区同步⽂件策略,由参数appendfsync
控制:
可配置值 | 说明 |
always | 命令写⼊ aof_buf 后调⽤ fsync 同步,完成后返回 |
everysec | 命令写⼊aof_buf 后只执⾏ write 操作,不进⾏fsync。每秒由同步线程进⾏ fsync |
no | 命令写⼊ aof_buf 后只执⾏ write 操作,由 OS 控制fsync 频率 |
系统调⽤ write
和 fsync
说明:
-
write
操作会触发延迟写(delayed write)机制。Linux 在内核提供⻚缓冲区⽤来提供硬盘 IO 性能。write 操作在写⼊系统缓冲区后⽴即返回。同步硬盘操作依赖于系统调度机制,例如:缓冲区⻚空间写满或达到特定时间周期。同步⽂件之前,如果此时系统故障宕机,缓冲区内数据将丢失。 -
fsync
针对单个⽂件操作,做强制硬盘同步,fsync 将阻塞直到数据写⼊到硬盘。 - 配置为
always
时,每次写⼊都要同步 AOF ⽂件,性能很差,在⼀般的 SATA 硬盘上,只能⽀持⼤约⼏百 TPS 写⼊。除⾮是⾮常重要的数据,否则不建议配置。 - 配置为
no
时,由于操作系统同步策略不可控,虽然提⾼了性能,但数据丢失⻛险⼤增,除⾮数据重要程度很低,⼀般不建议配置。 - 配置为
everysec
,是默认配置,也是推荐配置,兼顾了数据安全性和性能。理论上最多丢失 1 秒的数据。
2.1.4 🍋重写机制🍋
随着命令不断写⼊ AOF,⽂件会越来越⼤,为了解决这个问题,Redis 引⼊ AOF 重写机制压缩⽂件体积。AOF ⽂件重写是把 Redis 进程内的数据转化为写命令同步到新的 AOF ⽂件。
重写后的 AOF 为什么可以变⼩?有如下原因:
- 进程内已超时的数据不再写⼊⽂件。
- 旧的 AOF 中的⽆效命令,例如 del、hdel、srem 等重写后将会删除,只需要保留数据的最终版本。
- 多条写操作合并为⼀条,例如 lpush list a、lpush list b、lpush list 从可以合并为 lpush list a b c。
较⼩的 AOF ⽂件⼀⽅⾯降低了硬盘空间占⽤,⼀⽅⾯可以提升启动 Redis 时数据恢复的速度。
AOF 重写过程可以手动触发和自动触发:
- ⼿动触发:调⽤
bgrewriteaof
命令。 - ⾃动触发:根据
auto-aof-rewrite-min-size
和auto-aof-rewrite-percentage
参数确定⾃动触发时机。
- auto-aof-rewrite-min-size:表⽰触发重写时 AOF 的最⼩⽂件⼤⼩,默认为 64MB。
- auto-aof-rewrite-percentage:代表当前 AOF 占⽤⼤⼩相⽐较上次重写时增加的⽐例。
AOF 重写流程:
- 执⾏ AOF 重写请求。
如果当前进程正在执⾏ AOF 重写,请求不执⾏。如果当前进程正在执⾏ bgsave 操作,重写命令延迟到 bgsave 完成之后再执⾏。 - ⽗进程执⾏ fork 创建⼦进程。
- 重写
- 3.1 主进程 fork 之后,继续响应其他命令。所有修改操作写⼊ AOF 缓冲区并根据 appendfsync 策略同步到硬盘,保证旧 AOF ⽂件机制正确。
- 3.2 ⼦进程只有 fork 之前的所有内存信息,⽗进程中需要将 fork 之后这段时间的修改操作写⼊AOF 重写缓冲区中。
- ⼦进程根据内存快照,将命令合并到新的 AOF ⽂件中。
- ⼦进程完成重写
- 5.1 新⽂件写⼊后,⼦进程发送信号给⽗进程。
- 5.2 ⽗进程把 AOF重写缓冲区内临时保存的命令追加到新 AOF ⽂件中。
- 5.3 ⽤新 AOF ⽂件替换⽼ AOF ⽂件。
另外大家或许还有一个问题:为什么父进程还要将fork之后的修改操作写入到aof-buf
中呢?
其实这是因为如果父进程写⼊aof_rewrite_buf
中时写入失败时可以从旧的AOF文件中获取fork之后的修改操作。
我们可以来验证一下 bgrewriteaof
命令:
刚开始aof文件中的数据是这样的:
当我们执行bgrewriteaof
命令后:
我们发现aof文件中的数据居然是二进制的,这是由于Redis引入了混合持久化的特性,在将命令写入aof文件时使用的还是文本的格式,可是当我们重写aof文件时就会将文本格式转化成二进制加快效率,下一次写入命令时仍然是文本格式:
这个混合持久化在配置文件中是默认打开的:
若想要关闭的话将yes
改为no
,然后重新启动服务器即可。
3 🍑回顾🍑
- Redis 提供了两种持久化⽅案:RDB 和 AOF。
- RDB 视为内存的快照,产⽣的内容更为紧凑,占⽤空间较⼩,恢复时速度更快。但产⽣ RDB 的开销较⼤,不适合进⾏实时持久化,⼀般⽤于冷备和主从复制。
- AOF 视为对修改命令保存,在恢复时需要重放命令。并且有重写机制来定期压缩 AOF ⽂件。
- RDB 和 AOF 都使⽤ fork 创建⼦进程,利⽤ Linux ⼦进程拥有⽗进程内存快照的特点进⾏持久化,尽可能不影响主进程继续处理后续命令。
注意:AOF与RDB都存在时以AOF为主。