Redis的数据持久化策略:RDB和AOF详解

1. 背景

Redis的数据全部存储在内存,如果机器突然宕机,那么数据就会全部丢失,因此必须有一种机制来保证 Redis 的数据不会因为故障而丢失,这种机制就是 Redis 的持久化机制。Redis为我们提供了两种持久化方案,一种是基于快照RDB(Redis DataBase),另外一种是基于 AOF (Append Only File)日志 。Redis也可以同时支持 AOF 持久化和 RDB 持久化。在这种情况下,当 AOF 重启时,会优先使用 AOF 文件去恢复原始数据。因为 AOF 中保存的数据通常比 RDB 中保存的数据更加完整。

图片[1]-Redis的数据持久化策略:RDB和AOF详解-不念博客

2. RDB 详解

RDB 是 Redis 默认的持久化方案。RDB通过快照的形式将数据保存到磁盘中。所谓快照,可以理解为在某一时间点将数据集的一个快照。在指定的时间间隔内,执行指定次数的写操作,则会将内存中的数据写入到磁盘中。即在指定目录下生成一个dump.rdb文件。Redis 重启会通过加载dump.rdb文件恢复数据。

2.1. RDB配置参数

打开 redis.conf 文件,RDB的规则配置

save <seconds> <changes>
# save ""
# 在 900 秒(15分钟)之后,如果至少有 1 个 key 发生变化,Redis 就会自动触发 BGSAVE 命令创建快照
save 900 1     
# 在 300 秒(5分钟)之后,如果至少有 10 个 key 发生变化,Redis 就会自动触发 BGSAVE 命令创建快照      
save 300 10 
# 在 60 秒(1分钟)之后,如果至少有 10000 个 key 发生变化,Redis 就会自动触发 BGSAVE命令创建快照         
save 60 10000  

1. 参数说明:

save <指定时间间隔> <执行指定次数更新操作>

满足条件就将内存中的数据同步到硬盘中。默认配置是 900秒内有1个更改,300秒内有10个更改以及60秒内有10000个更改,则将内存中的数据快照写入磁盘。
若想关闭RDB,可以把 save “” 的注释打开,其他关闭。

2 自定义本地数据库文件,默认认名词 dump.rdb

dbfilename dump.rdb

3 指定生成文件的路径。一般也用默认配置

dir ./

4 是否开启数据压缩

rdbcompression yes

参数说明:

配置存储至本地数据库时是否压缩数据,默认为yes。Redis采用LZF压缩方式,会占用部分CPU的时间。若关闭该选项,但会导致数据库文件变的巨大,建议开启。

2.2 生成RDB

生成RDB的步骤:

1 配置规则执行指定次数的写操作
2 执行save(同步阻塞) 或者是bgsave (异步)命令
3 执行flushall 命令,清空数据库所有数据。
4 执行shutdown 命令,保证服务器正常关闭且不丢失任何数据。

2.3 通过RDB恢复数据

将生成的本地dump.rdb 文件拷贝到redis的安装目录的bin目录下,重启redis服务会自动加载RDB。在实际开发中,一般会选择网络存储设备,比如对象存储,NAS或者云盘。

2.4 RDB 的优缺点

优点:
1 适合大规模的数据恢复,一般建议Redis单实例小于10GB。
2 如果追求系统的高可用和数据的高要求,RDB并不是很好的选择。

缺点:
1 数据的完整性和一致性不高,因为RDB可能在最后一次备份时宕机了。
2 备份时占用内存,因为Redis 在备份时会独立创建一个子进程,将数据写入到一个临时文件(此时内存中的数据是原来的两倍哦),最后再将临时文件替换之前的备份文件,不过这个问题在最新版的Redis已经优化为异步任务。

当我们生成RDB的过程中有新的写入,那么如何保障数据的一致性呢?这就是下午要展开讲的COW机制。

2.5 COW

