Redis的集群

本文详细解释了广义和狭义的集群概念,重点介绍了Redis的哈希求余、一致性哈希和CRC16分片算法。讨论了数据分片的必要性、扩容过程中的挑战和解决方案,以及故障转移和节点添加的步骤。
摘要由CSDN通过智能技术生成

集群:

广义的集群:只要你有多个机器,构成了分布式系统,都可以称为一个"集群"(前面主从结构,哨兵模式,也可以称为"广义的集群")

狭义的集群:Redis提供的集群,这个集群模式下,主要是要解决,存储空间不足的问题(拓展存储空间)

注:哨兵模式提高了系统的可用性,哨兵模式中,本质上还是Redis主从节点存储数据,其中要求一个主节点/从节点,就得存储整个数据的"全集"

此处的关键问题就是引入多台机器,每台机器上存储一部分数据:

  设有1TB数据需要存储

  拿两台机器来存储,每台机器上只需要存512G即可;拿三台机器来存,每台机器上只需要300多G即可;拿四台机器来存,每台机器上只需要256GB...随着机器个数的增加,每台机器的数据量就减少了

把数据分成多份的方法(三种主流的分片方法):

一.哈希求余:借鉴了哈希表的基本思想,借助hash函数,把一个key映射到整数,再针对数组的长度求余,就可以得到一个数组下标

比如:有三个分片,编号0,1,2

此时就可以针对要插入数据的key(Redis都是键值对结构的数据)计算hash值(比如,使用MD5)再把这个hash值余上分片个数,就得到一个下标,此时就可以把这个数据放到该下标对应分片中了

hash(key%N=>0)此时这个key就要存储在0号分片中(后续查询key的时候,也是用同样的算法,key是一样的,hash函数是一样的,得到的分片就是一样的)

注:MD5本省是一个计算hash值的算法,针对一个字符串,里面的内容进行一系列的数学变换=>整数

变成一个十六进制的数

MD5是一个非常广泛使用的算法:

特点:

1.MD5计算结果是定长的(无论输入的原字符串多长,最终算出的结果就是固定长度)

2.MD5的结算结果是分散的[哈希函数]:两个原字符串,哪怕大部分相同.只有一个小地方不一样,算出来的MD5值也会影响很大

3.MD5计算结构是不可逆的[加密]:给你原字符串,可以很容易算出MD5 的值,给你MD5的值很难还原出原始的字符串(理论上是不可行的)

问题:一旦服务器集群需要扩容,就需要更高的成本了,分片主要目的就是为了提高存储能力,分片越多,能存的数据越多,成本也更高

一般都是先少搞几个分片(3个),但是随着业务逐渐增长,数据变多了,3个分片已经不足以保存了,就需要"扩容"

引入新的分片,N就能改变了,hash(key)%N=>0(当hash函数和key都不变的情况下,如果N改变了,整体的分片结果仍然会变)

如果发现某个数据,在扩容之后不应该待在当前的分片中了,就直接重新分配(搬运数据)

一共有20个数据,只有3个数据不需要搬运,搬运了17次

如果是20亿个数据,17亿数据就需要搬运,就是一个大活了

上述级别的扩容,开销级别极大的,往往不能直接在生产环境上操作,只能通过"替换"的方式实现扩容,依赖的机器更多了,成本更高,操作步骤非常复杂

二.一次性哈希算法:在hash求余这种昂操作中,当key属于哪个分片时,是交替的;在一致性哈希这样的设定下,把交替出现,改进成连续出现

扩容后:

只需要把0好分片上的这一段数据给搬运到3号分片上即可,2 3号分片都是不变的,这种搬运的成本,也是有的,比刚才哈希求余的方式低了不少(虽然搬运成本低了,但是这几个个分片上的数据量,就可能不均匀了(数据倾斜))

如果是一次扩容高多个分片,确实是一个好的思路,可以避免当前的数据倾斜,总的搬运数据量是比求余的少,但是还是需要更多的机器

三.哈希槽分区算法(Redis真正采用的分片算法(这种算法本质上就是把一致性哈希和哈希求余这两种方式结合一下))

hash_slot=crc16(key)%16384

hash_slot:哈希槽

crc16(key):也是一种计算hash的算法

16384=>16*1024=>2^14(16K)

会进一步把上述的这些哈希槽,分配到不同的分片上

比如:

假设当前有三个分⽚,⼀种可能的分配⽅式:

• 0号分⽚:[0,5461],共5462个槽位

• 1号分⽚:[5462,10923],共5462个槽位

• 2号分⽚:[10924,16383],共5460个槽位

虽然不是严格意义的"均分"差异非常小,此时这三个分片上的数据就是比较均匀的了

