【redis】持久化:RDB和AOF

redis的持久化指将数据写入可靠内存中,如ssd。Redis提供了4种持久化策略

  • RDB:Redis Database,周期性的将某个时间点的数据集快照持久化
  • AOF:Append Only File,每次redis服务接收到写操作(修改内存的操作),都会将命令写入文件,这些操作可以在redis重启后被重新执行来重建数据集。这些指令的存储格式和redis本身的协议相同。
  • No persistence:不进行持久化,在某些缓存情况下使用。
  • RDB+AOF:两种持久化策略可以组合使用。

RDB优点

  • RDB文件是一个紧凑的单个文件,十分适合做备份。比如可以在24小时内每个小时都归档rdb文件,并且在30天内每天都保存rdb快照,这样在恢复的时候可以选择不同版本的数据。
  • RDB适合做灾备恢复,一个单个紧凑的文件很容易转储到其他数据中心,或者存储在云平台上。
  • RDB最大限度提升redis性能,在持久化过程种redis主进程只需要fork一个子进程,剩余的持久化工作都由子进程来完成,主进程永远不会执行磁盘I/O等操作。
  • 相比于AOF,RDB的重启恢复更快
  • 在主备模式下,RDB支持重启和故障切换后的部分重新同步。

RDB的缺点

  • 如果你想在redis故障的时候最小化数据丢失,RDB不适合。你可以设置不同的保存点(例如至少5分钟和100次写操作之后保存)。但是通常我们的保存间隔不会太短,所以redis宕机之后可能会丢失几分钟的数据。
  • RDB会经常使用fork来创建子进程。如果数据集过大的话fork可能会占用较多时间。如果数据集较大且cpu性能不好的话可能会造成redis的短暂不可用。AOF也需要fork但是频率较低。

AOF优点

  • AOF更加可靠,aof可以设置不同的fsync(刷盘)策略:1.不刷盘,缓冲区满自动刷盘,2.每秒刷盘,3.每次请求都刷盘。默认策略是每秒刷盘,写入性能依旧很高。fsync是由后台线程进行的,在未执行fsync的时候主线程会持续的执行写入,因此最多丢失1秒的数据。
  • AOF是追加日志文件,因此不需要磁盘寻址,断电等情况下也不会造成数据错乱。即使日志中最后保存的指令只写入了一半,redis-check-aof也能修复。
  • 当AOF文件过大的时候,redis会在后台自动重写AOF文件。AOF重写是完全安全的,因为在创建新aof文件(根据当前数据集)的同时,redis还会持续写入到旧文件,一旦写入完成redis就会切换两个文件,并开始在新文件上追加日志。
  • AOF文件包含一条接一条的指令,这些指令非常容易理解和解析,也很容易导出。如果你不小心flushall了所有数据,只要还没发生重写,你就可以停掉redis,并删除aof文件最后的flushall指令,然后重启redis就可以恢复所有数据。

AOF缺点

  • AOF通常比RDB更大
  • 根据刷盘策略的不同,AOF可能比RDB更慢。通常设置每秒刷盘的情况下性能依旧很高,如果设置不刷盘则理论上和RDB一样快,即使是在高负载下。但是RDB可以保证最大延迟。
如果redis版本<7.0
  • AOF可能占用很多内存,在重写时新的写入指令会保存在内存缓存中,并在新文件创建完成后追加到末尾。
  • 重写期间所有的写入指令都会在磁盘上写入两次。
  • redis可能会停止写入并将这些写入指令刷到新的aof文件中。

应该使用哪种

一般情况下,如果你希望有PostgreSql一样的数据安全性的话,你应该两种都使用。

如果你很关心你的数据,但是可以忍受几分钟的数据丢失的话,可以单独使用RDB。

很多用户单独使用AOF,但是我们不建议这样做。因为用RDB做数据备份、快速启动恢复很好,也可以防止AOF引擎出现错误。

快照

默认情况下,RDB将数据集快照存储在磁盘上的dump.rdb文件中。你可以设置rdb在至少有M个key发生变化时每N秒保存一次,或者可以使用save或bgsave命令手动调用保存。

例如,这个命令表示如果至少有1000个key发生变化,每60秒保存一次

save 60 1000

实现原理

  • fork一个子进程
  • 子进程开始将数据写入到临时文件中
  • 当子进程完成新的rdb文件的写入后,用新文件替换旧文件

