20-集群 1:李代桃僵 —— Sentinel
课程
1
开篇:授人以鱼不若授人以渔 —— Redis 可以用来做什么?
学习时长: 5分21秒
2
基础:万丈高楼平地起 —— Redis 基础数据结构
上次学到
学习时长: 16分14秒
3
应用 1:千帆竞发 —— 分布式锁
学习时长: 7分47秒
4
应用 2:缓兵之计 —— 延时队列
学习时长: 8分9秒
5
应用 3:节衣缩食 —— 位图
学习时长: 8分52秒
6
应用 4:四两拨千斤 —— HyperLogLog
学习时长: 14分17秒
7
应用 5:层峦叠嶂 —— 布隆过滤器
学习时长: 17分54秒
8
应用 6:断尾求生 —— 简单限流
学习时长: 4分37秒
9
应用 7:一毛不拔 —— 漏斗限流
学习时长: 7分22秒
10
应用 8:近水楼台 —— GeoHash
学习时长: 7分52秒
11
应用 9:大海捞针 —— Scan
学习时长: 8分42秒
12
原理 1:鞭辟入里 —— 线程 IO 模型
学习时长: 4分1秒
13
原理 2:交头接耳 —— 通信协议
学习时长: 3分34秒
14
原理 3:未雨绸缪 —— 持久化
学习时长: 5分27秒
15
原理 4:雷厉风行 —— 管道
学习时长: 3分51秒
16
原理 5:同舟共济 —— 事务
学习时长: 6分36秒
17
原理 6:小道消息 —— PubSub
学习时长: 7分7秒
18
原理 7:开源节流 —— 小对象压缩
学习时长: 7分14秒
19
原理 8:有备无患 —— 主从同步
学习时长: 4分9秒
20
集群 1:李代桃僵 —— Sentinel
学习时长: 3分52秒
21
集群 2:分而治之 —— Codis
学习时长: 7分28秒
22
集群 3:众志成城 —— Cluster
学习时长: 8分38秒
23
拓展 1:耳听八方 —— Stream
学习时长: 13分40秒
24
拓展 2:无所不知 —— Info 指令
学习时长: 4分4秒
25
拓展 3:拾遗补漏 —— 再谈分布式锁
学习时长: 2分18秒
26
拓展 4:朝生暮死 —— 过期策略
学习时长: 2分21秒
27
拓展 5:优胜劣汰 —— LRU
学习时长: 4分34秒
28
拓展 6:平波缓进 —— 懒惰删除
学习时长: 2分13秒
29
拓展 7:妙手仁心 —— 优雅地使用 Jedis
学习时长: 6分35秒
30
拓展 8:居安思危 —— 保护 Redis
学习时长: 2分19秒
31
拓展 9:隔墙有耳 —— Redis 安全通信
学习时长: 6分34秒
32
拓展 10:法力无边 —— Redis Lua 脚本执行原理
学习时长: 9分24秒
33
拓展 11:短小精悍 —— 命令行工具的妙用
学习时长: 9分21秒
34
源码 1:丝分缕析 —— 探索「字符串」内部
学习时长: 5分20秒
35
源码 2:循序渐进 —— 探索「字典」内部
学习时长: 7分24秒
36
源码 3:挨肩迭背 —— 探索「压缩列表」内部
学习时长: 10分42秒
37
源码 4:风驰电掣 —— 探索「快速列表」内部
学习时长: 3分49秒
38
源码 5:凌波微步 —— 探索「跳跃列表」内部
学习时长: 9分57秒
39
源码 6:破旧立新 —— 探索「紧凑列表」内部
学习时长: 2分42秒
40
源码 7:金枝玉叶 —— 探索「基数树」内部
学习时长: 5分36秒
41
源码 8:精益求精 —— LFU vs LRU
学习时长: 8分4秒
42
源码 9:如履薄冰 —— 懒惰删除的巨大牺牲
学习时长: 9分53秒
43
源码 10:跋山涉水 —— 深入字典遍历
学习时长: 9分24秒
44
源码 11:见缝插针 —— 探索 HyperLogLog 内部
学习时长: 13分3秒
45
尾声:百尺竿头 —— 继续深造指南
学习时长: 2分32秒
juejin_logo copyCreated with Sketch.

集群 1:李代桃僵 —— Sentinel

目前我们讲的 Redis 还只是主从方案,最终一致性。读者们可思考过,如果主节点凌晨 3 点突发宕机怎么办?就坐等运维从床上爬起来,然后手工进行从主切换,再通知所有的程序把地址统统改一遍重新上线么?毫无疑问,这样的人工运维效率太低,事故发生时估计得至少 1 个小时才能缓过来。如果是一个大型公司,这样的事故足以上新闻了。

