Redis 高可用方案原理初探
生产级Redis 高并发分布式锁实战1:高并发分布式锁如何实现 https://www.cnblogs.com/yizhiamumu/p/16556153.html
生产级Redis 高并发分布式锁实战2:缓存架构设计问题优化 https://www.cnblogs.com/yizhiamumu/p/16556667.html
总结篇3:redis 典型缓存架构设计问题及性能优化 https://www.cnblogs.com/yizhiamumu/p/16557996.html
总结篇4:redis 核心数据存储结构及核心业务模型实现应用场景 https://www.cnblogs.com/yizhiamumu/p/16566540.html
DB\redis\zookeeper分布式锁设计 https://www.cnblogs.com/yizhiamumu/p/16663243.html
在缓存和数据库双写场景下,一致性是如何保证的 https://www.cnblogs.com/yizhiamumu/p/16686751.html
如何保证 Redis 的高并发和高可用?讨论redis的单点,高可用,集群 https://www.cnblogs.com/yizhiamumu/p/16586968.html
分布式缓存应用场景与redis持久化机制 https://www.cnblogs.com/yizhiamumu/p/16702154.html
Redisson 源码分析及实际应用场景介绍 https://www.cnblogs.com/yizhiamumu/p/16706048.html
Redis 高可用方案原理初探 https://www.cnblogs.com/yizhiamumu/p/16709290.html
RedisCluster集群架构原理与通信原理 https://www.cnblogs.com/yizhiamumu/p/16704556.html
Redis 高可用的方案包括:持久化、主从复制(及读写分离)、哨兵和集群(Cluster)。
持久化
解决Redis 数据的单机备份问题(从内存到硬盘的备份),数据可用性以及可靠性。
主从复制
解决数据的多机热备,以及从实例角度进行解决提高了高可用(故障切换),主从复制还可以实现负载均衡(高性能)。
哨兵
自动进行监控和切换,进行解决实例之间提高了高可用(故障切换)
集群
进行数据容灾以及高可用的能力,此外可以扩展单节点 redis 的数据存储上限。
一:redis 持久化
Redis 数据恢复的背景
redis 的数据一般全部存储在内存中,这种情况下数据库一旦故障发生重启数据会全部丢失。
持久化功能在于能够有效地避免因进程退出造成的数据丢失问题,在下次重启时利用之前持久化的文件即可实现数据恢复。
Redis 因为在内存中进行数据存储和操作;如果仅仅是在内存中进行数据存储,那就会导致以下问题:
-
数据随进程退出而消失:当服务器断电或 Redis Server 进程退出时,内存肯定随之释放,最后数据也会丢失;
- 如果只是作为缓存,数据没有了则重新从数据库中读取放在里面即可,那么如果是高并发场景,数据库压力依然很大;
-
数据丢失之后无法进行恢复:数据只存在 Redis 中,而没有存在关系型数据库,如果数据丢失便不可恢复;
Redis 持久化有两种方案
-
RDB(Redis DataBase)是一种快照式的二进制数据存储,它会周期性的保存当前时间点 Redis 所有的数据到磁盘中。
-
AOF(Append Of FIle)是一种追加式的存储方式,会实时的记录 Redis 的写操作到磁盘中。
Redis 持久化机制实现
- Redis 是基于内存进行操作运算,如果不持久化数据再重启服务时会导致数据丢失。
- 开启 Redis 持久化功能后,数据会保存到磁盘中。当 redis 重启后,可以从磁盘中恢复数据。
RDB 快照(snapshot)
RDB 持久化方式
RDB 持久化把当前进程数据生成快照(.rdb)文件保存到硬盘的过程,有手动触发和自动触发。
RDB 手动触发
手动触发有 save 和 bgsave 两命令。
save 命令
-
该命令会阻塞当前 Redis 服务器,执行 save 命令期间,Redis 不能处理其他命令,直到 RDB 过程完成为止。
-
执行完成时候如果存在老的 RDB 文件,就把新的替代掉旧的。我们的客户端可能都是几万或者是几十万,这种方式显然不可取。
在执行 redis-cli shutdown 关闭 redis 服务时,如果没有开启 AOF 持久化,自动执行 save。
bgsave 命令
-
Redis 主进程 fork 一个子进程来创建临时 RDB 存储文件,创建文件完成后对这个临时文件 rename 替换原先的 RDB 文件。
-
RDB 文件是一个单文件很适合数据的容灾备份与恢复,通过 RDB 文件恢复数据库耗时较短,通常 1G 的快照文件载入内存只需 20s 左右。
bgsave 命令和 save 命令
命令名称 | save | bgsave |
---|---|---|
IO 类型 | 同步 | 异步 |
是否阻塞 | 是 | 否 |
复杂度 | O(n) | O(n) |
优点 | 不会耗费额外内存 | 不阻塞客户端访问 |
缺点 | 阻塞客户端访问 | 耗费多余客户端 |
在指定时间间隔内将内存中的数据库记录集 dump 到磁盘上,RDB 是默认的持久化方式,这种方式是就是将内存中数据以快照的方式写入到二进制文件中 , 默认的文件名为 dump.rdb。
RDB 自动触发
自动触发是由我们的配置文件来完成的,自动触发 bgsave。
配置方法
-
在 redis.conf 文件中配置 N 秒内数据集至少有 M 个改动时自动触发一次 RDB 持久化,Redis 会将数据集的快照 dump 到 dump.rdb 文件中。
-
我们也可以通过配置文件来修改 Redis 服务器 dump 快照的频率,在打开 6379.conf 文件之后,我们搜索 save,可以看到下面的配置信息:
60 秒内至少有 10000 个键被修改
- save 900 1 #在 900 秒 (15 分钟) 之后,如果至少有 1 个 key 发生变化,则 dump 内存快照。
- save 300 10 #在 300 秒 (5 分钟) 之后,如果至少有 10 个 key 发生变化,则 dump 内存快照。
- save 60 10000 #在 60 秒 (1 分钟) 之后,如果至少有 10000 个 key 发生变化,dump 内存快照。
关闭 RDB 方式持久化只需要将所有 save 保存策略注释掉即可
RDB 持久化命令
- 命令:config set dir /usr/local // 设置 rdb 文件保存路径
- 备份:bgsave // 将 dump.rdb 保存到 usr/local 下
- 恢复:将 dump.rdb 放到 redis 安装目录与 redis.conf 同级目录,重启 redis 即可
save 命令是同步命令,bgsave 命令是异步命令,会从 redis 主进程 fork 出一个子进程去处理,每次命令执行后会新生成一个 rdb 文件并覆盖原来的文件。
修改配置
-
rdbcompression yes:rdb 文件压缩是否开启
-
dbfilename dump.rdb:rdb 文件名称定义
RDB 的优缺点
优点
- 恢复速度快,适合大规模的数据恢复。RDB 保存的是某一个时间点的内存快照,非常适合灾难恢复。在恢复大数据集时速度快,1G 的 RDB 数据恢复耗时大概 20s,比 AOF 要快的多
- 如果业务对数据完整性和一致性要求不高,RDB 是很好的选择。
缺点
- 数据的完整性和一致性不高,间隔时间大并且丢失数据较为多,RDB 通过触发某个时间点条件生成快照文件,如果 5 分钟保存一次的话,一旦发生故障会丢失好几分钟的数据,配置不同的保存点让 RDB 至少可以保存 5 分钟的数据。redis 因为没有正确关闭而停止工作,则丢失最近几分钟的数据
- 备份时,CPU 资源占用过大、内存资源占用过多。因为 Redis 在备份时会独立创建一个子进程,将数据写入到一个临时文件(此时内存中的数据是原来的两倍),最后再将临时文件替换之前的备份文件。
- fork 子进程消耗内存和 CPU,由于 RDB 是通过 fork 子进程来协助完成数据持久化工作的,如果数据集较大,可能会导致整个服务器停止服务几百毫秒,甚至是 1 秒钟。(回写和覆盖的时候用的是主进程)。
AOF(append-only file)
由于 RDB 快照方式的缺点,如果 redis 由于某些原因导致机器故障时,则会丢失最近几分钟写入的数据,而 AOF 持久化方式,则通过追加的方式将操作命令添加到 appendonly.aof 文件中,存储的文件是 RESP 协议指令文件。
配置方法
-
appendonly yes # 开启 AOF 持久化,(默认不开启,为 no)
-
appendfilename “appendonly.aof”:默认文件名
配置好后,redis 每次修改操作的命令会追加到 AOF 末尾,当 redis 重启后会重新执行 AOF 里的命令达到重建缓存数据集的目的,配置刷命令的频率。
-
appendfsync always :每次有新命令追加到 AOF 文件时就执行一次 fsync,非常慢但最安全。服务器在每执行一个事件就把 AOF 缓冲区的内容强制性的写入硬盘上的 AOF 文件里,保证了数据持久化的完整性,效率是最慢的但最安全的;
-
appendfsync everysec # 服务端每隔一秒才会进行一次文件同步把内存缓冲区里的 AOF 缓存数据真正写入 AOF 文件里,兼顾了效率和完整性,极端情况服务器宕机只会丢失一秒内对 Redis 数据库的写操作;默认方式
-
appendfsync no # 从不 fsync,交由操作系统处理,速度快,表示默认系统的缓存区写入磁盘的机制,不做程序强制,数据安全性和完整性差一些。
-
bgrewriteaof:后台运作重写机制
-
auto-aof-rewrite-min-size 64mb # aof 文件至少要达到 64M 才会自动重写,文件太小恢复速度本来就很快,重写的意义不大
-
auto-aof-rewrite-percentage 100 # aof 文件自上一次重写后文件大小增长了 100%,则再次触发重写执行 bgrewriteaof 命令可以手动重写 aof 文件,AOF 重写 redis 会 fork 出一个子进程去做,不会对 redis 正常命令处理有太多影响。
redis 启动时如果既有 rdb 文件又有 aof 文件则优先选择 aof 文件恢复数据,因为 aof 一般来说数据更全一点。
-
全量同步 (RDB):每天定时(避开高峰期)或者采用一个周期实现将数据拷贝到一个地方。
-
增量同步 (AOF):比如采用对行为的操作实现对数据的同步。
-
增量同步比全量同步更加消耗服务器的内存,但是能够更加的保证数据的同步。
AOF 重写原理
AOF 的工作原理是将写操作追加到文件中,文件的冗余内容会越来越多。 Redis 新增了重写机制。当 AOF 文件的大小超过所设定的阈值时,Redis 就会对 AOF 文件的内容压缩。Redis 会 fork 出一条新进程,读取内存中的数据,并重新写到一个临时文件中。并没有读取旧文件。最后替换旧的 aof 文件。AOF 文件重写是把 Redis 进程内的数据转化为写命令同步到新 AOF 文件的过程。
需要压缩重写的案例:
-
AOF 重写,持久化文件会变得越来越大。比如,我们调用 INCR test 命令 100 次,文件中就必须保存全部的 100 条命令,但其实 99 条都是多余的。
- 因为要恢复数据库的状态其实文件中保存一条 SET test 100 就够了。
-
Redis 提供了 bgrewriteaof 命令,来合并重写 AOF 的持久化文件。收到命令后,Redis 将使用与快照类似的方式将内存中的数据以命令的方式保存到临时文件中,最后替换原来的文件,以此来实现控制 AOF 文件的合并重写(会将重写过程中接收的的新的指令和生成新的重写后 AOF 文件中的指令进行合并)。
注意:由于是模拟快照的过程,因此在重写 AOF 文件时并没有读取旧的 AOF 文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的 AOF 文件
AOF 重写目的
- AOF 重写降低了文件占用空间。
- 更小的 AOF 文件可以更快地被 Redis 加载。
AOF 重写过程
可以手动触发和自动触发:
- 配置重写 rewrite 触发机制
- 手动触发:直接调用 bgrewriteaof 命令
- 自动触发:根据 auto-aof-rewrite-min-size 和 auto-aof-rewrite-percentage 参数确定自动触发时机
- auto-aof-rewrite-min-size: 限制了允许重写的最小 AOF 文件,通常在 AOF 文件很小的时候即使其中有些冗余命令也可是可以忽略的。
- auto-aof-rewrite-percentage: 当前的 AOF 文件大小超过上一次重写的 AOF 文件大小的百分之多少时会再次进行重写,如果之前没有重写过,则以启动时的 AOF 大小为依据。
注意,执行 AOF 重写请求时,父进程依然响应命令,Redis 使用 "AOF 重写缓冲区" 保存这部分新数据,防止新 AOF 文件生成期间丢失这部分数据。
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
解释含义:当 AOF 文件大小是上次 rewrite 后大小的一倍且文件大于 64M 时触发。一般都设置为 3G,64M 太小了, 这里的 “一倍” 和 “64M” 可以通过配置文件修改。
重写后的 AOF 文件为什么可以变小?
有如下原因:
- 进程内已经超时的数据不再写文件。
- 旧的 AOF 文件含有无效命令,如 del key1、set a 111、set a 222 等。重写使用进程内数据直接生成,这样新的 AOF 文件只保留最终数据的写入命令。
- 多条写命令可以合并为一个,如 lpush list a、lpush list b、 lpush list c 可以转化为:lpush list a b c。
为了防止合并的数据过大造成客户端缓冲区溢出,对于 list、set、hash、zset 等类型,以 64 个元素为界拆分为多条。
AOF 的优缺点
优点
- 数据的完整性和一致性更高
- AOF 是通过保存 Redis 写操作的命令来实现持久化,使用 AOF 来持久化,Redis 数据的安全性将大幅提高,异常宕机情况下最多丢失 1s 的数据。AOF 文件记录了 redis 的写操作,格式清晰,易于理解和修改,利于数据的重建。
- AOF 日志是一个只附加的日志,因此如果断电,就不会出现查找或损坏问题。即使日志由于某种原因(磁盘已满或其他原因)以半写的命令结束,redis check aof 工具也可以轻松地修复它。
- 当 AOF 太大时,Redis 能够在后台自动重写 AOF。重写是完全安全的,因为当 Redis 继续附加到旧文件时,一个全新的文件会生成,只需创建当前数据集所需的最少操作集,一旦第二个文件就绪,Redis 就会切换这两个文件并开始附加到新文件中。
- AOF 以易于理解和解析的格式包含所有操作的日志。我们甚至可以轻松导出 AOF 文件。
AOF 劣势
-
同步策略的不同,AOF 在运行效率上往往会慢于 RDB。总之,每秒同步策略的效率是比较高的,同步禁用策略的效率和 RDB 一样高效。
-
数据的完整性和一致性更高因为 AOF 记录的内容多,文件会越来越大,数据恢复也会越来越慢。
-
对于相同数量的数据集而言,AOF 文件通常比相同数据集的等效 RDB 文件大。
-
根据具体的 fsync 策略,RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
总结
每秒同步策略的效率是比较高的,同步禁用策略的效率和 RDB 一样高效。
- AOF 的数据完整性比 RDB 高,但记录内容多了,会影响数据恢复的效率。
- RDB 与 AOF 二者选择的标准,就是看系统是愿意牺牲一些性能,换取更高的缓存一致性(aof),还是愿意写操作频繁的时候,不启用备份来换取更高的性能,待手动运行 save 的时候,再做备份(rdb)。
- Redis 允许同时开启 AOF 和 RDB,既保证了数据安全又使得进行备份等操作十分容易。此时重新启动 Redis 后 Redis 会使用 AOF 文件来恢复数据,因为 AOF 方式的持久化可能丢失的数据更少。
- 若只打算用 Redis 做缓存,可以关闭持久化。
使用建议
Redis 默认开启了持久化功能,而且是全量 RDB 的,缺点是服务器宕机后可能会造成数据丢失。
建议最好还是搭配使用 aof 的 everysec,既能够保证数据的同步,效率也还可以,但是会存在丢失一秒数据的可能性,就算丢失也关系不大,因为数据库中已经存在了数据。
使用总结
-
Redis 默认开启 RDB 持久化方式,在指定的时间间隔内,执行指定次数的写操作,则将内存中的数据写入到磁盘中。
-
RDB 持久化适合大规模的数据恢复但它的数据一致性和完整性较差。
-
Redis 需要手动开启 AOF 持久化方式,默认是每秒将写操作日志追加到 AOF 文件中。
所以 Redis 的持久化和数据的恢复要选择在夜深人静的时候执行是比较合理的。
混合持久化
如果开启了混合持久化,AOF 在重写时,不再是单纯将内存数据转换为 RESP 命令写入 AOF 文件,而是将重写这一刻之前的内存做 RDB 快照处理,并且将 RDB 快照内容和增量的 AOF 修改内存数据的命令存在一起,都写入新的 AOF 文件,新的 AOF 文件会原子覆盖掉原来的 AOF 文件。
开启混合持久化命令
aof-use-rdb-preamble yes # 开启混合持久化
appendonly.aof 文件中同时保存着 RDB 和 AOF 两种格式的数据
RDB、AOF、混合持久化对比
二:redis 主从复制
Redis 主从复制的内容包括:如何使用主从复制、主从复制的原理(重点是全量复制和部分复制、以及心跳机制)、实际应用中需要注意的问题(如数据不一致问题、复制超时问题、复制缓冲区溢出问题)、主从复制相关的配置(重点是 repl-timeout、client-output-buffer-limit slave)等。
主从复制的概述
总体过程
-
主节点负责写数据,从节点负责读数据。
-
主从复制,是指将一台 Redis 服务器的数据,复制到其他的 Redis 服务器。
-
前者称为主节点 (master),后者称为从节点 (slave);
-
数据的复制是单向的,只能由主节点到从节点。
默认情况下,每台 Redis 服务器都是主节点;且一个主节点可以有多个从节点 (或没有从节点),但一个从节点只能有一个主节点。
主从复制的作用
-
数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式,数据可以保证可用性和一致性。
-
故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
-
负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写 Redis 数据时应用连接主节点,读 Redis 数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高 Redis 服务器的并发量。
-
高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是 Redis 高可用的基础。
主从复制的拓扑
一主一从
- 主节点故障转移从节点,当主节点的 “写” 命令并发高且需要持久化,可以只在从节点开启 AOF(主节点不需要),这样即保证了数据的安全性,也避免 AOF 持久化对主节点的影响
一主多从
- 针对 “读” 较多的场景,“读” 由多个从节点来分担,但节点越多,主节点同步到多节点的次数也越多,影响带宽,也加重主节点的负担
树状主从:
- 一主多从的缺点(主节点推送次数多压力大)可用些方案解决,主节点只推送一次数据到从节点 B,再由从节点 B 推送到 C,减轻主节点推送的压力。
主从复制的原理
了解 redis 复制原理对日后运维有很大帮助,包括如何规划节点,如何处理节点故障,redis 复制过程可分为三个阶段:
- 复制初始化阶段
- 数据同步阶段
- 命令传播阶段
复制初始化阶段
主从复制主要实现的一个流程如上图:
1. 当执行完 slaveof/replica masterip port 命令时候,从库根据指明的 master 节点 ip 和 port 向主库发起 socket 连接,主库收到 socket 连接之后将连接信息保存,此时连接建立,从服务器保存主服务器的配置信息,保存之后待从服务器内部的定时器执行时,就会触发复制的流程。
注意:replicaof 是新版本的命令,旧版本是 slaveof 命令,如果你使用的是 Redis 5.0.0 之前的版本,那么请使用 SLAVEOF 命令代替本章中的 REPLICAOF 命令,并使用 slaveof 配置选项代替本章中的 replicaof 配置选项
2. 从服务器首先会与主服务器建立一个 socket 套字节连接,用作主从通信使用。后面主服务器发送数据给从服务器也是通过该套字节进行。
3. socket 套字节连接成功之后,接着发送鉴权 ping 命令,正常的情况下,主服务器会发送对应 pong 的响应。ping 命令的作用是为了,保证 socket 套字节是否可以用,同时也是为了验证主服务器是否接受操作命令,否则可能出现超时或者主库此时在处理其他任务阻塞那么此时从库将断开 socket 连接,然后进行重试
4. 如果主库连接设置了密码,则从库需要设置 masterauth 参数,此时从库会发送 auth 命令,命令格式为 “auth + 密码” 进行密码验证,其中密码为 masterauth 参数配置的密码,需要注意的是如果主库设置了密码验证,从库未配置 masterauth 参数则报错,socket 连接断开。当身份验证完成以后,从节点发送自己的监听端口,主库保存其端口信息,此时进入下一个阶段:数据同步阶段
5. 可以开始复制数据了。主服务器此时会进行全量复制,将主服务的数据全部发给从服务器,从服务器保存主服务器发送的数据。
6. 接下来就是持续复制操作。主服务器会进行异步复制,一边将写的数据写入自身,同时会将新的写命令发送给从服务器。
数据同步阶段
-
主库和从库都确认对方信息以后,便可开始数据同步,此时从库向主库发送 psync 命令 (需要注意的是 redis4.0 版本对 2.8 版本的 psync 做了优化,后续会进行说明),
-
主库收到该命令后判断是进行增量复制还是全量复制,然后根据策略进行数据的同步,当主库有新的写操作时候,此时进入复制第三阶段:命令传播阶段。
命令传播阶段
-
当数据同步完成以后,在此后的时间里主从维护着心跳检查来确认对方是否在线,每隔一段时间(默认 10 秒,通过 repl-ping-replica-period/repl-ping-slave-period 参数指定)主节点向从节点发送 PING 命令判断从节点是否在线。
-
从节点每秒 1 次向主节点发送 REPLCONF ACK 命令,命令格式为:REPLCONF ACK {offset},其中 offset 指从节点保存的复制偏移量。
- 作用一是汇报自己复制偏移量,主节点会对比复制偏移量向从节点发送未同步的命令。
- 作用二在于判断主节点是否在线,从库接送命令并执行,最终实现与主库数据相同。
主从乐观复制机制
Redis 采用量乐观复制策略,容忍在一定时间内主从数据内容是不同的,但是两者的数据最终会同步。
主从复制实现策略
Redis 主从复制主要分为三种弄策略方式,不同的策略方式都是针对不同的场景下进行使用。
三种场景方式分别如下:
全量复制
全量复制用在主从复制刚建立时或者从切主服务器时,从服务器没有主服务器的数据,主服务器会将自身的数据通过 rdb 文件方式发送给从服务器,从服务器会清空自身数据,接着将主服务器发送的数据加载到自身中。
1. 从服务器->主服务器: 1.psync ? -1
2. 主服务器->从服务器: 2.fullsync runid offset
3. 从服务器: 保存 runid offset
4. 主服务器: 执行bgsave生成rdb
5. 主服务器->从服务器: 发送rdb
6. 从服务器: 清空自身老数据
7. 从服务器: 加载主服务器数据
部分复制
部分复制用在一些异常情况下,例如主从延迟、从服务宕机之后重新启动接收主服务器发送的部分数据。
部分复制的实现主要依赖于复制缓存区、主服务的 runid、主从服务器各自复制偏移量 (offset)。
复制缓存区 **
repl_backlog_size:保存在主节点上的一个固定长度的先进先出队列,默认大小是 1MB。
-
主服务在接收写命令时,会将命令写入缓存区,以便从服务器在异常情况下,减少数据的丢失。
-
当从服务器正常连接之后,主服务器会将缓存区内的数据发送给从服务器。这里的缓存区是一个长队列。
-
主节点发送数据给从节点过程中,主节点还会进行一些写操作,这时候的数据存储在复制缓冲区中。从节点同步主节点数据完成后,主节点将缓冲区的数据继续发送给从节点,用于部分复制。
-
主节点响应写命令时,不但会把写命令发送给从节点,还会写入复制积压缓冲区,用于复制命令丢失的数据补救。
主服务器 runid
- 主服务器会在每次服务启动之后,会生成一个唯一的 ID,作为自身标识。从服务器会将该标识保存起来,发送部分复制命令时,会使用该 runid。
psync runid offset
主从复制各自偏移量:
主从服务在建立复制之后,都会有自身的偏移量 offset。
-
主节点在每次进行了写命令之后,也会增加自身的偏移量(offset=offset + 命令的字节长度)。这里的偏移量是通过命令的字节长度累加计算。
-
从节点会每秒钟发送自身复制的偏移量给主节点,主节点在发送写命令之后,从节点也会增加自身的复制偏移量(也会增加自己的 offset)。
主节点同时保存自己的 offset 和从节点的 offset,通过对比 offset 来判断主从节点数据是否一致。
异步复制
异步复制是针对主从建立复制关系之后,主从服务器持续保持复制关系。
场景问题
数据安全
- 从服务器只读
replica-read-only yes
- 从服务器连接密码
masterauth
数据延迟。
默认的情况下主节点存在新数据不会立即发送给从服务器,如果开启,则会理解延迟发送给从服务器。默认的时间间隔与 Linux 内核定义有关。
- 复制延迟
repl-disable-tcp-nodelay yes
主从节点连接状态。
主从节点一旦建立连接之后,会定时模拟成对方的客户端,检测对方的服务状态。主节点可以通过设置 repl-ping-replica-period/repl-ping-slave-period 配置参数进行设置。默认的频率是 10s。
从节点咋执行 **replconf ack {offsetid}** 时,也会将自身的复制偏移量发送给主服务器,主服务根据偏移量进行判断数据延迟。存在数据延迟就会从复制积压缓冲区的数据汇中,将对应的数据补发给从节点。
三:redis sentinel 原理
Sentinel出现的前提背景
Redis持久化机制和Redis主从架构的相辅相成实现了Redis的数据高可用性以及服务的可扩展性和负载性,但是只依靠持久化方案和主从复制能力(负载和数据的荣誉),在出现服务宕机的时候,故障切换无法自动去实现,还需要手工,这对人工成本造成了巨大的损失以及不稳定性。
持久化+主从复制 仍存在的痛点
当 主服务器下线后无法恢复服务使用主从复制,在master节点下线后,只能够手动将 slave 节点切换为 master,但是不能自动完成故障转移。
Sentinel的加入
Sentinel(哨兵)是Redis的高可用性解决方案:由一个或多个Sentinel实例组成的Sentinel系统可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器。
主从持久化机制与加入哨兵之后的对比:
Sentinel的主要功能
Redis Sentinel为Redis提供了完整的高可用解决方案。实际上这意味着使用Sentinel可以部署一套Redis,在没有人为干预的情况下去应付各种各样的失败事件。同时提供了一些其他的功能,例如:监控、通知、并为client提供配置。
Sentinel的概念定义
Redis-Sentinel是Redis官方推荐的高可用性(HA)解决方案 ,当用Redis做Master-slave的高可用方案时,假如master宕机了,Redis本身(包括它的很多客户端)都没有实现自动进行主备切换,而Redis-sentinel本身也是一个独立运行的进程,它能监控多个master-slave集群,发现master宕机后能进行自动切换。
Redis从 2.8发布了一个稳定版本的Redis Sentinel 。当前版本的 Sentinel称为Sentinel 2。它是使用更强大和更简单的预测算法来重写初始Sentinel实现。(Redis2.6版本提供Sentinel 1版本,但是有 一些问题)。
Sentinel的功能分布
-
监控(Monitoring):Sentinel会不断的检查你的主节点和从节点是否正常工作。
-
通知(Notification):被监控的Redis实例如果出现问题,Sentinel可以通过API(pub)通知系统管理员或者其他程序。
-
自动故障转移(Automatic failover):如果一个主节点没有按照预期工作,Sentinel 会开始进行故障转移,把一个从节点提升为主节点,并重新配置其他的从节点使用新的主节点,其他的从节点会开始复制新的主节点,并且使用Redis服务的应用程序在连接的时候也被通知新的地址。
-
配置提供(Configuration provider):客户端可以把 Sentinel 作为权威的配置发布者来获得最新的maste 地址。如果发生了故障转移,Sentinel集群会通知客户端新的master地址,并刷新 Redis 的配置。(sentinel会返回最新的master地址)
Sentinel的分布特性
-
如果只使用单个sentinel进程来监控redis集群是不可靠的,当sentinel进程宕掉后(sentinel本身也有单点问题,single-point-of-failure)整个集群系统将无法按照预期的方式运行。所以有必要将sentinel集群。
-
Redis Sentinel是一个分布式系统,Sentinel运行在有许多Sentinel进程互相合作的环境下,它本身就是这样被设计的。有许多Sentinel进程互相合作的优点如下:
-
当多个Sentinel同意一个master不再可用的时候,就执行故障检测。这明显降低了错误概率。
-
即使并非全部的Sentinel都在工作,Sentinel也可以正常工作,这种特性,让系统非常的健康(最好是奇数个,因为不容易选举成为同票)。
分布方式总体深入下图所示:
Sentinel的基本原理
总体而言:多个 Sentinel 进程(progress), 这些进程使用流言协议(gossip protocols)来 接收关于主服务器是否下线的信息, 并使用投票协议(agreement protocols)来决定是否执行自动故障迁移, 以及选择哪个从服务器作为新的主服务器。
Sentinel的主观下线(SDOWN)
一个服务器必须在 master-down-after-milliseconds 毫秒内, 一直返回无效回复才会被 Sentinel 标记为主观下线。
-
在Sentinel哨兵的运行阶段,(其会向其他的Sentinel哨兵、master和slave发送消息确认其是否存活),如果在指定的时间内未收到正常回应,暂时认为对方挂起了(被标记为主观宕机–SDOWN)。
-
【注意:当只有单个sentinel实例对redis实例做出无响应的判断,此时进入主观判断,不会触发自动故障转移等操作。】
-
【注意:一个服务器必须在 master-down-after-milliseconds 毫秒内, 一直返回无效回复才会被 Sentinel 标记为主观下线】
Sentinel的客观下线(ODOWN)
-
当多个Sentinel哨兵(数量由quorum参数设定)都报告同一个master没有响应了,通过投票算法(Raft算法),系统判断其已死亡(被标记为客观宕机–ODOWN)。
-
多个 Sentinel 实例在对同一个服务器做出 SDOWN 判断, 并且通过 SENTINEL is-master-down-by-addr 命令互相交流之后, 得出的服务器下线判断。
-
Sentinel可以通过向另一个 Sentinel 发送 SENTINEL is-master-down-by-addr 命令来询问对方是否认为给定的服务器已下线。
Sentinel下线操作
-
从主观下线状态切换到客观下线状态并没有使用严格的法定人数算法(strong quorum algorithm), 而是使用了流言协议: 如果 Sentinel 在给定的时间范围内(master_down_after_milliseconds), 从其他 Sentinel 那里接收到了足够数量的主服务器下线报告, 那么 Sentinel 就会将主服务器的状态从主观下线改变为客观下线。 如果之后其他 Sentinel 不再报告主服务器已下线, 那么客观下线状态就会被移除。
-
客观下线条件只适用于主服务器: 对于任何其他类型的 Redis 实例(其他sentinel和slave服务节点), Sentinel 在将它们判断为下线前不需要进行协商, 所以从服务器Slave或者其他 Sentinel 永远不会达到客观下线条件。
Sentinel的主从切换
- 此时Sentinel集群会选取领头的哨兵(leader)进行故障恢复,从现有slave节点中选出(算法后续有介绍)一个提升为Master,并把剩余Slave都指向新的Master,继续维护主从关系。
Sentinel自动发现机制
-
那么,Sentinel集群的机器是如何发现集群中的其他机器呢?
- 使用广播?很显然不合适,既然是redis的产品,自然要充分运用redis功能,Sentinel集群节点利用了Redis master的发布/订阅机制去自动发现其它节点。
每个Sentinel使用发布/订阅的方式持续地传播master的配置版本信息,配置传播的发布/订阅管道是: sentinel:hello,我们可以通过订阅其频道查看频道中的消息,如下:
Sentinel 利用 pub/sub(发布/订阅):
订阅了每个 master 和 slave 数据节点的 sentinel:hello 频道,去自动发现其它也监控了统一 master 的 sentinel 节点,Sentinel 向每 1s 向 sentinel:hello 中发送一条消息,包含了其当前维护的最新的master 配置。
-
如果某个sentinel发现自己的配置版本低于接收到的配置版本,则会用新的配置更新自己的 master 配置与发现的 Sentinel 之间相互建立命令连接,之后会通过这个命令连接来交换对于 master 数据节点的看法。
-
sentinel的状态会被持久化地写入sentinel的配置文件中。每次当收到一个新的配置时,或者新创建一个配置时,配置会被持久化到硬盘中,并带上配置的版本戳。这意味着,可以安全的停止和重启sentinel进程。
Sentinel的发现方式
原理中提及到了,当sentinel发现主库客观下线时候会进行领头哨兵选举(超过半数切大于阈值)进行故障恢复,其选举算法采用Raft算法,这也为什么说其设计思想类似与zookpeer,选举过程大体如下:
-
发现主库客观下线的哨兵节点(这里称为A)向每个哨兵节点发送命令要求对方选举自己为领头哨兵(leader);
-
如果目标哨兵没有选举过其他人,则同意将A选举为领头哨兵;
-
如果A发现有超过半数且超过quorum参数值的哨兵节点同意选自己成为领头哨兵,则A哨兵成功选举为领头哨兵。
-
【sentinel集群执行故障转移时需要选举leader,此时涉及到majority,majority 代表 sentinel 集群中大部分 sentinel 节点的个数,只有大于等于 max(quorum, majority) 个节点给某个 sentinel 节点投票,才能确定该sentinel节点为leader,majority 的计算方式为:num(sentinels) / 2 + 1】
-
当有多个哨兵节点同时参与领头哨兵选举时,出现没有任何节点当选可能,此时每个参选节点等待一个随机时间进行下一轮选举,直到选出领头哨兵。
故障恢复时从Slave中间选出Master的算法
-
按照slave优先级进行排序,slave-priority越低,优先级就越高;
-
如果slave priority相同,那么比较复制偏移量,offset越靠后(越大)则表明和旧的主库数据同步越接近,优先级就越高 ;
-
如果上面两个条件都相同,那么选择一个run id最小的从库;
主要根据slave-priority进行排序做控制选举,先比较slave_offset值越大优先级越高,如果相等在获取runid最小的(代表启动时间越早)。
Sentinel(哨兵)的运作流程
-
每个Sentinel以每秒钟一次的频率向它所知的Master,Slave以及其他 Sentinel 实例发送一个 PING 命令。(心跳机制)
-
如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 则这个实例会被 Sentinel 标记为主观下线。
-
如果一个Master被标记为主观下线,则正在监视这个Master的所有 Sentinel 要以每秒一次的频率确认Master的确进入了主观下线状态。(确认投票下线)
-
当有足够数量的 Sentinel(大于等于配置文件指定的值)在指定的时间范围内确认Master的确进入了主观下线状态, 则Master会被标记为客观下线 。
-
在一般情况下, 每个 Sentinel 会以每 10 秒一次的频率向它已知的所有Master,Slave发送 INFO 命令。(同步数据)
-
当Master被 Sentinel 标记为客观下线时,Sentinel 向下线的 Master 的所有 Slave 发送 INFO 命令的频率会从 10 秒一次改为每秒一次。
-
若没有足够数量的 Sentinel 同意 Master 已经下线, Master 的客观下线状态就会被移除。
-
若 Master 重新向 Sentinel 的 PING 命令返回有效回复, Master 的主观下线状态就会被移除。
Sentinel部署配置
-
redis源码中提供了 sentinel 配置的模板:sentinel.conf
-
Sentinel部署很简单,只需要配置一下/etc/redis-sentinel.conf配置文件就可以了,如下
#工作端口 port 26379 #工作目录 dir "/var/lib/redis/sentinel" #sentinel id ,建议注释掉,会自动生成 #sentinel myid 827f0104ad153f34db5a29b8cbb51ef21a31d6d5 #配置要监控的master名字和地址,最后一个2代表当sentinel集群中有2个sentinel认为master故障时候才判定master真正不可用。 官方把该参数称为quorum,在后续选举领头哨兵时候会用到 sentinel monitor mymaster 10.130.2.155 6379 2 #配置master密码:配置主服务器的密码(如没设置密码,可以省略) sentinel auth-pass mymaster Password #日志 logfile "/var/log/redis/sentinel.log" 配置完成后,使用systemctl start redis-sentinel启动即可。 Sentinel可以调整的相关参数 #主观SDOWN时间,单位毫秒,默认30秒。(心跳检测) sentinel down-after-milliseconds mymaster 30000 #在发生failover主备切换时候,最多允许多少个slave同时同步新的master。这个数字越小,完成failover所需的时间就越长,但是如果这个数字越大,就意味着越多的slave因为replication而不可用。可以通过将这个值设为 1 来保证每次只有一个slave处于不能处理命令请求的状态。 sentinel parallel-syncs mymaster 1 #failover-time超时时间,当failover开始后,在此时间内仍然没有触发任何failover操作,当前sentinel将会认为此次failover失败,单位毫秒。默认3分钟。 sentinel failover-timeout mymaster 180000
核心配置
sentinel monitor <master-name> <ip> <redis-port> <quorum>: 监控的 redis 主节点 #配置主服务器的密码(如没设置密码,可以省略) sentinel auth-pass mymaster 123456 #修改心跳检测 5000毫秒 sentinel down-after-milliseconds mymaster 5000
- sentinel 是 redis 配置的提供者,而不是代理,客户端只是从 sentinel 获取数据节点的配置,因此这里的 ip 必须是 redis 客户端能够访问的。
Sentinel 启动
虽然哨兵(sentinel) 释出为一个单独的可执行文件 redis-sentinel ,但实际上它只是一个运行在特殊模式下的 Redis 服务器,你可以在启动一个普通 Redis 服务器时通过给定 --sentinel 选项来启动哨兵(sentinel)。
如果你使用redis-sentinel可执行文件,你可以使用下面的命令来运行Sentinel:
$ redis-sentinel /path/to/sentinel.conf
当然也可以采用 redis服务的方式启动:
$ redis-server sentinel.conf --sentinel &
两种方式是一样的。
不管咋样,使用一个配置文件来运行Sentinel是必须的,这个文件被系统使用来存储当前状态,如果重启,这些状态会被重新载入。如果没有配置文件或者配置文件的路径不对,Sentinel将会拒绝启动。
默认情况下,Sentinels监听TCP端口26379,所以为了让Sentinels运行,你的机器的26379端口必须是打开的,用来接收其他Sentinel实例的连接,否则,Sentinels不能互相交流,也不知道该干什么,也不会执行故障转移。
-
初始化一个普通的redis服务器
-
加载Sentinel专用配置,例如命令表、参数等,Sentinel 使用 sentinel.c 中的命令表、函数等配置,普通 Redis 则使用 redis.c 中的配置
-
除了保存服务器一般状态之外,Sentinel 还会保存 Sentinel 相关状态
注意:
1 .当启动哨兵模式之后,如果你的master服务器宕机之后,哨兵自动会在从redis服务器里面 投票选举一个master主服务器出来;这个主服务器也可以进行读写操作!
-
如果之前宕机的主服务器已经修好,可以正式运行了。那么这个服务器只能进行读的操作,会自动跟随由哨兵选举出来的新服务器!
-
大家可以进入./redis-cli,输入info,查看你的状态信息;
持久化仍存在的问题:
-
一旦主节点宕机,从节点晋升成主节点,同时需要修改应用方的主节点地址,还需要命令所有从节点去复制新的主节点,整个过程需要人工干预。[哨兵已解决]
-
节点的写能力受到单机的限制。[集群已解决]
-
节点的存储能力受到单机的限制。[集群已解决]
那么,redis 集群是如何实现高可用的,它又如何部署呢?