Redis持久化

RDB模式

RDB持久化以指定的时间间隔执行数据集的时间点快照。实现类似照片记录效果的方式,就是把某一时刻的数据状态以文件的形式写到磁盘上,也就是快照。这样一来即使Redis宕机,快照文件也不会丢失,数据的可靠性就得到了保证,这个快照文件就是RDB文件(dump.rdb),其中RDB就是Redis DataBase的缩写。

配置文件

默认情况下,RDB持久化是关闭的,需要手动配置开启。RDB持久化的配置项主要包括以下:

  • save: 设置在多长时间内,有多少次更新操作,就将数据同步到磁盘上。例如,配置为save 900 1表示900秒内有至少1个key发生变化,则将数据同步到磁盘上。可以配置多个save选项来实现定期备份。
  • stop-writes-on-bgsave-error: 当Redis执行RDB持久化操作失败时,是否停止接受写请求。默认情况下,该配置项为yes,表示当RDB持久化失败时,Redis将停止接受写请求。
  • rdbcompression: 是否开启RDB文件压缩。默认情况下,该配置项为yes,表示开启RDB文件压缩。
  • rdb-save-incremental-fsync: 是否开启增量同步RDB文件到磁盘。默认情况下,该配置项为yes,表示开启增量同步RDB文件到磁盘。增量同步可以减少磁盘I/O,提升性能。
  • rdb-save-incremental-fsync-interval: 设置增量同步的时间间隔。默认情况下,该配置项为1,表示每秒同步一次。可以根据实际需求调整时间间隔。
save 900 1
stop-writes-on-bgsave-error yes
rdbcompression yes
rdb-save-incremental-fsync yes
rdb-save-incremental-fsync-interval 1

触发方式

触发rdb持久化的方式有2种,分别是手动触发和自动触发。

手动触发

手动触发分别对应savebgsave命令
save:阻塞当前Redis服务器,直到RDB过程完成为止,对于内存比较大的实例会造成长时间阻塞,线上环境不建议使用。
bgsave:Redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束。阻塞只发生在fork阶段,一般时间很短。

通知主进程bgsave命令Redis主进程已有子进程?直接返回fork子进程子进程将RDB文件同步到磁盘
  1. redis客户端执行bgsave命令或者自动触发bgsave命令。
  2. 主进程判断当前是否已经存在正在执行的子进程,如果存在,那么主进程直接返回。
  3. 如果不存在正在执行的子进程,那么就fork一个新的子进程进行持久化数据,fork过程是阻塞的,fork操作完成后主进程即可执行其他操作。
  4. 子进程先将数据写入到临时的rdb文件中,待快照数据写入完成后再原子替换旧的rdb文件。
  5. 同时发送信号给主进程,通知主进程rdb持久化完成,主进程更新相关的统计信息(info Persitence下的rdb_*相关选项)。

自动触发

  1. 当redis.conf配置文件中的save选项被触发时,例如save 900 1表示900秒内有至少1个key发生变化,就自动触发bgsave就将数据同步到磁盘上。
  2. 主从复制时,从节点要从主节点进行全量复制时也会触发bgsave操作,生成当时的快照发送到从节点。
  3. 执行debug reload命令重新加载redis时也会触发bgsave操作。
  4. 默认情况下执行shutdown命令时,如果没有开启aof持久化,那么也会触发bgsave操作。

RDB的优缺点

优点

官方文档

  • RDB文件是某个时间节点的快照,默认使用LZF算法进行压缩,压缩后的文件体积远远小于内存大小,适用于备份、全量复制等场景。
  • Redis加载RDB文件恢复数据要远远快于AOF方式。
  • RDB最大限度地提高了Redis的性能,因为Redis父进程为了持久化而需要做的唯一工作就是派生一个将完成所有其余工作的子进程。父进程永远不会执行磁盘I/O或类似的操作。
缺点

官方文档

  • RDB方式实时性不够,无法做到秒级的持久化。Redis只会在一定时间间隔做一次备份,比如配置save 60 50 ,在某一分钟内Redis宕机了,但是在一分钟内只有40个key变化(Redis不会执行备份),这种情况下这40个Key就会丢失。
  • 每次调用bgsave都需要fork子进程,fork子进程属于重量级操作,频繁执行成本较高。在很大的数据集中,fork的时候数据被克隆了一份,大概膨胀了2倍。
  • RDB文件是二进制的,没有可读性,AOF文件在了解其结构的情况下可以手动修改或者补全。

AOF模式

AOF(append only file) 以日志的形式记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),只允许追加文件但不可以改写文件,Redis启动之初会读取该文件重新构建数据,换言之,Redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。默认情况下Redis是没有开启AOF的。开启AOF需要修改配置文件为appendonly yes 。Redis是“写后”日志,Redis先执行命令,把数据写入内存,然后才记录日志。日志里记录的是Redis收到的每一条命令,这些命令是以文本形式保存。