这种方法可以让redis使用写时复制技术。

写时复制指的是对于一个文件对象,不同的进程映射到虚拟地址空间的都是同一个物理内存,只有某个进程试图修改数据时,才会在物理内存中拷贝一份数据。

追加文件

快照并不是很可靠。如果你的redis服务停止了,或者断电了,或者你不小心杀掉了进程,那么最近的数据就会丢失。虽然对某些应用来说问题不大,但是有些应用却不能接受,所以单独使用RDB不得行。

AOF是一个完全可靠的持久化方案,在1.1版本中推出。

你可以在配置文件中打开AOF

appendonly yes

现在开始,每次redis接收到一个写指令的时候都会追加到aof文件中。当你重启redis的时候就会重新执行aof文件来恢复到之前的状态。

从7.0开始,redis使用多个aof文件机制。原来的一个aof文件会分割为一个base文件(最多一个)和incremental文件(增量文件,可能有多个)。base文件代表重写时的最初的数据集快照,增量文件保存了base文件创建之后追加的指令。这些文件分布在不同的目录下并且被manifest文件追踪。

日志重写

随着写操作的增加,aof文件会越来越大。比如我们执行了100次的自增操作,那么redis中只有一个key,但是aof中有100条指令。其中99条都是无用的。

重写过程是绝对安全的。在重写时redis会持续写入旧文件,并根据当前数据集创建一个最小指令的新文件。一旦新文件准备就绪,redis就会切换两个文件,并在开始在新文件后追加指令。

所以redis提供一个有趣的特性:它能够在后台重写AOF而不中断对客户端的服务。当你执行bgrewriteaof命令时,redis会根据当前数据集创建一个最小化的aof文件。如果你使用的是2.2版本,那么你需要时不时的调用bgrewriteaof。从2.4开始redis会自动重写aof。

从7.0版本开始,当AOF重写被调度的时候,父进程会开启一个增量文件并将接下来的指令写入进去,子进程会执行重写逻辑并生成一个新的base文件。redis会用临时的manifest文件来追踪新创建的base和增量文件。当这些文件准备好的时候,redis会执行一个原子性的替换操作来让临时manifest文件生效。为了避免在AOF重写重复失败和重试时创建许多增量文件的问题,Redis引入了AOF重写限制机制,以确保以越来越慢的速度重试失败的AOF重写。

AOF的持久性

你可以设置fsync的策略,有3种模式:

  1. always:每次新指令追加到aof文件时都执行fsync。这种模式很慢也很安全。在从多个客户端或pipeline接收到一系列指令之后,这写指令会追加到aof种,这意味着只有一次写入和同步(在同步给副本之前)
  2. everysecond:每秒同步一次。足够快(在2.4版本之后可以与快照一样快),你可能最多会丢失1秒的数据
  3. no:从不fsync,数据都交给操作系统处理。最快且最不安全的模式。正常情况下linux会30秒同步一次,取决于内核

推荐的和默认的模式是每秒同步。速度和安全性都有保障。always策略在实践表现很慢,但是支持分组提交,redis会执行单次fsync。

如果AOF被truncate怎么办

这可能发生在重写时redis崩溃,或者重写时aof文件所在的磁盘满了。当这种情况发生时,AOF仍然包含表示数据集的给定时间点版本的一致数据(使用默认AOF fsync策略,该数据集可能早到一秒),但AOF中的最后一个命令可能会被截断。Redis的最新主要版本无论如何都可以加载AOF,只需丢弃文件中最后一个格式不正确的命令即可。在这种情况下,服务器将发出如下日志:

* Reading RDB preamble from AOF file...
* Reading the remaining AOF tail...
# !!! Warning: short read while loading the AOF file !!!
# !!! Truncating the AOF at offset 439 !!!
# AOF loaded anyway because aof-load-truncated is enabled

如果AOF文件被破坏了怎么办

如果aof文件的字节序列在中间发生错误,情况会比truncate更复杂。redis会在启动时报出:

* Reading the remaining AOF tail...
# Bad file format reading the append only file: make a backup of your AOF file, then use ./redis-check-aof --fix <filename>

最好的办法是运行redis-check-aof,不使用–fix选项。然后分析问题,找到给出的文件offset处,并查看是否可以手动修复aof文件:aof文件使用和redis一样的协议,很容易理解和修复。否则可以让工具为我们修复,但是这可能丢失从错误开始到文件结尾的部分,如果错误发生在很靠前的位置,可能丢失大量的数据。