这里只是一种可能得分片方式,实际上分片时非常灵活的;每个分片持有的槽位号,可以是连续的,也可以是不连续的

此处,每个分片都会用"位图"这样的数据结构表示当前有多少槽位号(16384个比特位,用每一位0/1来区分自己这个分片当前是否持有该槽位号)

扩容后:

• 0号分⽚:[0,4095],共4096个槽位

• 1号分⽚:[5462,9557],共4096个槽位

• 2号分⽚:[10924,15019],共4096个槽位

• 3号分⽚:[4096,5461]+[9558,10923]+[15019,16383],共4096个槽位

上述过程中,只有被移动的槽位,对应的数据才需要搬运

针对某个分片,上面的槽位号,不一定非得是连续的区间

注:

1.Redis集群是最多有16384个分⽚吗?

 每个分片上就只有一个槽位,此时难以保证数据在各个分片上的均衡性(key是先映射到槽位,再映射到分片的;如果每个分片包含的槽位比较多,如果槽位个数想当,就可以认为是包含的key数量想当;如果每个分片包含的槽位非常少,槽位个数不一定能直观的反应到key的数目) 

实际上Redis的作者建议集群分片数不应该超过1000(如果真是1.6w个分片,整个数据服务器的集群规模就太可怕了,几万台主机构成的集群,整个集群的可用性是非常堪忧的)

2.为什么是16384个槽位?

心跳包中包含了该节点持有那些slots(需要表示出该节点有那些槽位)

2KB基本上够用了,同时占用的硬件资源(网络带宽有不是很大

搭建Redis集群:

前提:停掉之前启动的Redis容器

在Linux上,一.sh后缀结尾的文件,称为"shell脚本"(在使用Linux的时候,都是通过一些命令来进行操作的,使用命令操作,就非常合适把命令写到一个文件中,批量化执行;同时,还能加入条件,循环,函数等机制)

本次,需要创建11个Redis节点,这些Redis的配置文件内容,大同小异,此时,就可以使用脚本来批量生成(也可以不是用脚本,手动一个一个改)

port就是Redis节点自身绑定的端口(容器内的端口),不同的容器内部可以有相同的端口,后续进行端口映射,再把这些容器内的端口映射到容器外的不同端口即可

业务端口:用来进行业务数据通信的(响应Redis客户端的请求的)

管理端口:为了完成一些管理上的任务来进行通信的(如果某个分片中的Redis主节点挂了,就需要让从节点成为主节点,就需要通过刚才管理端口来完成对应的操作)

创建出Redis容器

把上述Redis节点构成集群:

redis-cli --cluster create 172.30.0.101:6379 172.30.0.102:6379 172.30.0.103:6379 172.30.0.104:6379 172.30.0.105:6379 172.30.0.106:6379 172.30.0.107:6379 172.30.0.108:6379 172.30.0.109:6379 --cluster-replicas 2

列出每个参与构建集群的IP和端口,端口都是写容器内部的端口号

cluster-replicas 2:描述集群的每个节点,应该是有两个从节点;这个配置设置了之后,Redis就知道了3个节点是一伙的(一个分片)一共是9个节点,一共是3个分片

注:Redis在构建集群的时候,谁是主节点,谁是从节点,谁和谁是分片,都是不固定的(本身从集群的角度看,提供的这些节点之间本身就应该是等价的)

步骤:

1)生成每个Redis节点的配置文件

2)使用docker创建出11个Redis节点,并且启动容器

3)使用docker-cli执行构建集群命令

注:集群之后不易定所有的命令就都能够使用,像是控制多个key

从101-109九个节点,现在是一个整体,使用客户端上连接任意一个节点,本质上都是等价的

用cluster nodes查看当前集群信息:

设置集群后,当前数据就分片了:(当前key这个key通过hash计算之后,通过slot 12539,属于103这个节点)

-c选项添加之后,Redis客户端就会根据当前key实际算出来的槽位号,自动找到匹配的分片主机,进一步就可以完成操作了

如果集群中,有节点挂了?

如果挂了的节点是从节点,没事;如果挂了的节点是主节点,此处集群做的工作,就和之前哨兵做的工作类似了,就会自动把该主节点旗下从节点,挑一个出来,提拔成主节点

没有故障的集群

停掉Redis1:docker stop redis1

停掉redis1后的集群:此时redis5就是主节点了

重启redis1节点:docker start  redis1

重启redis1节点之后:redis1节点变成从节点

此处就是故障转移

1.故障判定:识别出某个节点是否挂了(心跳包)

①. 节点A给节点B发送ping包,B就会给A返回⼀个pong包.ping和pong除了 message type 属性之外,其他部分都是⼀样的.这⾥包含了集群的配置信息(该节点的id,该节点从属于哪个分⽚, 是主节点还是从节点,从属于谁,持有哪些slots的位图...).

