redis的高可用

一:redis高可用概述

web服务器中,高可用是指服务器可以正常访问的时间,衡量的标准是在多长时间内可以提供正常服务(99.9%、99.99%、99.999% 等等)。但是在redis语境中,高可用的含义似乎要宽泛一些,除了保证提供正常服务(如主从分离、快速容灾技术),还需要考虑数据容量的扩展、数据安全不会丢失等。

redis中,实现高可用的技术主要包括持久化、复制、哨兵和集群,下面分别说明它们的作用,以及解决了什么样的问题。

1、持久化:持久化是最简单的高可用方法(有时甚至不被归为高可用的手段),主要作用是数据备份,即将数据存储在硬盘,保证数据不会因进程退出而丢失。

2、复制:复制是高可用redis的基础,哨兵和集群都是在复制基础上实现高可用的。复制主要实现了数据的多机备份,以及对于读操作的负载均衡和简单的故障恢复。缺陷:故障恢复无法自动化写操作无法负载均衡;存储能力受到单机的限制。

3、哨兵:在复制的基础上,哨兵实现了自动化的故障恢复。缺陷:写操作无法负载均衡;存储能力受到单机的限制。

4、集群通过集群,redis解决了写操作无法负载均衡,以及存储能力受到单机限制的问题,实现了较为完善的高可用方案。

二:redis持久化概述

1、持久化的功能:redis是内存数据库,数据都是存储在内存中,为了避免进程退出导致数据的永久丢失,需要定期将redis中的数据以某种形式(数据或命令)从内存保存到硬盘;当下次redis重启时,利用持久化文件实现数据恢复。除此之外,为了进行灾难备份,可以将持久化文件拷贝到一个远程位置。

2、持久化的分类:RDB持久化和AOF持久化:前者将当前数据保存到硬盘,后者则是将每次执行的写命令保存到硬盘(类似于MySQL的binlog);由于AOF持久化的实时性更好,即当进程意外退出时丢失的数据更少,因此AOF是目前主流的持久化方式,不过RDB持久化仍然有其用武之地。

三:RDB持久化

RDB持久化是将当前进程中的数据生成快照保存到硬盘(因此也称作快照持久化),保存的文件后缀是rdb;当redis重新启动时,可以读取快照文件恢复数据。

1、触发条件:手动触发和自动触发两种。

(1)手动触发:save和bgsave命令

区别:save命令会阻塞redis服务器进程,直到RDB文件创建完毕为止,redis服务器阻塞期间,服务器不能处理任何命令请求。bgsave命令会创建一个子进程,由子进程来负责创建RDB文件,父进程(即redis主进程)则继续处理请求。

bgsave命令执行过程中,只有fork子进程时会阻塞服务器,而对于save命令,整个过程都会阻塞服务器,因此save已基本被废弃,线上环境要杜绝save的使用;后文中也将只介绍bgsave命令。此外,在自动触发RDB持久化时,redis也会选择bgsave而不是save来进行持久化;下面介绍自动触发RDB持久化的条件。

(2)、自动触发:save m n 表示m秒内发生n次变化时,会触发bgsave

 

其中save 900 1的含义是:当时间到900秒时,如果redis数据发生了至少1次变化,则执行bgsave;save 300 10和save 60 10000同理。当三个save条件满足任意一个时,都会引起bgsave的调用。

save m n的实现原理:

redissave 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条件,只有下面两条同时满足时才算满足:当前时间-lastsave > m && dirty >= n

2、bgsave命令的执行流程


 bgsave的子进程不能同时执行,主要是基于性能方面的考虑:两个并发的子进程同时执行大量的磁盘写操作,可能引起严重的性能问题。父进程执行fork操作创建子进程,这个过程中父进程是阻塞的,redis不能执行来自客户端的任何命令

3、RDB文件:

存储路径

RDB文件的存储路径既可以在启动前配置,也可以通过命令动态设定

配置:dir配置指定目录,dbfilename指定文件名。默认是redis根目录下的dump.rdb文件。

动态设定:redis启动后也可以动态修改RDB存储路径,在磁盘损害或空间不足时非常有用;执行命令为config set dir {newdir}和config set dbfilename {newFileName}。

RDB文件格式

 

其中各个字段的含义说明如下:

1)redis:常量,保存着redis”5个字符。

2)db_version:RDB文件的版本号,注意不是redis的版本号。

3)SELECTDB 0 pairs:表示一个完整的数据库(0号数据库),同理SELECTDB 3 pairs表示完整的3号数据库;只有当数据库中有键值对时,RDB文件中才会有该数据库的信息(上图所示的redis中只有0号和3号数据库有键值对);如果redis中所有的数据库都没有键值对,则这一部分直接省略。其中:SELECTDB是一个常量,代表后面跟着的是数据库号码;0和3是数据库号码;pairs则存储了具体的键值对信息,包括key、value值,及其数据类型、内部编码、过期时间、压缩信息等等。

4)EOF:常量,标志RDB文件正文内容结束。

5)check_sum:前面所有内容的校验和;redis在载入RBD文件时,会计算前面的校验和并与check_sum值比较,判断文件是否损坏。

四、AOF持久化

redis执行的每次写命令记录到单独的日志文件中;当redis重启时再次执行AOF文件中的命令来恢复数据。与RDB相比,AOF的实时性更好,因此已成为主流的持久化方案。

1、AOF的执行流程

1命令追加(append)

redis先将写命令追加到缓冲区aof_buf,而不是直接写入文件,主要是为了避免每次有写命令都直接写入硬盘,导致硬盘IO成为redis负载的瓶颈。在AOF文件中,除了用于指定数据库的select命令(如select 0 为选中0号数据库)是由redis添加的,其他都是客户端发送来的写命令。

2文件同步(sync)

redis提供了多种AOF缓存区的同步文件策略,AOF缓存区的同步文件策略由参数appendfsync控制,各个值的含义如下:

always:命令写入aof_buf后立即调用系统fsync操作同步到AOF文件,fsync完成后线程返回。这种情况下,每次有写命令都要同步到AOF文件,硬盘IO成为性能瓶颈,redis只能支持大约几百TPS写入,严重降低了redis的性能;即便是使用固态硬盘(SSD),每秒大约也只能处理几万个命令,而且会大大降低SSD的寿命。

no:命令写入aof_buf后调用系统write操作,不对AOF文件做fsync同步;同步由操作系统负责,通常同步周期为30秒。这种情况下,文件同步的时间不可控,且缓冲区中堆积的数据会很多,数据安全性无法保证。

everysec:命令写入aof_buf后调用系统write操作,write完成后线程返回;fsync同步文件操作由专门的线程每秒调用一次。everysec是前述两种策略的折中,是性能和数据安全性的平衡,因此是redis的默认配置,也是我们推荐的配置。

3文件重写(rewrite)

文件重写是指定期重写AOF文件,减小AOF文件的体积。需要注意的是,AOF重写是把redis进程内的数据转化为写命令,同步到新的AOF文件;不会对旧的AOF文件进行任何读取、写入操作!

关于文件重写需要注意的另一点是:对于AOF持久化来说,文件重写虽然是强烈推荐的,但并不是必须的;即使没有文件重写,数据也可以被持久化并在redis启动的时候导入;因此在一些实现中,会关闭自动的文件重写,然后通过定时任务在每天的某一时刻定时执行。

文件重写之所以能够压缩AOF文件,原因在于:

A:过期的数据不再写入文件

B:无效的命令不再写入文件:如有些数据被重复设值(set mykey v1, set mykey v2)、有些数据被删除了(sadd myset v1, del myset)等等

C:多条命令可以合并为一个:如sadd myset v1, sadd myset v2, sadd myset v3可以合并为sadd myset v1 v2 v3。

文件重写的触发:手动触发和自动触发

手动触发:直接调用bgrewriteaof命令,都是fork子进程进行具体的工作,且都只有在fork时阻塞。

自动触发:根据auto-aof-rewrite-min-size执行AOF重写时,文件的最小体积,默认值为64MBauto-aof-rewrite-percentage执行AOF重写时,当前AOF大小(即aof_current_size)和上一次重写时AOF大小(aof_base_size)的比值参数,以及aof_current_size和aof_base_size状态确定触发时机。只有当auto-aof-rewrite-min-size和auto-aof-rewrite-percentage两个参数同时满足时,才会自动触发AOF重写,即bgrewriteaof操作。

2、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。

五:持久化对比以及策略选择

1、RDB和AOF的优缺点

RDB持久化:

优点:RDB文件紧凑,体积小,网络传输快,适合全量复制;恢复速度比AOF快很多。当然,与AOF相比,RDB最重要的优点之一是对性能的影响相对较小。

缺点:RDB文件的致命缺点在于其数据快照的持久化方式决定了必然做不到实时持久化,而在数据越来越重要的今天,数据的大量丢失很多时候是无法接受的,因此AOF持久化成为主流。此外,RDB文件需要满足特定格式,兼容性差(如老版本的redis不兼容新版本的RDB文件)。

AOF持久化:RDB持久化相对应,AOF的优点在于支持秒级持久化、兼容性好,缺点是文件大、恢复速度慢、对性能影响大。

2、持久化策略选择:

无论是RDB还是AOF,持久化的开启都是要付出性能方面代价的:对于RDB持久化,一方面是bgsave在进行fork操作时redis主进程会阻塞,另一方面,子进程向硬盘写数据也会带来IO压力;对于AOF持久化,向硬盘写数据的频率大大提高(everysec策略下为秒级),IO压力更大,甚至可能造成AOF追加阻塞问题,此外,AOF文件的重写与RDB的bgsave类似,会有fork时的阻塞和子进程的IO压力问题。相对来说,由于AOF向硬盘中写数据的频率更高,因此对redis主进程性能的影响会更大。

分场景来讨论持久化策略的选择

1)如果redis中的数据完全丢弃也没有关系(如redis完全用作DB层数据的cache),那么无论是单机,还是主从架构,都可以不进行任何持久化。

2)在单机环境下(对于个人开发者,这种情况可能比较常见),如果可以接受十几分钟或更多的数据丢失,选择RDB对redis的性能更加有利;如果只能接受秒级别的数据丢失,应该选择AOF。

3)但在多数情况下,我们都会配置主从环境,slave的存在既可以实现数据的热备,也可以进行读写分离分担redis读请求,以及在master宕掉后继续提供服务。

在这种情况下,一种可行的做法是:

master:完全关闭持久化(包括RDB和AOF),这样可以让master的性能达到最好

slave:关闭RDB,开启AOF(如果对数据安全要求不高,开启RDB关闭AOF也可以),并定时对持久化文件进行备份(如备份到其他文件夹,并标记好备份的时间);然后关闭AOF的自动重写,然后添加定时任务,在每天redis闲时(如凌晨12点)调用bgrewriteaof。

4)异地灾备:上述讨论的几种持久化策略,针对的都是一般的系统故障,如进程异常退出、宕机、断电等,这些故障不会损坏硬盘。但是对于一些可能导致硬盘损坏的灾难情况,如火灾地震,就需要进行异地灾备。例如对于单机的情形,可以定时将RDB文件或重写后的AOF文件,通过scp拷贝到远程机器,如阿里云、AWS等;对于主从的情形,可以定时在master上执行bgsave,然后将RDB文件拷贝到远程机器,或者在slave上执行bgrewriteaof重写AOF文件后,将AOF文件拷贝到远程机器上。一般来说,由于RDB文件文件小、恢复快,因此灾难恢复常用RDB文件;异地备份的频率根据数据安全性的需要及其他条件来确定,但最好不要低于一天一次。

猜你喜欢

转载自blog.csdn.net/aubrey_cr7/article/details/81060371