Redis 4.x系列(十六):Redis 持久化之 RDB 与 AOF

  Redis 是在内存在存储数据,当服务器重启则会丢失内存中的数据。 为保证数据安全, Redis 提供了对数据持久化的支持,数据持久化是防止数据丢失的最好方法。

  Redis 共有两个数据持久化方式:RDBAOF。持久化功能有效地避免因进程退出造成数据丢失问题,当下次重启时利用之前持久化的文件即可实现数据恢复。

  Redis Persistence 官网Redis persistence demystified(Redis 持久化揭秘)Redis 持久化 –中文译

Reids 提供了 RDBAOF 两种持久化方式,在同一实例中还可组合使用AOFRDB

RDB 并不能保证数据的强一致性,通常使用 AOF 来重建原始数据,因为它保证是最完整的。

RDB

RDB(redis data base):按指定的时间间隔将数据生成时间点快照(snapshot)保存到硬盘。 Redis 默认启用 RDB 方式来持久化数据。

RDB 配置

可在 redis.conf 文件中设置 save 参数,也可调用CONFIG SET命令来动态设置 RDB 持久化。

  1. 快照配置

    save <seconds> <changes>
    如果同时给定了时间秒数(seconds) 和定入操作数(changes) ,将保存数据库。

    执行生成快照时间间隔的默认配置参数:
    save 900 1 //在 900秒(15分钟)之内,至少 1 个键发生改变
    save 300 10 //在 300秒(5分钟)之内,至少 10 个键发生改变
    save 60 10000 //在 60秒(1分钟)之内,至少 10000 个键发生改变

    可注释掉所有 save 参数来完全禁用 RDB 持久化。 可以设置save的值为单个空字符清空持久化配置(也相当于禁用持久化)。
  2. stop-writes-on-bgsave-error yes
    默认情况下,如果启用了 RDB 快照并且最新的后台保存失败,Redis 将停止接写入,目的是为了让用户意识到可能出现了错误。如果后台保存继承开始工作,Redis 将自动再次允许写入。
    如果已设置了对 Redis 服务器和持久化的监视,则可以禁用此功能(no),即使持久化因为磁盘或权等问题,Redis 也可以继续工作。
  3. rdbcompression yes
    RDB 快照文件默认使用 LZF 压缩字符串对像。若设置为no不压缩,则 RDB 文件会比较大。
  4. rdbchecksum yes
    RDB v5.0 版本开始,在 RDB 文件末尾存放了CRC64校验和来校验数据的完整性,但在保存和加载 RDB 文件时会多消耗大约 10% 的性能。可以设置为 no 禁用它获得最好的性能。当禁用校验和后,创建 RDB 文件的校验和为零,加载 RDB 文件就会跳过检查。
  5. dbfilename dump.rdb
    设置快照文件名,默认是 dump.rdb
  6. dir .
    设置快照文件存储目录,默认是当前目录

RDB 恢复

RDB 快照文件也可以用作数据备份文件。在Linux 系统可使用 crontab 命令定时将 RDB文件备份到本地目录或远程分布式文件系统中,供日后恢复使用。

cp dump.rdb /data/backup/redis/dump.$(date +%Y%m%d%H%M).rdb

若要使用快照文件恢复,需要把快照文件复制到 dir 参数指定的目录,并且保证启动 Redis 实例的用户有该文件的读/写权限。接下来使用shutdown nosave命令停止实例,并将复制的快照文件重命名为dbfilename指定的文件名。重新启动 Redis 服务,数据就会从备份文件中加载并还原回 Redis。

RDB 快照文件可通过 Redis 提供的RDB检查工具(redis-check-rdb)来查看快照信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#./redis-check-rdb dump.rdb 
[offset 0] Checking RDB file dump.rdb
[offset 26] AUX FIELD redis-ver = '4.0.9'
[offset 40] AUX FIELD redis-bits = '64'
[offset 52] AUX FIELD ctime = '1542179624'
[offset 67] AUX FIELD used-mem = '1917160'
[offset 85] AUX FIELD repl-stream-db = '0'
[offset 135] AUX FIELD repl-id = '33b06e6495b743ed3d09eb6e870c2a16af41cf8a'
[offset 151] AUX FIELD repl-offset = '199'
[offset 167] AUX FIELD aof-preamble = '0'
[offset 169] Selecting DB ID 0
[offset 225] Checksum OK
[offset 225] \o/ RDB looks OK! \o/
[info] 3 keys read
[info] 1 expires
[info] 0 already expired