实现原理

aof重写和快照一样使用了写时复制技术:

7.0版本后
  • fork创建子进程
  • 子进程开始写入新的base文件到一个临时文件中
  • 父进程打开一个新的增量文件并将接下来的指令写入。如果重写失败,则旧base+旧增量+新增量文件代表全部的数据,因此是安全的。
  • 如果子进程重写完成,父进程会接收到一个信号,使用新打开的增量文件和子进程创建的base文件来构建一个临时的manifest文件并持久化。
  • redis对manifest文件做一个原子性的替换操作,新的aof文件生效。redis也会清理旧的base文件和用不到的增量文件。
7.0版本前
  • fork子进程
  • 子进程开始重写新的aof文件到临时文件
  • 父进程将接收到的指令写入内存缓冲区和旧的aof文件中,如果重写失败的话数据也是安全的。
  • 当子进程重写完成,父进程收到一个信号,将缓冲区中的数据同步到新创建的aof文件中。
  • redis原子性的将新文件重命名为旧文件,并开始向新文件中追加指令。

如果我正在使用RDB快照,如何切换到AOF

这个问题在2.2之前和之后的版本不一样,可以理解为2.2版本之后更简单且完全不需要重启。

2.2版本后
  • 创建一个dump.rdb文件的备份
  • 把这个备份保存到安全的地方
  • 执行下面的命令
  • redis-cli config set appendonly yes
  • redis-cli config set save “”
  • 确保数据库中的key数量和aof中的key数量相同
  • 确保新的指令正确的追加到aof文件中

第一条命令打开aof
第二条命令关闭快照持久化,这个命令的是可选的,如果你希望使用两种策略的话。
PS:不要忘记修改配置文件,否则重启之后上面的配置会失效并依然使用旧的配置文件中的配置。

2.0版本
  • 创建一个dump.rdb文件的备份
  • 把这个备份保存到安全的地方
  • 停止服务的所有写入指令
  • 执行redis-cli BGREWRITEAOF,这回创建aof文件
  • 生成完aof文件后停止redis服务
  • 编辑redis.conf文件开启aof
  • 重启服务
  • 确保包含的key和修改前的一致
  • 确保新的指令正确的追加到aof文件中

RDB和APOF的影响

redis以后的版本会避免rdb执行期间触发aof重写,或者在aof重写时使用bgsave保存rdb。这可以避免redis的后台进程执行过重的磁盘I/O。

当后台正在执行rbd快照保存,用户明确使用bgrewriteaof指令时,服务会返回ok告诉用户重写操作已加入调度,一旦rdb完成就会立刻执行aof。

如果AOF和RDB都打开了,redis重启时会使用aof来重新构建数据,因为aof能保证数据更完整。

备份redis数据

redis的备份功能很友好,因为redis运行时可以复制rdb文件:rdb一旦创建就不会再更改,新的rdb会保存再临时文件里,新的文件准备就绪时就会原子性的重命名为旧的文件名。

这意味这redis运行时复制rdb文件是安全的,我们建议:

  • 创建一个定时任务,每小时都保存一个rdb快照到一个目录下,然后每天保存一个rdb快照到另一个目录下。
  • 每次定时脚本运行时,确保调用find命令来保证过旧的rdb文件被删除:例如你可以48小时内每小时都保存快照,且1-2个月内每天都保存快照。确保用日期和时间来命名快照。
  • 每天至少将RDB快照转移一次,至少不能在redis运行的物理机中。

备份aof

如果你的redis只使用了aof,也可以备份。7.0开始,aof分为多个文件,这些文件保存在appenddirname配置的目录下。通常情况下你需要做的就是复制或者打包这些文件作为备份。使用这种方式备份要确保备份时关闭aof重写:

  • 关闭aof重写命令:
    CONFIG SET auto-aof-rewrite-percentage 0
    确保不会手动调用aof重写(bgrewriteaof)
  • 检查是否正在重写
    INFO persistence
    并且确定aof_rewrite_in_progress为0,如果为1意味着正在重写,需要等一会
  • 现在可以安全的复制目录下的文件
  • 重新打开aof
    CONFIG SET auto-aof-rewrite-percentage <prev-value>

原文档:redis持久化

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值