Redis 4.x系列(十六):Redis 持久化之 RDB 与 AOF
Redis 是在内存在存储数据,当服务器重启则会丢失内存中的数据。 为保证数据安全, Redis 提供了对数据持久化的支持,数据持久化是防止数据丢失的最好方法。
Redis 共有两个数据持久化方式:RDB
和AOF
。持久化功能有效地避免因进程退出造成数据丢失问题,当下次重启时利用之前持久化的文件即可实现数据恢复。
Redis Persistence 官网,Redis persistence demystified(Redis 持久化揭秘),Redis 持久化 –中文译
Reids 提供了 RDB 和 AOF 两种持久化方式,在同一实例中还可组合使用AOF和RDB。
RDB 并不能保证数据的强一致性,通常使用 AOF 来重建原始数据,因为它保证是最完整的。
RDB
RDB(redis data base)
:按指定的时间间隔将数据生成时间点快照(snapshot)保存到硬盘。 Redis 默认启用 RDB 方式来持久化数据。
RDB 配置
可在 redis.conf 文件中设置 save 参数,也可调用CONFIG SET命令来动态设置 RDB 持久化。
- 快照配置
save <seconds> <changes>
如果同时给定了时间秒数(seconds) 和定入操作数(changes) ,将保存数据库。执行生成快照时间间隔的默认配置参数:
save 900 1 //在 900秒(15分钟)之内,至少 1 个键发生改变
save 300 10 //在 300秒(5分钟)之内,至少 10 个键发生改变
save 60 10000 //在 60秒(1分钟)之内,至少 10000 个键发生改变 - stop-writes-on-bgsave-error yes
默认情况下,如果启用了 RDB 快照并且最新的后台保存失败,Redis 将停止接写入,目的是为了让用户意识到可能出现了错误。如果后台保存继承开始工作,Redis 将自动再次允许写入。
如果已设置了对 Redis 服务器和持久化的监视,则可以禁用此功能(no),即使持久化因为磁盘或权等问题,Redis 也可以继续工作。 - rdbcompression yes
RDB 快照文件默认使用 LZF 压缩字符串对像。若设置为no不压缩,则 RDB 文件会比较大。 - rdbchecksum yes
RDB v5.0 版本开始,在 RDB 文件末尾存放了CRC64校验和来校验数据的完整性,但在保存和加载 RDB 文件时会多消耗大约 10% 的性能。可以设置为 no 禁用它获得最好的性能。当禁用校验和后,创建 RDB 文件的校验和为零,加载 RDB 文件就会跳过检查。 - dbfilename dump.rdb
设置快照文件名,默认是 dump.rdb - 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 | #./redis-check-rdb dump.rdb |
RDB 优点
- RDB 是一个非常紧凑单一文件,保存某个时间点数据集。
- 非常适合备份和灾难恢复。例如定期归档保存在远程数据中心,出现灾难时可以轻松恢复同不时间点的数据集。
- RDB 最大限度地提高了 Redis 性能。因为 Redis 父进程只需创建子进程,不需操作磁盘I/O,由子进程完成快照操作,主进程继续做自己的事。
- 与 AOF 相比,RDB 允许使用大数据集更快地重启。
RDB 缺点
- RDB 自动执行根据时间间隔和频率来执行的,如果 Redis 服务异常停止工作(例如断电,进程被强杀),则可能会丢失最新几分钟的数据。
- RDB 需要经常 **fork()创建子进程来持久化数据,如果数据非常大且设备性能底,fork()**可能会很耗时,甚至可能导致 Redis 停止客服几毫秒或一秒。
RDB 文件
上图是将 RDB 文件是用十六进制打开。
RDB 文件开头就显示了 RDB格式的版本号(REDIS0008),后面紧跟保存的是 RDB 文件中的 8 种元数据。
- redis-ver:Redis 实例的版本。
- redis-bits:运行 Redis 实例的主机架构,64位或32位。
- ctime:RDB 创建时的 Unix 时间戳。
- used-mem:转存储时使用的内存大小。
- repl-stream-db:redis 的 db 索引。
- repl-id:主实例的ID(replication id)。
- repl-offset:主实例的偏称量(replication offset)。
- 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] 段落设置。
appendonly no
是否开启 AOF 持久化数据, 开启=yes,禁用=no。可动态开启 AOF 持久化(config set appendonly yes)appendfilename “appendonly.aof”
AOF 文件名,默认是 appendonly.aof。appendfsync everysec
追加写入数据到磁盘的模式,默认是每秒写入(everysec)。Redis 支持三种模式。**no(none):从不调用 fsync, 交由操作系统决定何时刷新数据(写入磁盘),大多数Linux 系统中,这个频率是 30 秒。速度快,但不安全。
always:每次写入就调用 fsync() 将数据追加到日志文件。 速度慢,最安全。
everysec:每秒调用一次fsync()**,默认值。兼顾速度和安全, 足够快(和RDB持久化差不多),故障时只会丢失 1 秒钟数据。Redis AOF 是通过调用 fsync()函数同步内存中所有已修改的文件数据到磁盘,有的操作系统会立即执行,有的操作系统会尽快尝试执行。调用fsync不会等缓冲区的数据达到一定量时再来写入。
内存数据写入到磁盘文件,底层完全是由操作系统控制的。 操作系统会维护一个缓冲区,Redis 命令首先会被写入该缓冲区,而缓冲区的数据必须被刷新到磁盘才能完成持久化。这个过程是通过 Linux 系统调用 **fsync()**,这是个阻塞调用,只有磁盘设备报告缓冲区的数据写入完成后才会返回。
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** 的日志数据。
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 **。aof-load-truncated yes
当 Redis 服务崩溃,特别是在没有设置 data = ordered选项的情况下挂载 ext4 文件系统时,可能出现 AOF 文件末尾被截断的异常。Redis 启动过程加载 AOF 数据到内存,可能发现 AOF 文件末尾被截断的处理方式。设置为 yes,表示会加载截断的 AOF 文件,加载能够加载的数据到内存中,Redis 会记录日志;如果设置为 no,则服务中止拒绝启动并显示错误,用户需要使用 redis-check-aof 修复 AOF 文件,再启动 Redis 服务加载数据到内存。
aof-use-rdb-preamble no
AOF模式下是否同时启动RDB,默认是** no **。
AOF 文件
Redis 提供了检测 AOF 文件的命令工具:./redis-check-aof [--fix] <file.aof>
1 | # ./redis-check-aof appendonly.aof |
若 AOF 文件出现损坏,也可用此工具加--fix
参数来修复
1 | ./redis-check-aof --fix appendonly.aof |
**AOF **文件可直接使用 Linux 查看文件命令查看,文件内容以 Redis 协议格式显示。其中 SELECT 是选择 DB ,默认有 16 个DB(0-15)。
AOF 重写
AOF 重写使用了与生成快照同样的写时复制技术,执行重建的子进程不会占用与父进程相同数量的内存。
- Redis 调用 fork(), 会创建子进程,同时拥有了父子进程。
- 子进程开始把新的 AOF 数据写入到一个临时文件。
- 父进程继承响应请求,并将新的修改累积到内存缓冲区(aof_rewrite_buf_blocks), 同时追加到旧的 AOF 文件(现有在使用的)中,即使重写失败,AOF 数据也是安全的。
- 当子进程完成重写后,会向父进程发送一个信号,父进程获取这个信号后,将内存缓冲区的数据追加到子进程创建新 AOF 文件末尾。
- 这样, Redis 原子的用新文件替换旧文件,并将新数据追加到新文件中。
AOF 优点
- 数据完整性更优,可使用不同的fsyn策略,见appendfsync配置。
- AOF是追加日志方式写入数据,即使停电或磁盘满而没完全写入(末尾截断),很容易用** redis-check-aof **工具修复。
- 当 Redis 数据很大时,Reids 可以在后台自动重写 AOF, 并且是安全的,见【AOF 重写】。
- AOF 文件格格式易于理解和解析,可以编辑 AOF 文件中的命令来重建数据。
AOF 缺点
- AOF 文件通常比同一数据集的RDB文件大。
- 根据确切换** fsync 策略,AOF** 可能比 RDB 慢,在大量写入负载的情况下, RDB 延迟更优。
- 罕见错误(例如,有一个涉及阻塞命令,如BRPOPLPUSH,导致生成的AOF文件在重新加时与原始数据不完全相同的情况),极少出现。
个人注:2、3缺点几乎可以忽略。
RDB 切换到 AOF
从 Redis 2.2 版本开始,支持在不重启 Redis 服务的情况下,将持久化方式从 RDB 切换到 AOF。见appendfsync配置。
- 手动触发生成最新的快照文件(dump.rdb),并进行备份。
- 执行开启 AOF 命令:config set appendonly yes
- 手动触发保存 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。
BGSAVE和BGREWRITEAOF两个命令不能同时执行,是为了防止两个 Redis 后台进程同时对磁盘进行大量的 I/O 操作。如果其中一个命令正在执行,又显式调用了另一个命令,另一个命令会做为计划任务,等前一个命令执行完后执行。
持久化相关命令
- info persistence
获取持久化详细信息1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18127.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 //写时点用内存 - config set save
动态设置持久化保存参数1
127.0.0.1:6379> config set save "900 1 300 10 60 10000"
- config get save
获取持久化保存信息1
2
3127.0.0.1:6379> config get save
1) "save"
2) "900 1 300 10 60 10000" - save
手动生成 RDB 快照,直接输入 save 命令,在 该命令是使用 Redis 主线程创建快照,会阻塞 redis-cli一段时间。1
2127.0.0.1:6379> save
OK - bgsave
执行非阻塞生成 RDB 快照, Redis 主进程会通过 fork() 系统调用创建一个子进程在后台执行转存储数据到一个名为 temp-.rdb 的临时文件,当转存储结束后,重命名临时文件为 dbfilename 定义的快照文件名,并覆盖旧的快照文件。 bgsave命令的子进程会保存该命令被处理时间点的所有数据。采用了写时复制(Copy-On-Write,COW)机制,所以不需使用 Redis 服务器所占用的同等数量的内存。1
2127.0.0.1:6379> bgsave
Background saving started
其它参考:
Redis 4.x系列(十六):Redis 持久化之 RDB 与 AOF
http://blog.gxitsky.com/2018/11/13/Redis-16-persistence-rdb-aof/