大多数的数据库采用的是写前日志(WAL),例如MySQL,通过写前日志和两阶段提交,实现数据和逻辑的一致性。

工作流程

  1. Client作为命令的来源,会有多个源头以及源源不断地请求命令。
  2. 这些命令到达Redis Server以后并不是直接写入AOF文件,会将其这些命令先放入AOF缓存中进行保存。这些AOF缓冲区实际上是内存中的一片区域,存在的目的是当这些命令达到一定量以后再写入磁盘,避免频繁的磁盘IO操作。
  3. AOF缓冲会根据AOF缓冲区同步文件的三种写回策略将命令写入磁盘上的AOF文件。
  4. 随着写入AOF内容的增加为避免文件膨胀,会根据规则进行命令的合并(又称AOF重写),从而起到AOF文件压缩的目的。

AOF的三种写回策略

always : 同步写回,每个命令执行完毕立刻同步地将日志写回磁盘。
everysec : 每秒写回,每个命令执行完毕,只是先把日志写到AOF文件的内存缓冲区,每隔1秒把缓冲区中的内容写入磁盘。
no : 操作系统控制的写回,每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,有操作系统决定何时将缓冲区内容写回磁盘。

写回策略 写回时机 优点 缺点
always 同步写回 可靠性高,数据基本不丢失 每个写命令都要落盘,性能影响较大
everysec 每秒写回 性能适中 宕机时丢失1秒内的数据
no 操作系统控制 性能好 宕机时可能丢失较多数据

重写原理

  1. 在重写开始之前,Redis会创建一个“重写子进程”,这个子进程会读取现在的AOF文件,将其包含的指令进行分析压缩然后写入到一个临时文件中。
  2. 与此同时,主进程会将新接收到的写指令一边累积到内存缓冲区中,一边继续写入到原有的AOF文件中,这样做是保证原有AOF文件的可用性,避免在重写过程中出现意外。
  3. 当”重写子进程“完成重写工作后,他会给父进程发一个信号,父进程收到信号后就会将内存中缓存的写指令追加到新AOF文件中。
  4. 当追加结束后,Redis就会使用新AOF文件来替代旧的AOF文件,之后再有新的指令就会直接追加到新的AOF文件中。
  5. 重写AOF文件的操作,并没有读取旧的AOF文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的AOF文件,这点和RDB快照类似。

配置文件

配置指令 配置含义 配置示例
appendonly 是否开启AOF appendonly yes
appendfilename 文件名称 appendfilename appendonly.aof
appendonly fsync 同步方式 everysec/always/no
no-appendfsync-on-rewrite AOF重写期间是否同步 no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage
auto-aof-rewrite-min-size
重写触发机制、文件重写策略 auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

Redis7AOF的配置文件被分成了多个部分分别是baseincrmainfest

# Redis 7及更高版本使用一组追加文件来持久化数据集和应用于数据集的更改。有两种基本类型的文件:
# - 基础文件,它们是在创建文件时代表数据集的完整状态的快照。基础文件可以是
#   RDB(二进制序列化)或AOF(文本命令)的形式。
# - 增量文件,其中包含在先前文件之后应用于数据集的其他命令。
# 此外,使用清单文件跟踪文件及其创建和应用顺序。
# Redis根据特定模式创建追加文件名。文件名的前缀基于appendfilename配置参数,后面跟有关序列和类型的其他信息。
# 例如,如果appendfilename设置为appendonly.aof,则可以导出以下文件名:
# - appendonly.aof.1.base.rdb作为基本文件。
# - appendonly.aof.1.incr.aof,appendonly.aof.2.incr.aof作为增量文件。
# - appendonly.aof.manifest作为清单文件。
appendfilename "appendonly.aof"
#Redis为了方便起见,将所有持久化的追加文件存储在一个专用的目录中。目录的名称由appenddirname配置参数确定。
appenddirname "appendonlydir"
  • BASE:表示基础AOF,它一般由子进程通过重写产生,该文件最多只有一个。
  • INCR:表示增量AOF,它一般会在AOFRW开始执行时被创建,该文件可能存在多个。
  • HISTORY:表示历史AOF,它由BASE和INCR AOF变化而来,每次AOFRW成功完成时,本次AOFRW之前对应的BASE和INCR AOF都将变为HISTORY,HISTORY类型的AOF会被Redis自动删除

为了管理这些AOF文件,Redis引入了mainfest(清单)文件来跟踪、管理这些AOF。同时,为了便于AOF备份和拷贝,将所有的AOF文件和mainfest文件放入一个单独的文件目录中,目录名可以在配置文件中的appenddirname 配置。

AOF的优缺点

优点

