本节引言什么是Redis集群
Redis集群是一个提供在多个Redis节点之间共享数据的程序集。
它并不像Redis主从复制模式那样只提供一个master节点提供写服务,而是会提供多个master节点提供写服务,每个master节点中存储的数据都不一样,这些数据通过数据分片的方式被自动分割到不同的master节点上。
为了保证集群的高可用,每个master节点下面还需要添加至少1个slave节点,这样当某个master节点发生故障后,可以从它的slave节点中选举一个作为新的master节点继续提供服务。不过当某个master节点和它下面所有的slave节点都发生故障时,整个集群就不可用了。这样就组成了下图中的结构模式:
![1690102892252950.jpg 1.jpg](http://www.res.gamecolg.com/attachs/202307/ueditor/catchimage/0723/1690104308309529.jpg)
Redis集群中的哈希槽
Redis集群中引入了哈希槽的概念,Redis集群有16384个哈希槽,进行set操作时,每个key会通过CRC16校验后再对16384取模来决定放置在哪个槽,搭建Redis集群时会先给集群中每个master节点分配一部分哈希槽。比如当前集群有3个master节点,master1节点包含0~5500号哈希槽,master2节点包含5501~11000号哈希槽,master3节点包含11001~16384号哈希槽,当我们执行“set key value”时,假如 CRC16(key) % 16384 = 777,那么这个key就会被分配到master1节点上,如下图:
![1690102899862486.jpg 2.jpg](http://www.res.gamecolg.com/attachs/202307/ueditor/catchimage/0723/1690104308620944.jpg)
Redis集群中节点的通信
既然Redis集群中的数据是通过哈希槽的方式分开存储的,那么集群中每个节点都需要知道其他所有节点的状态信息,包括当前集群状态、集群中各节点负责的哈希槽、集群中各节点的master-slave状态、集群中各节点的存活状态等。Redis集群中,节点之间通过建立TCP连接,使用gossip协议来传播集群的信息。如下图:
![1690102927377899.jpg 3.jpg](http://www.res.gamecolg.com/attachs/202307/ueditor/catchimage/0723/1690104309212198.jpg)
所谓gossip协议,指的是一种消息传播的机制,类似人们传递八卦消息似的,一传十,十传百,直至所有人都知道这条八卦内容。Redis集群中各节点之间传递消息就是基于gossip协议,最终达到所有节点都会知道整个集群完整的信息。gossip协议有4种常用的消息类型:PING、PONG、MEET、FAIL。
MEET:当需要向集群中加入新节点时,需要集群中的某个节点发送MEET消息到新节点,通知新节点加入集群。新节点收到MEET消息后,会回复PONG命令给发送者。
PING:集群内每个节点每秒会向其他节点发送PING消息,用来检测其他节点是否正常工作,并交换彼此的状态信息。PING消息中会包括自身节点的状态数据和其他部分节点的状态数据。
PONG:当接收到PING消息和MEET消息时,会向发送发回复PONG消息,PONG消息中包括自身节点的状态数据。节点也可以通过广播的方式发送自身的PONG消息来通知整个集群对自身状态的更新。
FAIL:当一个节点判定另一个节点下线时,会向集群内广播一个FAIL消息,其他节点接收到FAIL消息之后,把对应节点更新为下线状态。
Redis集群的MOVED重定向
Redis客户端可以向Redis集群中的任意master节点发送操作指令,可以向所有节点(包括slave节点)发送查询指令。当Redis节点接收到相关指令时,会先计算key落在哪个哈希槽上(对key进行CRC16校验后再对16384取模),如果key计算出的哈希槽恰好在自己节点上,那么就直接处理指令并返回结果,如果key计算出的哈希槽不在自己节点上,那么当前节点就会查看它内部维护的哈希槽与节点ID之间的映射关系,然后给客户端返回一个MOVED错误:MOVED [哈希槽] [节点IP:端口]。
这个错误包含操作的key所属的哈希槽和能处理这个请求的Redis节点的IP和端口号,例如“MOVED 3999 127.0.0.1:6379”,客户端需要根据这个信息重新发送查询指令到给定的IP和端口的Redis节点。
![1690103004347009.jpg 4.jpg](http://www.res.gamecolg.com/attachs/202307/ueditor/catchimage/0723/1690104309634357.jpg)
集群解决的问题
服务器的容量不足或者进行并发写操作的用户过多等情况下可以使用多台Redis集群的方式缓解压力。
注:(高并发的写操作,如果是一主多从模式主服务器承受的压力会很大,因此引入集群)
代理主机和无中心化集群
以电商项目的用户、订单、商品三个模块来演示代理主机和无中心化集群,如下图:
用户信息、订单信息、商品信息分别使用三台Redis服务器存储, 这样一来,相应的操作就会去请求相应的Redis服务器。
![1690103141980175.jpg 1.jpg](http://www.res.gamecolg.com/attachs/202307/ueditor/catchimage/0723/1690104309774416.jpg)
问题:客户端通过何种方式知道需要去请求哪个服务?
①代理主机方式通过加一层代理服务器解决此问题(不推荐使用)
![1690103216400269.jpg 2.jpg](http://www.res.gamecolg.com/attachs/202307/ueditor/catchimage/0723/1690104310327432.jpg)
访问的时候请求代理服务器,由代理服务器进行分发。
局部可以使用主从模式,如果其中某一台Redis服务器挂掉,从机上位变为主服务器继续提供服务。
可以看到,以代理主机方式按照目前的需求搭建集群,至少需要8台服务器。搭建和后期维护都很不方便,所以不推荐使用此方式创建集群,于是引入了无中心化方式的集群!!!
②无中心化搭建Redis集群方式解决此问题(推荐)
任何一台Redis服务器都可以作为集群的入口。比如要访问订单数据,通过用户服务器进入集群,用户服务器会在集群内部把请求进行转移,直到找到订单服务器。
![1690103320571419.jpg 3.jpg](http://www.res.gamecolg.com/attachs/202307/ueditor/catchimage/0723/1690104310839380.jpg)
无中心化Redis集群搭建方式最少需要6台服务器即可。和代理主机方式相比优势明显。
Redis集群的特点
Redis 集群实现了对Redis的水平扩容,即启动N个redis节点,将整个数据库分布存储在这N个节点中,每个节点存储总数据的1/N。
Redis 集群通过分区来提供一定程度的可用性: 即使集群中有一部分节点失效或者无法进行通讯, 集群也可以继续处理命令请求。
Redis集群环境搭建
根据上面介绍,我们了解到无中心化集群搭建方式至少需要6台Redis服务器。接下来演示无中心化Redis集群环境搭建:
接下来演示在一台linux主服务器上模拟六台Redis服务器,演示无中心化Redis集群环境的搭建:
①在Linux服务器中新建一个myredis文件夹
如下图:
![1690103421463999.jpg 5.jpg](http://www.res.gamecolg.com/attachs/202307/ueditor/catchimage/0723/1690104310820194.jpg)
②创建6个redis实例,端口号为:6379、6380、6381、6389、6390、6391
(如果使用云服务器记得设置这几个端口的安全组)
实现:
6379为主机,6389为从机
6380为主机,6390为从机
6381为主机,6391为从机
vim redis6379.conf 创建配置文件并写入如下内容:
![1690103467626740.jpg 6.jpg](http://www.res.gamecolg.com/attachs/202307/ueditor/catchimage/0723/1690104311642580.jpg)
复制出其余5个配置文件
![1690103503581502.jpg 7.jpg](http://www.res.gamecolg.com/attachs/202307/ueditor/catchimage/0723/1690104311756132.jpg)
依次通过字符串替换修改5个文件内容(下图是修改redis6381.conf的过程,修改其余配置文件操作类似)
![1690103576647678.jpg 8.jpg](http://www.res.gamecolg.com/attachs/202307/ueditor/catchimage/0723/1690104311673578.jpg)
③启动6个Redis服务
格式:redis-server redis6379.conf(启动redis6379服务)
![1690103629212953.jpg 9.jpg](http://www.res.gamecolg.com/attachs/202307/ueditor/catchimage/0723/1690104311617682.jpg)
通过ll命令查看,确保如下的节点文件生成成功
![1690103651457611.jpg 10.jpg](http://www.res.gamecolg.com/attachs/202307/ueditor/catchimage/0723/1690104311260492.jpg)
④一个集群至少要有三个主节点,先组合创建只有三个主节点的集群(6379、6380、6381)
组合之前,务必请确保所有redis实例启动后,nodes-xxxx.conf文件都生成正常。
Redis版本低的话,需要额外装上rubby环境。新版的redis自带了rubby环境,。我使用的是新版本redis7
redis安装目录下的src目录中可以看到,,关于rubby的文件
![1690103710949515.jpg 1.jpg](http://www.res.gamecolg.com/attachs/202307/ueditor/catchimage/0723/1690104312192817.jpg)
使用命令进行合并创建集群
redis-cli --cluster create --cluster-replicas 0 -a 你的redis密码
47.116.4.200:6379 47.116.4.200:6380 47.116.4.200:6381
其中参数:
-replicas 0 表示创建集群的方式,以0个从机的方式创建集群(此处即为创建仅有三个主机的集群)。
-a参数填写redis服务器的密码(没有设置密码可以不写-a)
最后面跟的是服务器的ip和端口号。
⑤启动redis服务后,需要连接Redis。
集群之前的连接redis方式为:redis-cli -p 端口号
集群连接redis方式为:redis-cli -c -p 端口号(使用-c参数表示 采用集群策略连接。因为是无中心化,所以连接任何一个节点(如6379、6380、6381)都可以)
![1690103829556639.jpg 2.jpg](http://www.res.gamecolg.com/attachs/202307/ueditor/catchimage/0723/1690104312666400.jpg)
⑥通过 cluster nodes 命令查看集群信息
![1690103848177854.jpg 3.jpg](http://www.res.gamecolg.com/attachs/202307/ueditor/catchimage/0723/1690104312237494.jpg)
⑦增加三个从机到集群中去,并设置相应的主机
redis-cli --cluster add-node 47.116.4.200:6389 47.116.4.200:6379 --cluster-slave --cluster-master-id 主机的id (-a Redis密码):增加从节点6389到集群中去,并将其设置为6379主机的从机。
此处的47.116.4.200:6389为目标从机及其端口号
此处的47.116.4.200:6379为目标主机及其端口号
slave 表示此操作要添加从节点
cluster-master-id 主机的id: 要添加到哪一个主节点,集群中主机的id可以通过cluster nodes查看
-a :如果设置了redis密码,需要-a参数并填入密码
![1690103893857095.jpg 4.jpg](http://www.res.gamecolg.com/attachs/202307/ueditor/catchimage/0723/1690104312961812.jpg)
以同样的方式,把6390添加为6380的从机;把6391添加为6381的从机。
添加完毕之后通过redis-cli -c -p 6379连接一台集群中的redis服务器。并通过cluster nodes查看节点信息。如下图:
![1690103916867670.jpg 6.jpg](http://www.res.gamecolg.com/attachs/202307/ueditor/catchimage/0723/1690104312609669.jpg)
此时可以看到集群中节点已经分为三组:
6379为主机,6389为从机
6380为主机,6390为从机
6381为主机,6391为从机
slots(插槽)
1、一个 Redis 集群包含 16384 个插槽(hash slot), 数据库中的每个键都属于这 16384 个插槽的其中一个。
2、插槽用来把值平均分配到不同主机中去,达到分担压力的效果。(比如set k1 v1操作的时候,会计算k1所在插槽值,根据各节点管辖的插槽范围,放入相应的节点中去)
3、集群使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽。
4、集群中的每个节点负责处理一部分插槽。 如下图:
![1690103971502029.jpg 1.jpg](http://www.res.gamecolg.com/attachs/202307/ueditor/catchimage/0723/1690104312576912.jpg)
上图中的当前集群有三个主节点, 其中:
节点 6379 负责处理 0 号至 5460 号插槽。
节点 6380 负责处理 5461 号至 10922 号插槽。
节点 6381 负责处理 10923 号至 16383 号插槽。
在集群中录入值
①录入单个值
![1690104025463407.jpg 2.jpg](http://www.res.gamecolg.com/attachs/202307/ueditor/catchimage/0723/1690104312582908.jpg)
6381管理的插槽范围是10923 号至 16383 号,12706在其范围内。
一开始,请求进入6379,执行set k1 值操作时,计算k1的插槽值,发现k1插槽值不在6379服务器管理范围内,于是会转移请求找到6381服务器并执行set操作。
②尝试录入多个值
![1690104089685319.jpg 1.jpg](http://www.res.gamecolg.com/attachs/202307/ueditor/catchimage/0723/1690104312762069.jpg)
无法计算多个key的插槽值。集群中mset不能执行成功。
如果非要在集群中录入多个值,可以使用添加分组的方法
key值后面添加{ 组名 },就可以根据组名计算插槽,而不用再根据key值计算。此方法插入即可成功。如下图:
![1690104094474322.jpg 2.jpg](http://www.res.gamecolg.com/attachs/202307/ueditor/catchimage/0723/1690104312578275.jpg)
查询集群中的值
①cluster keyslot key:计算key值的插槽数
![1690104153426058.jpg 1.jpg](http://www.res.gamecolg.com/attachs/202307/ueditor/catchimage/0723/1690104312750940.jpg)
②cluster countkeysinslot 插槽值:计算插槽中有几个key(键)
![1690104158477486.jpg 2.jpg](http://www.res.gamecolg.com/attachs/202307/ueditor/catchimage/0723/1690104313195944.jpg)
注意: 虽然当前12706插槽中有一个k1,但12706在 6381节点的管辖的插槽值范围(10923~16383) 内。此命令只能计算当前6380节点管辖的插槽值范围(5461~10922) 内的数据。上图中的12706超出范围,因此返回0。
③cluster getkeysinslot 插槽数 个数:返回count个插槽中的key
![1690104175619268.jpg 3.jpg](http://www.res.gamecolg.com/attachs/202307/ueditor/catchimage/0723/1690104313867590.jpg)
上图表明,此时的4576插槽中只有一个k3。
故障恢复
此时我们搭建的集群中为三组“一主一从”,如果集群中某台主机挂掉,其从机立马上位接替主机工作,继续提供服务。此即为故障恢复。 如下图:
![1690104266921661.jpg 1.jpg](http://www.res.gamecolg.com/attachs/202307/ueditor/catchimage/0723/1690104313178429.jpg)
此时思考:如果此时再次重启6379服务器,6379的角色是什么?
答案是重启6379服务器,6379角色变为6389的从机,上位主机后的6389服务器依然是主机。如下图:
![1690104276275436.jpg 2.jpg](http://www.res.gamecolg.com/attachs/202307/ueditor/catchimage/0723/1690104313244938.jpg)
再次思考:如果集群中某台主机挂掉之后,此主机的所有从机也都挂掉了(如下图),整个集还能正常提供服务吗?
![1690104289506624.jpg 3.jpg](http://www.res.gamecolg.com/attachs/202307/ueditor/catchimage/0723/1690104313496331.jpg)
答案为不一定。主要要看配置文件。
某一段插槽的主从都挂掉时,集群能否提供服务主要看配置信息(cluster-require-full-coverage ):
1、cluster-require-full-coverage 为yes,那么 某一段插槽的主从都挂掉时,整个集群都挂掉
2、cluster-require-full-coverage 为no ,那么 某一段插槽的主从都挂掉时,只是该段插槽数据不能使用,也无法存储;其他段插槽依旧正常提供服务。
Redis集群的优缺点
优点:多台服务器集群可以分摊并发操作压力、实现扩容。
缺点:不支持多键操作(如mset命令会操作失败)、不支持多键的Redis事务、不支持lua脚本.