②. 每个节点,每秒钟,都会给⼀些随机的节点发起ping包,⽽不是全发⼀遍.这样设定是为了避免在节 点很多的时候,⼼跳包也⾮常多(⽐如有9个节点,如果全发,就是9*8有72组⼼跳了,⽽且这是按 照N^2这样的级别增⻓的).

③. 当节点A给节点B发起ping包,B不能如期回应的时候,此时A就会尝试重置和B的tcp连接,看能 否连接成功.如果仍然连接失败,A就会把B设为PFAIL状态(相当于主观下线).

④. A判定B为PFAIL之后,会通过redis内置的Gossip协议,和其他节点进⾏沟通,向其他节点确认B 的状态.(每个节点都会维护⼀个⾃⼰的"下线列表",由于视⻆不同,每个节点的下线列表也不⼀定相 同). ⑤. 此时A发现其他很多节点,也认为B为PFAIL,并且数⽬超过总集群个数的⼀半,那么A就会把B标 记成FAIL(相当于客观下线),并且把这个消息同步给其他节点(其他节点收到之后,也会把B标记成 FAIL).

2.故障迁移

①如果B是从节点,那么不需要进⾏故障迁移.

②如果B是主节点,那么就会由B的从节点(⽐如C和D)触发故障迁移了.

所谓故障迁移,就是指把从节点提拔成主节点,继续给整个redis集群提供⽀持.

具体流程如下:

①. 从节点判定⾃⼰是否具有参选资格.如果从节点和主节点已经太久没通信(此时认为从节点的数据和 主节点差异太⼤了),时间超过阈值,就失去竞选资格.

②. 具有资格的节点,⽐如C和D,就会先休眠⼀定时间.休眠时间=500ms基础时间+[0,500ms]随机 时间+排名*1000ms.offset的值越⼤,则排名越靠前(越⼩).

③. ⽐如C的休眠时间到了,C就会给其他所有集群中的节点,进⾏拉票操作.但是只有主节点才有投票 资格.

④. 主节点就会把⾃⼰的票投给C(每个主节点只有1票).当C收到的票数超过主节点数⽬的⼀半,C就 会晋升成主节点.(C⾃⼰负责执⾏slaveofnoone,并且让D执⾏slaveofC).

⑤. 同时,C还会把⾃⼰成为主节点的消息,同步给其他集群的节点.⼤家也都会更新⾃⼰保存的集群结构信息.

某个或者某些节点宕机,有的时候会引起整个集群都宕机(称为fail状态).

以下三种情况会出现集群宕机:

① 某个分⽚,所有的主节点和从节点都挂了.

② 某个分⽚,主节点挂了,但是没有从节点.

③超过半数的master节点都挂了. 

前两种情况:该分片就无法提供数据服务了

第三种情况:此时master挂了,但是后面还有slave做补充(如果一系列的master都挂了,此时说明集群遇到了非常严重的情况,此时就的赶紧停下来,检查检查是不是有什么问题)

集群扩容:

101-109 9个主机  构成了3主 6从结构的集群

扩容:以110为master,111为slave=>把数据分片从3->4

1.新的主节点110计入到集群中:redis-cli --cluster add-node 172.30.0.110:6379(新增节点是什么) 172.30.0.101:6379(集群上任意一个节点,表示要把新节点放到那个集群中)

2.重新分配slots(把之前的三组拎一些出来,分配给新的master):redis-cli --cluster reshard 172.30.0.101:6379

①4个分片一共是16384个,除以4得到的就是4096

②会询问那个节点来接收:

③输入从哪些节点来移动slots

1)all,表示从其他每个持有slots的master都拿过来点(输入all之后,会给出搬运计划(还没真正搬运),当输入yes之后才真正搬运,此时不仅是slots重新划分,也会把slots上对应的数据,也搬运到新的主机上)

2)手动指定,从某一个或者某几个节点来移动slots(以done为结尾)

扩容之后:

注:如果在搬运slots/key的过程中,此时客户端能否访问redis集群呢?

 搬运key,大部分key是不用搬运的,针对这些未搬运的key,此时可以正常访问的,针对这些正在搬运中的key,是有可能出现访问出错的情况(假设客户端访问key1,集群通过分片算法,得到key1是第一个分片数据,就会重定向到第一个分片的节点,就可能在重定向过去之后,key1就被搬走了,自然就无法访问了)

3.把从节点加到集群中

111添加进来,作为110的从节点

redis-cli --cluster add-node 172.30.0.111:6379 172.30.0.101:6379 --cluster-slave

添加之后:

  • 15
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值