持久化

为什么要持久化

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流程

  1. 执行 bgsave 命令,Redis 父进程判断当前是否存在正在执行的子进程,如 RDB/AOF 子进程,如果存在 bgsave 命令直接返回。
  2. 父进程执行 fork 操作创建子进程,fork操作过程中父进程会阻塞。
  3. fork 完成后,bgsave 命令返回“Background saving started”信息并不再阻塞父进程,父进程可以继续响应其他命令。
  4. 子进程会共享一部分主进程的数据空间,并且把共享的数据置为read-only的状态,子进程创建 RDB 文件,根据父进程内存生成临时快照文件,完成后对原有文件进行原子替换。在持久化的过程中是避免不了有新的数据写入的,因为我们有一部分的数据是共享的,两个进程同时拥有一块数据,肯定会导致数据不一致的问题, 但是依赖于操作系统的fork机制,在修改的时候一定是修改部分内存页的数据,这个时候会触发对应内存页的copyonwrite的操作
  5. 进程发送信号给父进程表示完成,父进程更新信息。

配置文件

# 900s内至少达到一条写命令 
save 900 1

# 300s内至少达至10条写命令 
save 300 10

# 60s内至少达到10000条写命令 
save 60 10000

RDB 文件的处理

  1. 保存

RDB 文件保存在 dir 配置指定的目录下,文件名通过 dbfilename 配置指定。可以通过执行 config set dir{newDir}和 config set dbfilename{newFileName}运行期动态执行,当下次运行时 RDB 文件会保存到新目录。 当遇到坏盘或磁盘写满等情况时,可以通过 config set dir{newDir}在线修改文件路径到可用的磁盘路径,之后执行 bgsave 进行磁盘切换,同样适用于 AOF 持久化文件。

  1. 压缩

Redis 默认采用 LZF 算法对生成的 RDB 文件做压缩处理,压缩后的文件远远小于内存大小,默认开启,可以通过参数 config set rdbcompression{yes|no}动态修改。 虽然压缩 RDB 会消耗 CPU,但可大幅降低文件的体积,方便保存到硬盘或通过网络发送给从节点,因此线上建议开启。

RDB方式的优点

  1. RDB 是一个非常紧凑的文件,它保存了 Redis 在某个时间点上的数据集。这种文件非常适合用于进行备份: 比如说,你可以在最近的 24小时内,每小时备份一次 RDB文件,并且在每个月的每一天,也备份一个 RDB 文件。 这样的话,即使遇上问题,也可以随时将数据集还原到不同的版本。
  2. RDB 可以最大化 Redis 的性能:父进程在保存 RDB 文件时唯一要做的就是 fork 出一个子进程,然后这个子进程就会处理接下来的所有保存 工作,父进程无须执行任何磁盘 I/O 操作。
  3. RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。

RDB方式的缺点

  1. RDB 方式数据没办法做到实时持久化/秒级持久化。 如果服务器宕机的话,采用RDB的方式会造成某个时段内数据的丢失,比如我们设置10分钟同步一次或5分钟达到1000次写入就同步一次,那么如果还没达到触发条件服务器就死机了,那么这个时间段的数据会丢失。
  2. 使用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线程。

流程

image

  1. 所有的写入命令会追加到 aof_buf(缓冲区)中。(Redis使用单线程响应命令,如果每次AOF文件命令都追加到磁盘,会极大的影响处理性能)
  2. AOF 缓冲区根据对应的策略向硬盘做同步操作。
  3. 随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩的目的。
  4. 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 加载。

重写流程

重写机制

  1. 执行 AOF 重写请求。 如果当前进程正在执行 AOF 重写,请求不执行并返回如下响应: ERR Background append only file rewriting already in progress
  2. 父进程 fork 创建子进程,并拿到 fork 时的 AOF 文件数据写到一个临时AOF文件中。
  3. 主进程 fork 操作完成后,继续响应其他命令。所有修改命令依然写入 AOF 缓冲区并根据 appendfsync 策略同步到硬盘,保证原有 AOF 机制正确 性。
  4. 由于 fork 操作运用写时复制技术,子进程只能共享 fork 操作时的内存数据。由于父进程依然响应命令,Redis 使用“AOF 重写缓冲区”保存这部分新数据,防止新 AOF 文件生成期间丢失这部分数据。
  5. 子进程根据内存快照,按照命令合并规则写入到新的 AOF 文件。每次批量写入硬盘数据量由配置 aof-rewrite-incremental-fsync 控制,默认为 32MB,防止单 次刷盘数据过多造成硬盘阻塞。
    • 新 AOF 文件写入完成后,子进程发送信号给父进程,父进程更新统计信息。
    • 父进程把 AOF 重写缓冲区的数据写入到新的 AOF 文件。
    • 使用新 AOF 文件替换老文件,完成 AOF 重写。

触发重写

  1. 手动触发: 直接调用 bgrewriteaof 命令。

  2. 自动触发: 根据 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的优点

  1. AOF可以设置 完全不同步、每秒同步、每次操作同,默认是每秒同步。因为AOF是操作指令的追加,所以可以频繁的大量的同步。
  2. AOF文件是一个值追加日志的文件,即使服务宕机为写入完整的命令,也可以通过redis-check-aof工具修复这些问题。
  3. 如果AOF文件过大,Redis会在后台自动地重写AOF文件。重写后会使AOF文件压缩到最小所需的指令集。
  4. AOF文件是有序保存数据库的所有写入操作,易读,易分析。即使如果不小心误操作数据库,也很容易找出错误指令,恢复到某个数据节点。例如不小心FLUSHALL,可以非常容易恢复到执行命令之前。

AOF的缺点

  1. 相同数据量下,AOF的文件通常体积会比RDB大。因为AOF是存指令的,而RDB是所有指令的结果快照。但AOF在日志重写后会压缩一些空间。
  2. 在大量写入和载入的时候,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

  1. 一般情况,为了可靠性, rdb 和 aof 都是一起开的,aof 设置为 1s
  2. 当然,也有特殊,为了性能,也有主库不开启持久化,从库做持久化,但重启需要注意,防止主库重启后直接把从库刷空了,建议先断开主从连接,导入主库后再开启主从连接

关注和赞赏都是对小欧莫大的支持! 🤝 🤝 🤝
公众号