深入理解Redis的持久化

1,360 阅读15分钟

Redis持久化

Java大猿帅成长手册,GitHub JavaEgg ,N线互联网开发必备技能兵器谱

Redis 的数据全部在内存里,如果突然宕机,数据就会全部丢失,因此必须有一种机制来保证 Redis 的数据不会因为故障而丢失,这种机制就是 Redis 的持久化机制。

Redis有两种持久化的方式:快照(RDB文件)和追加式文件(AOF文件)

RDB(Redis DataBase)

是什么

在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里。

Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能,如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那 RDB 方式要比 AOF 方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。

> What ? Redis 不是单进程的吗?

Redis 使用操作系统的多进程 COW(Copy On Write) 机制来实现快照持久化, fork是类Unix操作系统上创建进程的主要方法。COW(Copy On Write)是计算机编程中使用的一种优化策略。

Fork

fork 的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等)数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程。 子进程读取数据,然后序列化写到磁盘中

rdb 默认保存的是dump.rdb文件

你可以对 Redis 进行设置, 让它在“ N 秒内数据集至少有 M 个改动”这一条件被满足时, 自动保存一次数据集。

你也可以通过调用 SAVE 或者 BGSAVE , 手动让 Redis 进行数据集保存操作。

比如说, 以下设置会让 Redis 在满足“ 60 秒内有至少有 1000 个键被改动”这一条件时, 自动保存一次数据集:

save 60 1000

这种持久化方式被称为快照(snapshot)。

配置位置: SNAPSHOTTING

redis-snapshotting.png

如何触发RDB快照

  • 配置文件中默认的快照配置

    冷拷贝后重新使用 可以cp dump.rdb dump_new.rdb

  • 命令save或者是bgsave

    • Save:save时只管保存,其它不管,全部阻塞
    • BGSAVE:Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求。可以通过lastsave命令获取最后一次成功执行快照的时间
    • 执行flushall命令,也会产生dump.rdb文件,但里面是空的,无意义

快照的运作方式

当 Redis 需要保存 dump.rdb 文件时, 服务器执行以下操作:

  1. Redis 调用 fork() ,产生一个子进程,此时同时拥有父进程和子进程。
  2. 子进程将数据集写入到一个临时 RDB 文件中。
  3. 当子进程完成对新 RDB 文件的写入时,Redis 用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。

这种工作方式使得 Redis 可以从写时复制(copy-on-write)机制中获益。

如何恢复

将备份文件 (dump.rdb) 移动到 redis 安装目录并启动服务即可(CONFIG GET dir获取目录)

优势

  • 一旦采用该方式,那么你的整个Redis数据库将只包含一个文件,这对于文件备份而言是非常完美的。比如,你可能打算每个小时归档一次最近24小时的数据,同时还要每天归档一次最近30天的数据。通过这样的备份策略,一旦系统出现灾难性故障,我们可以非常容易的进行恢复。适合大规模的数据恢复
  • 对于灾难恢复而言,RDB是非常不错的选择。因为我们可以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上。
  • 性能最大化。对于Redis的服务进程而言,在开始持久化时,它唯一需要做的只是fork出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行IO操作了。
  • 相比于AOF机制,如果数据集很大,RDB的启动效率会更高。

劣势

  • 如果你想保证数据的高可用性,即最大限度的避免数据丢失,那么RDB将不是一个很好的选择。因为系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失(丢失最后一次快照后的所有修改)。
  • 由于RDB是通过fork子进程来协助完成数据持久化工作的,内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟。

如何停止

动态停止RDB保存规则的方法:redis-cli config set save ""

总结

redis-rdb.png

  • RDB是一个非常紧凑的文件

  • RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO操作,所以RDB持久化方式可以最大化redis的性能

  • 与AOF相比,在恢复大的数据集的时候,RDB方式会更快一些

  • 数据丢失风险大

  • RDB需要经常fork子进程来保存数据集到硬盘上,当数据集比较大的时候,fork的过程是非常耗时的,可能会导致redis在一些毫秒级不能响应客户端的请求

AOF(Append Only File)

是什么

以日志的形式来记录每个写操作,将 Redis 执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis 启动之初会读取该文件重新构建数据,也就是「重放」。换言之,redis 重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。

AOF 默认保存的是 **appendonly.aof ** 文件

配置位置: APPEND ONLY MODE

redis-aof-conf.jpg

