参考转载于:https://www.cnblogs.com/williamjie/p/11132211.html
点个赞 很全面
虚拟槽分区
虚拟槽分区是Redis Cluster采用的分区方式
预设虚拟槽,每个槽就相当于一个数字,有一定范围。每个槽映射一个数据子集,一般比节点数大
步骤:
1.把16384槽按照节点数量进行平均分配,由节点进行管理
2.对每个key按照CRC16规则进行hash运算 3.把hash结果对16383进行取余 4.把余数发送给Redis节点 5.节点接收到数据,验证是否在自己管理的槽编号的范围 如果在自己管理的槽编号范围内,则把数据保存到数据槽中,然后返回执行结果 如果在自己管理的槽编号范围外,则会把数据发送给正确的节点,由正确的节点来把数据保存在对应的槽中
需要注意的是:Redis Cluster的节点之间会共享消息,每个节点都会知道是哪个节点负责哪个范围内的数据槽
虚拟槽分布方式中,由于每个节点管理一部分数据槽,数据保存到数据槽中。当节点扩容或者缩容时,对数据槽进行重新分配迁移即可,数据不会丢失。
虚拟槽分区特点:
使用服务端管理节点,槽,数据:例如Redis Cluster
可以对数据打散,又可以保证数据分布均匀
客户端路由
moved重定向
1.每个节点通过通信都会共享Redis Cluster中槽和集群中对应节点的关系
2.客户端向Redis Cluster的任意节点发送命令,接收命令的节点会根据CRC16规则进行hash运算与16383取余,计算自己的槽和对应节点 3.如果保存数据的槽被分配给当前节点,则去槽中执行命令,并把命令执行结果返回给客户端 4.如果保存数据的槽不在当前节点的管理范围内,则向客户端返回moved重定向异常 5.客户端接收到节点返回的结果,如果是moved异常,则从moved异常中获取目标节点的信息 6.客户端向目标节点发送命令,获取命令执行结果
ask重定向
在对集群进行扩容和缩容时,需要对槽及槽中数据进行迁移
当客户端向某个节点发送命令,节点向客户端返回moved异常,告诉客户端数据对应的槽的节点信息
如果此时正在进行集群扩展或者缩空操作,当客户端向正确的节点发送命令时,槽及槽中数据已经被迁移到别的节点了,就会返回ask,这就是ask重定向机制
1.客户端向目标节点发送命令,目标节点中的槽已经迁移支别的节点上了,此时目标节点会返回ask转向给客户端 2.客户端向新的节点发送Asking命令给新的节点,然后再次向新节点发送命令 3.新节点执行命令,把命令执行结果返回给客户端
moved异常与ask异常的相同点和不同点:
两者都是客户端重定向
moved异常:槽已经确定迁移,即槽已经不在当前节点 ask异常:槽还在迁移中
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!"
多节点命令实现
1.串行mget
定义for循环,遍历所有的key,分别去所有的Redis节点中获取值并进行汇总,简单,但是效率不高,需要n次网络时间
2.串行IO
对串行mget进行优化,在客户端本地做内聚,对每个key进行CRC16hash,然后与16383取余,就可以知道哪个key对应的是哪个槽
本地已经缓存了槽与节点的对应关系,然后对key按节点进行分组,成立子集,然后使用pipeline把命令发送到对应的node,需要nodes次网络时间,大大减少了网络时间开销
3.并行IO
并行IO是对串行IO的一个优化,把key分组之后,根据节点数量启动对应的线程数,根据多线程模式并行向node节点请求数据,只需要1次网络时间
4.hash_tag
将key进行hash_tag的包装,然后把tag用大括号括起来,保证所有的key只向一个node请求数据,这样执行类似mget命令只需要去一个节点获取数据即可,效率更高
故障发现
Redis Cluster通过ping/pong消息实现故障发现:不需要sentinel
ping/pong不仅能传递节点与槽的对应消息,也能传递其他状态,比如:节点主从状态,节点故障等
故障发现就是通过这种模式来实现,分为主观下线和客观下线
主观下线
某个节点认为另一个节点不可用,'偏见',只代表一个节点对另一个节点的判断,不代表所有节点的认知
主观下线流程:
1.节点1定期发送ping消息给节点2 2.如果发送成功,代表节点2正常运行,节点2会响应PONG消息给节点1,节点1更新与节点2的最后通信时间 3.如果发送失败,则节点1与节点2之间的通信异常判断连接,在下一个定时任务周期时,仍然会与节点2发送ping消息 4.如果节点1发现与节点2最后通信时间超过node-timeout,则把节点2标识为pfail状态
客观下线
当半数以上持有槽的主节点都标记某节点主观下线时,可以保证判断的公平性
集群模式下,只有主节点(master)才有读写权限和集群槽的维护权限,从节点(slave)只有复制的权限
客观下线流程:
1.某个节点接收到其他节点发送的ping消息,如果接收到的ping消息中包含了其他pfail节点,这个节点会将主观下线的消息内容添加到自身的故障列表中,故障列表中包含了当前节点接收到的每一个节点对其他节点的状态信息 2.当前节点把主观下线的消息内容添加到自身的故障列表之后,会尝试对故障节点进行客观下线操作
故障列表的周期为:集群的node-timeout * 2,保证以前的故障消息不会对周期内的故障消息造成影响,保证客观下线的公平性和有效性
故障恢复
资格检查
对从节点的资格进行检查,只有难过检查的从节点才可以开始进行故障恢复
每个从节点检查与故障主节点的断线时间
超过cluster-node-timeout * cluster-slave-validity-factor数字,则取消资格 cluster-node-timeout默认为15秒,cluster-slave-validity-factor默认值为10 如果这两个参数都使用默认值,则每个节点都检查与故障主节点的断线时间,如果超过150秒,则这个节点就没有成为替换主节点的可能性
准备选举时间
使偏移量最大的从节点具备优先级成为主节点的条件
选举投票
对选举出来的多个从节点进行投票,选出新的主节点
替换主节点
当前从节点取消复制变成离节点(slaveof no one)
执行cluster del slot撤销故障主节点负责的槽,并执行cluster add slot把这些槽分配给自己 向集群广播自己的pong消息,表明已经替换了故障从节点
Redis Cluster的缺点
当节点数量很多时,性能不会很高
解决方式:使用智能客户端。智能客户端知道由哪个节点负责管理哪个槽,而且当节点与槽的映射关系发生改变时,客户端也会知道这个改变,这是一种非常高效的方式
key批量操作支持有限:例如mget,mset必须在一个slot key事务和Lua支持有限:操作的key必须在一个节点 key是数据分区的最小粒度:不支持bigkey分区 不支持多个数据库:集群模式下只有一个db0 复制只支持一层:不支持树形复制结构 Redis Cluster满足容量和性能的扩展性,很多业务'不需要' 大多数时客户端性能会'降低' 命令无法跨节点使用:mget,keys,scan,flush,sinter等 Lua和事务无法跨节点使用 客户端维护更复杂:SDK和应用本身消耗(例如更多的连接池)
很多场景Redis Sentinel已经够用了