我们先看两个函数:fork()和exec()。需要说明的是exec()并不是一个特定的函数, 它是一组函数的统称, 它包括了execl()、execlp()、execv()、execle()、execve()、execvp()。

fork是类Unix操作系统上创建进程的主要方法。fork用于创建子进程(等同于当前进程的副本)。Linux下init进程是所有进程的父进程。exec函数的作用就是:装载一个新的程序(可执行映像)覆盖当前进程内存空间中的映像,从而执行不同的任务

由于进程之前的内存空间是隔离的,如果fork后直接copy父进程数据到子进程,那么后写入的数据就会丢失。很多时候我们直接复制给子进程的数据是无效的,于是就需要Copy On Write(COW)技术了,它的原理如下:

  • fork()执行后,kernel把父进程中所有的内存页的权限都设为read-only,然后子进程的地址空间指向父进程。
  • 当某个进程更新内存时,CPU硬件检测到内存页是read-only的,于是触发页异常中断(page-fault),陷入kernel的一个中断程序。
  • 在中断程序中,kernel就会把触发的异常的页复制一份,于是父子进程各自持有独立的一份。

Copy On Write的好处:

  • COW技术可减少分配和复制大量资源时带来的瞬间延时。
  • COW技术可减少不必要的资源分配。比如fork进程时,并不是所有的页面都需要复制,父进程的代码段和只读数据段都不被允许修改,所以无需复制。

Copy On Write的缺点:

如果在fork()之后,父子进程都还需要继续进行写操作,那么会产生大量的分页错误(页异常中断page-fault),这样就得不偿失。

3. AOF 详解

AOF :Redis 默认不开启。它的目的是为了解决生成RDB后数据不能实时一致的问题,所以它采用日志的形式来记录每个写操作,并追加到文件中。Redis 重启的会根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。

AOF持久化功能的实现可以分为命令追加(append)、文件写入、 文件同步(sync)三个步骤。下面详细说明其中的步骤:

a.命令追加:服务器在执行完一个写命令之后,会以协议格式将被执行的写命令追加到服务器状态的aof_buf缓冲区的末尾

举个栗子,如果执行以下指令:

redis> SET 
VALUE
OK
1212

那么会将以下协议内容追加到 aof_buf缓冲区的末尾:

*3\r\n$3\r\nSET\r\n$3\r\nAGE\r\n$5\r\
\r\n
11

b.文件写入:通过 write() 系统调用,将 aof_buf 缓冲区的数据写入到 AOF 文件,此时数据并没有写入到硬盘,而是拷贝到了内核缓冲区 page cache,等待内核将数据写入硬盘;具体内核缓冲区的数据什么时候写入到硬盘,由内核决定。

c.文件同步

如果由内核决定将数据写入硬盘的话,如果服务器宕机,那么就会有数据丢失的风险。为了解决这个问题,系统提供了fsync和fdatasync两个同步函数三种写回策略,它们可以强制让操作系统立即将缓冲区中的数据写入到硬盘里面,从而确保写入数据的安全性。

在 redis.conf 配置文件中的 appendfsync 配置项可以有以下 3 种参数可填:

  • always:服务器在每次写操作后都将 aof_buf缓冲区中的所有内容写入到AOF文件,然后立即执行fsync()函数同步AOF文件到磁盘,所以always的效率是最低,但也是最安全的。可靠性高,性能低。
  • everysec:服务器在每次写操作后都要 将aof_buf缓冲区中的所有内容写入到AOF文件,并且每隔一秒就要在子线程中对AOF文件进行一次同步,创建一个异步任务执行fsync()函数可靠性和性能都适中。
  • no:将缓冲区的内容写入AOF文件后,何时进行同步由操作系统控制,不执行fsync()函数性能好,可靠性低,宕机可能会丢失较多数据。

3.1 配置AOF

打开 redis.conf 文件,找到 APPEND ONLY MODE 对应内容
1 yes开启,默认不开启

appendonly yes

2 自定义本地数据库文件名,默认值为 appendonly.aof

