itbird 2020-10-26
1 集群的意义
从单机的一主多从复制架构到现在的分布式架构
主要有如下维度:
2 meet
节点之间完成相互通信的基础,有一定的频率和规则。
CLUSTER MEET命令被用来连接不同的开启集群支持的 Redis 节点,以进入工作集群。
2.1 基本思想
每个节点默认都是相互不信任的,并且被认为是未知的节点,以便万一因为系统管理错误或地址被修改,而不太可能将多个不同的集群节点混成一个集群。
因此,为了使给定节点能将另一个节点接收到组成 Redis Cluster 的节点列表中,这里只有两种方法:
Redis Cluster 需要形成一个完整的网络(每个节点都连接着其他每个节点),但为创建一个集群,不需要发送形成网络所需的所有CLUSTER MEET命令。发送CLUSTER MEET消息以便每个节点能够到达其他每个节点只需通过一条已知的节点链就够了。由于在心跳包中会交换 gossip 信息,将会创建节点间缺失的链接。
所以,如果我们通过CLUSTER MEET链接节点 A 和 B ,并且 B 和 C 有链接,那么节点 A 和 C 会发现他们握手和创建链接的方法。
2.2 案例
假设某一集群有A、B、C、D四个节点,可以只发送以下一组命令给 A :
CLUSTER MEET B-ip B-port CLUSTER MEET C-ip C-port CLUSTER MEET D-ip D-port
由于 A 知道及被其他所有节点知道,它将会在发送的心跳包中包含gossip部分,这将允许其他每个节点彼此都创建一个链接,即使集群很大,也能在数秒内形成一个完整网络。
CLUSTER MEET无需相互执行,即若发送命令给 A 以加入B ,那就不必也发送给 B 以加入 A。
2.3 实现细节:MEET 和 PING 包
当某一给定节点接收到一个MEET消息时,命令中指定的节点仍不知发送了该命令,所以为使节点强制将接收命令的节点将它作为信任的节点接受它,它会发送MEET包而非PING包。两个消息包有相同的格式,但是MEET强制使接收消息包的节点确认发送消息包的节点为可信任的。
3 指派槽
客户端与指派槽
4 集群伸缩
集群的伸缩包括新节点的加入和旧节点退出。
4.1 加入新节点
Redis集群加入新节点主要如下几步:
启动一个集群模式下的Redis节点
通过与任意一集群中的节点握手加入新节点
再向新节点分配它负责的slot并向其迁移slot对应数据。
由于Redis采用Gossip协议,所以可让新节点与任一现有集群节点握手,一段时间后整个集群都会知道加入了新节点。
4.1.1 案例
向如下集群中新加入一个节点6385。由于负载均衡的要求,加入后四个节点每个节点负责4096个slots,但集群中原来的每个节点都负责5462个slots,所以6379、6380、6381节点都需要向新的节点6385迁移1366个slots。
Redis集群并没有一个自动实现负载均衡的工具,把多少slots从哪个节点迁移到哪个节点完全是由用户指定。
迁移数据的流程图
迁移key可以用pipeline进行批量的迁移。
对于扩容,原理已经很清晰了,至于具体操作,网上很多。
至于缩容,也是先手动完成数据迁移,再关闭redis。收缩时如果下线的节点有负责的槽需要迁移到其他节点,再通过cluster forget命令让集群内所有节点忘记被下线节点
5 客户端路由
5.1 moved重定向
每个节点通信共享Redis Cluster中槽和集群中对应节点的关系。
1.如果保存数据的槽被分配给当前节点,则去槽中执行命令,并把命令执行结果返回给客户端
2.如果保存数据的槽不在当前节点的管理范围内,则向客户端返回moved重定向异常
3.客户端接收到节点返回的结果,如果是moved异常,则从moved异常中获取目标节点的信息
4.客户端向目标节点发送命令,获取命令执行结果
客户端不会自动找到目标节点执行命令,需要二次执行
5.2 ask重定向
由于集群伸缩时,需要数据迁移。
当客户端访问某key,节点告诉客户端key在源节点,再去源节点访问时,却发现key已迁移到目标节点,就会返回ask。
为什么不能简单使用MOVED重定向?
因为虽然MOVED意味着我们认为哈希槽由另一个节点永久提供,并且应该对指定节点尝试下一个查询,所以ASK意味着仅将下一个查询发送到指定节点。
之所以需要这样做,是因为下一个关于哈希槽的查询可能是关于仍在A中的键的,因此我们始终希望客户端尝试A,然后在需要时尝试B。由于只有16384个可用的哈希槽中有一个发生,因此群集上的性能下降是可以接受的。
5.3 moved V.S ask
都是客户端重定向
5.4 smart智能客户端
目标
追求性能
设计思路
注意事项
全面图示
6 批量操作
mget、mset须在同一槽。
Redis Cluster不同于Redis 单节点,甚至和一个 Sentinel 监控的主从模式也不一样。主要是因为集群自动分片,将一个key 映射到16384槽之一,这些槽分布在多节。因此操作多 key 的命令必须保证所有的key都映射同一槽,避免跨槽执行错误。
一个单独的集群节点,只服务一组专用的keys,请求一个命令到一个Server,只能得到该Server上拥有keys的对应结果。
一个非常简单的例子是执行KEYS命令,当发布该命令到集群中某节点时,只能得到该节点上拥有key,并非集群中所有key。要得到集群中所有key,必须从集群的所有主节点上获取所有key。
对于分散在redis集群中不同节点的数据,我们如何比较高效地批量获取数据呢?
6.1 串行mget
定义for循环,遍历所有key,分别去所有的Redis节点中获取值并进行汇总,简单,但效率不高,需n次网络时间。
6.2 串行I/O
优化串行的mget,在客户端本地做内聚,对每个key hash,然后取余,知道key对应槽
本地已缓存了槽与节点的对应关系,然后对key按节点进行分组,成立子集,然后使用pipeline把命令发送到对应的node,需要nodes次网络时间,大大减少了网络时间开销。
6.3 并行I/O
优化串行IO,分组key后,根据节点数量启动对应的线程数,根据多线程模式并行向node节点请求数据,只需1次网络时间
6.4 hash_tag
不做任何改变,hash后就比较均匀地散在每个节点上
是否能像单机,一次IO将所有key取出呢?hash-tag提供了这样功能:若将上述key改为如下,即大括号括起来相同的内容,保证所有的key只向一个node请求数据,这样执行类似mget命令只需要去一个节点获取数据即可,效率更高。
6.5 选型对比
第一种方式,使用多线程解决批量问题,减少带宽时延,提高效率,这种做法就如上面所说简单便捷(我们目前批量操作类型比较多),有效。但问题比较明显。批量操作数量不大即可满足。搜狐的cachecloud采用第二点,先将key获取槽点,然后分node pipeline操作。这种做法相对比第一种做法较优。
7 故障转移
7.1 故障发现
Redis Cluster通过ping/pong消息实现故障发现:不需要sentinel。ping/pong不仅能传递节点与槽的对应消息,也能传递其他状态,比如:节点主从状态,节点故障等
故障发现就是通过这种模式来实现,分为主观下线和客观下线:
7.1.1 主观下线
定义
某节点认为另一节点不可用,这仅代表一个节点对另一节点的判断,不代表所有节点的认知。
流程
7.1.2 客观下线
定义
当半数以上持有槽的主节点都标记某节点主观下线。可以保证判断的公平性。
集群模式下,只有主节点(master)才有读写权限和集群槽的维护权限,从节点(slave)只有复制的权限。
流程
1.某个节点接收到其他节点发送的ping消息,如果接收到的ping消息中包含了其他pfail节点,这个节点会将主观下线的消息内容添加到自身的故障列表中,故障列表中包含了当前节点接收到的每一个节点对其他节点的状态信息
2.当前节点把主观下线的消息内容添加到自身的故障列表之后,会尝试对故障节点进行客观下线操作
7.2 故障恢复
从节点接收到它的主节点客观下线的通知,则进行故障恢复。
资格检查
准备选举时间
使偏移量最大的从节点具备优先级成为主节点的条件。
选举投票
对选举出来的多个从节点进行投票,选出新的主节点。
替换主节点
8 开发运维常见问题
8.1 集群完整性
但是大多数业务都无法容忍,建议把cluster-require-full-coverage设为no
8.2 带宽消耗
带宽优化
8.3 Pub/Sub广播
解决方案
单独“走”一套redis sentinel。就是针对目标的几个节点构建redis sentinel,在这个里面实现广播。
8.4 集群倾斜
分布式数据库存在倾斜问题是比较常见的。集群倾斜也就是各个节点使用的内存不一致
数据倾斜原因
1.节点和槽分配不均,如果使用redis-trib.rb工具构建集群,则出现这种情况的机会不多
redis-trib.rb info ip:port查看节点,槽,键值分布 redis-trib.rb rebalance ip:port进行均衡(谨慎使用)
2.不同槽对应键值数量差异比较大
3.包含bigkey:例如大字符串,几百万的元素的hash,set等
4.内存相关配置不一致
5.请求倾斜:热点key
重要的key或者bigkey
Redis Cluster某个节点有一个非常重要的key,就会存在热点问题
优化
9 读写分离
读写分离:更加复杂(成本很高,尽量不要使用)
10 集群优劣
集群的限制
集群不一定好
参考
https://www.slideshare.net/iammutex/redis-cluster
https://zhuanlan.zhihu.com/p/105569485
https://sunweiguo.github.io/2019/02/01/redis/Redis-Cluster%E7%90%86%E8%AE%BA%E8%AF%A6%E8%A7%A3/