1. RDB快照(snapshot)
在默认情况下, Redis 将内存数据库快照保存在名字为 dump.rdb的二进制文件中。
你可以对 Redis 进行设置, 让它在“N 秒内数据集至少有 M 个改动”这一条件被满足时,
自动保存一次数据集。
比如说, 以下设置会让 Redis 在满足“60 秒内有至少有 1000 个键被改动”这一条件时,
自动保存一次数据集:
save 60 1000
优点:
RDB 是一个非常紧凑(compact)的文件,体积小,因此在传输速度上比较快,因此适合灾难恢复。
RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快得多。
缺点:
RDB是一个快照过程,无法完整的保存所有数据,尤其在数据量比较大时候,一旦出现故障丢失的数据将更多。
RDB文件是特定的格式,阅读性差,由于格式固定,可能存在不兼容情况。
~~~
bgsave
~~~
bgsave执行原理:
针对save命令进行优化,Redis中所有涉及RDB操作都采用bgsava方式
优势
1.采用子线程创建RDB文件(),不会对redis服务器性能造成大的影响( 性能最大化);
2.快照生成的RDB文件是一种压缩的二进制文件,可以方便的在网络中传输和保存。通过RDB文件可以方便的将redis数据恢复到某一历史时刻,可以提高数据安全性,避免宕机等意外对数据的影响。
3.适合大规模的数据恢复, RDB的启动恢复效率高。
4.如果业务对数据完整性和一致性要求不高,RDB是很好的选择。
劣势
1.在redis文件在时间点A生成,之后产生了新数据,还未到达另一次生成RDB文件的条件,redis服务器崩溃了,那么在时间点A之后的数据会丢失掉,数据一致性不是完美的好,如果可以接受这部分丢失的数据,可以用生成RDB的方式;
2.快照持久化方法通过调用fork()方法创建子线程。当redis内存的数据量比较大时,创建子线程和生成RDB文件会占用大量的系统资源和处理时间,对 redis处理正常的客户端请求造成较大影响。
3.数据的完整性和一致性不高,因为RDB可能在最后一次备份时宕机了。
4.备份时占用内存,因为Redis 在备份时会独立创建一个子进程,将数据写入到一个临时文件(此时内存中的数据是原来的两倍哦),最后再将临时文件替换之前的备份文件。所以 Redis 的持久化和数据的恢复要选择在夜深人静的时候执行是比较合理的。
Q: 通过RDB文件恢复数据?
答: 将dump.rdb 文件拷贝到redis的安装目录的bin目录下,重启redis服务即可。在实际开发中,一般会考虑到物理机硬盘损坏情况,选择备份dump.rdb 。 作者:WeiyiGeek https://www.bilibili.com/read/cv13235983 出处:bilibili
RDB文件恢复数据流程
1、先备份一份 dump.rdb 为 dump_bak.rdb(模拟线上)
2、flushall 清空数据(模拟数据丢失,需要注意 flushall 也会触发rbd持久化)
3、将 dump_bak.rdb 替换为 dump.rdb
4、重启redis服务,恢复数据
2. AOF(append-only file)
快照功能并不是非常耐久(durable): 如果 Redis 因为某些原因而造成故障停机, 那么服务器将丢失最近写入、且仍未保存到快照中的那些数据。从 1.1 版本开始, Redis 增加了一种完全耐久的持久化方式: AOF 持久化,将修改的每一条指令记录进文件
AOF文件生成机制
答: 生成过程包括三个步骤,即。
redis 打开AOF持久化功能之后,redis在执行完一个写命令后,把执行的命令首先追加到redis内部的aof_buf缓冲区膜末尾,此时缓冲区的记录还没有写到Appendonly.aof文件中。然后,缓冲区的写命令会被写入到 AOF 文件,这一过程是文件写入过程。对于操作系统来说,调用write函数并不会立刻将数据写入到硬盘,为了将数据真正写入硬盘,还需要调用fsync函数,调用fsync函数即是文件同步的过程,只有经过了文件的同步过程,写命令才真正的被保存到了AOF文件中。选项 Appendfsync 就是配置同步的频率的。
你可以通过修改配置文件来打开 AOF 功能:
appendonly yes
从现在开始, 每当 Redis 执行一个改变数据集的命令时(比如 SET), 这个命令就会被追加到 AOF 文件的末尾。
这样的话, 当 Redis 重新启动时, 程序就可以通过重新执行 AOF 文件中的命令来达到重建数据集的目的。
你可以配置 Redis 多久才将数据 fsync 到磁盘一次。
有三个选项:
appendfsync always。每次有新命令追加到 AOF 文件时就执行一次 fsync :非常慢,也非常安全。
appendfsync everysec。每秒 fsync 一次:足够快(和使用 RDB 持久化差不多),并且在故障时只会丢失 1 秒钟的数据。
appendfsync no。从不 fsync :将数据交给操作系统来处理。更快,也更不安全的选择。
推荐(并且也是默认)的措施为每秒 fsync 一次, 这种 fsync 策略可以兼顾速度和安全性。
优点:
数据更完整,秒级数据丢失(取决于设置fsync策略)。、
兼容性较高,由于是基于redis通讯协议而形成的命令追加方式,无论何种版本的redis都兼容。
aof文件是明文的,可阅读性较好。
缺点:
数据文件体积较大,即使有重写机制,但是在相同的数据集情况下,AOF文件通常比RDB文件大。
相对RDB方式,AOF速度慢于RDB,并且在数据量大时候,恢复速度AOF速度也是慢于RDB。
由于频繁地将命令同步到文件中,AOF持久化对性能的影响相对RDB较大。
AOF文件重写
1、redis不断的将写命令保存到AOF文件中,导致AOF文件越来越大,当AOF文件体积过大时,数据恢复的时间也是非常长的,因此,redis提供了重写或者说压缩AOF文件的功能。
比如,AOF重写功能就是干这个事情的。重写时,可以调用BGREWRITEAOF命令重写AOF文件,与新建子线程bgsave命令的工作原理相似。也可以通过配置文件配置什么条件下对AOF文件重写。
2、重写的原理:Redis 会fork出一条新进程,读取内存中的数据,并重新写到一个临时文件中。并没有读取旧文件(太大了)。最后替换旧的aof文件
3、重写触发机制:当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发,这里的“一倍”和“64M” 可以通过配置文件修改
aof 文件记录的是每一条redis命令,有时候我们会对某一个key进行多次set,中间会产生很多条命令,但是结果只有一个。
优势
1.该机制可以带来更高的数据安全性,即数据持久化; 常规三种同步策略即每秒同步()、每修改同步()和不同步;
2.由于该机制对日志文件的写入操作采用的是Append模式,即使过程中出现宕机也不会破坏日志文件中已经存在的内容,如果数据不完整在Redis下次启动之前, 通过redis-check-aof解决数据一致性问题;
3.如果日志文件体积过大可以启动rewrite机制,即redis以Append模式不断的将修改数据写到老的磁盘文件中,同时创建新文件记录期间有哪些修改命令执行,此项极大的保证数据的安全性;
4.AOF文件可读性强,其包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作()
5.数据的完整性和一致性更高
劣势
1.AOF文件比RDB文件较大, 对于相同数量的数据集而言;
2.redis负载较高时,RDB文件比AOF文件具有更好的性能;
3.RDB使用快照的方式持久化整个redis数据,而aof只是追加写命令,因此从理论上来说,RDB比AOF方式更加健壮,另外,官方文档也指出,在某些情况下,AOF的确也存在一些bug,比如使用阻塞命令时,这些bug的场景RDB是不存在的。
4.因为AOF记录的内容多,文件会越来越大,数据恢复也会越来越慢。
5.根据同步策略的不同,AOF在运行效率上往往会慢于RDB,总的来说每秒同步策略的效率还是比较高的
set name 1
...
set name 12
set name 123
...
set name 1234
set name 12345
例如我们执行了上述命令,aof 文件会记录着每一条命令。在redis重启时,会逐条执行上述的命令。但是其实可以精简为set name 12345,其余的几条命令其实没有意义。AOF重写就是实现了这个功能。
相关配置:
auto-aof-rewrite-percentage 100 # 指当前aof文件比上次重写的增长比例大小,达到这个大小就进行 aof 重写
auto-aof-rewrite-min-size 64mb # 最开始aof文件必须要达到这个文件时才触发,后面的每次重写就不会根据这个变量了
以上配置的意思是:
在 aof 文件小于64mb的时候不进行重写,当到达64mb的时候,就重写一次。重写后的 aof 文件可能是10mb。上面配置了auto-aof-rewrite-percentage 100,即 aof 文件到了20mb的时候,又开始重写一次。以此类推。
AOF 是在后台自动重写(redis会fork一个子进程来进行备份,保证主进程不会阻塞),也可以人为执行命令 bgrewriteaof 重写 AOF。
使用子进程来进行AOF重写时会遇到的问题
问题:
子进程在进行AOF重写期间,服务器进程还要继续处理命令请求,而新的命令可能对现有的数据进行修改,这会让当前数据库的数据和重写后的AOF文件中的数据不一致。
解决方案:
要知道redis是怎么处理这个问题的,需要先了解下AOF重写的具体实现:
AOF文件重写过程与RDB快照bgsave工作过程有点相似,都是通过fork子进程,由子进程完成相应的操作,同样的在fork子进程简短的时间内,redis是阻塞的。
(1)开始bgrewriteaof,判断当前有没有bgsave命令(RDB持久化)/bgrewriteaof在执行,倘若有,则这些命令执行完成以后在执行。
(2)主进程fork出子进程,在这一个短暂的时间内,redis是阻塞的。
(3)主进程fork完子进程继续接受客户端请求。此时,客户端的写请求不仅仅写入aof_buf缓冲区,还写入aof_rewrite_buf重写缓冲区。一方面是写入aof_buf缓冲区并根据appendfsync策略同步到磁盘,保证原有AOF文件完整和正确。另一方面写入aof_rewrite_buf重写缓冲区,保存fork之后的客户端的写请求,防止新AOF文件生成期间丢失这部分数据。
(4.1)子进程写完新的AOF文件后,向主进程发信号,父进程更新统计信息。
(4.2)主进程把aof_rewrite_buf中的数据写入到新的AOF文件。
(5.)使用新的AOF文件覆盖旧的AOF文件,标志AOF重写完成。
手动执行重写
AOF写和重写流程:
Q: 如何触发AOF快照?
答: 根据配置文件触发,可以是每次执行触发,可以是每秒触发,可以不同步。
Q: 如何根据AOF文件恢复数据?
答: 正常情况下,将Appendonly.aof 文件拷贝到redis的安装目录的bin目录下,重启redis服务即可。但在实际开发中,可能因为某些原因导致 文件格式异常,从而导致数据还原失败,可以通过命令进行修复 。
配置说明:
~~~
cat > redis.conf <<'EOF'
# 持久化数据存储
dir "/data"
# 是否开启AOF默认为否
appendonly yes
# AOF文件名字及路径,若RDB路径已设置这里可不设置
appendfilename "appendonly.aof"
# AOF的3种模式,no(使用系统缓存处理,快)、always(记录全部操作,慢但比较安全)、everysec(每秒同步,折中方案,默认使用)
appendfsync everysec
# 重写期间是否同步数据,默认为no
no-appendfsync-on-rewrite no
# 配置重写触发机制: 确保AOF日志文件不会过大,保持跟redis内存数据量一致。
# 配置说明:当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发(根据实际环境进行配置)
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 256mb
# AOF重写策略是否启用,默认为yes
aof-rewrite-incremental-fsync yes
# 加载AOF时如果报错则会继续但写入log,如果为no则不会继续
aof-load-truncated yes
# Redis5.0有的功能AOF重写及恢复可以使用RDB文件及AOF文件,速度更快,默认yes
aof-use-rdb-preamble yes
EOF
~~~
Q: 如何通过AOF文件恢复数据流程?
1、执行flushall,模拟数据丢失
2、重启 redis 服务,恢复数据
3、修改 appendonly.aof,模拟文件异常
4、重启 Redis 服务失败,这同时也说明了RDB和AOF可以同时存在,且优先加载AOF文件。
5、使用 redis-check-aof 校验 appendonly.aof 文件。
# 针对Redis aof 持久化文件进行完整性检测并进行修复
/usr/local/redis/bin/redis-check-aof --fix appendonly.aof
# The AOF appears to start with an RDB preamble.
# Checking the RDB preamble to start:
# [offset 0] Checking RDB file appendonly.aof
# [offset 27] AUX FIELD redis-ver = '5.0.10'
# [offset 41] AUX FIELD redis-bits = '64'
# [offset 53] AUX FIELD ctime = '1631088747'
# [offset 68] AUX FIELD used-mem = '31554944'
# [offset 84] AUX FIELD aof-preamble = '1'
# [offset 86] Selecting DB ID 0
# [offset 13350761] Checksum OK
# [offset 13350761] \o/ RDB looks OK! \o/
# [info] 157070 keys read
# [info] 0 expires
# [info] 0 already expired
# RDB preamble is OK, proceeding with AOF tail...
# 0x 27b66b0: Expected prefix '*', got: 'R'
# AOF analyzed: size=116993914, ok_up_to=41641648, ok_up_to_line=1816854, diff=75352266
# AOF is not valid. Use the option to try fixing it.
6、重启Redis 服务后正常。
# 利用源实例生成的aof文件数据进行恢复到其它主机中。
redis-cli -h 17.20.0.2 -a password --pipe < applendonly.aof
注意:当你使用 flushall 清空数据的时候,重启redis服务发现数据没恢复,是因为 FLUSHALL 命令也被写入AOF文件中,会导致数据恢复失败,所只需要删除aof文件中的flushall就行了。
在数据库恢复时把 aof(Append only file) 从中对redis数据库操作的命令,增删改操作的命令,执行了一遍即可。
实际案例:
描述: 用于异步执行一个 文件重写操作, 重写会创建一个当前 AOF 文件的体积优化版本,因为AOF为记录每次的操作会导致实际记录冗杂、使得文件过大,所以需要做重写操作。
重写方式分为以下两种:
# (1) AOF自动重写:按配置文件条件自动触发重写
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 256mb
# (2) AOF手动重写:使用 redis-cli 连接到 server 端执行 bgrewriteaof 进行手动重写。
# 注意:从 Redis 2.4 开始, AOF 重写由 Redis 自行触发, BGREWRITEAOF 仅仅用于手动触发重写操作。
127.0.0.1:6379> BGREWRITEAOF
# 即使 Bgrewriteaof 执行失败,也不会有任何数据丢失,因为旧的 AOF 文件在 Bgrewriteaof 成功之前不会被修改。
Background append only file rewriting started
合并两个不同实例的数据
描述: 我们可以利用如下方式进行集群多个主节点持久化数据的合并。
(1) AOF 备份合并: 我们说过它实际上是一些列Redis的命令文本。
例如,假设有两台 Redis(6379, 6479),它们的AOF文件名分别为(6379.aof, 6479.aof),现在要将6379的数据合并到 6479.aof
# 首先
cp 6379.aof 6379.aof.bak, cp 6479.aof 6479.aof.bak
# 合并
cat 6379.aof 6479.aof > new.aof
# 检查&修复
/usr/local/redis/bin/redis-check-aof --fix appendonly.aof
(2) RDB 备份合并: 注意以下方法可能由于服务端版本不同而有些许差异。
RDB格式如下:头5个字节是字符REDIS,之后4个字节代表版本号,阿里的版本分别是 00 00 00 06,之后2个字节 FE 00,FE是标识 00是数据库,还好我们只有一个库, 最后的结尾9个字节 , FF 加上8个字节的CRC64校验码(实在没空弄,后来偷了一个懒)
# 1.线上服务使用的阿里云的集群版本redis服务,数据量1千万,rdb文件4GB,8个rdb文件,每个500MB。
#文件1 大小566346503,截取尾部的9个字节
dd bs=1 if=src_1.rdb of=1.rdb count=566346494
#文件2 大小570214520,跳过头部的11个字节,再截取尾部的9个字节
dd bs=1 if=src_2.rdb of=2.rdb skip=11 count=570214500
...
#文件8 大小569253061,跳过头部的11个字节,再截取尾部的8个字节,保留FF。
dd bs=1 if=src_8.rdb of=8.rdb skip=11 count=569253042
# 2.合并文件(得到备份文件dump.rdb)
cat 1.rdb > dump.rdb
cat 2.rdb >> dump.rdb
...
cat 8.rdb >> dump.rdb
# 3.检查备份文件(应该会提示没有crc校验)
redis-check-rdb dump.rdb
# 4.修改配置文件,因为数据库备份文件里面不包含crc64的校验码,配置文件中关闭选项。
rdbchecksum no
Tips : 数据恢复到此结束,此方法只适合用于临时恢复和导出数据,数据完整性不敢保证。
参考地址: https://github.com/sripathikrishnan/redis-rdb-tools/wiki/Redis-RDB-Dump-File-Format
其它工具:
https://github.com/leonchen83/redis-rdb-cli/ | 一个可以解析, 过滤, 分割, 合并 rdb 离线内存分析的工具. 也可以在两个redis之前同步数据并允许用户自定义同步服务来把redis数据同步到其他地方.
3.混合持久化(redis4.0之后才支持)
重启 Redis 时,我们很少使用 rdb 来恢复内存状态,因为会丢失大量数据。
如果使用 AOF 日志重放,性能则相对 rdb 来说要慢很多,这样在 Redis 实例很大的情况下,启动的时候需要花费很长的时间。
Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化。
混合持久化同样也是通过bgrewriteaof完成的,不同的是当开启混合持久化时,fork出的子进程先将共享的内存副本全量的以RDB方式写入aof文件,然后在将aof_rewrite_buf重写缓冲区的增量命令以AOF方式写入到文件,写入完成后通知主进程更新统计信息,并将新的含有RDB格式和AOF格式的AOF文件替换旧的的AOF文件。简单的说:新的AOF文件前半段是RDB格式的全量数据后半段是AOF格式的增量数据,如下图:
在redis重启的时候,加载 aof 文件进行恢复数据:先加载 rdb 内容再加载剩余的 aof。
混合持久化配置:
aof-use-rdb-preamble yes # yes:开启,no:关闭
RDB和AOF,应该用哪一个?
如果你可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化。
有很多用户都只使用 AOF 持久化, 但我们并不推荐这种方式: 因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快。如果只用AOF持久化,数据量很大时,在redis启动的时候需要花费大量的时间。
如果你非常关心你的数据,建议使用 redis 4.0 以后的混合持久化特性。
混合模式配置项
save " "
dbfilename "dump.rdb"
appendonly "yes"
appendfilename "appendonly.aof"
AOF重写
aof文件里可能有太多“琐碎”指令,所以aof会定期根据内存的最新数据重新生成aof文件
有两个配置可以控制aof自动重写的频率:
auto-aof-rewrite-min-size 64mb
#aof文件至少要达到64m才会触发制动重写,文件太小恢复速度本来就很快,重写的意义不大
auto-aof-rewrite-percentage 100
#aof文件上一次重写后文件大小增长了100%则再次触发重写
appendfsync "everysec"
aof-use-rdb-preamble "yes"
参考:https://www.bilibili.com/read/cv13235983