-
目录
3、为什么Redis Cluster槽位设计成16384个?
- 单节点模式
- 主从模式
- 哨兵模式(Sentinel)【2.8版本开始提供】
- 集群模式(Cluster)【3.0版本开始提供】
一、单节点模式
Redis默认启动,什么都不配置,默认就是Master节点,简单却不具备高可用。
优点
- 配置简单,操作简单
缺点
- 单点的宕机引来的服务的灾难、数据丢失
- 单点服务器内存瓶颈,无法无限纵向扩容
解决办法
- 单节点宕机,可以由其他节点暂时顶替,宕机的慢慢排查
二、主从模式
优点
- 有了主从,提高了Redis整体的可用性,当主节点(master)挂了,可以把从节点(slave)手动升级为主节点继续服务。
缺点
- master挂了整个Redis将失去写操作的能力,仅具备读操作,需要运维半夜爬起来手动升级,中间的请求失败数据丢失无法容忍。
解决办法
- 可以有一种方式自动升级slave为master ------【哨兵】
2.1 主从复制
从一台Redis服务器的数据(主节点master),复制到其他Redis服务器(从节点slave)。数据复制单向,只能由主节点到从节点,master可读可写,slave只可读不可写;默认每台Redis服务器都是主节点,从节点需要在配置文件中单独配置,才会从默认的主节点变成从节点。一个主节点可以有0个或多个从节点,但每个从节点只能有一个主节点。
2.1.1 复制原理
- slave第一次连接master,一定会执行一次全量复制
- 全量复制数据量过大,会造成很大的网络开销,消耗CPU/内存/硬盘IO
- 增量复制用于处理在主从复制中因网络等数据丢失的场景,当slave再次连接上master,并且就是原来的master,如果条件允许,master补发数据给slave,补发数据量小,避免全量复制的开销(到底能不能复制还要看offset和buffer的情况)
- 如果slave再次连上的master是新选举的master,那么只能进行全量复制
- 早期的redis只有全量复制,增量复制是对全量复制的重大优化,尽量采用2.8以上版本
2.1.1.1 全量复制
- slave给master发一个sync同步命令
- master通过bgsave命令fork子进程,持久化生成RDB文件
- master通过网络将RDB文件传给slave
- slave清空老数据,载入新的RDB文件,此时slave阻塞,无法响应客户端,专心复制
2.1.1.2 增量复制
- 主从节点各自维护自己的复制偏移量offset,主节点写入命令时,offset=offset+命令字节长度;从节点收到主节点命令也会相应增加自己的offset,并同步给主节点。主节点同时维护自己的offset和从节点的offset,以此来判断主从节点数据是否一致。
- 主节点指令记录在本地buffer(缓冲区),异步将buffer同步给从节点
- 若网络不好,同步速度慢了,buffer满了就会从头开始覆盖前面的内容,于是无法增量复制,必须全量复制
2.2 读写分离
大部分情况都是读操作,将读操作放在从节点,写操作放在主节点,减缓服务器压力;同时一些执行耗时比较久的操作也可以放在一台从节点完成,例如keys、sort。(什么时候连主节点写,什么时候连从节点读,由客户端自己控制)
最低配:一主二从,当主节点宕机后,其中一个从节点升级为主节点,还能剩一个从节点。
2.3 主要作用
- 数据冗余:热备份,持久化另一种方式
- 故障恢复:master宕机,快速升级slave为master
- 读写分离:master写,slave,提高服务器负载能力,同时可以根据需求添加slave
- 负载均衡:配合读写分离,读多写少场景,多个slave分担负载,大大提高并发
- 高可用基石:是实现哨兵和集群的基础
三、哨兵模式
试想一下,如果主从模式中,大半夜主节点挂了,运维从床上迷迷糊糊爬起来,打开电脑,手动升级处理,怕不是第二天就要上头条了。
哨兵模式的出现用于解决主从模式中无法自动升级主节点的问题,一个哨兵是一个节点,用于监控主从节点的健康,当主节点挂掉的时候,自动选择一个最优从节点升级为主节点。
但哨兵如果挂了怎么办?于是哨兵一般都会是一个集群,是集群高可用的心脏,一般由3-5个节点组成,即使个别节点挂了,集群还可以正常运行。
优点
- 可以在master挂掉后自动选举新的master
缺点
- master挂了,切换新的master会造成未来得及主从同步的数据丢失
- 大数据高并发,单个master内存仍存在上限
- 主从全量同步仍然要耗费大量时间
- 单个Redis只能利用CPU的单个核心,应对海量数据捉襟见肘
解决办法
- Redis集群方案
客户端连接Redis,会首先连接Sentinel,通过Sentinel查询master地址,然后再连接master进行数据交互。当master挂了,客户端重新跟Sentinel要master地址,连接新的master。
上图中可看,master挂了,原先的主从复制断开,客户端和master也断开。然后一个slave变成新的master,和其余的slave进行新的主从复制,客户端通过新的master继续交互,Sentinel持续监控已经挂掉的旧的master,一旦旧的master恢复,集群会变为下图,旧的master成为新的slave,从新的master建立主从复制关系。
- 一个Sentinel或Sentinel集群可以管理多个主从Redis,如果只有一个Redis,Sentinel没有意义
- master挂了,Sentinel在slave中选举一个升级为master,并修改配置文件
- 挂了的master恢复后,只能当slave,跟新选举的master主从复制
- Sentinel有可能会挂,所以Sentinel一般都是一个集群
- 每个Sentinel节点定期检测Redis数据节点、其余Sentinel节点是否可达
- 对于节点的故障是由多个Sentinel节点共同完成,有效防止误判
- Redis主从复制为异步,master挂了,从节点可能会丢失一部分信息,Sentinel无法保证消息完全不丢失,但可以做到尽量少丢失
- Sentinel.slave_for方法可以从连接池内采用轮询方案拿出一个slave地址,实现负载均衡
3.1 定时监控任务
- 每隔10秒,每个Sentinel节点会向主节点和从节点发送info命令获取最近的拓扑结构,可以感知新加入或故障转移的Redis数据节点
- 每隔2秒,每个Sentinel节点会与其他Sentinel节点通信,可以发现新的Sentinel节点,并与其他Sentinel节点交换对主节点的判断信息,方便故障转移及选举
- 每隔1秒,每个Sentinel节点会向主节点、从节点以及其他的Sentinel节点发送ping做一次心跳检测,来确认这些节点是否可达,实现对每个节点的监控
3.2 主观下线和客观下线
主观下线
当Sentinel节点ping其他节点,超时未回复,Sentinel节点就会对该节点做失败判定。主观下线是当前Sentinel节点的一家之言,存在误判可能
客观下线
当Sentinel主观下线的节点是master,该Sentinel会询问其他Sentinel对master的判断,当大部分Sentinel都对master的下线做了同意判断,那么这个判断就是比较客观的
3.3 Sentinel集群的领导者选举
Sentinel节点已经对master做了客观下线,但还不能立刻进行故障转移,因为故障转移工作只需要一个Sentinel节点来完成,因此在Sentinel集群中要选出一个leader来进行故障转移。
Redis使用了Raft算法实现领导者选举:
- 每个在线的Sentinel节点都有资格成为领导者,他要求其他节点来投自己一票
- 先收到谁的要求就给谁投票,但不能给自己投
- 首先拿到大多数(即一半+1)的票当选领导者
3.4 故障转移
Sentinel集群领导者负责此次故障转移
- 排除主观下线的从节点
- 选择优先级高的从节点,如果没有再进行3
- 选择复制偏移量最大的从节点(复制的最完整),如果没有再进行4
- 选择runid最小的从节点
四、 集群模式
集群方案真正实现了Redis高可用,有很多种实现方式,目前最常用的Redis Cluster是Redis的亲儿子,是Redis作者自己提供的Redis集群化方案,从Redis3.0版本开始正式提供。
集群由多个Redis主从组成,每一个主从代表一个节点,每个节点负责一部分数据,他们之间通过一种特殊的二进制协议交互集群信息。
Redis Cluster将所有数据分片,分成16384个槽位,Redis Cluster对key值使用crc16算法进行hash,然后用除留余数发模除16384得到具体的槽位,每个节点负责其中一部分槽位。
当客户端连接集群,会得到一份集群的槽位匹配信息,当客户端要查找key,可以直接定位到目标节点。
Cluster去中心化,由多个节点组成,客户端连接时可以只用一个节点的地址,其余节点可通过该节点自动发现,但如果该节点挂了,就必须手动更换地址,因此连接多个地址安全性更高。
容错
Redis Cluster拥有类似哨兵的功能,每个节点仍需设置若干从节点,主节点发生故障,集群可将slave升级为master;否则如果master挂了,集群完全不可用;
且Redis Cluster是去中心化,集群内某个节点不可用时,一个节点认为他失联并不代表所以节点都认为他失联,集群要进行一次商议,只有大多数节点认为他失联,才会认为其需要主从切换来容错。
动态扩容
假如原先集群中有3个节点,一共3000个数据,可能1-1000在第一个节点,1001-2000在第二个节点,2001-3000在第三个节点。
当新节点加入集群,需要手动将槽和数据迁移到新节点,可以使用redis-trib工具或手动命令迁移(略)。
大致流程:
- 从源节点获取内容
- 存到目标节点
- 删除源节点内容
优点
- 无中心架构,支持动态扩容
- Cluster自动具备哨兵监控和故障转移(主从切换)能力
- 客户端连接集群内部地址可自动发现
- 高性能、高可用,有效解决了Redis分布式需求
缺点
- 运维复杂
- 只能使用0号数据库
五、常见问题
1、Sentinel节点为什么是至少三个且奇数个?
- 首先必须是集群,所以不能是1个。而选举过程要大多数同意才行(即最少一半+1个),而奇数个节点在同样选举条件上可以节省一台机器。
- 比如一共5台,有3台同意了就行;而4台,大多数同意也要3台。
2、Redis集群节点数为什么至少是6个?
- 6个包含一主一从,如果每个节点一主二从则需要9个
- 最少3个节点,是因为容错能力相同情况下,奇数节点更节省资源(3个节点的集群允许一个节点宕机;而4个节点的集群也允许一个节点宕机)
3、为什么Redis Cluster槽位设计成16384个?
- 2^14=16384,当槽位再扩就是32768、65536,此时每一次ping都要将槽位作为心跳包,信息太大,占用带宽
- 由于集群节点越多,心跳包携带的数据就越多。当节点超过1000,会导致网络拥堵。因此Redis作者不建议Redis Cluster节点数量超过1000,那么16384槽位也足够用