一、前言

    redis最开始使用主从模式做集群,若master宕机需要手动配置slave转为master;后来为了高可用提出来哨兵模式,该模式下有一个哨兵监视master和slave,若master宕机可自动将slave转为master,但它也有一个问题,就是不能动态扩充;所以在3.x提出cluster集群模式。

二、redis-cluster设计

    Redis-Cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接。每个节点都负责进行数据读写操作,每个节点之间会进行通信。meet操作是节点之间完成相互通信的基础,meet操作有一定的频率和规则。

 1、客户端路由

1.1 moved重定向

1.每个节点通过通信都会共享Redis Cluster中槽和集群中对应节点的关系
2.客户端向Redis Cluster的任意节点发送命令,接收命令的节点会根据CRC16规则进行hash运算与16383取余,计算自己的槽和对应节点
3.如果保存数据的槽被分配给当前节点,则去槽中执行命令,并把命令执行结果返回给客户端
4.如果保存数据的槽不在当前节点的管理范围内,则向客户端返回moved重定向异常
5.客户端接收到节点返回的结果,如果是moved异常,则从moved异常中获取目标节点的信息
6.客户端向目标节点发送命令,获取命令执行结果

需要注意的是:客户端不会自动找到目标节点执行命令,槽命中:直接返回

 1.2 ask重定向

 

在对集群进行扩容和缩容时,需要对槽及槽中数据进行迁移,当客户端向某个节点发送命令,节点向客户端返回moved异常,告诉客户端数据对应的槽的节点信息。如果此时正在进行集群扩展或者缩空操作,当客户端向正确的节点发送命令时,槽及槽中数据已经被迁移到别的节点了,就会返回ask,这就是ask重定向机制。

步骤:

1.客户端向目标节点发送命令,目标节点中的槽已经迁移支别的节点上了,此时目标节点会返回ask转向给客户端
2.客户端向新的节点发送Asking命令给新的节点,然后再次向新节点发送命令
3.新节点执行命令,把命令执行结果返回给客户端

moved异常与ask异常的相同点和不同点

两者都是客户端重定向
moved异常:槽已经确定迁移,即槽已经不在当前节点
ask异常:槽还在迁移中

 1.3 smart智能客户端

使用智能客户端的首要目标:追求性能。从集群中选一个可运行节点,使用Cluster slots初始化槽和节点映射。将Cluster slots的结果映射在本地,为每个节点创建JedisPool,相当于为每个redis节点都设置一个JedisPool,然后就可以进行数据读写操作。读写数据时的注意事项:

每个JedisPool中缓存了slot和节点node的关系
key和slot的关系:对key进行CRC16规则进行hash后与16383取余得到的结果就是槽
JedisCluster启动时,已经知道key,slot和node之间的关系,可以找到目标节点
JedisCluster对目标节点发送命令,目标节点直接响应给JedisCluster
如果JedisCluster与目标节点连接出错,则JedisCluster会知道连接的节点是一个错误的节点
此时JedisCluster会随机节点发送命令,随机节点返回moved异常给JedisCluster
JedisCluster会重新初始化slot与node节点的缓存关系,然后向新的目标节点发送命令,目标命令执行命令并向JedisCluster响应
如果命令发送次数超过5次,则抛出异常"Too many cluster redirection!"
        

 2、故障发现

Redis Cluster通过ping/pong消息实现故障发现:不需要sentinel。ping/pong不仅能传递节点与槽的对应消息,也能传递其他状态,比如:节点主从状态,节点故障等。故障发现就是通过这种模式来实现,分为主观下线和客观下线

2.1 主观下线

某个节点认为另一个节点不可用,'偏见',只代表一个节点对另一个节点的判断,不代表所有节点的认知

主观下线流程:

1.节点1定期发送ping消息给节点2
2.如果发送成功,代表节点2正常运行,节点2会响应PONG消息给节点1,节点1更新与节点2的最后通信时间
3.如果发送失败,则节点1与节点2之间的通信异常判断连接,在下一个定时任务周期时,仍然会与节点2发送ping消息
4.如果节点1发现与节点2最后通信时间超过node-timeout,则把节点2标识为pfail状态

2.2 客观下线

当半数以上持有槽的主节点都标记某节点主观下线时,可以保证判断的公平性.集群模式下,只有主节点(master)才有读写权限和集群槽的维护权限,从节点(slave)只有复制的权限

客观下线流程:

1.某个节点接收到其他节点发送的ping消息,如果接收到的ping消息中包含了其他pfail节点,这个节点会将主观下线的消息内容添加到自身的故障列表中,故障列表中包含了当前节点接收到的每一个节点对其他节点的状态信息
2.当前节点把主观下线的消息内容添加到自身的故障列表之后,会尝试对故障节点进行客观下线操作

故障列表的周期为:集群的node-timeout * 2,保证以前的故障消息不会对周期内的故障消息造成影响,保证客观下线的公平性和有效性

 

3.  故障恢复

3.1 资格检查

