Redis 集群是一个提供在多个Redis间节点间共享数据的程序集。通过分区来提供一定程度上的可用性。
观察一个典型的Redis Cluster集群部署方式
这种集群部署方式可以提供横向扩展的能力,当数据量不断变大的时候,只需要添加Redis节点即可扩展服务,其背后的思想是通过一定的算法将数据分布到不同的节点上,当节点越多,则每个节点需要处理的数据就越少,但很明显,这种部署方式需要解决以下几个问题:
- 数据如何均匀的分布到各个节点?
- 客户端如何知道该向哪个节点发起请求?
- 节点故障时,如何做到继续提供服务?
- 如何增加或删除节点?删除节点的时候,原来节点的数据会如何变化?
- 如果同一条指令操作不同节点的数据,会发生什么?
数据分片
Redis 没有使用一致性hash,而是引用了hash slot 的概念,Redis将键空间分成16384个slot,将key的CRC16结果对16384取余得到的值作为该key所存放的slot。每个Redis节点管理一部分的slot,假如有3个主Redis节点A,B,C,那么可以分配A管理slot编号为0-6000, B管理slot编号为6001-12340, C管理slot编号为12341-16383。
而对于同一个指令中含有不同slot的key时,redis服务器会无法执行指令,向客户段返回错误信息。所以redis提供了一个额外的机制来应对这种需要多key的操作。如果一个key有包含{substring} 这种模式的字符串,那么计算key的CRC16时,只会去{}中的字符串进行计算,例如 key{avatar}1, key{avatar}2, key{avatar}3 在计算时都只取avatar进行计算,所以会导致这几个key必定被分配到同一个slot中
重定向服务
那么我们怎么知道该向哪个节点发起服务呢?
对于客户端发上来的请求,服务器会对请求命令中的key做判断,判断其所属的slot是否是该服务器所管理的,如果是该服务器管理的节点,那么就会继续执行命令;而如果不是,则会拒绝执行命令,向客户端返回MOVE 指令,该指令包含key所在的slot 以及该lot所在的ip和端口,客户端可以通过返回的信息重新对新的Redis服务器发起请求。
客户端可以缓存该MOVE指令的数据,可以下次调用的时候,本地计算key所在的slot,然后查询slot所在服务器,然后直接向对应Redis服务器发起请求,可以获得更高的性能。
主从切换
为了使得在节点故障得的时候,集群仍然可以使用,Redis采用了主从复制的模型,当有主节点下线的时候,从节点会代替主节点继续工作。
Redis有两种方式可以执行主从切换,一种是自动的切换,另一种是手动的切换。
自动切换
通过哨兵机制来发现主节点掉线,其原理和过程在
https://blog.csdn.net/ii0789789789/article/details/123309187
已经分析完毕了。
手动切换
除了自动切换外,Redis提供了可以手动切换主从的命令,其一般用来实现Redis主节点的平滑升级,一般步骤是先切换主节点为从节点,然后对原先主节点进行版本升级,完毕之后在切换成主节点,期间服务不会中断。
当一个从节点收到cluster failover命令后, 会开始切换主从,其流程如下:
- 从节点会向集群发送mfstart包,表示开始主从切换
- 主节点阻塞命令继续执行,并向对应从节点开始同步数据
- 从节点开始同步处理命令的偏移位置,直到和主节点相同
- 开始切换流程(和自动部分切换流程一致)
- 切换完毕后,原先的主节点会将阻塞的客户端命令通过MOVED指令重定向到新的主节点。
- 至此,手动切换完毕。
副本漂移
考虑经典的部署方式,集群共有6个Redis实例,三主三从,每个主从只能有一个处于故障状态,如果同一时刻有一对主从同时发送故障,那么集群还是会丢失某些slot,不能提供对应服务。那么为了提高可用性,可以采取的方式是每个主服务器多挂载几个从服务器。但这样带来的问题就是如果有10个主服务器,那么就要有10*N个从服务器,其成本还是很高的。
基于上述问题,Redis提供了一种副本漂移的方式,如图
我们只给节点C额外添加2个从服务器,当主服务器A发生故障后,从服务器A1会立马带代替主服务器A继续提供服务,但这个时候,A1就成为了单点服务,集群检测到这个单点问题后,就会从多余的C从服务器上挑选一个从服务器给A,让其避免成为单点服务。
Redis集群会定期检测两个情况:
- 集群中是否存在单点的主服务器
- 集群中是否有主服务器拥有多个从服务器
如果两个情况都满足,Redis集群就会开始执行副本漂移,按照名字进行排序后,选择最小的服务器进行下述操作:
- 将被挑选的从服务器节点C1从其主服务器C的列表中删除
- 将C1主节点目标改为单点服务器A1
- A1列表中添加C1
- 设置C1同步源为A1
- 至此副本漂移结束
集群中其他节点会在漂移结束后通过集群的心跳消息来感知到节点拓扑的变化
切片漂移
当在Redis集群中新添加了一个主节点后,就需要重新对slot进行分片;或者在Redis集群中删除了某一个主节点后,也需要对被删除的slot进行迁移;甚至对于负载不均匀的时候,也要通过一定方式对slot重新进行分布。
Redis提供了一些命令可以操作slot的迁移:
具体命令功能可以通过官网进行查看
http://www.redis.cn/commands.html
当在B节点执行 CLUSTER SET SLOT 10000 import A 时, 代表B节点接管了A节点中的slot 10000 中的所有内容,这个时候,客户端的命令会依旧发送给A(因为分片漂移还未全部完成)。
如果A收到命令后就,发现命令中的key还在A,则会执行命令,并正常回复;
如果key这个时候不再A中了,怎么会返回 ASK 10000 B 这条消息给客户端,代表客户端需要向B发起请求,客户端这时候需要先发送 asking命令给B,然后才能将要执行的命令发送给B;
当所有属于slot 10000 的key 都被迁移到B之后,集群才会收到slot迁移完毕的消息,此时A节点如果收到属于slot 10000 的key命令时,会直接返回MOVED消息,将命令重定向到B。