所以我们必须有一个高可用方案来抵抗节点故障,当故障发生时可以自动进行从主切换,程序可以不用重启,运维可以继续睡大觉,仿佛什么事也没发生一样。Redis 官方提供了这样一种方案 —— Redis Sentinel(哨兵)。

我们可以将 Redis Sentinel 集群看成是一个 ZooKeeper 集群,它是集群高可用的心脏,它一般是由 3~5 个节点组成,这样挂了个别节点集群还可以正常运转。

它负责持续监控主从节点的健康,当主节点挂掉时,自动选择一个最优的从节点切换为主节点。客户端来连接集群时,会首先连接 sentinel,通过 sentinel 来查询主节点的地址,然后再去连接主节点进行数据交互。当主节点发生故障时,客户端会重新向 sentinel 要地址,sentinel 会将最新的主节点地址告诉客户端。如此应用程序将无需重启即可自动完成节点切换。比如上图的主节点挂掉后,集群将可能自动调整为下图所示结构。

从这张图中我们能看到主节点挂掉了,原先的主从复制也断开了,客户端和损坏的主节点也断开了。从节点被提升为新的主节点,其它从节点开始和新的主节点建立复制关系。客户端通过新的主节点继续进行交互。Sentinel 会持续监控已经挂掉了主节点,待它恢复后,集群会调整为下面这张图。

此时原先挂掉的主节点现在变成了从节点,从新的主节点那里建立复制关系。

消息丢失

Redis 主从采用异步复制,意味着当主节点挂掉时,从节点可能没有收到全部的同步消息,这部分未同步的消息就丢失了。如果主从延迟特别大,那么丢失的数据就可能会特别多。Sentinel 无法保证消息完全不丢失,但是也尽可能保证消息少丢失。它有两个选项可以限制主从延迟过大。

min-slaves-to-write 1
min-slaves-max-lag 10

第一个参数表示主节点必须至少有一个从节点在进行正常复制,否则就停止对外写服务,丧失可用性。

何为正常复制,何为异常复制?这个就是由第二个参数控制的,它的单位是秒,表示如果 10s 没有收到从节点的反馈,就意味着从节点同步不正常,要么网络断开了,要么一直没有给反馈。

Sentinel 基本使用

接下来我们看看客户端如何使用 sentinel,标准的流程应该是客户端可以通过 sentinel 发现主从节点的地址,然后在通过这些地址建立相应的连接来进行数据存取操作。我们来看看 Python 客户端是如何做的。

>>> from redis.sentinel import Sentinel
>>> sentinel = Sentinel([('localhost', 26379)], socket_timeout=0.1)
>>> sentinel.discover_master('mymaster')
('127.0.0.1', 6379)
>>> sentinel.discover_slaves('mymaster')
[('127.0.0.1', 6380)]

sentinel 的默认端口是 26379,不同于 Redis 的默认端口 6379,通过 sentinel 对象的 discover_xxx 方法可以发现主从地址,主地址只有一个,从地址可以有多个。

>>> master = sentinel.master_for('mymaster', socket_timeout=0.1)
>>> slave = sentinel.slave_for('mymaster', socket_timeout=0.1)
>>> master.set('foo', 'bar')
>>> slave.get('foo')
'bar'

通过 xxx_for 方法可以从连接池中拿出一个连接来使用,因为从地址有多个,redis 客户端对从地址采用轮询方案,也就是 RoundRobin 轮着来。

有个问题是,但 sentinel 进行主从切换时,客户端如何知道地址变更了 ? 通过分析源码,我发现 redis-py 在建立连接的时候进行了主库地址变更判断。

连接池建立新连接时,会去查询主库地址,然后跟内存中的主库地址进行比对,如果变更了,就断开所有连接,重新使用新地址建立新连接。如果是旧的主库挂掉了,那么所有正在使用的连接都会被关闭,然后在重连时就会用上新地址。

但是这样还不够,如果是 sentinel 主动进行主从切换,主库并没有挂掉,而之前的主库连接已经建立了在使用了,没有新连接需要建立,那这个连接是不是一致切换不了?

继续深入研究源码,我发现 redis-py 在另外一个点也做了控制。那就是在处理命令的时候捕获了一个特殊的异常ReadOnlyError,在这个异常里将所有的旧连接全部关闭了,后续指令就会进行重连。

主从切换后,之前的主库被降级到从库,所有的修改性的指令都会抛出ReadonlyError。如果没有修改性指令,虽然连接不会得到切换,但是数据不会被破坏,所以即使不切换也没关系。

作业

  1. 尝试自己搭建一套 redis-sentinel 集群;
  2. 使用 Python 或者 Java 的客户端对集群进行一些常规操作;
  3. 试试主从切换,主动切换和被动切换都试一试,看看客户端能否正常切换连接;
