Redis是目前使用最广泛的缓存程序之一,也被应用于多种场景,例如数据缓存、分布式锁等,Redis官方提供了多种部署架构,以满足不同应用场景下对于高可用和扩展性的要求。
01
单节点(single)
单节点的部署是最简单的,只要启动一个redis进程就可以了,但是不具备高可用性,一般生产环境不建议使用,其主要有以下问题:
- 单节点,一旦出问题,服务将会不可用【1】
- 单节点读能力有限,无法扩展【2】
- 单节点写能力有限,无法扩展【3】
- 单节点的存储能力受到单机的限制【4】
02
主从复制(master-slave)
由于单节点服务不可用问题【1】,Redis提供了主从复制的功能,使用主从复制模式,一旦主节点服务不可用,则可以启用从节点,尽量保证数据量不丢失(Redis主从复制提供的是最终一致性)。另外一方面从节点可以扩展主节点的读能力,分担大并发量的读操作【2】。
redis主从复制每从节点只能有一个主节点,而主节点可以同时具备多个从节点,复制流量只能是单向的,只能从主节点流向从节点。配置Master-Slave有三种方式:
在redis配置文件中增加配置:slaveof {masterHost} {masterPort}
在启动redis-server的命令行后添加:--slaveof {masterHost} {masterPort}
启动redis-server以后登陆redis-server终端输入命令:slaveof {masterHost} {masterPort}
注意:如果主节点配置了requirepass,则需要从节点配masterauth参数。
如何查看节点是否已经成功配置了Master-Slave配置呢?可以在节点上执行命令info replication,这个命令在master节点和slave节点上展示的内容不一样,可以看到各自的角色信息master/role,主节点或者从节点IP和端口信息。
主节点127.0.0.1:6379>info replication
127.0.0.1:6379>info replication # Replication role:master connected_slaves:1 slave0:ip=127.0.0.1,port=6380,state=online,......
从节点127.0.0.1:6380>info replication
127.0.0.1:6380>info replication # Replication role:slave master_host:127.0.0.1 master_port:6379 master_link_status:up master_last_io_seconds_ago:5 master_sync_in_progress:0 ...
一个节点可以使用slaveof {masterHost} {masterPort}变成一个从节点,也可以使用slaveof no one断开复制,重新变成一个主节点。
另外,从节点还可以实现切换主节点的功能,将当前从接待你对主节点的复制切换到对另外一个新的主节点的复制,只需要操作命令:slaveof {newMasterHost} {newMasterPort}
切换主节点涉及流程如下:
- 断开与当前主节点的复制关系
- 与新的主节点建立复制关系
- 删除从节点当前的所有数据(特别注意,一旦操作失误,原来的数据将会被全部清空)
- 从新的主节点复制数据
Redis主从复制解决了单节点的第【1】【2】问题,但是【3】【4】问题仍然无法解决,并且引入了新的问题:
主节点服务不可用,需要手工介入启用从节点升级为主节点,并且需要通知应用方修改主节点的地址,需要人工介入【5】
03
哨兵模式(redis sentinel)
为了解决第【5】个问题,Redis官方提供了哨兵sentinel模式,保证主从复制模式下从节点升级为主节点的过程全部自动化,sentinel有完善的不可达检测机制,主节点选举机制,以及主节点更换通知机制,从而实现了真正的高可用。
Redis sentinel有若干个sentinel节点和数据节点组成,sentinel节点定期会对所有节点的状态进行监控。当sentinel集群判断某个节点不可达时,会选择其中一个sentinel节点执行故障转移任务,并通知到调用的客户端。以一主两从三哨兵为例:
故障转移过程如下:
- 主节点故障,两个从节点与主节点失去连接,主从复制失败
- sentinel节点通过监控发现主节点的故障,多个sentinel达成一致以后,选择了其中一个sentinel节点作为leader sentinel
leader sentinel选择其中一个slave,发送slaveof no one命令给这个从节点,使其成为新的主节点。
- sentinel通知应用端新的主节点信息
- sentinel发送命令给另外的slave,让其去复制新的master节点
- 等旧的主节点恢复以后让其以slave的身份去复制新的master节点
注意事项
- sentinel模式本质上还是主从模式,但是具备了故障的自动转移的功能。
- sentinel节点本身也会有单点的问题,至少两个节点才能解决单节点问题,但还是建议至少三个节点,多个节点可以有效防止误判。
- sentinel集群可以配置同时监控多个主节点,也即监控多个主从集群,每个集群只能有一个master,但是slave可以有多个。
04
集群模式(redis cluster)
以上分析我们知道,即便使用了哨兵模式,但是还是存在【3】【4】问题,本质上是原来的架构写扩展能力受限,当遇到大并发、大流量、内存不足等情况,就会出现瓶颈。典型的解决方案有:
- 客户端分区 由客户端对数据进行分区处理,部署多套集群,不同的数据依据不同的逻辑转发到不同的集群,需要客户端自行处理数据路由,需要维护多套redis,成本较高。
- 使用代理搭建redis客户端代理,简化客户端的访问,数据路由等由代理实现。但是这引入了新的组件,部署架构更加复杂,并且还涉及代理本身的高可用问题。
为了解决以上的不足,官方终于提供了分布式集群方案,这就是Redis Cluster。如下图所示,Redis Cluster至少要求6个节点才能组成一个高可用的集群,6个节点是三主三从。这有两个方面的原因,一方面要求至少3个主节点,是为了避免网络分区的情况导致的脑裂问题,所以判断节点是否可听过服务需要至少过半数的节点的同意才行。另外一方面,每个节点至少需要有一个slave节点,当master故障时可以提升为主节点继续提供服务。
要理解Redis Cluster架构,我们需要理解其数据分布原理、节点通信原理、请求路由、扩缩容的方案。
数据分布原理
由于集群中有多个master节点,所以用户的数据需要依据某种规则被存放在某个节点下,当查询的时候也要知道数据从哪个节点去取,这就是数据分布方案要解决的问题。
Redis-Cluster使用了虚拟槽分区的方案来完成数据映射。首先,定义了一个整数集合,命名为槽slot,其范围为0~16383,然后使用哈希函数slot=CRC16(key)&16383将数据映射到某个槽上,再将槽分配给不同的节点,每个节点负责一定数量的槽,这样子达到了将数据分配到节点的目的。槽是redis cluster数据管理和迁移的基本单位。
节点通信原理
分布式系统一般都会涉及保存集群元数据信息的功能,对应到redis集群则是例如数据槽分配给哪个节点,节点的IP和端口等信息是多少等信息。一般来说有两种方案:集中式和P2P方式。集中式一般会选出一个leader节点或者master节点,由此节点来进行元数据管理,P2P方式则是通过数据交换,最终使得集群的所有节点都获得集群的元数据信息,节点间的地位是平等的。redis cluster选择的是P2P的方式。
每个redis节点都会单独开辟一个TCP端口用于节点的通信,采用Gossip协议进行交互,以ping消息为例流程如下:
1、每个节点开启用于集群内通信的TCP端口,用于集群通信。
2、每个节点配置几个种子节点,发送ping消息。
3、接收到ping消息的节点回复pong消息响应每个节点可能知道集群的全部节点信息,也可能只知道一部分,但是当进行一段时间的ping/pong消息交互以后,最终会达成每个节点都获得集群全部状态的效果。
Gossip协议定义了多种消息格式:ping、pong、meet、fail等,作用各不相同。
请求路由
上面说到所有的master节点都是平等的,客户端连接时是不知道请求的数据是应该落在哪个节点上的,那么应该如何进行请求的路由呢?
这里可能会有疑问,为何不适用代理的方式呢,可以完全与redis节点解耦合。我的理解是增加了一层代理以后引入了新的组件,架构会更加复杂,且性能会有损耗,所以官方选择直连redis节点的方案。
redis任意节点都可以接收客户端的请求,收到请求以后会根据哈希函数计算出数据对应的槽,而槽分配在哪个节点经过节点通信以后是节点本身是清楚的,于是,如果发现槽分配的是自己,则进行请求处理,如果发现是在其他节点,则返回MOVED重定向,表示数据在其他的节点,由客户端重定向后去请求其他的节点。