本文将先说明持久化、主存复制(及读写分离)、哨兵、以及集群几种技术分别解决了Redis高可用的什么问题;
然后详细介绍Redis的持久化技术,主要是RDB和AOF两种持久化方案;在介绍RDB和AOF方案时,不仅介绍其作用及操作方法,同时介绍持久化实现的一些原理细节及需要注意的问题。
最后,介绍在实际使用中,持久化方案的选择,以及经常遇到的问题等。
在介绍Redis高可用之前,先说明在Redis的语境中高可用的含义。
我们知道,在Web服务器中,高可用是指服务器可以正常访问的时间,衡量的标准是在多长时间内可以提供正常服务(99.9%、99.99%、99.999% 等等)。但是在Redis语境中,高可用的含义似乎要宽泛一些,除了保证提供正常服务(主存分离、快速容灾技术)还需要考虑数据容量的扩展,数据安全不会丢失等。
在Redis中,实现高可用技术主要包括持久化、主存复制、哨兵和集群,下面分别说明他们的作用以及解决了什么问题。
读写分离
)。缺陷:故障恢复无法自动化;写操作无法负载均衡;存储能力受到单机的限制
。写操作无法负载均衡;存储能力受单机限制
不支持故障恢复以及自动转移
Redis是内存数据库,数据都是存储在内存中,为了避免进程退出导致数据永久丢失,需要定期将Redis中的数据以某种形式(数据/命令)从内存保存到磁盘;当下次Redis重启时,利用持久化文件实现数据恢复。除此之外,为了进行灾难备份,可以将持久化文件拷贝到一个远程位置;
RDB 持久化: 是指在指定的时间间隔内将内存中的数据集快照写入磁盘
,也就是 snapshot内存快照
,它恢复时再将磁盘快照文件(dump.rdb
)直接读回到内存。
实现类似照片记录效果的方式,就是把某一时刻的数据和状态以文件的形式写到磁盘上,也就是快照。这样一来即使故障宕机,快照文件也不会丢失,数据的可靠性也就得到了保证。
这个快照文件就称为RDB文件(dump.rdb
),其中,RDB就是Redis DataBase的缩写。
AOF持久化: 是指以日志的形式来记录每个写操作
,将Redis所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据。
换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次已完成数据的恢复工具。
默认情况下,redis是没有开启AOF(append of file)的。开启AOF功能需要设置配置:
appendonly yes
AOF 保存的是 appendonly.aof
文件
疑问:rdb vs aof 可否共存?如果共存以哪种方式为准?
下面是配置文件的说明
数据恢复顺序与加载流程:在同时开启 rdb和aof 持久化时,重启时只会加载aof文件,不会加载rdb文件。
同时关闭 RDB+AOF
// 禁用rdb
save ""
// 禁用aof
appendonly no
注意在禁用rdb和aof后仍然可以使用持久化命令生成对应的持久化文件。
save
bgsave
生成rdb文件。bgrewriteaof
生成aof文件。RDB保存到磁盘的文件叫dump.rdb
。
RDB 持久化是指在指定的时间间隔内将内存中的数据快照写入磁盘,实际操作过程是 fork 一个子进程,先将数据写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。
第一步: 配置文件说明
redis 6.0.16以下配置
redis 6.2 以及redis 7
第二步: 2) 操作说明
操作主要分为手动触发
和自动触发
两种方式。
方式一:自动触发
Redus 7 版本 按照 redis.conf 里配置的 save
比如 5秒修改2次
第三步:如何恢复
将备份文件(dump.rdb
) 移动到redis安装目录并启动服务器即可。
验证一:备份成功后故意使用flushdb
清空redis,看看是否可以恢复数据。
验证二:物理恢复,服务和备份分机隔离
。
备注:不可以把备份文件dump.rdb和生产redis服务器放在同一台机器,必须分开各自存储,以防生产机物理损坏后备份文件也挂了。
方式二:手动触发
redis 提供了两个命令来支持生成rdb文件,分别是 save
和 bgsave
(1)save 同步阻塞生成rdb文件 主要是在主程序执行,会阻塞当前redis服务器,直到持久化工作完成,执行save命令期间,redis不能处理其它命令,线上禁止使用
。
案例说明:
(2)bgsave 异步生成rdb文件(默认):redis会在后台异步快照操作,不阻塞
快照,同时还可以响应客户端请求,该触发方式会 fork一个子进程,由子进程复制持久化过程。
fork 是什么?
在Linux程序中,fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,尽量避免膨胀。
案例说明:
lastsave 可以通过lastsave命令获取最后一次成功执行快照的时间。
优势:
劣势:
数据丢失的案例:
kill -9
故意模拟意外宕机方式一:使用命令设置保存rdb规则的方法:redis-cli config set save ""
方式二:配置文件,快照禁用
命令写入 (append)、文件同步(sync)、文件重写(rewrite)、重启加载 (load)。
# 每次有新命令追加到 AOF 文件时就执行一次同步 :非常慢,也非常安全
$ always
# 每秒同步一次:足够快(和使用 RDB 持久化差不多),并且在故障时只会丢失 1 秒钟的数据
# 推荐(并且也是默认)的措施为每秒同步一次, 这种策略可以兼顾速度和安全性
$ everysec
# 从不同步:将数据交给操作系统来处理。更快,也更不安全的选择
$ no
策略一:同步写回Always
:每个写命令执行完立刻同步的将命令写回磁盘。
策略二:每秒写回everysec
:每个写命令执行完,只是先把日志写到 AOF文件的内存缓冲区,每隔一秒把缓冲区的内容写入磁盘。
策略三:操作系统控制写回no
:每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,由操作系统决定何时将缓冲区的内容写入磁盘。
具体配置如下
优势:
劣势:
AOF 是存放每条写命令的,所以会不断变大,达到一定的时候,AOF做rewrite操作,会重新生成一个新的AOF文件。
文件越大,占用服务器内存越大以及 AOF 恢复要求时间越长。
为了解决这个问题,Redis新增了重写机制,当AOF文件的大小超过所设定的峰值时,Redis就会自动启动AOF文件的内容压缩,
简单说就是将对一个数据的多个命令的最终结束结果储存到AOF文件中。
重写 rewrite的作用
重写规则
手动重写
自动重写
RDB和AOF持久化各有优缺点,RDB会导致一段时间内的数据丢失,AOF文件会越来越大,会影响Redis的启动速度,为了同时兼顾RDB,AOF的优点,Redis在4.0版本之后
提供了混合持久化
方式。
AOF 重写时会把 Redis 的持久化数据,以 RDB 的格式写入到 AOF 文件的开头,之后的数据再以 AOF 的格式化追加的文件的末尾,如下图所示。
开启混合 将no改为yes即可
优点:混合持久化结合了 RDB 和 AOF 持久化的优点,开头为 RDB 的格式,使得 Redis 可以更快的启动,同时结合 AOF 的优点,有减低了大量数据丢失的风险。
缺点:AOF 文件中添加了 RDB 格式的内容,会使得 AOF 文件的可读性会很差,不容易阅读;
如果开启混合持久化,就必须使用 Redis 4.0 以及之后版本。
使用混合持久化的时候可以根据自身业务选择关闭RDB或者AOF,或者关闭持久化。
二者选择的标准,就是看是否愿意牺牲一些性能,换取更高的缓存一致性(AOF),还是愿意写操作频繁的时候,不启用备份来换取更高的性能,待手动运行 save 的时候,再做备份(RDB)。
注: 未来 Redis 可能会将 AOF 和 RDB 整合成单个持久化模型.
Redis 的读写操作都是在内存中,所以 Redis 性能才会高,但是当 Redis 重启后,内存中的数据就会丢失,那为了保证内存中的数据不会丢失,Redis 实现了数据持久化的机制,这个机制会把数据存储到磁盘,这样在 Redis 重启就能够从磁盘中恢复原有的数据。
Redis 共有三种数据持久化的方式:
Redis 在执行完一条写操作命令后,就会把该命令以追加的方式写入到一个文件里,然后 Redis 重启时,会读取该文件记录的命令,然后逐一执行命令的方式来进行数据恢复。
我这里以「set name xiaolin」命令作为例子,Redis 执行了这条命令后,记录在 AOF 日志里的内容如下图:
我这里给大家解释下。
「*3」表示当前命令有三个部分,每部分都是以「$+数字」开头,后面紧跟着具体的命令、键或值。然后,这里的「数字」表示这部分中的命令、键或值一共有多少字节。例如,「$3 set」表示这部分有 3 个字节,也就是「set」命令这个字符串的长度。
Reids 是先执行写操作命令后,才将该命令记录到 AOF 日志里的,这么做其实有两个好处。
当然,这样做也会带来风险:
先来看看,Redis 写入 AOF 日志的过程,如下图:
具体说说:
Redis 提供了 3 种写回硬盘的策略,控制的就是上面说的第三步的过程。 在 Redis.conf 配置文件中的 appendfsync 配置项可以有以下 3 种参数可填:
我也把这 3 个写回策略的优缺点总结成了一张表格:
因为 AOF 日志记录的是操作命令,不是实际的数据,所以用 AOF 方法做故障恢复时,需要全量把日志都执行一遍,一旦 AOF 日志非常多,势必会造成 Redis 的恢复操作缓慢。
为了解决这个问题,Redis 增加了 RDB 快照。所谓的快照,就是记录某一个瞬间东西,比如当我们给风景拍照时,那一个瞬间的画面和信息就记录到了一张照片。
所以,RDB 快照就是记录某一个瞬间的内存数据,记录的是实际数据,而 AOF 文件记录的是命令操作的日志,而不是实际的数据。
因此在 Redis 恢复数据时, RDB 恢复数据的效率会比 AOF 高些,因为直接将 RDB 文件读入内存就可以,不需要像 AOF 那样还需要额外执行操作命令的步骤才能恢复数据。
Redis 提供了两个命令来生成 RDB 文件,分别是 save 和 bgsave,他们的区别就在于是否在「主线程」里执行:
会阻塞主线程
;可以避免主线程的阻塞
;Redis 还可以通过配置文件的选项来实现每隔一段时间自动执行一次 bgsave 命令,默认会提供以下配置:
// 900 秒之内,对数据库进行了至少 1 次修改;
// 300 秒之内,对数据库进行了至少 10 次修改;
// 60 秒之内,对数据库进行了至少 10000 次修改。
save 900 1
save 300 10
save 60 10000
别看选项名叫 save,实际上执行的是 bgsave 命令,也就是会创建子进程来生成 RDB 快照文件。 只要满足上面条件的任意一个,就会执行 bgsave。
Redis 的快照是全量快照
,也就是说每次执行快照,都是把内存中的「所有数据」都记录到磁盘中。所以执行快照是一个比较重的操作,如果频率太频繁,可能会对 Redis 性能产生影响。如果频率太低,服务器故障时,丢失的数据会更多。
可以的,执行 bgsave 过程中,Redis 依然可以继续处理操作命令
的,也就是数据是能被修改的,关键的技术就在于写时复制技术(Copy-On-Write, COW)
。
执行 bgsave 命令的时候,会通过 fork() 创建子进程,此时子进程和父进程是共享同一片内存数据的,因为创建子进程的时候,会复制父进程的页表,但是页表指向的物理内存还是一个,此时如果主线程执行读操作,则主线程和 bgsave 子进程互相不影响。
如果主线程执行写操作,则被修改的数据会复制一份副本,然后 bgsave 子进程会把该副本数据写入 RDB 文件,在这个过程中,主线程仍然可以直接修改原来的数据。
RDB 优点是数据恢复速度快,但是快照的频率不好把握。频率太低,丢失的数据就会比较多,频率太高,就会影响性能。
AOF 优点是丢失数据少,但是数据恢复不快。
为了集成了两者的优点, Redis 4.0 提出了混合使用 AOF 日志和内存快照
,也叫混合持久化,既保证了 Redis 重启速度,又降低数据丢失风险。
混合持久化工作在 AOF 日志重写过程
,当开启了混合持久化时,在 AOF 重写日志时,fork 出来的重写子进程会先将与主线程共享的内存数据以 RDB 方式写入到 AOF 文件,然后主线程处理的操作命令会被记录在重写缓冲区里,重写缓冲区里的增量命令会以 AOF 方式写入到 AOF 文件,写入完成后通知主进程将新的含有 RDB 格式和 AOF 格式的 AOF 文件替换旧的的 AOF 文件。
也就是说,使用了混合持久化,AOF 文件的前半部分是 RDB 格式的全量数据,后半部分是 AOF 格式的增量数据
。
这样的好处在于,重启 Redis 加载数据的时候,由于前半部分是 RDB 内容,这样加载的时候速度会很快。
加载完 RDB 的内容后,才会加载后半部分的 AOF 内容,这里的内容是 Redis 后台子进程重写 AOF 期间,主线程处理的操作命令,可以使得数据更少的丢失
。
混合持久化优点:混合持久化结合了 RDB 和 AOF 持久化的优点,开头为 RDB 的格式,使得 Redis 可以更快的启动,同时结合 AOF 的优点,有减低了大量数据丢失的风险。
混合持久化缺点: