Redis 高可用方案
Redis 高可用方式主要有以下四种:
- 数据持久化
- 主从数据同步(主从复制)
- Redis 哨兵模式 (Sentinel)
- Redis 集群(Cluster)
数据持久化保证系统在发生宕机或者重启之后数据不会丢失,增加了系统的可靠性和减少了系统不可用的时间(省去了手动恢复数据的过程);
主从数据同步可以将数据存储至多台服务器,这样当遇到一台服务器宕机之后,可以很快地切换到另一台服务器以继续提供服务;
哨兵模式发生故障之后自动切换主服务器;
Redis 集群提供多主多从的 Redis 分布式集群环境,用于提供性能更好的 Redis 服务,并且它自身拥有故障自动切换的能力。
数据持久化
Redis 4.0 之前数据持久化方式有两种:AOF 方式和 RDB 方式。
RDB
RDB (Redis Database, 快照方式) 按照一定的时间周期
策略把内存的数据以快照(snapshot)
的方式保存到硬盘的二进制文件中。
即:snapshot 快照存储,对应产生数据文件为 dump.db
,通过配置文件中的 save
参数来定义快照的周期。
核心函数:rdbSave
(生成 RDB 文件) 和 rdbLoad
(从文件加载内存)两个函数。
优点:是以二进制存储的,占用空间更小、数据存储更紧凑,并且与 AOF 相比,RDB 具备更快的重启恢复能力。
AOF
AOF 是 (append only file) 的缩写。Redis 会将每一个收到的写命令都通过 Write
函数追加到文件最后,类似于 MySQL 的 binlog.
当 Redis 重启时,就会从 appendlonly.aof
中加载命令来重建整个 Redis 数据库。
每当执行服务器函数或函数时,flushAppendOnlyFile
函数都会被调用,这个函数执行两个工作:
- WRITE
根据条件,将aof_buf
中的缓存写入 AOF 文件中; - SAVE
根据条件,调用fsync
或fdatasync
函数,将 AOF 文件保存到磁盘中
总结:
- RDB 占用空间更小、占用空间小,具备更快的重启恢复能力。
- AOF 存储频率更高,数据丢失的风险越低,招用空间大,重启速度较慢。
- 也可以使用混合持久化功能,通过
config get aof-use-rdb-preamble
的命令来查询 Redis 混合持久化的功能是否开启。
Redis 主从同步
在 Redis 集群环境中,对于主机器和从机器数据一致的操作,就需要主从同步,也叫 application,来同步主节点的数据。
Redis 主从同步分为:主从模式 和 从从模式
主从复制的三种机制
- 主从节点连接正常时,主设备会发送一连串的数据流来保持对
slave
的更新:包括客户端的写入,key 的过期或被逐出等待。
- 主从节点连接正常时,主设备会发送一连串的数据流来保持对
- 当 master 和 slave 之间的连接断开之后(网络原因、连接超时等),slave 重新连接 master 会尝试进行部分重同步:尝试获取在断开连接期间丢失的命令流
- 当第二步中无法进行部分重同步,slave 会进行全量同步
Redis 使用的默认是异步复制,其特点是低延迟
和高性能
。
Redis 哨兵模式
Redis 哨兵模式是用来监视 Redis 主从服务器的,当 Redis 的主从服务器发生故障之后,Redis 哨兵提供了自动容灾修复的功能。
Redis 哨兵模块存储在 Redis 的 src/redis-sentinel 目录。我们可以使用命令./src/redis-sentinel sentinel.conf
来启动哨兵功能。
Redis Sentinel 是社区版本推出的原生高可用解决方案,其部署架构主要包括两部分:Redis Sentinel 集群和 Redis 数据集群。
其中 Redis Sentinel 集群是由若干个 Sentinel 节点组成的分布式集群,可以实现
- 故障发现
- 故障自动转移
- 配置中心
- 客户端通知
Redis Sentinel 的节点数量要满足 2n+1
的奇数个。
哨兵的工作主要是:
- 监控
- 故障检测
- 故障转移
- sentinel leader
- master 重选
哨兵(Sentinel)工作方式
- 每个 Sentinel 以每秒钟一次的频率向它管理的 master, slave 以及其他 Sentinel 实例发送一个 PING 命令。
- 如果一个节点距离最后一次有效回复 PING 命令的时间超过
own-after-milliseconds
选项所指定的值,则这个实例会被Sentinel
标记为主观下线。
- 如果一个节点距离最后一次有效回复 PING 命令的时间超过
- 若一个 master 节点被标记为主观下线,则正在监视这个 master 的所有 Sentinel 要以每秒一次的频率确认 master 是否进入了主观下线的状态
- 当有足够数量的 Sentinel (大于等于配置文件指定的值) 在指定的时间范围内确认 master 的确进入了主观下线状态,则 master 会被标记为客观下线。
- 在一般情况下,每个 Sentinel 会以 10 s 一次的频率向它已知的所有 master, slave 发送 INFO 命令。
- 当 master 被 Sentinel 标记为客观下线时,sentinel 向下线的 master 的所有 slave 发送 INFO 命令的频率会从 10s/次改为 1s/次
故障检测
- 主观下线(Subjective Down, SDOWN): 是指一个哨兵通过检测发现某个主节点发生故障的一种状态。
- 客观下线(Objective Down, ODOWN): 是指哨兵检测到某个主节点发生故障,通过命令 SENTINEL
is-master-down-by-addr
与其他哨兵节点协商,并且在指定时间内接收到指定数量的其他哨兵的确认反馈时的一种状态。
主观下线表示哨兵通过检测发现节点宕机,客观下线表示哨兵不但自己认为该节点宕机,而且该哨兵与其他节点沟通后,达到一定数量的哨兵都认为节点宕机了。
故障转移
Sentinel 判定主节点客观宕机(ODOWN)后,将进入故障转移过程。
进入故障转移的前提是:主节点为客观宕机状态,当前没有故障转移在执行、上次故障转移已经超时。Sentinel 确认可以执行故障转移后,会进行以下几项准备工作:
- 故障转移等待开始;
设置 failover_state: SENTINEL_FAILOVER_STATE_WAIT_START - 主节点处于故障转移过程中
设置当前主节点标识位:SRI_FAILOVER_IN_PROGRESS (主节点处于故障转移过程中) - 记录故障转移开始时间以及 failover_state 状态修改时间;
Sentinel 故障转移 Leader 选举
当一个主节点被判断为客观下线时,监控这个主节点的所有 Sentinel 会进行协商,选举一个 Leader 对下线的主节点执行故障转移操作。
Sentinel 需要在集群内进行”拉票”,”拉票” 的依据就是配置 quorum 以及 “拉票”的时间。
- 配置 quorum 越大,优先级越高;
- “拉票” 请求时间越早,优先级越高;
“投票”完成后就到了”唱票”环节,该过程是在 SENTINEL_FAILOVER_STATE_WAIT_START
状态下执行的。
Redis 使用了 Raft 算法实现领导者选举,大体思路:
- 每个在线的 Sentinel 节点都有资格成为 Leader, 每个 Sentinel 节点发现当它确认主节点客观下线检查的时候,会向其他 Sentinel 节点发送
sentinel is-master-down-by-addr
命令,要求将自己设置为leader
- 每个节点在每个选举轮次中只有一次投票权,接收到命令的 Sentinel 节点,如果没有同意过其他 Sentinel 节点的
sentinel is-master-down-by-addr
命令,
将同意该请求,否则拒绝 - 如果该 Sentinel 节点发现自己的票数已经大于等于 max (quorum, num(sentinels)/ 2 + 1),那么它将成为领导者。其他的投票就会终止,即使后续还有其他的哨兵节点到达配置,也没有作用
- 如果此过程中没有产生领导者,暂停一段时间,再进行下一轮选举,current_epoch 加 1
合理部署 Sentinel 的节点的个数,以及配置 sentinel 选举的法定人数- sentinel 节点个数最好 >= 3.
- sentinel 节点个数最好是奇数.
- sentinel 的选举法定人数设置为 (n/2 + 1)
配置
1 | sentinel.conf |
其中 quorum
是法定人数。作用:多个 sentinel 进行相互选举,有超过法定人数的 sentinel 选举某个 sentinel 为 leader,那么他就成为 leader,
leader 负责故障转移。这个法定人数,一般配置为 (n/2 + 1)较为合理
新主节点的选举
当主节点客观下线时,Sentinel Leader 会从该主节点存活的从节点中选出一个新的主节点。
首先,过滤掉相关从节点:
- 主观下线的或者处于断线状态的从节点;
- 最近 5s 内未回复过 Sentinel Leader INFO 命令的从节点;
- 从节点的优先级为 0 的从节点,由配置项 replica-priority 决定;
- 与主节点断开连接超过 10 倍
down-after-milliseconds
的从节点;
当 Sentinel 集群选举出 Sentinel Leader 后,由 Sentinel Leader 从 redis 从节点中选择一个 redis 节点作为主节点:
- 过滤故障的节点
- 选择优先级
slave-priority
最大的从节点作为主节点,如不存在则继续
- 选择优先级
- 选择复制偏移量最大的从节点作为主节点,如不存在则继续
- 选择 runid(redis 每次启动的时候生成随机的 runid 作为 redis 的标识)最小的从节点作为主节点
Redis 集群
Redis 集群是 Redis 3.0 版本推出的 Redis 集群方案,将数据分布在不同的主服务器上,以此来降低系统对单主节点的依赖,并且可以大大提高 Redis 服务的读写性能。
Redis 集群除了拥有主从模式 + 哨兵模式的所有功能之外,还提供了多个主从节点的集群功能,实现了真正意义上的分布式集群服务。
Redis 集群可以实现数据分片服务,也就是说在 Redis 集群中有 16384 个槽位来存储所有的数据,当我们有 N 个主节点时,可以把 16384 个槽位平均分配到 N 台主服务器上。
当有 key-value 存储时,Redis 会使用 crc16 算法进行 hash 得到一个整数值,然后用这个整数值对 16384进行取模来得到具体槽位,再把此值存储在对应的服务器上,读取操作也是同样的道理,这样我们就实现了数据分片的功能。