AOF启动/修复/恢复

  • 正常恢复

    • 启动:设置Yes 修改默认的appendonly no,改为yes
    • 将有数据的 aof 文件复制一份保存到对应目录(config get dir)
    • 恢复:重启redis然后重新加载
  • 异常恢复

    • 启动:设置Yes 修改默认的appendonly no,改为yes
    • 备份被写坏的AOF文件
    • 修复:redis-check-aof --fix进行修复 + AOF文件
    • 恢复:重启redis然后重新加载

rewrite(AOF 重写)

  • 是什么:AOF采用文件追加方式,文件会越来越大为避免出现此种情况,新增了重写机制,当 AOF 文件的大小超过所设定的阈值时,Redis就会启动 AOF 文件的内容压缩,只保留可以恢复数据的最小指令集,可以使用命令bgrewriteaof
  • 重写原理:AOF 文件持续增长而过大时,会 fork 出一条新进程来将文件重写(也是先写临时文件最后再rename),遍历新进程的内存中数据,每条记录有一条的 Set 语句。重写 aof 文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的 aof 文件,这点和快照有点类似
  • 触发机制:Redis 会记录上次重写时的 AOF 大小,默认配置是当 AOF 文件大小是上次 rewrite 后大小的一倍且文件大于64M 时触发

AOF耐久性

你可以配置 Redis 多久才将数据 fsync 到磁盘一次。

有三个选项:

  • 每次有新命令追加到 AOF 文件时就执行一次 fsync :非常慢,也非常安全。
  • 每秒 fsync 一次:足够快(和使用 RDB 持久化差不多),并且在故障时只会丢失 1 秒钟的数据。
  • 从不 fsync :将数据交给操作系统来处理。更快,也更不安全的选择。

推荐(并且也是默认)的措施为每秒 fsync 一次, 这种 fsync 策略可以兼顾速度和安全性。

总是 fsync 的策略在实际使用中非常慢,频繁调用 fsync 注定了这种策略不可能快得起来。

如果 AOF 文件出错了,怎么办?

服务器可能在程序正在对 AOF 文件进行写入时停机, 如果停机造成了 AOF 文件出错(corrupt), 那么 Redis 在重启时会拒绝载入这个 AOF 文件, 从而确保数据的一致性不会被破坏。

当发生这种情况时, 可以用以下方法来修复出错的 AOF 文件:

  1. 为现有的 AOF 文件创建一个备份。
  2. 使用 Redis 附带的 redis-check-aof 程序,对原来的 AOF 文件进行修复。

$ redis-check-aof --fix

  1. (可选)使用 diff -u 对比修复后的 AOF 文件和原始 AOF 文件的备份,查看两个文件之间的不同之处。
  2. 重启 Redis 服务器,等待服务器载入修复后的 AOF 文件,并进行数据恢复。

AOF运作方式

AOF 重写和 RDB 创建快照一样,都巧妙地利用了写时复制机制。

以下是 AOF 重写的执行步骤:

  1. Redis 执行 fork() ,现在同时拥有父进程和子进程。
  2. 子进程开始将新 AOF 文件的内容写入到临时文件。
  3. 对于所有新执行的写入命令,父进程一边将它们累积到一个内存缓存中,一边将这些改动追加到现有 AOF 文件的末尾: 这样即使在重写的中途发生停机,现有的 AOF 文件也还是安全的。
  4. 当子进程完成重写工作时,它给父进程发送一个信号,父进程在接收到信号之后,将内存缓存中的所有数据追加到新 AOF 文件的末尾。
  5. 搞定!现在 Redis 原子地用新文件替换旧文件,之后所有命令都会直接追加到新 AOF 文件的末尾。

优势

  • 该机制可以带来更高的数据安全性,即数据持久性。Redis中提供了3种同步策略,即每秒同步、每修改同步和不同步。事实上,每秒同步也是异步完成的,其效率也是非常高的,所差的是一旦系统出现宕机现象,那么这一秒钟之内修改的数据将会丢失。而每修改同步,我们可以将其视为同步持久化,即每次发生的数据变化都会被立即记录到磁盘中。可以预见,这种方式在效率上是最低的。至于无同步,无需多言,我想大家都能正确的理解它。
  • 由于该机制对日志文件的写入操作采用的是append模式,因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容。然而如果我们本次操作只是写入了一半数据就出现了系统崩溃问题,不用担心,在Redis下一次启动之前,我们可以通过redis-check-aof工具来帮助我们解决数据一致性的问题。
  • 如果日志过大,Redis可以自动启用rewrite机制。即Redis以append模式不断的将修改数据写入到老的磁盘文件中,同时Redis还会创建一个新的文件用于记录此期间有哪些修改命令被执行。因此在进行rewrite切换时可以更好的保证数据安全性。
  • AOF 包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作。事实上,我们也可以通过该文件完成数据的重建。因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。 导出(export) AOF 文件也非常简单: 举个例子, 如果你不小心执行了 FLUSHALL 命令, 但只要 AOF 文件未被重写, 那么只要停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就可以将数据集恢复到 FLUSHALL 执行之前的状态。