appendfilename "appendonly.aof"

3 自定义更新日志条件

# appendfsync always
appendfsync everysec
# appendfsync no

参数说明:
always:同步持久化,每次发生数据变化会立刻写入到磁盘中。性能较差当数据完整性比较好(慢,安全)
everysec:默认推荐,每秒异步记录一次(默认值)
no:不同步

4 配置重写触发机制

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

参数说明:当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发。一般都设置为3G,默认的64M太小。

3.2 生成AOF

根据配置文件触发,可以是每次执行触发,可以是每秒触发,可以不同步。

3.3 根据AOF恢复数据

正常情况下,将appendonly.aof 文件拷贝到redis的安装目录的bin目录下,重启redis服务即可。在实际开发中,文件也会存储在网络设备上,保障数据高可靠。如果因为某些原因导致appendonly.aof 文件格式异常,从而导致数据还原失败,可以通过命令redis-check-aof –fix appendonly.aof

进行修复 。

3.4 AOF的重写压缩机制

AOF的工作原理是将写操作追加到文件中,文件的冗余内容会越来越多。所以Redis 新增了重写机制。

AOF重写可以生成一个新的AOF文件,新文件和老文件所保存的信息一样,但体积更小,还可以做命令的合并,进而节省了 Redis 的存储空间。比如对一个list里面的元素写了又删,我就可以合并多条为一条新命令 → LPUSH v2 v3…。

AOF重写的目的

解决AOF文件越来越大产生性能问题:

  1. Linux文件系统对单文件大小有限制,无法保存过大的文件。
  2. 如果文件太大,之后再往里面追加命令记录的话,效率也会变低。
  3. 如果发生宕机,AOF中记录的命令要一个个被重新执行,用于故障恢复。如果日志文件太大,整个恢复过程就会非常缓慢,影响到 Redis 的正常使用。

AOF重写流程

AOF重写不会阻塞主线程,其重写过程是由后台子进程 bgrewriteaof 来完成的,从而避免了性能下降。具体流程如下:

  1. 把主线程的内存拷贝一份给fork出来的 bgrewriteaof 子进程,这里面包含了Redis中最新的数据。
  2. 子进程将其中的数据进行重写。
  3. 主线程维护一个AOF缓冲区(实际上无论重不重写都有这个缓冲区,因为AOF日志写入是 → AOF缓冲区 → AOF文件),如果此时有写操作,则会写入到AOF缓冲区以及AOF日志中,保证数据完整。
  4. 主线程在重写时维护一个AOF重写缓冲区,将重写过程中的写操作记入其中,保证重写后的AOF日志也能记录在重写过程中产生的新数据。
  5. 用新AOF替换老AOF日志。

AOF重写为什么不采用覆盖写:父子进程写同一个文件必然会产生竞争,如果控制竞争就意味着会影响父进程的性能。如果AOF重写失败,那么原本的AOF文件相当于被污染了,就直接废了。而采用覆盖的方式则不会有这种负面影响。

3.5 AOF 的优缺点

优点:数据的完整性和一致性更高
缺点:因为AOF记录的内容多,文件会越来越大,数据恢复也会越来越慢。

4.总结

  1. Redis 默认开启RDB持久化方式,在指定的时间间隔内,执行指定次数的写操作,则将内存中的数据写入到磁盘中。RDB 持久化适合大规模的数据恢复但它的数据一致性和完整性较差。采用COW可以保障数据一致,单高频写入会引发缺页中断。
  2. Redis 需要手动开启AOF持久化方式,默认是每秒将写操作日志追加到AOF文件中。AOF 的数据完整性比RDB高,但记录内容多了,会影响数据恢复的效率。Redis 针对 AOF文件大的问题,提供重写的瘦身机制。
  3. 若只打算用Redis 做缓存,可以关闭持久化。
  4. 若打算使用Redis 的持久化。建议RDB和AOF都开启。
© 版权声明
THE END