redis集群
redis集群数据分布
哈希槽算法
- 什么是哈希槽算法?
Redis集群通过分布式存储的方式解决了单节点的海量数据存储的问题,对于分布式存储,需要考虑的重点就是如何将数据进行拆分到不同的Redis服务器上。
普通hash算法:将key使用hash算法计算之后,按照节点数量来取余,即hash(key)%N。优点就是比较简单,但是扩容或者摘除节点时需要重新根据映射关系计算,会导致数据重新迁移。
一致性hash算法:为每一个节点分配一个token,构成一个哈希环;查找时先根据key计算hash值,然后顺时针找到第一个大于等于该哈希值的token节点。优点是在加入和删除节点时只影响相邻的两个节点,缺点是加减节点会造成部分数据无法命中,所以一般用于缓存,而且用于节点量大的情况下,扩容一般增加一倍节点保障数据负载均衡。
- Redis集群采用的算法是哈希槽分区算法。
- Redis集群中有16384个哈希槽(槽的范围是 0 -16383,哈希槽)
- 对数据进行操作时,会使用CRC16算法对key进行计算并对16384取模,slot = CRC16(key)%16383
- 哈希槽的作用
- 解耦数据和节点之间的关系,简化了扩容和收缩难度
- 节点自身维护槽的映射关系
- 支持节点、槽、键之间的映射查询,用于数据路由,在线伸缩等场景
- 哈希槽的使用
数据key不是与节点绑定,而是与插槽绑定。redis会根据key的有效部分计算插槽值,分两种情况:
- key中包含"{}",且“{}”中至少包含1个字符,“{}”中的部分是有效部分
- key中不包含“{}”,整个key都是有效部分
redis集群通信机制
goosip协议
在Redis集群中,不同的节点之间采用gossip协议进行通信
gossip协议,是基于流行病传播方式的节点或者进程之间信息交换的协议。原理就是在不同的节点间不断地通信交换信息,一段时间后,所有的节点就都有了整个集群的完整信息,并且所有节点的状态都会达成一致。每个节点可能知道所有其他节点,也可能仅知道几个邻居节点,但只要这些节可以通过网络连通,最终他们的状态就会是一致的。Gossip协议最大的好处在于,即使集群节点的数量增加,每个节点的负载也不会增加很多,几乎是恒定的。
Redis集群中节点的通信过程如下:
- 集群中每个节点都会单独开一个TCP通道,用于节点间彼此通信。
- 每个节点在固定周期内通过待定的规则选择几个节点发送ping消息
- 接收到ping消息的节点用pong消息作为响应
使用gossip协议的优点在于将元数据的更新分散在不同的节点上面,降低了压力;但是缺点就是元数据的更新有延时,可能导致集群中的一些操作会有一些滞后。另外,由于 gossip 协议对服务器时间的要求较高,时间戳不准确会影响节点判断消息的有效性。而且节点数量增多后的网络开销也会对服务器产生压力,同时结点数太多,意味着达到最终一致性的时间也相对变长,因此官方推荐最大节点数为1000左右。
redis cluster架构下的每个redis都要开放两个端口号,比如一个是6379,另一个就是加1w的端口号16379。
6379端口号就是redis服务器入口。
16379端口号是用来进行节点间通信的,也就是 cluster bus 的东西,cluster bus 的通信,用来进行故障检测、配置更新、故障转移授权。cluster bus 用的是一种叫gossip 协议的二进制协议
redis扩容和收缩
由于每个节点中保存着槽数据,因此当缓存节点数出现变动时,这些槽数据会根据对应的虚拟槽算法被迁移到其他的缓存节点上。
扩容
- 添加一个节点到集群中,创建节点(略)
redis-cli --cluster add-node 127.0.0.1:6385 127.0.0.1:6379
- 查看集群状态
可以看到,当前8085这个redis节点已经被加入到集群中,但并没有对应的槽,这个时候是不能存储数据
- 查看这台新主机的keys
可以发现,我们已经同步了其他主机的key值
尝试set a 666
发现并不能set,是因为我们没有使用redis-cli -c 启动
使用之后,可以set值
4. 分配插槽
redis-cli --cluster reshard 127.0.0.1:6385
输入3000个
那个node来接收这些插槽??
找到对应的id
这里询问,你的插槽是从哪里移动过来的?
- all:代表全部,也就是三个节点各转移一部分
- 具体的id:目标节点的id
- done:没有了
分配成功
收缩
- 迁移槽。
- 忘记节点。通过命令 cluster forget {downNodeId} 通知其他的节点,应该是自动操作的
redis故障检测和故障恢复机制
集群故障检测
Redis集群的故障检测是基于gossip协议的,集群中的每个节点都会定期地向集群中的其他节点发送PING消息,以此交换各个节点状态信息,检测各个节点状态:在线状态、疑似下线状态PFAIL、已下线状态FAIL。
(1)主观下线(pfail):当节点A检测到与节点B的通讯时间超过了cluster-node-timeout 的时候,就会更新本地节点状态,把节点B更新为主观下线。
(2)客观下线:
由于集群内的节点会不断地与其他节点进行通讯,下线信息也会通过 Gossip 消息传遍所有节点,因此集群内的节点会不断收到下线报告。
当半数以上的主节点标记了节点B是主观下线时,便会触发客观下线的流程(该流程只针对主节点,如果是从节点就会忽略)。
接着向集群广播一条主节点B的Fail 消息,所有收到消息的节点都会标记节点B为客观下线。
故障恢复
当故障节点下线后,如果是持有槽的主节点则需要在其从节点中找出一个替换它,从而保证高可用。此时下线主节点的所有从节点都担负着恢复义务,这些从节点会定时监测主节点是否进入客观下线状态,如果是,则触发故障恢复流程。
2.1、从节点过滤:
检查每个slave节点与master节点断开连接的时间,如果超过了cluster-node-timeout * cluster-slave-validity-factor,那么就没有资格切换成master
2.2、投票选举:
(1)节点排序:
对通过过滤条件的所有从节点进行排序,按照priority、offset、run id排序,排序越靠前的节点,越优先进行选举。
- priority的值越低,优先级越高
- offset越大,表示从master节点复制的数据越多,选举时间越靠前,优先进行选举
- 如果offset相同,run id越小,优先级越高
(2)更新配置纪元:
每个主节点会去更新配置纪元(clusterNode.configEpoch),这个值是不断增加的整数。这个值记录了每个节点的版本和整个集群的版本。每当发生重要事情的时候(例如:出现新节点,从节点精选)都会增加全局的配置纪元并且赋给相关的主节点,用来记录这个事件。更新这个值目的是,保证所有主节点对这件“大事”保持一致,大家都统一成一个配置纪元,表示大家都知道这个“大事”了。
(3)发起选举:
更新完配置纪元以后,从节点会向集群发起广播选举的消息(CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST),要求所有收到这条消息,并且具有投票权的主节点进行投票。每个从节点在一个纪元中只能发起一次选举。
(4)选举投票:
如果一个主节点具有投票权,并且这个主节点尚未投票给其他从节点,那么主节点将向要求投票的从节点返回一条CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,表示这个主节点支持从节点成为新的主节点。每个参与选举的从节点都会接收CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,并根据自己收到了多少条这种消息来统计自己获得了多少主节点的支持。
如果超过(N/2 + 1)数量的master节点都投票给了某个从节点,那么选举通过,这个从节点可以切换成master,如果在 cluster-node-timeout*2 的时间内从节点没有获得足够数量的票数,本次选举作废,更新配置纪元,并进行第二轮选举,直到选出新的主节点为止。
docker中redis集群部署
- 拉取redis镜像
docker pull redis:5.0.5
- 创建6个redis容器
启动模式为host,则直接是使用docker本机的端口
其实也可以使用-net,只是要自己配置网络
docker create --name redis-node1 --net host -v /data/redis-data/node1:/data redis:5.0.5 --cluster-enabled yes --cluster-config-file nodes-node-1.conf --port 6379
docker create --name redis-node2 --net host -v /data/redis-data/node2:/data redis:5.0.5 --cluster-enabled yes --cluster-config-file nodes-node-2.conf --port 6380
docker create --name redis-node3 --net host -v /data/redis-data/node3:/data redis:5.0.5 --cluster-enabled yes --cluster-config-file nodes-node-3.conf --port 6381
docker create --name redis-node4 --net host -v /data/redis-data/node4:/data redis:5.0.5 --cluster-enabled yes --cluster-config-file nodes-node-4.conf --port 6382
docker create --name redis-node5 --net host -v /data/redis-data/node5:/data redis:5.0.5 --cluster-enabled yes --cluster-config-file nodes-node-5.conf --port 6383
docker create --name redis-node6 --net host -v /data/redis-data/node6:/data redis:5.0.5 --cluster-enabled yes --cluster-config-file nodes-node-6.conf --port 6384
- 启动容器
docker start redis-node1 redis-node2 redis-node3 redis-node4 redis-node5 redis-node6
-
搭建一个集群
进入一个redis实例
redis-cli --cluster create 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 --cluster-replicas 1
搭建成功
参考链接:https://www.cnblogs.com/niceyoo/p/14118146.html
http://t.csdn.cn/2njdU