劣势

  • 对于相同数量的数据集而言,AOF文件通常要大于RDB文件。恢复速度慢于rdb。
  • 根据同步策略的不同,AOF在运行效率上往往会慢于RDB。总之,每秒同步策略的效率是比较高的,同步禁用策略的效率和RDB一样高效。

总结

redis-aof.png

  • AOF 文件是一个只进行追加的日志文件
  • Redis 可以在 AOF 文件体积变得过大时,自动在后台对 AOF 进行重写
  • AOF文件有序的保存了对数据库执行的所有写入操作,这些写入操作以Redis协议的格式保存,因此AOF文件的内容非常容易被人读懂,对文件进行分析也很轻松
  • 对于相同的数据集来说,AOF 文件的体积通常需要大于 RDB 文件的体积
  • 根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB

怎么从 RDB 持久化切换到 AOF 持久化

在 Redis 2.2 或以上版本,可以在不重启的情况下,从 RDB 切换到 AOF :

  1. 为最新的 dump.rdb 文件创建一个备份。
  2. 将备份放到一个安全的地方。
  3. 执行以下两条命令:
 redis-cli> CONFIG SET appendonly yes 	

 redis-cli> CONFIG SET save "" 
  1. 确保命令执行之后,数据库的键的数量没有改变。
  2. 确保写命令会被正确地追加到 AOF 文件的末尾。

步骤 3 执行的第一条命令开启了 AOF 功能: Redis 会阻塞直到初始 AOF 文件创建完成为止, 之后 Redis 会继续处理命令请求, 并开始将写入命令追加到 AOF 文件末尾。

步骤 3 执行的第二条命令用于关闭 RDB 功能。 这一步是可选的, 如果你愿意的话, 也可以同时使用 RDB 和 AOF 这两种持久化功能。

别忘了在 redis.conf 中打开 AOF 功能! 否则的话, 服务器重启之后, 之前通过 CONFIG SET 设置的配置就会被遗忘, 程序会按原来的配置来启动服务器。

Which one

  • RDB 持久化方式能够在指定的时间间隔能对你的数据进行快照存储

  • AOF 持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以 redis 协议追加保存每次写的操作到文件末尾。Redis还能对AOF文件进行后台重写(bgrewriteaof),使得 AOF 文件的体积不至于过大

  • 只做缓存:如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式。

  • 同时开启两种持久化方式

    • 在这种情况下,当 redis 重启的时候会优先载入 AOF 文件来恢复原始的数据,因为在通常情况下 AOF 文件保存的数据集要比 RDB 文件保存的数据集要完整。
    • RDB 的数据不实时,同时使用两者时服务器重启也只会找 AOF 文件。那要不要只使用AOF 呢?建议不要,因为 RDB 更适合用于备份数据库(AOF 在不断变化不好备份),快速重启,而且不会有 AOF 可能潜在的bug,留着作为一个万一的手段。

性能建议

  • 因为 RDB 文件只用作后备用途,建议只在 Slave上持久化 RDB 文件,而且只要15分钟备份一次就够了,只保留save 900 1这条规则。
  • 如果Enalbe AOF,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的 AOF 文件就可以了。代价一是带来了持续的 IO,二是 AOF rewrite 的最后将 rewrite 过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少 AOF rewrite 的频率,AOF 重写的基础大小默认值64M太小了,可以设到5G以上。默认超过原大小100%大小时重写可以改到适当的数值。
  • 如果不 Enable AOF ,仅靠 Master-Slave Replication 实现高可用性也可以。能省掉一大笔 IO ,也减少了rewrite 时带来的系统波动。代价是如果 Master/Slav e同时宕掉,会丢失十几分钟的数据,启动脚本也要比较两个Master/Slave中的RDB文件,载入较新的那个。

Redis Persistence

某免费教学视频