文章目录
前言
Redis虽然是一个内存级别的缓存程序,也就是redis是使用内存进行数据的缓存的,但是其可以将内存的数据按照一定的策略保存到硬盘中,这样的话就可以实现持久保存的目的;目前的话redis支持的两种不同方式的数据持久化保存机制,分别是RDB和AOF,这两种方式的话很类似于MySQL数据库的dump和二进制日志的方式。
1、RDB持久化
1.1 RDB理论概述
官网原话
RDB (Redis Database): RDB persistence performs point-in-time snapshots of your dataset at specified intervals
翻译:RDB (Redis 数据库) : RDB 持久性以指定的时间间隔执行数据集的时间点快照。
实现类似照片记录效果的方式,就是在指定的时间间隔内
将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot内存快照。这样一来即使故障宕机,快照文件也不会丢失,重启Redis时,将快照文件中的数据再读到内存当中,数据的可靠性也就得到了保证。这个快照文件就称为RDB文件(dump.rdb),其中,RDB就是Redis Data Base的缩写。
前面谈到在指定的时间间隔内,这个时间具体是多少呢?
可以从redis中的配置文件中看到
上述含义:
3600秒(1小时)内至少有1次修改
300秒(5分钟)内至少有100次修改
60秒(1分钟)内至少有10000次修改
满足上述任意一个条件,就会保存一份新的RDB文件,不过这是Redis7版本中的规定,Redis7之间的版本稍有不同,保存频率要高一些
1.2 RDB操作
先看一下官方文档的结束:
翻译:
默认情况下,Redis将数据集的快照保存在磁盘上,保存在一个名为dump.rdb的二进制文件中。你可以配置Redis,让它每N秒内,数据集中至少有M个更改,就保存一次数据集,或者你可以手动调用SAVE或BGSAVE命令
例如,如果至少有1000个key发生变化,这个配置会让Redis每60秒自动将数据集转储到磁盘上
RDB触发有两种模式,分别为手动触发和自动触发
1.2.1 手动触发
案例:每隔5秒检测一次,如果在5秒内修改了两次就持久化rdb文件
-
修改配置文件中RDB持久化的频率
-
修改dump文件的保存路径
此时需要注意,这个文件夹需要提前创建好 -
指定dump.rdb文件的名字
默认叫dump.rdb,不改也可以。但是如果涉及到多态redis,个人还是建议请加上端口号,我们才能知道这个dump.rdb文件来自哪台机器 -
重启redis,查看路径是否配置成功
-
在5秒内进行2次插入操作,产看是否产生了dump.rdb文件
如果插入k3 v3,超过5秒,不会持久化,再插入k4 v4,此时才会持久化
6. 数据恢复
将rdb备份,使用flashall删除所有数据,然后再删除rdb文件,再将备份的rdb文件还原,重启redis
此时数据并没有丢,说明使用rdb文件恢复数据成功。如果我们没有使用备份的dump.rdb文件,而是使用原有的dump.rdb文件,此时依然没有数据,因为执行flushall/flushdb命令也会产生dump.rdb文件,但里面是空的,无意义。除此之外,redis关机,也会产生dump.rdb文件
注意:当产生了有数据的rdb文件后,一定要进行备份迁移,不要让rdb备份文件所在的主机和redis所在的主机是同一台,一定要实现服务和备份分机隔离,以防生产机物理损坏后备份文件也挂了
它是如何工作的?可以查看官方文档
翻译:
当 Redis 需要保存 dump.rdb 文件时, 服务器执行以下操作:Redis 调用 fork() ,同时拥有父进程和子进程。
子进程将数据集写入到一个临时 RDB 文件中。
当子进程完成对新 RDB 文件的写入时,Redis 用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。
这种工作方式使得 Redis 可以从写时复制(copy-on-write)机制中获益。
1.2.2 自动触发
如果现在有数据缓冲在redis中,此时还没达到备份自动触发的条件,但是这个数据非常重要,需要立刻备份,因此redis提供了两个命令:SAVE和BGSAVE
SAVE和BGSAVE的区别:
使用save命令,在主程序中执行会阻塞当前redis服务器,直到持久化工作完成执行save命令期间,Redis不能处理其他命令,也就相当于终止了对外提供服务的能力,丧失了缓存功能,需要完成持久化后,才能处理其他命令,在生产上这是一个非常严重的问题。save是同步保存。
使用bgsave命令,Redis会在后台异步进行快照操作,不阻塞快照同时还可以响应客户端请求,该触发方式会fork一个子进程由子进程复制持久化过程,这就允许主进程同时可以修改数据。bgsave是异步保存
还以使用lastsave命令获取最后一次成功执行快照的时间
不过该命令返回的是一个时间戳,在Linux上,可以用命令date -d @时间戳,将其转换为时间
1.3 如何检测修复dump.rdb文件
在写入dump.rdb文件,或者进行文件迁移过程中,可能会出现文件破损等情况。
比如说set k1 v1111111111,当正在持久化rdb时,才将v11111写入,而后面的11111还没来得及写入,此时redis就宕机了,那么这个rdb文件就是破损的。如果这个rdb文件中有1w条数据,因为某一条数据时破损的,导致整rdb文件就失效了,所以我们就需要有一种方式,在一定范围内,检测并能修复破损的rdb文件,保证文件能被成功读取,并实现数据恢复
在/usr/local/bin有一个redis-check-rdb
这个命令就能修复破损的rdb文件
此时就表示修复成功,如果修复失败了,会进行报错,那么只能通过其他途径找回数据了
1.4 哪些情况会触发RDB快照以及如何禁用
- 配置文件中默认的快照配置
- 手动save/bgsave命令
- 执行flushall/flushdb命令也会产生dump.rdb文件,但里面是空的,无意义
- 执行shutdown且没有设置开启AOF持久化
- 主从复制时,主节点自动触发
在某些情况下,可能需要禁止rdb,比如数据量太大,每次后台进行rdb时,需要fork出子进程,此时占用时间太长,导致严重IO,或者不适合当前业务
动态所有停止RDB保存规则的方法: redis-cli config set save,在本次redis活跃期间生效,redis重启后失效
改配置文件:
1.5 RDB参数优化
stop-writes-on-bgsave-error
默认yes
如果配置成no,表示你不在乎数据不一致或者有其他的手段发现和控制这种不一致,那么在快照写入失败时,也能确保redis继续接受新的写请求。个人建议默认就行,因为数据一致性很重要
rdbcompression
默认yes
对于存储到磁盘中的快照,可以设置是否进行压缩存储。如果是的话,redis会采用LZF算法进行压缩
如果你不想消耗CPU来进行压缩的话,可以设置为关闭此功能。现在CPU、内存等硬件资源都不是瓶颈,使用默认即可
rdbchecksum
默认yes
在存储快照后,还可以i让redis使用CRC64算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。默认即可
rdb-del-sync-files
在没有持久性的情况下删除复制中使用的RDB文件启用。默认情况下no,此选项是禁用的。
1.6 模拟数据丢失
第一步,清空数据,并删除rdb文件
第二步,在5秒内插入两条数据,并查看rdb文件
第三步,再次插入一条数据,查看rdb文件,此时这条数据并没有持久化到rdb文件中,因为不满足5秒内写入两次
第四步,使用pgrep查看redis-server的pid,并用kill -9杀死redis-server,模拟redis-server意外宕机
第五步,重启redis,登录,查看数据是否丢失
此时数据k3 v3丢失
1.7 RDB优缺点
看看官方文档怎么说:
优点:
- RDB是一种表示某个即时点的Redis数据的紧凑文件。RDB文件适合用于备份。例如,你可能想要每小时归档最近24小时的RDB文件,每天保存近30天的RDB快照。这允许你很容易的恢复不同版本的数据集以容灾。
- RDB非常适合于灾难恢复,作为一个紧凑的单一文件,可以被传输到远程的数据中心,或者是Amazon S3(可能得加密)。
- RDB最大化了Redis的性能,因为Redis父进程持久化时唯一需要做的是启动(fork)一个子进程,由子进程完成所有剩余工作。父进程实例不需要执行像磁盘IO这样的操作。
- RDB在重启保存了大数据集的实例时比AOF要快
- 在副本上,RDB支持重启和故障转移后的部分重新同步
小总结:
- 适合大规模的数据恢复
- 按照业务定时备份
- 对数据完整性和一致性要求不高
- RDB文件在内存中的加载速度要比AOF快得多
缺点:
- 当你需要在Redis停止工作(例如停电)时最小化数据丢失,RDB可能不太好。你可以配置不同的保存点(save point)来保存RDB文件(例如,至少5分钟和对数据集100次写之后,但是你可以有多个保存点)。然而,你通常每隔5分钟或更久创建一个RDB快照,所以一旦Redis因为任何原因没有正确关闭而停止工作,你就得做好最近几分钟数据丢失的准备了。
- RDB需要经常调用fork()子进程来持久化到磁盘。如果数据集很大的话,fork()比较耗时,结果就是,当数据集非常大并且CPU性能不够强大的话,Redis会停止服务客户端几毫秒甚至一秒。AOF也需要fork(),但是你可以调整多久频率重写日志而不会有损(trade-off)持久性(durability)。
小总结:
- 在一定间隔时间做一次备份,所以如果redis意外down掉的话,就会丢失从当前至最近一次快照期间的数据,快照之间的数据会丢失
- 内存数据的全量同步,如果数据量太大会导致I/O严重影响服务器性能
- RDB依赖于主进程的fork,在更大的数据集中,这可能会导致服务请求的瞬间延迟。fork的时候内存中的数据被克隆了一份, 大致2倍的膨胀性,需要考虑
2、AOF持久化
2.1 AOF理论概述
官方原话:
AOF (Append Only File): AOF persistence logs every write operation received by the server. These operations can then be replayed again at server startup, reconstructing the original dataset. Commands are logged using the same format as the Redis protocol itself.
翻译:
AOF 持久化记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集。 AOF 文件中的命令全部以 Redis 协议的格式来保存
简单来说:
就是以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作
默认情况下,redis是没有开启AOF的,开启AOF功能需要设置配置:appendonly yes
不是有了rdb持久化了吗?为什么还要引入aof,看看官方文档怎么说:
大致意思:快照不是很持久。如果运行Redis的计算机停止了,电源线故障了,或者你不小心使用kill -9杀死了Redis实例,最新写入Redis的数据将丢失。虽然对于某些应用程序来说这可能不是什么大问题,但有些用例需要完全的持久性,在这些情况下,单独使用Redis快照并不是一个可行的选择。
除此之外aof保存的是appendonly.aof文件
2.2 AOF持久化工作流程
- Client作为命令的来源,会有多个源头以及源源不断的请求命令
- 在这些命令到达Redis Server以后并不是直接写入AOF文件,会将其这些命令先放入AOF缓存中进行保存。这里的AOF缓冲区实际上是内存中的一片区域,存在的目的是当这些命令达到一定量以后再写入磁盘,避免频繁的磁盘IO操作。
- AOF缓冲会根据AOF缓冲区同步文件的三种写回策略将命令写入磁盘上的AOF文件
- 随着写入AOF内容的增加为避免文件膨胀,会根据规则进行命令的合并(又称AOF重写,从而起到AOF文件压缩的目的
2.3 AOF缓冲区三种写回策略
always
同步写回,每个写命令执行完立刻同步地将日志写回磁盘
everysec
每秒写回,每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,每隔1秒把缓冲区中的内容写入磁盘。该策略是redis的默认写回策略
no
操作系统控制的写回,每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘
2.4 AOF操作
开启aof
因为redis默认是用rdb进行持久化的,因此要使用aof是需要在配置文件中打开的
使用默认的回写策略
设置aof保存路径
对于redis6,AOF保存文件的位置和RDB保存文件的位置一样,都是通过redis.conf配置文件的dir配置。比如:
对于redis7
把对应的dir该一下:dir /myredis
设置aof保存名称
redis6,aof文件只有一个,而redis7使用了Multi Part AOF的设计,aof文件从1个变为了3个
2.4.1 正常恢复
登录redis,插入两条数据,查看是否有对应的rdb文件和aof文件产生
确实在对应目录下产生了rdb文件和3个aof文件
重启redis,查看数据是否恢复成功,在此之前,有两个疑问?
aof和rdb都能恢复数据,aof和rdb是否能同时存在?
数据是由aof恢复还是由rdb恢复
首先将rdb文件删除,证明aof确实能恢复数据
备份整个appendonlydir
清空缓冲,退出redis,删除rdb文件,重启redis,查看是否恢复数据
这里数据时空的,因为前面使用了flushall命令,它也是写操作命令,也会追加到对应的aof文件中,即使数据恢复后,最终也会使用flushall清除所有缓存数据
退出redis,删除对应的aof文件和rdb文件,将备份的aof文件还原,再重新启动redis,查看数据是否恢复
可以看到,数据恢复成功
对应的写操作会放在aof的哪个文件中?验证一下
结果是放在了incr.aof文件中。
那读取操作会写入吗?验证一下
答案是并不会
2.4.2 异常恢复
如果aof每一秒写入一次,在高并发场景下,aof才写入一小半,并没有写入完整,突然redis宕机,导致aof文件错误,那如何修复这个aof文件呢?
appendonly.aof.1.incr.aof文件内部格式
如果在文件末尾随机添加数据,然后再重启redis
重启Redis之后就会进行AOF文件的载入,发现启动都不行
此时需要进行aof文件修复,使用命令:redis-check-aof --fix aof文件
修复完成,重启redis,再查看数据是否恢复
2.5 AOF优缺点
看看官方文档怎么说?
AOF 的优点:
- 使用AOF Redis会更具有可持久性(durable):你可以有很多不同的fsync策略:没有fsync,每秒fsync,每次请求时fsync。使用默认的每秒fsync策略,写性能也仍然很不错(fsync是由后台线程完成的,主线程继续努力地执行写请求),即便你也就仅仅只损失一秒钟的写数据。
- AOF日志是一个追加文件,所以不需要定位,在断电时也没有损坏问题。即使由于某种原因文件末尾是一个写到一半的命令(磁盘满或者其他原因),redis-check-aof工具也可以很轻易的修复。
- 当AOF文件变得很大时,Redis会自动在后台进行重写。重写是绝对安全的,因为Redis继续往旧的文件中追加,使用创建当前数据集所需的最小操作集合来创建一个全新的文件,一旦第二个文件创建完毕,Redis就会切换这两个文件,并开始往新文件追加。
- AOF文件里面包含一个接一个的操作,以易于理解和解析的格式存储。你也可以轻易的导出一个AOF文件。例如,即使你不小心错误地使用FLUSHALL命令清空一切,如果此时并没有执行重写,你仍然可以保存你的数据集,你只要停止服务器,删除最后一条命令,然后重启Redis就可以。
AOF缺点:
- 对同样的数据集,AOF文件通常要大于等价的RDB文件。
- AOF可能比RDB慢,这取决于准确的fsync策略。通常fsync设置为每秒一次的话性能仍然很高,如果关闭fsync,即使在很高的负载下也和RDB一样的快。不过,即使在很大的写负载情况下,RDB还是能提供能好的最大延迟保证。
2.6 AOF重写机制
随着写操作的执行,AOF变得越来越大,因此redis提供了一种瘦身计划,那就是AOF重写机制。
由于AOF持久化是Redis不断将写命令记录到AOF文件中,随着Redis不断的进行,AOF的文件会越来越大,文件越大,占用服务器内存越大以及AOF恢复要求时间越长。为了解决这个问题,Redis新增了重写机制,当AOF文件的大小超过所设定的峰值时,Redis就会自动启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集,或者可以手动使用命令bgrewriteaof来重新。
保留可以恢复数据的最小指令集的含义
举个例子:比如有个key
一开始你set k1 v1
然后改成set k1 v2
最后改成set k1 v3
如果不重写,那么这3条语句都在aof文件中,内容占空间不说启动的时候都要执行一遍,共计3条命令;但是,我们实际效果只需要setk1 v3这一条, 所以,开启重写后,只需要保存set k1 v3就可以了只需要保留最后一次修改值, 相当于给aof文件瘦身减肥,性能更好。
AOF重写不仅降低了文件的占用空间,同时更小的AOF也可以更快地被Redis加载。
触发机制
可分为自动触发和手动触发
自动触发
注意:同时满足才会触发
- 根据上次重写后的aof大小,判断当前aof大小是不是增长了1倍
- 重写时满足的文件大小超过64mb
如果上次aof文件大小为32mb,插入许多数据,导致增加到64mb,此时就会触发aof重写机制
文件配置,开启aof
重写峰值改为1k
关闭混合模式,设置为no
删除之前的全部aof和rdb,清除干扰项
完成上述正确配置,重启redis服务器,执行set k1 v1查看aof文件是否正常
持续插入数据,再看看aof文件
appendonly.aof.1.incr.aof文件内部
持续插入直到触发重写机制
可以发现incr.aof文件大小为0,而base.aof文件大小从0变为了117,并且它们的名字也发生了改变,可以看看base.aof内部数据
里面只记录了可以恢复数据的最小指令集
手动触发
插入数据,然后使用命令bgrewriteaof
,然后查看aof文件
此时还没有到达触发条件,使用bgrewriteaof强制触发重写机制,对应的aof文件也发生了改变
重写机制的原理:
-
在重写即将开始之际,redis 会创建(fork)一个“重写子进程”,这个子进程会首先读取现有的 AOF 文件,并将其包含的指令进行分析压缩并写入到一个临时文件中。
-
与此同时,主工作进程会将新接收到的写指令一边累积到内存缓冲区中,一边继续写入到原有的 AOF 文件中,这样做是保证原有的 AOF 文件的可用性,避免在重写过程中出现意外。
-
当“重写子进程”完成重写工作后,它会给父进程发一个信号,父进程收到信号后就会将内存中缓存的写指令追加到新 AOF 文件中。
-
当追加结束后,redis 就会用新 AOF 文件来代替旧 AOF 文件,之后再有新的写指令,就都会追加到新的 AOF 文件中了。
小总结
AOF文件重写并不是对原文件进行重新整理,而是直接读取服务器现有的键值对,然后用一条命令去代替之前记录这个键值对的多条命令,生成个新的文件后去替换原来的AOF文件。
3、RDB-AOF混合持久化
官方文档明确告诉我们能进行混合持久化
RDB + AOF: You can also combine both AOF and RDB in the same instance.
从配置文件中也能看出,aof和rdb能同时共存,但是在redis启动时,会加载aof,而不是rdb,因此aof的优先级更高
据恢复顺序和加载流程
RDB和AOF如何选择,用哪个?
作者建议我们同时开启REB和AOF
同时开启
将配置文件中的aof-use-rdb-preamble改为yes
在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RB文件保存的数据集要完整。
RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件。那要不要只使用AOF呢?
作者建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份),留着rdb作为一个万一的手段。
RDB+AOF混合模式
结合了RDB和AOF的优点,既能快速加载又能避免丢失过多的数据。
RDB镜像做全量持久化,AOF做增量持久化
先使用RDB进行快照存储,然后使用AOF持久化记录所有的写操作,当重写策略满足或手动触发重写的时候,将最新的数据存储为新的RDB记录。这样的话,重启服务的时候会从RDB和AOF两部分恢复数据,既保证了数据完整性,又提高了恢复数据的性能。简单来说:混合持久化方式产生的文件一部分是RDB格式,一部分是AOF格式。-----》AOF包括了RDB头部+AOF混写
4、纯缓存模式
redis是基于内存的数据库,最主要,最重要的功能就是做缓存,如果开启了rdb或者aof持久化功能,或多或少会降低redis的性能。假设我们要做高并发,高性能的缓存服务器时,干脆关闭持久化功能,不需要redis做备份,备份的事由其他东西做。
同时关闭RDB和AOF
禁用rdb
禁用rdb持久化模式下,我们仍然可以使用命令save、bgsave生成rdb文件
禁用aof
禁用aof持久化模式下,我们仍然可以使用命令bgrewriteaof生成aof文件