持久化
为什么要持久化
Redis是内存数据库,数据都是存储在内存中,为了避免进程退出导致数据的永久丢失,需要定期将Redis中的数据以某种形式从内存保存到硬盘。当下次Redis重启时,利用持久化文件实现数据恢复。除此之外,为了进行灾难备份,可以将持久化文件拷贝到一个远程位置。
Redis持久化分为RDB和AOF,前者将当前数据保存到硬盘,后者则是将执行的写命令保存到硬盘。
RDB
RDB是一种快照存储持久化方式。具体来说就是将Redis某一时刻的内存数据保存到硬盘的文件当中,默认保存的文件名为dump.rdb。而在Redis服务器启动时,会加载dump.rdb文件的数据到内存当中恢复数据。
触发机制
触发 RDB 持久化过程分为手动触发和自动触发。
手动触发命令
- save 命令
阻塞当前 Redis 进程,直到 RDB 过程完成为止(内存比较大的实例会造成长时间阻塞,线上环境不建议使用)
- bgsave 命令
Redis 进程执行 fork 操作创建子进程,RDB 持久化过程由子进程负责,完成后自动结束。(阻塞只发生在 fork 阶段,时间很短)
bgsave 命令是针对 save 阻塞问题做的优化。
除了执行命令手动触发之外,Redis 内部存在自动触发 RDB 的持久化机制,例如:
- 使用 save 相关配置,如“save m n”。表示 m 秒内数据集存在 n 次修改时,自动触发 bgsave。
- 如果从节点执行全量复制操作,主节点自动执行 bgsave 生成 RDB 文件并发送给从节点。
- 默认情况下执行 shutdown 命令时,如果没有开启 AOF 持久化功能则自动执行 bgsave。
bgsave流程
- 执行 bgsave 命令,Redis 父进程判断当前是否存在正在执行的子进程,如 RDB/AOF 子进程,如果存在 bgsave 命令直接返回。
- 父进程执行 fork 操作创建子进程,fork操作过程中父进程会阻塞。
- fork 完成后,bgsave 命令返回“Background saving started”信息并不再阻塞父进程,父进程可以继续响应其他命令。
- 子进程会共享一部分主进程的数据空间,并且把共享的数据置为read-only的状态,子进程创建 RDB 文件,根据父进程内存生成临时快照文件,完成后对原有文件进行原子替换。在持久化的过程中是避免不了有新的数据写入的,因为我们有一部分的数据是共享的,两个进程同时拥有一块数据,肯定会导致数据不一致的问题, 但是依赖于操作系统的fork机制,在修改的时候一定是修改部分内存页的数据,这个时候会触发对应内存页的copyonwrite的操作
- 进程发送信号给父进程表示完成,父进程更新信息。
配置文件
# 900s内至少达到一条写命令
save 900 1
# 300s内至少达至10条写命令
save 300 10
# 60s内至少达到10000条写命令
save 60 10000
RDB 文件的处理
- 保存
RDB 文件保存在 dir 配置指定的目录下,文件名通过 dbfilename 配置指定。可以通过执行 config set dir{newDir}和 config set dbfilename{newFileName}运行期动态执行,当下次运行时 RDB 文件会保存到新目录。 当遇到坏盘或磁盘写满等情况时,可以通过 config set dir{newDir}在线修改文件路径到可用的磁盘路径,之后执行 bgsave 进行磁盘切换,同样适用于 AOF 持久化文件。
- 压缩
Redis 默认采用 LZF 算法对生成的 RDB 文件做压缩处理,压缩后的文件远远小于内存大小,默认开启,可以通过参数 config set rdbcompression{yes|no}动态修改。 虽然压缩 RDB 会消耗 CPU,但可大幅降低文件的体积,方便保存到硬盘或通过网络发送给从节点,因此线上建议开启。
RDB方式的优点
- RDB 是一个非常紧凑的文件,它保存了 Redis 在某个时间点上的数据集。这种文件非常适合用于进行备份: 比如说,你可以在最近的 24小时内,每小时备份一次 RDB文件,并且在每个月的每一天,也备份一个 RDB 文件。 这样的话,即使遇上问题,也可以随时将数据集还原到不同的版本。
- RDB 可以最大化 Redis 的性能:父进程在保存 RDB 文件时唯一要做的就是 fork 出一个子进程,然后这个子进程就会处理接下来的所有保存 工作,父进程无须执行任何磁盘 I/O 操作。
- RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
RDB方式的缺点
- RDB 方式数据没办法做到实时持久化/秒级持久化。 如果服务器宕机的话,采用RDB的方式会造成某个时段内数据的丢失,比如我们设置10分钟同步一次或5分钟达到1000次写入就同步一次,那么如果还没达到触发条件服务器就死机了,那么这个时间段的数据会丢失。
- 使用bgsave命令在forks子进程时,如果数据量太大,forks的过程也会发生阻塞,另外,forks子进程会耗费内存。
AOF
记录客户端对服务器的每一次写操作命令到日志当中,并将这些写操作以Redis协议追加保存到以后缀为aof文件末尾
配置
redis.conf
appendonly yes
#启用aof持久化方式
appendfsync always/no/everysec
设置为always时,会极大消弱Redis的性能,因为这种模式下每次write后都会调用fsync(Linux为调用fdatasync)。
如果设置为no,则write后不会有fsync调用,由操作系统自动调度刷磁盘,性能是最好的。
everysec为最多每秒调用一次fsync,这种模式性能并不是很糟糕,一般也不会产生毛刺,这归功于Redis引入了BIO线程,所有fsync操作都异步交给了BIO线程。
流程
- 所有的写入命令会追加到 aof_buf(缓冲区)中。(Redis使用单线程响应命令,如果每次AOF文件命令都追加到磁盘,会极大的影响处理性能)
- AOF 缓冲区根据对应的策略向硬盘做同步操作。
- 随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩的目的。
- Redis 服务器重启时,加载 AOF 文件进行数据恢复。
重写机制
AOF将客户端的每一个写操作都追加到aof文件末尾,随着命令不断写入 AOF,文件会越来越大,为了解决这个问题,Redis 引入了AOF 重写机制压缩文件体积。AOF 文件重写是把 Redis 进程内的数据转化为写命令同步到新 AOF 文件的过程。 比如: 多条写命令可以合并为一个,如:lpush list a、lpush list b、lpush list c 可以转化为:lpush list a b c。
AOF 重写降低了文件占用空间,除此之外,另一个目的是:更小的 AOF 文件可以更快地被 Redis 加载。
重写流程
- 执行 AOF 重写请求。 如果当前进程正在执行 AOF 重写,请求不执行并返回如下响应: ERR Background append only file rewriting already in progress
- 父进程 fork 创建子进程,并拿到 fork 时的 AOF 文件数据写到一个临时AOF文件中。
- 主进程 fork 操作完成后,继续响应其他命令。所有修改命令依然写入 AOF 缓冲区并根据 appendfsync 策略同步到硬盘,保证原有 AOF 机制正确 性。
- 由于 fork 操作运用写时复制技术,子进程只能共享 fork 操作时的内存数据。由于父进程依然响应命令,Redis 使用“AOF 重写缓冲区”保存这部分新数据,防止新 AOF 文件生成期间丢失这部分数据。
- 子进程根据内存快照,按照命令合并规则写入到新的 AOF 文件。每次批量写入硬盘数据量由配置 aof-rewrite-incremental-fsync 控制,默认为 32MB,防止单 次刷盘数据过多造成硬盘阻塞。
- 新 AOF 文件写入完成后,子进程发送信号给父进程,父进程更新统计信息。
- 父进程把 AOF 重写缓冲区的数据写入到新的 AOF 文件。
- 使用新 AOF 文件替换老文件,完成 AOF 重写。
触发重写
-
手动触发: 直接调用 bgrewriteaof 命令。
-
自动触发: 根据 auto-aof-rewrite-min-size和auto-aof-rewrite-percentage 参数确定自动触发时机。
auto-aof-rewrite-min-size:表示运行 AOF 重写时文件最小体积,默认为 64MB。
auto-aof-rewrite-percentage:代表当前 AOF 文件空间(aof_current_size)和上一次重写后 AOF 文件空间(aof_base_size)的比值。
示例:
auto-aof-rewrite-percentage:100 auto-aof-rewrite-min-size:64mb
默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发
异常处理
AOF文件损坏 在写入aof日志文件时,如果Redis服务器宕机,则aof日志文件文件会出格式错误,在重启Redis服务器时,Redis服务器会拒绝载入这个aof文件, 可以通过命令修复aof并恢复数据
redis-check-aof -fix file.aof
AOF的优点
- AOF可以设置 完全不同步、每秒同步、每次操作同,默认是每秒同步。因为AOF是操作指令的追加,所以可以频繁的大量的同步。
- AOF文件是一个值追加日志的文件,即使服务宕机为写入完整的命令,也可以通过redis-check-aof工具修复这些问题。
- 如果AOF文件过大,Redis会在后台自动地重写AOF文件。重写后会使AOF文件压缩到最小所需的指令集。
- AOF文件是有序保存数据库的所有写入操作,易读,易分析。即使如果不小心误操作数据库,也很容易找出错误指令,恢复到某个数据节点。例如不小心FLUSHALL,可以非常容易恢复到执行命令之前。
AOF的缺点
- 相同数据量下,AOF的文件通常体积会比RDB大。因为AOF是存指令的,而RDB是所有指令的结果快照。但AOF在日志重写后会压缩一些空间。
- 在大量写入和载入的时候,AOF的效率会比RDB低。因为大量写入,AOF会执行更多的保存命令,载入的时候也需要大量的重执行命令来得到最后的结果。RDB对此更有优势。
配置汇总
save 60 100:60s内至少达到100条写命令
appendonly yes:是否开启AOF
appendfilename "appendonly.aof":AOF文件名
dir ./:RDB文件和AOF文件所在目录
appendfsync everysec:fsync持久化策略
no-appendfsync-on-rewrite no:AOF重写期间是否禁止fsync;如果开启该选项,可以减轻文件重写时CPU和硬盘的负载(尤其是硬盘),但是可能会丢 失AOF重写期间的数据;需要在负载和安全性之间进行平衡
auto-aof-rewrite-percentage 100:文件重写触发条件之一
auto-aof-rewrite-min-size 64mb:文件重写触发提交之一
aof-load-truncated yes:如果AOF文件结尾损坏,Redis启动时是否仍载入AOF文件
more
- 一般情况,为了可靠性, rdb 和 aof 都是一起开的,aof 设置为 1s
- 当然,也有特殊,为了性能,也有主库不开启持久化,从库做持久化,但重启需要注意,防止主库重启后直接把从库刷空了,建议先断开主从连接,导入主库后再开启主从连接
关注和赞赏都是对小欧莫大的支持! 🤝 🤝 🤝