3.Redis持久化及备份
[toc]
总体介绍
Redis数据安全问题
官网介绍:http://www.redis.io
前面我们提到,Redis是一个缓存中间件,它的最大特点是使用内存从而使其性能强悍。
但是使用内存的方式有一个致命的特点就是数据没办法持久化保存。
然而Redis持久化存储有两种持久化方案,RDB(Redis DataBase)和 AOF(Append-Only File)。
其中RDB是将内存中的数据进行快照存储到磁盘,AOF则为可回放的命令日志记录redis内的所有操作。它们各有特点也相互独立。
Redis4之后支持RDB-AOF混合持久化的方式,结合了两者的优点,可以通过 aof-use-rdb-preamble 配置项可以打开混合开关。
什么是持久化
将内存中的数据写入到磁盘,永久保存
持久化的模式
- RDB模式
- AOF模式
RDB模式
快照持久化(RDB)
概述
- RDB(Redis DataBase)是将Redis内存中的数据进行Snaptshot快照存储在磁盘内,是Redis的默认持久化方案。使用RDB持久化默认有三种策略,该持久化策略在redis.conf中可配置,会以一段时间内有指定次数据修改的规则触发快照动作,快照文件名为dump.rdb,该文件默认使用LZF压缩算法 。每当Redis服务重启的时候会从该文件中加载数据进内存。
- RDB持久化除了可以根据配置中的策略触发,也可以手动触发,使用save和bgsave命令即可。这两个命令的区别的save会阻塞服务器进程,在进行save的过程中,服务器不能处理任何请求,而bgsave会通过一个子进程在后台处理rdb持久化。事实上save和bgsave调用的都是rdbSave函数,因此Redis不允许save和bgsave同时运行,这也是为了避免出现竞争导致rdb文件数据不准确。
- bgsave操作使用CopyOnWrite机制进行写时复制,是由一个子进程将内存中的最新数据遍历写入临时文件,此时父进程仍旧处理客户端的操作,当子进程操作完毕后再将该临时文件重命名为dump.rdb替换掉原来的dump.rdb文件,因此无论bgsave是否成功,dump.rdb都不会受到影响。
- 另外在主从全量同步、debug reload以及shutdown的情况下也会触发RDB数据持久化。
RDB模式含义
可以在指定的时间间隔内生成数据集的时间点快照(point-in-time snapshot)并写入磁盘, 也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里
RDB备份执行
Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到 一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。 整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能 如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。
Fork
- Fork的作用是复制一 个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等) 数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程
- 在Linux程序中,fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,Linux中引入了“写时复制技术”
- 一般情况父进程和子进程会共用同一段物理内存,只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。
RDB执行流程、配置方式
RDB持久化流程
默认情况下,Redis保存数据集快照到磁盘,名为dump.rdb的二进制文件。
- 你可以设置让Redis在N秒内至少有M次数据集改动时保存数据集,或者你也可以手动调用SAVE或者BGSAVE命令。
在上文中我们已经在配置文件中做过对应的配置:
- 例如,这个配置会让Redis在每个60秒内至少有1000次键改动时自动转储数据集到磁盘:
save 60 1000
- 例如,这个配置会让Redis在每个60秒内至少有1000次键改动时自动转储数据集到磁盘:
当 Redis 需要保存 dump.rdb 文件时,服务器执行以下操作:
- Redis 调用 fork() ,同时拥有父进程和子进程。
- 子进程将数据集写入到一个临时的 RDB 文件中。
- 当子进程完成对新 RDB 文件的写入时, Redis 用新RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。
这种方式使得 Redis 可以从写时复制机制中获益。
快照持久化快照相关配置
1 | # 编辑配置文件 |
RDB的优势和劣势
RDB优势
- RDB是一种表示某个即时点的Redis数据的紧凑文件。RDB文件适合用于备份。例如,你可能想要每小时归档最近24小时的RDB文件,每天保存近30天的RDB快照。这允许你很容易的恢复不同版本的数据集以容灾。
- RDB非常适合于灾难恢复,作为一个紧凑的单一文件,可以被传输到远程的数据中心。
- RDB最大化了Redis的性能,因为Redis父进程持久化时唯一需要做的是启动(fork)一个子进程,由子进程完成所有剩余工作。父进程实例不需要执行像磁盘IO这样的操作。
- RDB在重启保存了大数据集的实例时比AOF要快。
RDB劣势
- 当你需要在Redis停止工作(例如停电)时最小化数据丢失,RDB可能不太好。你可以配置不同的保存点(save point)来保存RDB文件(例如,至少5分钟和对数据集100次写之后,但是你可以有多个保存点)。然而,你通常每隔5分钟或更久创建一个RDB快照,所以一旦Redis因为任何原因没有正确关闭而停止工作,你就得做好最近几分钟数据丢失的准备了。
- RDB需要经常调用fork()子进程来持久化到磁盘。如果数据集很大的话,fork()比较耗时,结果就是,当数据集非常大并且CPU性能不够强大的话,Redis会停止服务客户端几毫秒甚至一秒。AOF也需要fork(),但是你可以调整多久频率重写日志而不会有损(trade-off)持久性(durability)。
RDB优缺点总结
- 优点:对数据完整性和一致性要求不高更适合使用、适合大规模的数据恢复、节省磁盘空间、恢复速度快、主从复制也是基于RDB持久化功能实现的。
- 缺点:会有数据丢失、导致服务停止几秒、消耗性能较高
AOF模式
AOF概述
- AOF(Append-Only File)记录Redis中每次的写命令,类似mysql中的binlog,服务重启时会重新执行AOF中的命令将数据恢复到内存中,RDB(按策略持久化)持久化方式记录的力度不如AOF(记录每条写命令),因此很多生产环境都是开启AOF持久化。
- AOF中记录了操作和数据,在日志文件中追加完成后才会将内存中的数据进行变更。
- AOF(append only file)只追加文件,记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集。
- AOF 文件中的命令全部以 Redis 协议的格式来保存,新命令会被追加到文件的末尾。
AOF执行流程、配置方式
AOF持久化流程
- 客户端的请求写命令会被append追加到AOF缓冲区内;
- AOF缓冲区根据AOF持久化策略[always,everysec,no]将操作sync同步到磁盘的AOF文件中;
- AOF文件大小超过重写策略或手动重写时,会对AOF文件rewrite重写,压缩AOF文件容量;
- Redis服务重启时,会重新load加载AOF文件中的写操作达到数据恢复的目的;
AOF持久化配置
1 | 开启了AOF之后,RDB就默认不使用了。使用下面的配置开启AOF以及策略。(如果使用AOF,推荐选择always方式持久化,否则在高并发场景下,每秒钟会有几万甚至百万条请求,如果使用everysec的方式的话,万一服务器挂了那几万条数据就丢失了)。 |
AOF持久化策略
AOF分别有三种备份策略,分别是:
always
:每个命令都记录everysec
:每秒记录一次no
:看机器的心情高兴了就记录
针对这三种策略给出如下说明。
策略说明
策略 | 说明 | 优点 | 缺点 |
---|---|---|---|
Always | 每次执行,都会持久化到AOF文件中 | 不丢失数据 | IO开销大 |
Everysec | 每秒持久化一次 | 减少IO | 丢1秒钟的数据 |
No | 根据服务器性能持久化 | 全自动 | 不可控 |
AOF重写机制
AOF重写机制概述
因为 AOF 的运作方式是不断地将命令追加到文件的末尾,所以随着写入命令的不断增加, AOF 文件的体积也变得越来越大。
举个例子,如果你对一个计数器调用了 100 次 INCR ,那么仅仅是为了保存这个计数器的当前值, AOF 文件就需要使用 100 条记录。
然而在实际上,只使用一条 SET 命令已经足以保存计数器的当前值了,其余 99 条记录实际上都是多余的。
为了处理这种情况, Redis 支持一种有趣的特性:
- 可以在不断服务客户端的情况下,对 AOF 文件进行重建。
- 执行 BGREWRITEAOF 命令, Redis 将生产一个新的 AOF 文件,这个文件包含重建当前数据集所需的最少命令。
AOF重写配置
配置名 | 含义 |
---|---|
appendonly | 开启AOF持久化功能 |
auto-aof-rewrite-min-size | 触发重写的最小尺寸 |
auto-aof-rewrite-percentage | AOF文件增长率 |
aof_current_size | AOF当前尺寸 |
aof_base_size | AOF上次启动和重写的尺寸(单位:字节) |
AOF重写触发机制
1 | # 根据配置,AOF持久化触发机制如下: |
AOF重写流程
- bgrewriteaof触发重写,判断是否当前有bgsave或bgrewriteaof在运行,如果有,则等待该命令结束后再继续执行。
- 主进程fork出子进程执行重写操作,保证主进程不会阻塞。
- 子进程遍历redis内存中数据到临时文件,客户端的写请求同时写入aof_buf缓冲区和aof_rewrite_buf重写缓冲区保证原AOF文件完整以及新AOF文件生成期间的新的数据修改动作不会丢失。
- 子进程写完新的AOF文件后,向主进程发信号,父进程更新统计信息。
- 主进程把aof_rewrite_buf中的数据写入到新的AOF文件。
- 使用新的AOF文件覆盖旧的AOF文件,完成AOF重写。
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缺点
- 对同样的数据集,AOF文件通常要大于等价的RDB文件。
- AOF可能比RDB慢,这取决于准确的fsync策略。通常fsync设置为每秒一次的话性能仍然很高,如果关闭fsync,即使在很高的负载下也和RDB一样的快。不过,即使在很大的写负载情况下,RDB还是能提供能好的最大延迟保证。
- 在过去,我们经历了一些针对特殊命令(例如,像BRPOPLPUSH这样的阻塞命令)的罕见bug,导致在数据加载时无法恢复到保存时的样子。这些bug很罕见,我们也在测试套件中进行了测试,自动随机创造复杂的数据集,然后加载它们以检查一切是否正常,但是,这类bug几乎不可能出现在RDB持久化中。为了说得更清楚一点:Redis AOF是通过递增地更新一个已经存在的状态,像MySQL或者MongoDB一样,而RDB快照是一次又一次地从头开始创造一切,概念上更健壮。
- 注意
- 要注意Redis每次重写AOF时都是以当前数据集中的真实数据从头开始,相对于一直追加的AOF文件(或者一次重写读取老的AOF文件而不是读内存中的数据),对bug的免疫力更强。
- 我们还没有收到一份用户在真实世界中检测到崩溃的报告。
总结:
- 比起RDB占用更多的磁盘空间。
- 恢复备份速度要慢。
- 每次读写都同步的话,有一定的性能压力。
- 存在个别Bug,造成恢复不能。
AOF持久化优缺点总结
- 优点:可以最大程度保证数据不丢失
- 缺点:日志记录量级比较大
RDB与AOF抉择
RDB与AOF比较
命令 | RDB | AOF |
---|---|---|
启动优先级 | 低 | 高 |
体积 | 小 | 大 |
恢复速度 | 快 | 慢 |
数据安全性 | 丢数据 | 根据策略的不同,丢数据的情况也不同 |
轻重 | 重 | 轻 |
官网建议
- RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储
- AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾.
- Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大
- 只做缓存:如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式.
- 同时开启两种持久化方式
- 在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据, 因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整.
- RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件。那要不要只使用AOF呢?
建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份), 快速重启,而且不会有AOF可能潜在的bug,留着作为一个万一的手段。 - 性能建议
因为RDB文件只用作后备用途,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留save 900 1这条规则。
如果使用AOF,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了。
代价,一是带来了持续的IO,二是AOF rewrite的最后将rewrite过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。
只要硬盘许可,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上。
默认超过原大小100%大小时重写可以改到适当的数值。
个人建议
- 一般来说,如果想达到足以媲美 PostgreSQL 的数据安全性, 你应该同时使用两种持久化功能。
- 如果你非常关心你的数据,但仍然可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化。
- 有很多用户单独使用AOF,但是我们并不鼓励这样,因为时常进行RDB快照非常方便于数据库备份,启动速度也较之快,还避免了AOF引擎的bug。
- 个人感触:在企业中,通常都使用RDB来做持久化,因为一般redis是在做MySQL的缓存,就算缓存数据丢失,真实的数据还是在MySQL中,之所以用缓存是为了速度,性能而考虑,所以还是建议使用RDB持久化,相对来说会好一些,除非专门用redis来做一个key:value的数据库,而且数据很重要,那么可以考虑使用AOF
- 注意:基于这些原因,将来我们可能会统一AOF和RDB为一种单一的持久化模型(长远计划)。
RDB与AOF之间的优劣势
RDB
- 优点
- 压缩后的二进制文件,速度快,适合于用作备份、全量复制及灾难恢复。
- 主从复制也是基于RDB持久化功能实现的
- RDB恢复数据性能优于AOF方式。
- 缺点
- 无法做到实时持久化,每次都要创建子进程,频繁操作成本过高
- 保存后的二进制文件,不同版本直接存在兼容性问题
- 会有数据丢失、导致服务停止几秒
- 优点
AOF
- 优点
- 以文本形式保存,易读
- 记录写操作保证数据不丢失
- 缺点
- 存储所有写操作命令,且文件为文本格式保存,未经压缩,文件体积高。
- 恢复数据时重放AOF中所有代码,恢复性能弱于RDB方式。
- 优点
RDB 和 AOF 之间的相互作用
- 在版本号大于等于 2.4 的 Redis 中, BGSAVE 执行的过程中,不可以执行 BGRWRITEAOF 。反过来说,在 BGRWRITEAOF 执行的过程中,也不可以执行 BGSAVE 。
- 这可以防止两个 Redis 后台进程同时对磁盘进行大量的 I/O 操作。如果 BGSAVE 正在执行,并且用户显示地调用 BGRWRITEAOF 命令,那么服务器将向用户回复一个 OK 状态,并告知用户,BGRWRITEAOF 已经被预定执行; 一旦 BGSAVE 执行完毕, BGRWRITEAOF 就会正式开始。
- 当 Redis 启动时,如果 RDB 持久化和 AOF 持久化都被打开了,那么程序会优先使用 AOF 文件来恢复数据集,因为 AOF 文件所保存的数据通常是最完整的。
AOF与RDB混合
看了上面的RDB和AOF的介绍后,我们可以发现,使用RDB持久化会有数据丢失的风险,但是恢复速度快,而使用AOF持久化可以保证数据完整性,但恢复数据的时候会很慢。于是从Redis4之后新增了混合AOF和RDB的模式,先使用RDB进行快照存储,然后使用AOF持久化记录所有的写操作,当重写策略满足或手动触发重写的时候,将最新的数据存储为新的RDB记录。这样的话,重启服务的时候会从RDB何AOF两部分恢复数据,即保证了数据完整性,又提高了恢复的性能。
开启混合模式后,每当bgrewriteaof命令之后会在AOF文件中以RDB格式写入当前最新的数据,之后的新的写操作继续以AOF的追加形式追加写命令。当redis重启的时候,加载 aof 文件进行恢复数据:先加载 rdb 的部分再加载剩余的 aof部分。
混合配置
1 | # 修改下面的参数即可开启AOF,RDB混合持久化 |
混合模式示例
1 | 开启混合持久化模式后,重写之后的aof文件里和rdb一样存储二进制的快照数据,继续往redis中进行写操作,后续操作在aof中仍然是以命令的方式追加。因此重写后aof文件由两部分组成,一部分是类似rdb的二进制快照,另一部分是追加的命令文本。 |
RDB备份
Redis如何备份
- Redis 对于数据备份是非常友好的,因为你可以在服务器运行的时候对 RDB 文件进行复制: RDB 文件一旦被创建,就不会进行任何修改。
- 当服务器要创建一个新的 RDB 文件时,它先将文件的内容保存在一个临时文件里面,当临时文件写入完毕时,程序才使用临时文件替换原来的 RDB 文件。
- 这也就是说,无论何时, 复制 RDB 文件都是绝对安全的。
建议:
- 创建一个定期任务(cron job), 每小时将一个 RDB 文件备份到一个文件夹, 并且每天将一个 RDB 文件备份到另一个文件夹。
- 确保快照的备份都带有相应的日期和时间信息, 每次执行定期任务脚本时, 使用 find 命令来删除过期的快照: 比如说, 你可以保留最近 48 小时内的每小时快照, 还可以保留最近一两个月的每日快照。
- 至少每天一次, 将 RDB 备份到你的数据中心之外, 或者至少是备份到你运行 Redis 服务器的物理机器之外。
执行步骤
- 备份
- 先通过config get dir 查询rdb文件的目录
- 将*.rdb的文件拷贝到别的地方
- 恢复
- 先停止redis
- 将rdb文件放到指定目录下
- 启动redis
停止备份
在配置文件中就设置save ""
或在命令行中config set save ""
。
手动开始备份
- save:会立即生成dump.rdb,但是会阻塞往redis内存中写入数据。
- bgsave:后台异步备份。
- 如果是使用flushdb命令,会把之前的快照更新成当前的空状态,所以执行了flushdb后更新的快照是没有数据的。
save与bgsave对比
命令 | save | bgsave |
---|---|---|
IO类型 | 同步 | 异步 |
是否阻塞 | 是 | 否(阻塞发生在fork) |
优点 | 不会消耗额外内存 | 不阻塞客户端命令 |
缺点 | 阻塞客户端命令 | 创建fork,消耗内存 |
高级配置
RDB持久化高级配置
1 | # 编辑配置文件 |
AOF持久化高级配置
1 | # 编辑配置文件 |