对从节点的资格进行检查,只有难过检查的从节点才可以开始进行故障恢复
每个从节点检查与故障主节点的断线时间
超过cluster-node-timeout * cluster-slave-validity-factor数字,则取消资格
cluster-node-timeout默认为15秒,cluster-slave-validity-factor默认值为10
如果这两个参数都使用默认值,则每个节点都检查与故障主节点的断线时间,如果超过150秒,则这个节点就没有成为替换主节点的可能性

3.2 准备选举时间

使偏移量最大的从节点具备优先级成为主节点的条件

 

3.3 选举投票

对选举出来的多个从节点进行投票,选出新的主节点

 

3.4 替换主节点

当前从节点取消复制变成离节点(slaveof no one)
执行cluster del slot撤销故障主节点负责的槽,并执行cluster add slot把这些槽分配给自己
向集群广播自己的pong消息,表明已经替换了故障从节点

 三、常见问题

3.1 集群完整性

cluster-require-full-coverage默认为yes,即是否集群中的所有节点都是在线状态且16384个槽都处于服务状态时,集群才会提供服务

集群中16384个槽全部处于服务状态,保证集群完整性

当某个节点故障或者正在故障转移时获取数据会提示:(error)CLUSTERDOWN The cluster is down

建议把cluster-require-full-coverage设置为no

3.2 带宽消耗

Redis Cluster节点之间会定期交换Gossip消息,以及做一些心跳检测,

官方建议Redis Cluster节点数量不要超过1000个,当集群中节点数量过多时,会产生不容忽视的带宽消耗

消息发送频率:节点发现与其他节点最后通信时间超过cluster-node-timeout /2时,会直接发送PING消息

消息数据量:slots槽数组(2kb空间)和整个集群1/10的状态数据(10个节点状态数据约为1kb)

节点部署的机器规模:集群分布的机器越多且每台机器划分的节点数越均匀,则集群内整体的可用带宽越高

带宽优化:

避免使用'大'集群:避免多业务使用一个集群,大业务可以多集群
cluster-node-timeout:带宽和故障转移速度的均衡
尽量均匀分配到多机器上:保证高可用和带宽

3.3 Pub/Sub广播

在任意一个cluster节点执行publish,则发布的消息会在集群中传播,集群中的其他节点都会订阅到消息,这样节点的带宽的开销会很大

publish在集群每个节点广播,加重带宽

解决办法:需要使用Pub/Sub时,为了保证高可用,可以单独开启一套Redis Sentinel

3.4 集群倾斜

对于分布式数据库来说,存在倾斜问题是比较常见的

集群倾斜也就是各个节点使用的内存不一致

3.4.1 数据倾斜原因

1.节点和槽分配不均,如果使用redis-trib.rb工具构建集群,则出现这种情况的机会不多

redis-trib.rb info ip:port查看节点,槽,键值分布
redis-trib.rb rebalance ip:port进行均衡(谨慎使用)

2.不同槽对应键值数量差异比较大

CRC16算法正常情况下比较均匀
可能存在hash_tag
cluster countkeysinslot {slot}获取槽对应键值个数

3.包含bigkey:例如大字符串,几百万的元素的hash,set等

在从节点:redis-cli --bigkeys
优化:优化数据结构

4.内存相关配置不一致

hash-max-ziplist-value:满足一定条件情况下,hash可以使用ziplist
set-max-intset-entries:满足一定条件情况下,set可以使用intset
在一个集群内有若干个节点,当其中一些节点配置上面两项优化,另外一部分节点没有配置上面两项优化
当集群中保存hash或者set时,就会造成节点数据不均匀
优化:定期检查配置一致性

5.请求倾斜:热点key

重要的key或者bigkey
Redis Cluster某个节点有一个非常重要的key,就会存在热点问题

3.4.2 集群倾斜优化:

避免bigkey
热键不要用hash_tag
当一致性不高时,可以用本地缓存+ MQ(消息队列)

3.5 集群读写分离

只读连接:集群模式下,从节点不接受任何读写请求

当向从节点执行读请求时,重定向到负责槽的主节点

readonly命令可以读:连接级别命令,当连接断开之后,需要再次执行readonly命令

读写分离:

同样的问题:复制延迟,读取过期数据,从节点故障
修改客户端:cluster slaves {nodeId}

3.6 数据迁移

官方迁移工具:redis-trib.rb和import

只能从单机迁移到集群

不支持在线迁移:source需要停写

不支持断点续传

单线程迁移:影响深度

在线迁移:

唯品会:redis-migrate-tool
豌豆荚:redis-port

3.7 集群VS单机

集群的限制:

key批量操作支持有限:例如mget,mset必须在一个slot
key事务和Lua支持有限:操作的key必须在一个节点
key是数据分区的最小粒度:不支持bigkey分区
不支持多个数据库:集群模式下只有一个db0
复制只支持一层:不支持树形复制结构
Redis Cluster满足容量和性能的扩展性,很多业务'不需要'
大多数时客户端性能会'降低'
命令无法跨节点使用:mget,keys,scan,flush,sinter等
Lua和事务无法跨节点使用
客户端维护更复杂:SDK和应用本身消耗(例如更多的连接池)

四、文章出处

https://www.cnblogs.com/williamjie/p/11132211.html 

更多推荐