RDB 优点

  1. RDB 是一个非常紧凑单一文件,保存某个时间点数据集。
  2. 非常适合备份和灾难恢复。例如定期归档保存在远程数据中心,出现灾难时可以轻松恢复同不时间点的数据集。
  3. RDB 最大限度地提高了 Redis 性能。因为 Redis 父进程只需创建子进程,不需操作磁盘I/O,由子进程完成快照操作,主进程继续做自己的事。
  4. 与 AOF 相比,RDB 允许使用大数据集更快地重启。

RDB 缺点

  1. RDB 自动执行根据时间间隔和频率来执行的,如果 Redis 服务异常停止工作(例如断电,进程被强杀),则可能会丢失最新几分钟的数据。
  2. RDB 需要经常 **fork()创建子进程来持久化数据,如果数据非常大且设备性能底,fork()**可能会很耗时,甚至可能导致 Redis 停止客服几毫秒或一秒。

RDB 文件

RDB文件
上图是将 RDB 文件是用十六进制打开。

RDB 文件开头就显示了 RDB格式的版本号(REDIS0008),后面紧跟保存的是 RDB 文件中的 8 种元数据。

  1. redis-ver:Redis 实例的版本。
  2. redis-bits:运行 Redis 实例的主机架构,64位或32位。
  3. ctime:RDB 创建时的 Unix 时间戳。
  4. used-mem:转存储时使用的内存大小。
  5. repl-stream-db:redis 的 db 索引。
  6. repl-id:主实例的ID(replication id)。
  7. repl-offset:主实例的偏称量(replication offset)。
  8. aof-preamble:是否在 AOF 文件头放置 RDB 快照(即开启混合持久化)。

RDB 文件中的 FA是一个辅助操作编码,用于分隔前后键值对,05基本上对的键和值的分隔符(.), 文件尾以 EOF 和 CRC64校验和结束。

可在 GitHub 上搜索 Redis RDB,可看到 RDB文件详细格式和 RDB文件 转换工具等。

AOF

AOF(append only file):以日志的方式追加 Redis 每个写入操作,服务重启时再重新执行AOF文件中的命令来重建原始数据。
AOF主要作用解决了数据持久化的实时性,目前已经是Redis持久化的主流方式。

AOF 配置

Redis 的 AOF 配置在 redis.conf 配置文件 [APPEND ONLY MODE] 段落设置。

  1. appendonly no
    是否开启 AOF 持久化数据, 开启=yes,禁用=no。可动态开启 AOF 持久化(config set appendonly yes)

  2. appendfilename “appendonly.aof”
    AOF 文件名,默认是 appendonly.aof

  3. appendfsync everysec
    追加写入数据到磁盘的模式,默认是每秒写入(everysec)。Redis 支持三种模式。

    **no(none):从不调用 fsync, 交由操作系统决定何时刷新数据(写入磁盘),大多数Linux 系统中,这个频率是 30 秒。速度快,但不安全。
    always:每次写入就调用 fsync() 将数据追加到日志文件。 速度慢,最安全。
    everysec:每秒调用一次
    fsync()**,默认值。兼顾速度和安全, 足够快(和RDB持久化差不多),故障时只会丢失 1 秒钟数据。

    Redis AOF 是通过调用 fsync()函数同步内存中所有已修改的文件数据到磁盘,有的操作系统会立即执行,有的操作系统会尽快尝试执行。调用fsync不会等缓冲区的数据达到一定量时再来写入。

    内存数据写入到磁盘文件,底层完全是由操作系统控制的。 操作系统会维护一个缓冲区,Redis 命令首先会被写入该缓冲区,而缓冲区的数据必须被刷新到磁盘才能完成持久化。这个过程是通过 Linux 系统调用 **fsync()**,这是个阻塞调用,只有磁盘设备报告缓冲区的数据写入完成后才会返回。

  4. no-appendfsync-on-rewrite no
    当有子进程正在执行持久化时,AOF 再次写入的模式改为 no 模式,以防止再次调用**fsync()**被阻塞很长时间。建议保留默认设置。