官方文档

  • 使用AOFRedis更加持久,可以有不同的fsync策略
    1. 完全不fsync
    2. 每秒fsync
    3. 每次查询时fsync
  • 使用每秒fsync的默认策略,写入的性能依然很好。fsync是使用后台线程执行的,当没有fsync正在进行时,主线程将努力执行写入因此最多会丢失一秒的写入。
  • 当AOF文件变大时,Redis会在后台自动重写AOF。重写是完全安全的,因为当Redis继续附加到旧文件时,会使用创建当前数据集所需的最少操作集生成一个全新的文件,一旦第二个文件准备就绪,Redis就会切换两者并开始附加到新的那一个。
  • AOF以易于理解和解析的格式依次包含所有操作的日志。甚至如果不小心执行了FLUSHALL操作,只要在这期间没有执行日志重写,那么这时候可以通过关闭服务器、删除AOF里面最新的FLUSHALL命令日志并重启即可恢复。

缺点

官方文档

  • AOF通常比相同数据集的RDB文件大。
  • 根据确切的fsync策略,AOF可能比RDB慢。一般来说,将fsync设置为每秒,性能仍然非常高,并且在禁用fsync的情况下,即使在高负载下他也应该与RDB一样快。即使在巨大的写入负载情况下,RDB仍然能够提供关于最大延迟的更多保证。

AOF的重写机制

由于AOF持久化时Redis不断将写命令记录到文件中,所以随着时间推移,AOF文件不可避免的会越来越大。为了解决这个问题,Redis引入了重写机制,当AOF
文件的大小超过所设定的峰值时,Redis就会自动启动AOF文件的内容压缩,或者也可以手动使用BGREWRITEAOF来重写,只保留可以恢复数据的最小指令集。

配置文件

# 自动重写append only file
# 当AOF日志文件大小按指定百分比增长时,Redis可以隐式地调用BGREWRITEAOF自动重写日志文件。
# 工作原理如下:Redis会记住最新重写后的AOF文件大小(如果在重启后没有进行重写,则使用启动时的AOF大小)。
# 这个基础大小将与当前大小进行比较。 如果当前大小大于指定百分比,则触发重写。此外,需要指定要重写AOF文件的最小大小,这可以避免即使达到百分比增长但仍然相当小的情况下重写AOF文件。
# 为了禁用自动AOF重写功能,请将百分比设置为零。
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

注意:同时满足上面两个配置才会触发
auto-aof-rewrite-percentage:根据上次重写后的AOF大小,判断当前AOF大小是不是增长了1倍。
auto-aof-rewrite-min-size:重写时满足的文件大小。
举个例子:现在有一个key为K1。分别执行下列命令:

  1. SET k1 v1
  2. SET k1 v2
  3. SET k1 v3
    如果没有重写,那么这三条命令都会保存在AOF为文件,不仅增加了磁盘占用,且在恢复的时候都要执行一遍。但是实际上只需要最后一条SET k1 v3 命令就足够了,所以重写后,只需要保存SET k1 v3就可以了。AOF重写不仅仅降低了文件的磁盘占用,同时更小的AOF文件也可以更快的被加载。

结论

AOF文件重写并不是对原文件进行重新整理,而是直接读取服务器现有的键值对,然后用一条命令去代替之前记录这个键值对的多条命令,生成一个新的文件后去替换原来的AOF文件。
AOF重写触发机制:通过redis.conf配置文件中的auto-aof-rewrite-percentage(默认为100),以及auto-aof-rewrite-min-size(默认为64mb)配置,也就是说Redis会记录上次重写时AOF大小,默认配置是当AOF文件大小是上一次rewrite后大小的一倍且文件大于64M时才会触发。

RDB和AOF混合模式

Redis 4.0中提出了一个混合使用 AOF 日志和内存快照的方法。简单来说,内存快照以一定的频率执行,在两次快照之间,使用 AOF 日志记录这期间的所有命令操作。

同时开启RDB和AOF时的加载顺序

yesnoyesnoRedis存在AOF?加载AOF存在RDB?加载RDB启动成功或者失败
  • Redis重启时会先看AOF是否存在,若存在,则优先加载AOF。如果加载成功那么Redis启动成功,反之会打印日志表示启动失败,此时可以去修复AOF文件后重新启动。
  • 若AOF文件不存在,Redis则会判断RDB是否存在,若存在则加载RDB,反之直接启动成功(此时没有数据)。

优先加载AOF是因为其保存的数据更完整,通过上面的分析我们知道AOF基本上最多损失1s的数据。
那为什么AOF比RDB完整,还要使用两者的混合呢?首先Redis作者建议不要,因为RDB更适合用于备份数据库(AOF一直再变化不好备份),留着RDB作为一个万一手段。

开启RDB和AOF的混合模式

在配置文件中设置aof-use-rdb-preambleyes,设置为no则禁用。

混合模式原理

先使用RDB进行快照存储,然后使用AOF记录所有的写操作,当重写策略满足或手动触发重写的时候,将最新的数据存储为新的RDB记录。这样重启服务的时候就会从RDB和AOF两部分恢复数据,既保证了数据完整性,又提高了恢复数据的性能。简单说就是混合模式产生的文件一部分是RDB格式,一部分是AOF格式。(AOF包括了RDB头部+AOF缩写)。

appendonly.aofRDB格式AOF格式