在上一篇文章中,介绍了《Redis的内存模型》,从这篇文章开始,将依次介绍 Redis 高可用相关的知识——持久化、复制(及读写分离)、哨兵、以及集群。
三穗网站建设公司创新互联,三穗网站设计制作,有大型网站制作公司丰富经验。已为三穗上千多家提供企业网站建设服务。企业网站搭建\外贸网站建设要多少钱,请找那个售后服务好的三穗做网站的公司定做!
本文将先说明上述几种技术分别解决了 Redis 高可用的什么问题,然后详细介绍 Redis 的持久化技术,主要是 RDB 和 AOF 两种持久化方案。
在介绍 RDB 和 AOF 方案时,不仅介绍它的作用及操作方法,同时介绍持久化实现的一些原理细节及需要注意的问题。***,介绍在实际使用中,持久化方案的选择,以及经常遇到的问题等。
下面分别从以下几个方面讲解:
Redis 高可用概述
在介绍 Redis 高可用之前,先说明一下在 Redis 的语境中高可用的含义。在 Web 服务器中,高可用是指服务器可以正常访问的时间,衡量的标准是在多长时间内可以提供正常服务(99.9%、99.99%、99.999% 等等)。
但是在 Redis 语境中,高可用的含义似乎要宽泛一些,除了保证提供正常服务(如主从分离、快速容灾技术),还需要考虑数据容量的扩展、数据安全不会丢失等。
在 Redis 中,实现高可用的技术主要包括持久化、复制、哨兵和集群,下面分别说明它们的作用,以及解决了什么样的问题:
持久化:持久化是最简单的高可用方法(有时甚至不被归为高可用的手段),主要作用是数据备份,即将数据存储在硬盘,保证数据不会因进程退出而丢失。
复制:复制是高可用 Redis 的基础,哨兵和集群都是在复制基础上实现高可用的。复制主要实现了数据的多机备份,以及对于读操作的负载均衡和简单的故障恢复。
缺陷:故障恢复无法自动化,写操作无法负载均衡,存储能力受到单机的限制。
哨兵:在复制的基础上,哨兵实现了自动化的故障恢复。
缺陷:写操作无法负载均衡;存储能力受到单机的限制。
集群:通过集群,Redis 解决了写操作无法负载均衡,以及存储能力受到单机限制的问题,实现了较为完善的高可用方案。
Redis 持久化概述
持久化的功能:Redis 是内存数据库,数据都是存储在内存中。
为了避免进程退出导致数据的***丢失,需要定期将 Redis 中的数据以某种形式(数据或命令)从内存保存到硬盘;当下次 Redis 重启时,利用持久化文件实现数据恢复。
除此之外,为了进行灾难备份,可以将持久化文件拷贝到一个远程位置。
Redis 持久化分为 RDB 持久化和 AOF 持久化:
由于 AOF 持久化的实时性更好,即当进程意外退出时丢失的数据更少,因此 AOF 是目前主流的持久化方式,不过 RDB 持久化仍然有其用武之地。
下面依次介绍 RDB 持久化和 AOF 持久化;由于 Redis 各个版本之间存在差异,如无特殊说明,以 Redis 3.0 为准。
RDB 持久化
RDB 持久化是将当前进程中的数据生成快照保存到硬盘(因此也称作快照持久化),保存的文件后缀是 RDB;当 Redis 重新启动时,可以读取快照文件恢复数据。
触发条件
RDB 持久化的触发分为手动触发和自动触发两种:
手动触发:save 命令和 bgsave 命令都可以生成 RDB 文件。
save 命令会阻塞 Redis 服务器进程,直到 RDB 文件创建完毕为止,在 Redis 服务器阻塞期间,服务器不能处理任何命令请求。
而 bgsave 命令会创建一个子进程,由子进程来负责创建 RDB 文件,父进程(即 Redis 主进程)则继续处理请求。
此时服务器执行日志如下:
bgsave 命令执行过程中,只有 fork 子进程时会阻塞服务器,而对于 save 命令,整个过程都会阻塞服务器。
因此 save 已基本被废弃,线上环境要杜绝 save 的使用;后文中也将只介绍 bgsave 命令。
此外,在自动触发 RDB 持久化时,Redis 也会选择 bgsave 而不是 save 来进行持久化;下面介绍自动触发 RDB 持久化的条件。
自动触发:最常见的情况是在配置文件中通过 save m n,指定当 m 秒内发生 n 次变化时,会触发 bgsave。
例如,查看 Redis 的默认配置文件(Linux 下为 Redis 根目录下的 redis.conf),可以看到如下配置信息:
其中 save 900 1 的含义是:当时间到 900 秒时,如果 Redis 数据发生了至少 1 次变化,则执行 bgsave。
save 300 10 和 save 60 10000 同理,当三个 save 条件满足任意一个时,都会引起 bgsave 的调用。
save m n 的实现原理:Redis 的 save m n,是通过 serverCron 函数、dirty 计数器和 lastsave 时间戳来实现的。
serverCron 是 Redis 服务器的周期性操作函数,默认每隔 100ms 执行一次;该函数对服务器的状态进行维护,其中一项工作就是检查 save m n 配置的条件是否满足,如果满足就执行 bgsave。
dirty 计数器是 Redis 服务器维持的一个状态,记录了上一次执行 bgsave/save 命令后,服务器状态进行了多少次修改(包括增删改);而当 save/bgsave 执行完成后,会将 dirty 重新置为 0。
例如,如果 Redis 执行了 set mykey helloworld,则 dirty 值会 +1;如果执行了 sadd myset v1 v2 v3,则 dirty 值会 +3;注意 dirty 记录的是服务器进行了多少次修改,而不是客户端执行了多少修改数据的命令。
lastsave 时间戳也是 Redis 服务器维持的一个状态,记录的是上一次成功执行 save/bgsave 的时间。
save m n 的原理如下:每隔 100ms,执行 serverCron 函数;在 serverCron 函数中,遍历 save m n 配置的保存条件,只要有一个条件满足,就进行 bgsave。
对于每一个 save m n 条件,只有下面两条同时满足时才算满足:
save m n 执行日志:下图是 save m n 触发 bgsave 执行时,服务器打印日志的情况。
除了 save m n 以外,还有一些其他情况会触发 bgsave:
执行流程
前面介绍了触发 bgsave 的条件,下面将说明 bgsave 命令的执行流程,如下图所示:
图片中的 5 个步骤所进行的操作如下:
RDB 文件
RDB 文件是经过压缩的二进制文件,下面介绍关于 RDB 文件的一些细节。
存储路径
RDB 文件的存储路径既可以在启动前配置,也可以通过命令动态设定。
配置:dir 配置指定目录,dbfilename 指定文件名。默认是 Redis 根目录下的 dump.rdb 文件。
动态设定:Redis 启动后也可以动态修改 RDB 存储路径,在磁盘损害或空间不足时非常有用;执行命令为 config set dir {newdir}和 config set dbfilename {newFileName}。
如下所示(Windows 环境):
RDB 文件格式
RDB 文件格式如下图所示:
其中各个字段的含义说明如下:
只有当数据库中有键值对时,RDB 文件中才会有该数据库的信息(上图所示的 Redis 中只有 0 号和 3 号数据库有键值对);如果 Redis 中所有的数据库都没有键值对,则这一部分直接省略。
其中:SELECTDB 是一个常量,代表后面跟着的是数据库号码;0 和 3 是数据库号码;pairs 则存储了具体的键值对信息,包括 key、value 值,及其数据类型、内部编码、过期时间、压缩信息等等。
压缩
Redis 默认采用 LZF 算法对 RDB 文件进行压缩。虽然压缩耗时,但是可以大大减小 RDB 文件的体积,因此压缩默认开启;可以通过命令关闭:
需要注意的是,RDB 文件的压缩并不是针对整个文件进行的,而是对数据库中的字符串进行的,且只有在字符串达到一定长度(20 字节)时才会进行。
启动时加载
RDB 文件的载入工作是在服务器启动时自动执行的,并没有专门的命令。但是由于 AOF 的优先级更高,因此当 AOF 开启时,Redis 会优先载入 AOF 文件来恢复数据。
只有当 AOF 关闭时,才会在 Redis 服务器启动时检测 RDB 文件,并自动载入。服务器载入 RDB 文件期间处于阻塞状态,直到载入完成为止。
Redis 启动日志中可以看到自动载入的执行:
Redis 载入 RDB 文件时,会对 RDB 文件进行校验,如果文件损坏,则日志中会打印错误,Redis 启动失败。
RDB 常用配置总结
下面是 RDB 常用的配置项,以及默认值,前面介绍过的这里不再详细介绍:
设置为 no,则 Redis 无视 bgsave 的错误继续执行写命令,当对 Redis 服务器的系统(尤其是硬盘)使用了监控时,该选项考虑设置为 no。
AOF 持久化
RDB 持久化是将进程数据写入文件,而 AOF 持久化(即 Append Only File 持久化),则是将 Redis 执行的每次写命令记录到单独的日志文件中(有点像 MySQL 的 binlog),当 Redis 重启时再次执行 AOF 文件中的命令来恢复数据。
与 RDB 相比,AOF 的实时性更好,因此已成为主流的持久化方案。
开启 AOF
Redis 服务器默认开启 RDB,关闭 AOF;要开启 AOF,需要在配置文件中配置:appendonly yes。
执行流程
由于需要记录 Redis 的每条写命令,因此 AOF 不需要触发,下面介绍 AOF 的执行流程。
AOF 的执行流程包括:
命令追加(append)
Redis 先将写命令追加到缓冲区,而不是直接写入文件,主要是为了避免每次有写命令都直接写入硬盘,导致硬盘 IO 成为 Redis 负载的瓶颈。
命令追加的格式是 Redis 命令请求的协议格式,它是一种纯文本格式,具有兼容性好、可读性强、容易处理、操作简单避免二次开销等优点,具体格式略。
在 AOF 文件中,除了用于指定数据库的 select 命令(如 select 0 为选中 0 号数据库)是由 Redis 添加的,其他都是客户端发送来的写命令。
文件写入(write)和文件同步(sync)
Redis 提供了多种 AOF 缓存区的同步文件策略,策略涉及到操作系统的 write 函数和 fsync 函数,说明如下:
这样的操作虽然提高了效率,但也带来了安全问题:如果计算机停机,内存缓冲区中的数据会丢失。
因此系统同时提供了 fsync、fdatasync 等同步函数,可以强制操作系统立刻将缓冲区中的数据写入到硬盘里,从而确保数据的安全性。
AOF 缓存区的同步文件策略由参数 appendfsync 控制,各个值的含义如下:
这种情况下,每次有写命令都要同步到 AOF 文件,硬盘 IO 成为性能瓶颈,Redis 只能支持大约几百 TPS 写入,严重降低了 Redis 的性能。
即便是使用固态硬盘(SSD),每秒大约也只能处理几万个命令,而且会大大降低 SSD 的寿命。
这种情况下,文件同步的时间不可控,且缓冲区中堆积的数据会很多,数据安全性无法保证。
everysec 是前述两种策略的折中,是性能和数据安全性的平衡,因此是 Redis 的默认配置,也是我们推荐的配置。
文件重写(rewrite)
随着时间流逝,Redis 服务器执行的写命令越来越多,AOF 文件也会越来越大;过大的 AOF 文件不仅会影响服务器的正常运行,也会导致数据恢复需要的时间过长。
文件重写是指定期重写 AOF 文件,减小 AOF 文件的体积。需要注意的是,AOF 重写是把 Redis 进程内的数据转化为写命令,同步到新的 AOF 文件;不会对旧的 AOF 文件进行任何读取、写入操作!
关于文件重写需要注意的另一点是:对于 AOF 持久化来说,文件重写虽然是强烈推荐的,但并不是必须的。即使没有文件重写,数据也可以被持久化并在 Redis 启动的时候导入。
因此在一些实现中,会关闭自动的文件重写,然后通过定时任务在每天的某一时刻定时执行。
文件重写之所以能够压缩 AOF 文件,原因在于:
不过为了防止单条命令过大造成客户端缓冲区溢出,对于 list、set、hash、zset 类型的 key,并不一定只使用一条命令。
而是以某个常量为界将命令拆分为多条。这个常量在 redis.h/REDIS_AOF_REWRITE_ITEMS_PER_CMD 中定义,不可更改,3.0 版本中值是 64。
通过上述内容可以看出,由于重写后 AOF 执行的命令减少了,文件重写既可以减少文件占用的空间,也可以加快恢复速度。
文件重写的触发
文件重写的触发,分为手动触发和自动触发:
手动触发,直接调用 bgrewriteaof 命令,该命令的执行与 bgsave 有些类似:都是 fork 子进程进行具体的工作,且都只有在 fork 时阻塞。
此时服务器执行日志如下:
自动触发,根据 auto-aof-rewrite-min-size 和 auto-aof-rewrite-percentage 参数,以及 aof_current_size 和 aof_base_size 状态确定触发时机:
其中,参数可以通过 config get 命令查看:
状态可以通过 info persistence 查看:
只有当 auto-aof-rewrite-min-size 和 auto-aof-rewrite-percentage 两个参数同时满足时,才会自动触发 AOF 重写,即 bgrewriteaof 操作。
自动触发 bgrewriteaof 时,可以看到服务器日志如下:
文件重写的流程
文件重写流程如下图所示:
关于文件重写的流程,有两点需要特别注意:
对照上图,文件重写的流程如下:
启动时加载
前面提到过,当 AOF 开启时,Redis 启动时会优先载入 AOF 文件来恢复数据;只有当 AOF 关闭时,才会载入 RDB 文件恢复数据。
当 AOF 开启,且 AOF 文件存在时,Redis 启动日志:
当 AOF 开启,但 AOF 文件不存在时,即使 RDB 文件存在也不会加载(更早的一些版本可能会加载,但 3.0 不会),Redis 启动日志如下:
文件校验
与载入 RDB 文件类似,Redis 载入 AOF 文件时,会对 AOF 文件进行校验,如果文件损坏,则日志中会打印错误,Redis 启动失败。
但如果是 AOF 文件结尾不完整(机器突然宕机等容易导致文件尾部不完整),且 aof-load-truncated 参数开启,则日志中会输出警告,Redis 忽略掉 AOF 文件的尾部,启动成功。
aof-load-truncated 参数默认是开启的:
伪客户端
因为 Redis 的命令只能在客户端上下文中执行,而载入 AOF 文件时命令是直接从文件中读取的,并不是由客户端发送。
因此 Redis 服务器在载入 AOF 文件之前,会创建一个没有网络连接的客户端,之后用它来执行 AOF 文件中的命令,命令执行的效果与带网络连接的客户端完全一样。
AOF 常用配置总结
下面是 AOF 常用的配置项,以及默认值:
方案选择与常见问题
前面介绍了 RDB 和 AOF 两种持久化方案的细节,下面介绍 RDB 和 AOF 的特点、如何选择持久化方案,以及在持久化过程中常遇到的问题等。
RDB 和 AOF 的优缺点
RDB 和 AOF 各有优缺点:
RDB 持久化
优点:RDB 文件紧凑,体积小,网络传输快,适合全量复制;恢复速度比 AOF 快很多。当然,与 AOF 相比,RDB 最重要的优点之一是对性能的影响相对较小。
缺点:RDB 文件的致命缺点在于其数据快照的持久化方式决定了必然做不到实时持久化,而在数据越来越重要的今天,数据的大量丢失很多时候是无法接受的,因此 AOF 持久化成为主流。
此外,RDB 文件需要满足特定格式,兼容性差(如老版本的 Redis 不兼容新版本的 RDB 文件)。
AOF 持久化
与 RDB 持久化相对应,AOF 的优点在于支持秒级持久化、兼容性好,缺点是文件大、恢复速度慢、对性能影响大。
持久化策略选择
在介绍持久化策略之前,首先要明白无论是 RDB 还是 AOF,持久化的开启都是要付出性能方面代价的:
此外,AOF 文件的重写与 RDB 的 bgsave 类似,会有 fork 时的阻塞和子进程的 IO 压力问题。
相对来说,由于 AOF 向硬盘中写数据的频率更高,因此对 Redis 主进程性能的影响会更大。
在实际生产环境中,根据数据量、应用对数据的安全要求、预算限制等不同情况,会有各种各样的持久化策略。
如完全不使用任何持久化、使用 RDB 或 AOF 的一种,或同时开启 RDB 和 AOF 持久化等。
此外,持久化的选择必须与 Redis 的主从策略一起考虑,因为主从复制与持久化同样具有数据备份的功能,而且主机 master 和从机 slave 可以独立的选择持久化方案。
下面分场景来讨论持久化策略的选择,讨论也只是作为参考,实际方案可能更复杂更具多样性:
在这种情况下,一种可行的做法是:
然后关闭 AOF 的自动重写,然后添加定时任务,在每天 Redis 闲时(如凌晨 12 点)调用 bgrewriteaof。
这里需要解释一下,为什么开启了主从复制,可以实现数据的热备份,还需要设置持久化呢?
因为在一些特殊情况下,主从复制仍然不足以保证数据的安全,例如:
需要注意的是,即便是使用了哨兵(关于哨兵后面会有文章介绍)进行自动的主从切换,也有可能在哨兵轮询到 master 之前,便被自动拉起机制重启了。因此,应尽量避免“自动拉起机制”和“不做持久化”同时出现。
异地灾备:上述讨论的几种持久化策略,针对的都是一般的系统故障,如进程异常退出、宕机、断电等,这些故障不会损坏硬盘。
但是对于一些可能导致硬盘损坏的灾难情况,如火灾地震,就需要进行异地灾备。
例如对于单机的情形,可以定时将 RDB 文件或重写后的 AOF 文件,通过 scp 拷贝到远程机器,如阿里云、AWS 等。
对于主从的情形,可以定时在 master 上执行 bgsave,然后将 RDB 文件拷贝到远程机器,或者在 slave 上执行 bgrewriteaof 重写 AOF 文件后,将 AOF 文件拷贝到远程机器上。
一般来说,由于 RDB 文件文件小、恢复快,因此灾难恢复常用 RDB 文件;异地备份的频率根据数据安全性的需要及其他条件来确定,但***不要低于一天一次。
fork 阻塞:CPU 的阻塞
在 Redis 的实践中,众多因素限制了 Redis 单机的内存不能过大,例如:
首先说明一下 fork 操作:父进程通过 fork 操作可以创建子进程;子进程创建后,父子进程共享代码段,不共享进程的数据空间,但是子进程会获得父进程的数据空间的副本。
在操作系统 fork 的实际实现中,基本都采用了写时复制技术,即在父/子进程试图修改数据空间之前,父子进程实际上共享数据空间。
但是当父/子进程的任何一个试图修改数据空间时,操作系统会为修改的那一部分(内存的一页)制作一个副本。
虽然 fork 时,子进程不会复制父进程的数据空间,但是会复制内存页表(页表相当于内存的索引、目录);父进程的数据空间越大,内存页表越大,fork 时复制耗时也会越多。
在 Redis 中,无论是 RDB 持久化的 bgsave,还是 AOF 重写的 bgrewriteaof,都需要 fork 出子进程来进行操作。
如果 Redis 内存过大,会导致 fork 操作时复制内存页表耗时过多;而 Redis 主进程在进行 fork 时,是完全阻塞的,也就意味着无法响应客户端的请求,会造成请求延迟过大。
对于不同的硬件、不同的操作系统,fork 操作的耗时会有所差别,一般来说,如果 Redis 单机内存达到了 10GB,fork 时耗时可能会达到百毫秒级别(如果使用 Xen 虚拟机,这个耗时可能达到秒级别)。
因此,一般来说 Redis 单机内存一般要限制在 10GB 以内;不过这个数据并不是绝对的,可以通过观察线上环境 fork 的耗时来进行调整。
观察的方法如下:执行命令 info stats,查看 latest_fork_usec 的值,单位为微秒。
为了减轻 fork 操作带来的阻塞问题,除了控制 Redis 单机内存的大小以外,还可以适度放宽 AOF 重写的触发条件、选用物理机或高效支持 fork 操作的虚拟化技术等,例如使用 Vmware 或 KVM 虚拟机,不要使用 Xen 虚拟机。
AOF 追加阻塞:硬盘的阻塞
前面提到过,在 AOF 中,如果 AOF 缓冲区的文件同步策略为 everysec,则在主线程中,命令写入 aof_buf 后调用系统 write 操作,write 完成后主线程返回。
fsync 同步文件操作由专门的文件同步线程每秒调用一次。这种做法的问题在于,如果硬盘负载过高,那么 fsync 操作可能会超过 1s。
如果 Redis 主线程持续高速向 aof_buf 写入命令,硬盘的负载可能会越来越大,IO 资源消耗更快;如果此时 Redis 进程异常退出,丢失的数据也会越来越多,可能远超过 1s。
为此,Redis 的处理策略是这样的:主线程每次进行 AOF 会对比上次 fsync 成功的时间;如果距上次不到 2s,主线程直接返回;如果超过 2s,则主线程阻塞直到 fsync 同步完成。
因此,如果系统硬盘负载过大导致 fsync 速度太慢,会导致 Redis 主线程的阻塞;此外,使用 everysec 配置,AOF 最多可能丢失 2s 的数据,而不是 1s。
AOF 追加阻塞问题定位的方法:
info 命令与持久化
前面提到了一些通过 info 命令查看持久化相关状态的方法,下面来总结一下。
info Persistence
执行结果如下:
其中比较重要的包括:
info stats
其中与持久化关系较大的是:latest_fork_usec,代表上次 fork 耗时,可以参见前面的讨论。
总结
本文主要内容可以总结如下:
文章题目:3分钟深入学习Redis的高可用特性“持久化”
当前网址:http://www.shufengxianlan.com/qtweb/news24/190024.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联