当 AOF fsync 策略为 **always**或**everysec** 方式,后台保存数据对磁盘扫许大量的 **I/O**时,在某些 Linux 配置中,Redis 可能会阻塞较长时间的 **fsync()** 再次调用,此问题暂未修改,所以才有 **no-appendfsync-on-rewrite no** 选项配置,防正在**bgsave**或**bgrewriteaof**正在进行时主进程调用 **fsync()**, 即交由操作系统决定何时同步内存数据到 AOF 文件,但此方式在最糟糕的情况下(默认的 Linux 设置)可能会丢失 **30s** 的日志数据。
  1. AOF 文件重写(重建)
    Redis 支持自动重写 AOF 文件以压缩文件大小(剔除过期数据,相同的键只保留最新的数据)。因为 AOF 是以追加日志的方式写入文件,文件大小可能会快速增长,会存在过期的数据,某些键的值被多次修改,但只有最新的数据才是有效的需要存储在AOF文件中,并且且当 AOF 文件非常大时,Redis 启动重建数据就会非常缓慢,这就有了 AOF 重写策略,重写后的 AOF 文件只保留包含重键当前数据的最少命令。

    auto-aof-rewrite-percentage 100
    auto-aof-rewrite-min-size 64mb
    以上两项是一组配置,用于配置自动触发重写 AOF 文件。 Redis 会记住最近一次重写 AOF 文件的大小做为基准值,如果当前** AOF 大于基准值达到百分比(percentage)就触发自动重建。还需要设置一个最小值(min-size),这样可以避免小数据情况下频繁重建而影响性能。
    整体解读:如果
    AOF文件大于(min-size),并且当前 AOF 大于上次重写的大小达到(percentage),就触发自动重写。
    如果 auto-aof-rewrite-percentage 的值设置为
    0则禁用重写 AOF文件。
    也可以执行
    bgrewriteaof 手动触发重写 AOF **。

  2. aof-load-truncated yes
    当 Redis 服务崩溃,特别是在没有设置 data = ordered选项的情况下挂载 ext4 文件系统时,可能出现 AOF 文件末尾被截断的异常。

    Redis 启动过程加载 AOF 数据到内存,可能发现 AOF 文件末尾被截断的处理方式。设置为 yes,表示会加载截断的 AOF 文件,加载能够加载的数据到内存中,Redis 会记录日志;如果设置为 no,则服务中止拒绝启动并显示错误,用户需要使用 redis-check-aof 修复 AOF 文件,再启动 Redis 服务加载数据到内存。

  3. aof-use-rdb-preamble no
    AOF模式下是否同时启动RDB,默认是** no **。

AOF 文件

Redis 提供了检测 AOF 文件的命令工具:./redis-check-aof [--fix] <file.aof>

1
2
3
# ./redis-check-aof appendonly.aof 
AOF analyzed: size=96, ok_up_to=96, diff=0
AOF is valid

AOF 文件出现损坏,也可用此工具加--fix参数来修复

1
2
3
./redis-check-aof --fix appendonly.aof 
AOF analyzed: size=96, ok_up_to=96, diff=0
AOF is valid

**AOF **文件可直接使用 Linux 查看文件命令查看,文件内容以 Redis 协议格式显示。其中 SELECT 是选择 DB ,默认有 16 个DB(0-15)。

AOF 重写

AOF 重写使用了与生成快照同样的写时复制技术,执行重建的子进程不会占用与父进程相同数量的内存。

  1. Redis 调用 fork(), 会创建子进程,同时拥有了父子进程。
  2. 子进程开始把新的 AOF 数据写入到一个临时文件。
  3. 父进程继承响应请求,并将新的修改累积到内存缓冲区(aof_rewrite_buf_blocks), 同时追加到旧的 AOF 文件(现有在使用的)中,即使重写失败,AOF 数据也是安全的。
  4. 当子进程完成重写后,会向父进程发送一个信号,父进程获取这个信号后,将内存缓冲区的数据追加到子进程创建新 AOF 文件末尾。
  5. 这样, Redis 原子的用新文件替换旧文件,并将新数据追加到新文件中。

AOF 优点

  1. 数据完整性更优,可使用不同的fsyn策略,见appendfsync配置。
  2. AOF是追加日志方式写入数据,即使停电或磁盘满而没完全写入(末尾截断),很容易用** redis-check-aof **工具修复。
  3. 当 Redis 数据很大时,Reids 可以在后台自动重写 AOF, 并且是安全的,见【AOF 重写】。
  4. AOF 文件格格式易于理解和解析,可以编辑 AOF 文件中的命令来重建数据。

AOF 缺点

  1. AOF 文件通常比同一数据集的RDB文件大。
  2. 根据确切换** fsync 策略,AOF** 可能比 RDB 慢,在大量写入负载的情况下, RDB 延迟更优。
  3. 罕见错误(例如,有一个涉及阻塞命令,如BRPOPLPUSH,导致生成的AOF文件在重新加时与原始数据不完全相同的情况),极少出现。

个人注:2、3缺点几乎可以忽略

RDB 切换到 AOF

从 Redis 2.2 版本开始,支持在不重启 Redis 服务的情况下,将持久化方式从 RDB 切换到 AOF。见appendfsync配置。

  1. 手动触发生成最新的快照文件(dump.rdb),并进行备份。
  2. 执行开启 AOF 命令:config set appendonly yes
  3. 手动触发保存 AOF:config set save

执行config set appendonly yes,开启** AOF ,同时 Redis 会阻止生成 RDB 转存储,然后开启文件写入,将新的写入追加到 AOF文件。
执行
config set save**,Redis 会关闭 RDB 持久化方式。 如果同时启用,这项是可选的。

注意:动态切换 RDB 到 AOF,记得修改 redis.conf 文件开启 AOF,否则 Redis 服务重启仍使用旧的持久化(RDB)方式。

AOF/RDB混合持久化

Redis 4.0 开始支持混合持久化功能。
混合持久化:开启AOF 模式同时启用了 RDB(aof-use-rdb-preamble yes),这样重写的 AOF 文件由两部份组成:
【RDB 数据】【AOF 尾数据】
即开头使用 RDB 格式,RDB 的压缩格式可以实现更快速的重写和加载数据文件,同时也保留了 AOF 数据一致性的优点。

在混合模式下,Redis 启动时使用 AOF 文件来重建原始数据,在加载时会识别出 AOF 文件以 REDIS字符为前缀的RDB数据,然后继续加载 AOF部分数据,Redis 可以从混合持久化中获益。混合模式是在开启 AOF 基础上启用RDB,否则是无效的。

默认情况下,此配置是关闭的,避免格式切换时出现异外情况。另根据 Redis 作者的说明,为了防止 AOF 引擎的缺陷导致丢失数据,并不建议只使用 AOF

BGSAVEBGREWRITEAOF两个命令不能同时执行,是为了防止两个 Redis 后台进程同时对磁盘进行大量的 I/O 操作。如果其中一个命令正在执行,又显式调用了另一个命令,另一个命令会做为计划任务,等前一个命令执行完后执行。

持久化相关命令

  1. info persistence
    获取持久化详细信息
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    127.0.0.1:6380> info persistence
    # Persistence
    loading:0
    rdb_changes_since_last_save:0 //自上次持久化后修改的次数
    rdb_bgsave_in_progress:0 //是否正在执行 bgsave,1 表示是
    rdb_last_save_time:1542097646 //最后一次成功持久化的时间戳
    rdb_last_bgsave_status:ok //最后一次 bgsave 的状态
    rdb_last_bgsave_time_sec:0 //最后一次成功 bgsave 的持续时长
    rdb_current_bgsave_time_sec:-1 //正在执行 bgsave 持续时长
    rdb_last_cow_size:544768 //写时复制占用内存
    aof_enabled:0 //是否开启 aof, 1=是
    aof_rewrite_in_progress:0 //是否正在重写,1=是
    aof_rewrite_scheduled:0 //是否有重写计划,1=是
    aof_last_rewrite_time_sec:-1 //最后一次重写耗时
    aof_current_rewrite_time_sec:-1 //本次重写耗时
    aof_last_bgrewrite_status:ok //最后一次重写状态
    aof_last_write_status:ok //最后一次写状态
    aof_last_cow_size:0 //写时点用内存
  2. config set save
    动态设置持久化保存参数
    1
    127.0.0.1:6379> config set save "900 1 300 10 60 10000"
  3. config get save
    获取持久化保存信息
    1
    2
    3
    127.0.0.1:6379> config get save
    1) "save"
    2) "900 1 300 10 60 10000"
  4. save
    手动生成 RDB 快照,直接输入 save 命令,在 该命令是使用 Redis 主线程创建快照,会阻塞 redis-cli一段时间。
    1
    2
    127.0.0.1:6379> save
    OK
  5. bgsave
    执行非阻塞生成 RDB 快照, Redis 主进程会通过 fork() 系统调用创建一个子进程在后台执行转存储数据到一个名为 temp-.rdb 的临时文件,当转存储结束后,重命名临时文件为 dbfilename 定义的快照文件名,并覆盖旧的快照文件。
    1
    2
    127.0.0.1:6379> bgsave
    Background saving started
    bgsave命令的子进程会保存该命令被处理时间点的所有数据。采用了写时复制(Copy-On-Write,COW)机制,所以不需使用 Redis 服务器所占用的同等数量的内存。

其它参考:

  1. Linux 写时拷贝技术(Copy-On-Write)
  2. 标准C++类string的Copy-On-Write技术
  3. 借shared_ptr实现copy-on-write
  4. Copy On Write(写时复制)
  5. Java Copy-On-Write容器 详解
  6. 聊聊并发-Java中的Copy-On-Write容器

Redis 4.x系列(十六):Redis 持久化之 RDB 与 AOF

http://blog.gxitsky.com/2018/11/13/Redis-16-persistence-rdb-aof/

作者

光星

发布于

2018-11-13

更新于

2022-08-14

许可协议

评论