介绍
持久化的功能:Redis是内存数据库,数据都是存储在内存中,为了避免进程退出导致数据的永久丢失,需要定期将Redis中的数据以某种形式(数据或命令)从内存保存到硬盘中。当下次Redis重启时,利用持久化文件实现数据恢复。除此之外,为了进行灾难备份,可以将持久化文件拷贝到一个远程位置。Redis持久化分为RDB和AOF,前者将当前数据保存到硬盘,后者则是将每次执行的写命令保存的硬盘。
RDB持久化
RDB是一种快照存储持久方式,具体就是将Redis某一时刻的内存数据保存到硬盘的文件当中,默认保存的文件名为dump.rdb,而在Redis服务器启动时,会重新加载dump.rdb文件的数据到内存当中恢复数据。触发RDB持久化过程分为手动触发和自动触发。
触发机制
手动触发分别对应save
和bgsave
命令:
save
命令:阻塞当前Redis服务器,直到RDB过程完成为止,对于内存比较大的实例会造成长时间阻塞,线上环境不建议使用。
bgsave
命令:Redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束。阻塞只发生在fork阶段,一般时间很短。
显示bgsave
命令是针对save
阻塞问题做的优化。因此Redis内部所有的涉及RDB的操作都采用bgsave
的方式。
除了执行命令手动触发之外,Redis内部还存在自动触发RDB的持久化机制,例如以下场景:
- 使用
save
相关配置,如save m n
。表示m秒内数据集存在n次修改时,自动触发bgsave
。 - 如果从节点执行全量复制操作,主节点自动执行
bgsave
生成RDB文件并发送给从节点。 - 执行
debug reload
命令重载Redis时,也会自动触发save
操作。 - 默认情况下执行
shutdown
命令时,如果没有开启AOF持久化功能则自动执行bgsave
。
执行流程
bgsave
是主流的触发RDB持久化方式:
- 执行
bgsave
命令,Redis父进程判断当前是否存在正在执行的子进程,如RDB/AOF子进程,如果存在bgsave
命令直接返回。 - 父进程执行fork操作创建子进程,fork操作过程中父进程会阻塞,通过
info stats
命令查看latest_fork_usec
选项,可以获得最近一个fork操作的耗时,单位为微妙。 - 父进程fork完成后,
bgsave
命令返回Background saving started信息并不再阻塞父进程,可以继续响应其它命令。 - 子进程创建RDB文件,根据父进程内存生成临时快照文件,完成后原有文件进行原子替换。执行
lastsave
命令可以获取最后一次生成RDB的时间,对应info
统计的rdb_last_save_time
选项。 - 进程发送信号给父进程表示完成,父进程更新统计信息,具体见
info Persistence
下的rdb_*
相关选项。
服务器配置自动触发
除了通过客户端发送命令外,还有一种方式,就是在Redis配置文件中的save
指定到达触发RDB持久化的条件,比如【多少秒内至少达到多少写操作】就开启RDB数据同步。
例如我们可以在配置文件redis.conf指定如下的选项:
# 900s内至少达到1条写命令
save 900 1
# 300s内至少达到10条写命令
save 300 10
# 60s内至少达到1000条写命令
save 60 1000
这种通过服务器配置文件触发RDB的方式,与bgsave
命令类似,达到触发条件时,会fork一个子进程进行数据同步,不过最好不要通过这种方式来触发RDB持久化,因为设置触发的时间太短,则容易频繁写入rdb文件,影响服务器性能,时间设置太长会造成数据丢失。
RDB文件的处理
保存:
- RDB文件保存在dir配置指定的目录下,文件名通过dbfilename配置指定。可通过执行
config set dir {newDir}
和config set dbfilename {newFileName}
运行期动态执行,当下次运行时RDB文件会保存到新目录。
压缩:
- Redis默认采用LZF算法对生存的RDB文件做压缩处理,压缩后的文件远远小于内存大小,默认开启,可以通过参数
config set rdbcompression {yes|no}
动态修改。 - 虽然压缩RDB会消耗CPU,但可大幅度降低文件的体积,方便保存到硬盘或通过网络发送给从节点,因此线上建议开启。
RDB方式的优缺点
优点:
- RDB是一个非常紧凑的文件,它保存了Redis在某个时间点上的数据集。这种文件非常适合用于备份;比如说,你可以在最近的24小时内,每小时备份一次RDB文件,并且在每个月的每一天,也备份一个RDB文件。这样的话,即使遇上问题,也可以随时将数据集还原到不同的版本。
- RDB可以最大化Redis的性能;父进程在保存RDB文件时唯一要做的就是fork出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无需执行任何磁盘I/O操作。
- RDB在恢复大数据集时的速度要比AOF的恢复速度快。
缺点:
- RDB方式数据没办法做到实时持久化/秒级持久化。如果服务器宕机的话,采用RDB的方式会造成某个时段内数据的丢失,比如我们设置10分钟同步一次或者5分钟达到1000次写入就同步一次,那么如果还没达到触发条件服务器就死机了,那么这个时间段的数据会丢失。
- 使用
bgsave
命令在fork子进程时,如果数据量太大,fork的过程也会发生阻塞,另外,fork子进程会消耗内存。针对RDB不适合做实时持久化的问题,Redis提供了AOF持久化方式来解决。
AOF持久化
AOF(append only file)持久化;与RDB存储某个时刻的快照不同,AOF持久化方式会记录客户端对服务器的每一次写操作命令到日志当中,并将这些操作以Redis协议追加保存到以后缀为aof文件末尾。
使用AOF
开启AOF功能需要设置配置;appendonly yes
,默认不开启。AOF文件名通过appendfilename
配置设置,默认文件名是appendonly.aof。保存路径同RDB持久化方式一致,通过dir配置指定。
持久化配置
appendonly yes #启用aof持久化方式
appendfsync always #每次收到命令就立即强制写入磁盘,最慢的大概只有几百的TPS,但是保证完全的持久化,不推荐使用
appendfsync everysec #每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,推荐
appendfsync no #完全依赖os,性能最好,持久化没保证,Redis不会主动调用fsync去将AOF日志内容同步到磁盘,所以这一切完全依赖于操作系统的调试了。对于大多数Linux操作系统,是每30s进行一次fsync,将缓冲区中的数据写的磁盘上。
执行流程
- 所以的写入命令会追加到aof_buf(缓冲区)中。
- AOF缓冲区根据对应的策略向硬盘做同步操作。
- 随着AOF文件越来越大,需要定期对AOF文件进行重写,达到压缩的目的。
- 当Redis服务器重启时,可以加载AOF文件进行数据恢复。
在同步期间可能会发生阻塞问题
- 若果AOF文件fsync同步时间大于2s,Redis主进程就会阻塞。
- 若果AOF文件fsync同步时间小于2s,Redis主进程就会返回。
其实这样做是为了保证文件安全性的一种策略。
AOF追加阻塞会产生的问题:
- fsync大于2s时候,会阻塞redis主进程,我们都知道redis主进程是用来执行redis命令的,是不能阻塞的。
- 虽然每秒everysec刷盘策略,但是实际上不是丢失1s数据,实际有可能丢失2s数据。
重写机制
AOF将客户端的每一个写操作都追加到aof文件末尾,随着命令不断写入AOF,文件会越来越大,为了解决这个问题,Redis引入AOF重写机制压缩文件体积。
-
AOF文件重写是吧Redis进程内的数据转化为写命令同步到新AOF文件的过程。
比如:多条命令可以合并为一个,
lpush list a、lpush list b
可以转化为lpush list a b
。 AOF重写降低了文件占用空间,除此之外,另一个目的是:更小的AOF文件可以更快地被加载。
触发机制:
AOF重写过程可以手动触发和自动触发:
- 手动触发:直接调用
bgrewriteaof
命令。 - 自动触发:根据
auto-aof-rewrite-min-size
和auto-aof-rewrite-percentage
参数确定自动触发。-
auto-aof-rewrite-min-size
表示运行AOF重写时文件最小体积,默认为64MB。 -
auto-aof-rewrite-percentage
代表当前AOF文件空间(aof_current_size)和上一次重写后AOF文件空间(aof_base_size)的比值。
-
- 示例:
auto-aof-rewrite-percentage:100
auto-aof-rewrite-min-size:64mb
- 默认配置时当AOF文件大小是上次rewrite后大小的一倍且文件大于64mb时触发。
当触发AOF重写时,内部流程:
执行AOF重写请求。如果当前进程正在执行AOF重写,请求不执行并返回如下响应:ERR Background append only file rewriting already in progress
- 父进程执行fork创建子进程,开销等同于bgsave过程。
- 主进程fork操作完成后,继续响应其它命令。所以修改命令依然写入AOF缓冲区并根据
appendfsync
策略同步到硬盘,保证原有AOF机制正确性。 - 由于fork操作运用写时复制技术,子进程只能共享fork操作时的内存数据。由于父进程依然响应命令,Redis使用AOF重写缓冲区保存这部分新数据,防止新AOF文件生成期间丢失这部分数据。
- 主进程fork操作完成后,继续响应其它命令。所以修改命令依然写入AOF缓冲区并根据
- 子进程根据内存快照,按照命令合并规则写入新的AOF文件,每次批量写入硬盘数据量由配置
aof-rewrite-incremental-fsync
控制,默认为32MB,防止单词刷盘数据过多造成硬盘阻塞。- 新AOF文件写入完成后,子进程发送信号给父进程,父进程更新统计信息,具体见
info persistence
的aof_*相关统计。 - 父进程把AOF重写缓冲区的数据写入到新的AOF文件。
- 使用新AOF文件替换老文件,完成AOF重写。
- 新AOF文件写入完成后,子进程发送信号给父进程,父进程更新统计信息,具体见
注意事项
在写入AOF日志文件时,如果Redis服务器宕机,则aof日志文件会出现格式错误,在重启Redis服务器时,Redis服务器会拒绝载入这个aof文件,可以通过命令修复aof并恢复数据。
redid-check-aof -fix appendonly.aof
AOF的优缺点
优点:
- AOF可以设置完全不同步、每秒同步、每次操作同步,默认时每秒同步。因为AOF时操作指令的追加,所以可以频繁的大量的同步。
- AOF文件是一个值追加日志的文件,即使服务宕机为写入完整的命令,也可以通过redis-check-aof工具修复这些问题。
- 如果AOF文件过大,Redis会在后台自动地重写AOF文件。重写后会使AOF文件压缩到最小所需的指令集。
- AOF文件是有序保存数据看的所有写入操作,易读,易分析。即使如果不小心误操作数据看,也很容易找出业务错误指令,恢复到某个数据节点。例如不小
FLUSHALL
,可以非常容易恢复到执行命令之前。
缺点:
- 相同数据量下,AOF的文件通常体积会比RDB大。因为AOF是存指令的,而RDB是所有指令的结果快照。但AOF在日志重写后会压缩一些空间。
- 在大量写入和载入的时候,AOF的效率会比RDB低,因为大量写入,AOF会执行更多的保存命令,载入的时候也需要大量的重执行命令来得到最后的结果。RDB对此更有优势。
AOF常用配置
appendonly no
:是否开启AOF
appendfilename "appendonly.aof"
:AOF文件名
dir ./
:RDB文件和AOF文件所在目录
appendfsync everysec
:fsync持久化策略
no-appendfsync-on-rewrite no
:AOF重写期间是否禁止fsync;如果开启该选项,可以减轻文件重写时CPU和硬盘的负载(尤其是硬盘),但是可能会丢失AOF重写期间的数据;需要在负载和安全性之间进行平衡
auto-aof-rewrite-percentage 100
:文件重写触发条件之一
auto-aof-rewrite-min-size 64mb
:文件重写触发提交之一
aof-load-truncated yes
:如果AOF文件结尾损坏,Redis启动时是否仍载入AOF文件
重启加载的选择
AOF和RDB文件都可以用于服务器重启时的数据恢复。
持久化的选择
在实际生产环境中,根据数据量、应用对数据的安全要求、预算限制等不同情况,会有各种各样的持久化策略;如完全不使用任何持久化、使用RDB或AOF的一种,或同时开启RDB和AOF持久化等。
此外,持久化的选择必须与Redis的主从策略一起考虑,因为主从复制与持久化同样具有数据备份的功能,而且主机master和从机slave可以独立的选择持久化方案。
面分场景来讨论持久化策略的选择,下面的讨论也只是作为参考,实际方案可能更复杂更具多样性。
- 如果Redis中的数据完全丢弃也没有关系(如Redis完全用作DB层数据的cache),那么无论是单机,还是主从架构,都可以不进行任何持久化。
- 在单机环境下(对于个人开发者,这种情况可能比较常见),如果可以接受十几分钟或更多的数据丢失,选择RDB对Redis的性能更加有利;如果只能接受秒级别的数据丢失,应该选择AOF。
- 但在多数情况下,我们都会配置主从环境,slave的存在既可以实现数据的热备,也可以进行读写分离分担Redis读请求,以及在master宕掉后继续提供服务。在这种情况下的做法是:
- master:完全关闭持久化(包括RDB和AOF),这样可以让master的性能达到最好;
- slave:关闭RDB,开启AOF(如果对数据安全要求不高,开启RDB关闭AOF也可以),并定时对持久化文件进行备份(如备份到其他文件夹,并标记好备份的时间);然后关闭AOF的自动重写,然后添加定时任务,在每天Redis闲时(如凌晨12点)调用
bgrewriteaof
。 - 这里需要解释一下,为什么开启了主从复制,可以实现数据的热备份,还需要设置持久化呢?因为在一些特殊情况下,主从复制仍然不足以保证数据的安全,例如:
- master和slave进程同时停止:考虑这样一种场景,如果master和slave在同一个机房,则一次停电事故就可能导致master和slave机器同时关机,Redis进程停止;如果没有持久化,则面临的是数据的完全丢失。
- master误重启:考虑这样一种场景,master服务因为故障宕掉了,如果系统中有自动拉起机制(即检测到服务停止后重启该服务)将master自动重启,由于没有持久化文件,那么master重启后数据是空的,slave同步数据也变成了空的;如果master和slave都没有持久化,同样会面临数据的完全丢失。需要注意的是,即便是使用了哨兵进行自动的主从切换,也有可能在哨兵轮询到master之前,便被自动拉起机制重启了。因此,应尽量避免“自动拉起机制”和“不做持久化”同时出现。
- 异地灾备:上述讨论的几种持久化策略,针对的都是一般的系统故障,如进程异常退出、宕机、断电等,这些故障不会损坏硬盘。但是对于一些可能导致硬盘损坏的灾难情况,如火灾地震,就需要进行异地灾备。
- 例如对于单机的情形,可以定时将RDB文件或重写后的AOF文件,通过scp拷贝到远程机器,如阿里云;对于主从的情形,可以定时在master上执行
bgsave
,然后将RDB文件拷贝到远程机器,或者在slave上执行bgrewriteaof
重写AOF文件后,将AOF文件拷贝到远程机器上。 - 一般来说,由于RDB文件文件小、恢复快,因此灾难恢复常用RDB文件;异地备份的频率根据数据安全性的需要及其它条件来确定,但最好不要低于一 天一次。
- 例如对于单机的情形,可以定时将RDB文件或重写后的AOF文件,通过scp拷贝到远程机器,如阿里云;对于主从的情形,可以定时在master上执行
持久化配置方案
-
企业级的持久化的配置策略
-
save 60 10000
:如果你希望尽可能确保说,RDB最多丢1分钟的数据,那么尽量就是每隔1分钟都生成一个快照,低峰期,数据量很少,也没必要 10000->生成RDB,1000->RDB,这个根据你自己的应用和业务的数据量,自己去决定 - AOF一定要打开。
-
auto-aof-rewrite-percentage 100
: 就是当前AOF大小膨胀到超过上次100%,上次的两倍 -
auto-aof-rewrite-min-size 64mb
: 根据你的数据量来定,16mb,32mb
-
-
数据备份方案 RDB非常适合做冷备,每次生成之后,就不会再有修改了
- 写crontab定时调度脚本去做数据备份
- 每小时都copy一份rdb的备份,到一个目录中去,仅仅保留最近48小时的备份
- 每天都保留一份当日的rdb的备份,到一个目录中去,仅仅保留最近1个月的备份
- 每次copy备份的时候,都把太旧的备份给删了
- 每天晚上将当前服务器上所有的数据备份,发送一份到远程的云服务上去【crontab】