留言
Ctrl + Enter
全部评论(58)
BinK_1783的头像
删除
三分爱人,七分爱己。百毒不侵,永远年轻。秋冬冗长,你我皆好
点赞
回复
Hello Word!的头像
删除
这是我的做的笔记图,关于哨兵模式。
1. 它是一个单独的集群
2. 他的职责是监控,选主,主从切换,通知
9
回复
李辉君47606的头像
删除
哨兵是用在主从复制的吧,集群中没有哨兵吧。
3
1
删除
是的
1
回复
dotai的头像
删除
Web backend
”主从切换后,之前的主库被降级到从库,所有的修改性的指令都会抛出ReadonlyError。如果没有修改性指令,虽然连接不会得到切换,但是数据不会被破坏,所以即使不切换也没关系。“
这儿如果从库变成新主库后,新主库有的数据被修改,读取旧主库中的该数据是不是就会产生不一致的问题了?该如何处理呢?
点赞
5
删除
旧主库已经变成从库了, 为什么新主库还要去 从主读取数据???
点赞
回复
删除
问题指的是客户端读取旧主库,暂不切换连接,这个在主从同步之前都会有数据不一致的问题。
旧主库已经变成从库了, 为什么新主库还要去 从主读取数据???
点赞
回复
查看更多回复
yaoming的头像
删除
感觉为了一个节点高可用,多出了3个sentinel节点和两个从节点,想问一下是用3台机器部署集群还是用6台机器?
1
2
删除
个人觉得应该把 redis 服务节点和 sentinel 节点分开部署,也就是需要 6 台机器,欢迎交流讨论
点赞
回复
删除
交叉部署应该可以把。三台
点赞
回复
吴宇晨的头像
删除
后端开发 @ xxx
【如果没有修改性指令,虽然连接不会得到切换,但是数据不会被破坏,所以即使不切换也没关系。】
这句话,如果我其他的客户端修改了数据呢
点赞
1
删除
所有正在使用的连接都会被关闭,然后在重连时就会用上新地址。
点赞
回复
zyfsuzy的头像
删除
研发工程师 @ 百度
这篇文章写的更好:juejin.im
19
回复
cxh不想说话的头像
删除
叫主备才合适
2
回复
明天不想说话的头像
删除
通过分析源码,我发现 redis-py......
2
回复
more_money的头像
删除
make money @ money make
redis哨兵挂了怎么办?
1
1
删除
sentinel 有多节点,只要存活保留数过半,并且指定 mastername 的 quorum 小于等于存活数就可以了
点赞
回复
兜兜本尊71280的头像
删除
客户端与哨兵的连接 是 怎么处理断开的
点赞
回复
海子同志的头像
删除
redis 主如果不知道自己掉线,变成从库,并且此时已经被client端链接使用,应该不会抛出ReadOnlyError错误,这种情况下怎么处理?
1
1
删除
这就是网络分区了,网络分区是没有办法处理当前连接上的客户端的写入的,需要设置上面提到的两个参数就可以了,达到上面两个条件后 master 会变成不可用。
点赞
回复
xegk6un17n8tbpyggvche6uas的头像
删除
这个讲的有点简单了
1、讲哨兵机制,怎么能不讲主观下线和客观下线呢
2、选举机制是如何的,以及和zookeeper的选举算法是不是一样的,至少要说下吧。
26
回复
爱学习53960的头像
删除
java工程师 @ 体面厂
parallel-syncs 你好这个配置参数的目的是什么啊,我看网上说配置的值越小,发生切换的事件就越短
点赞
回复
One day58982的头像
删除
1、篇幅有点短,很多内容都是一笔带过,比如,sentinel是怎么重新选举master的?
2、作者只是回复刚写完文章的评论,可以在写完之后半年抽空回复下评论吗?
32
1
删除
去看《Redis设计与实现 》 细致到爆
点赞
回复
钱同志的头像
删除
sentinel的高可用怎么实现?会出现恰好脑裂的情况嘛?如果出现了改怎么办?
高可用的哨兵系统来监控和维护Redis的集群的主从切换和主从同步。另外,主挂掉后sentinel怎么判断最佳的从的呢?
点赞
回复
都是浮云啊的头像
删除
码奴
master和slave的主从切换懂了,sentinel的主从切换看的不是很懂,博主可以出个文章放公众号上吗
1
回复
金鹏_的头像
删除
redisgo可以做到自动判断么?
点赞
回复
Be Bold47680的头像
删除
翻阅了源码,发现文中sentinel 主动进行主从切换的场景,并不是把所有旧链接关闭,而是把当前的操作的链接关闭,博主可再详看下
7
回复
hongxiang的头像
删除
请问,为什么一定要进行领头sentinel的选举,而不是直接让第一个判定客观下线的sentinel进行故障转移?
2
回复

查看全部 58 条回复