• redis集群系列四


    输出是多么快乐的一件事情,hurray~

    1 集群的完整性

    • 如果参数cluster-require-full-coverage设置为yes(默认值),则所有槽都需要并指派到节点上,保护集群的完整性。
    • 如果参数cluster-require-full-coverage设置为no,在某些分片故障切换的时候,其他节点还是可用的
    # cluster-require-full-coverage yes
    
    • 1

    2 大集群的带宽消耗

    2.1 大集群的带宽消耗主要有三方面

    • 消息频率:节点发现与其他节点最后通讯时间超过cluster-node-timeout/2时会直接发送ping消息
    • 消息数据量:slots槽数组(2KB)+ 整个集群1/10的状态数据(1个节点的状态数据104bytes)
    • 物理机器上的节点规模

    2.2 大集群的带宽消耗预估量

    • 以200个节点为例子,20台物理机器,每台物理机器10个节点。

    • 实际通信的过程中, AB两个节点进行ping/pong,A会通过一个随机的端口向节点B的集群通讯端口发送ping消息,节点B通过集群通信端口向A发送pong消息。所以A节点的入口带宽,不仅包含本身的集群端口,也包含随机端口,而随机端口正是其他节点的集群通讯端口的出口带宽。

    • 那么可以计算出来,1台机器的入口带宽:

    每个消息是2kb+20*104bytes=4kb
    1台机器集群端口的入口带宽:4kb*190*2*10/15=1203kb
    实际的1台机器的入口带宽=所有机器集群端口的出口带宽+本台机器的集群端口的入口带宽=1203kb*20=24mb左右
    
    • 1
    • 2
    • 3

    有人做了一个抓包统计gossip消息实验及分析,很细致,感兴趣或者自己想抓包分析的可参考:https://github.com/moooofly/MarkSomethingDown/blob/master/Redis/Redis%20%E4%BD%BF%E7%94%A8%E9%97%AE%E9%A2%98%E6%B1%87%E6%80%BB%EF%BC%88%E7%BD%91%E6%96%87%EF%BC%89.md

    集群的带宽消耗,主要由读写命令和gossip消息消耗;所以搭建redis集群要注意根据业务规模和消息通信成本做好规划。

    官方建议集群的最大规模在1000以内。建议合理选择哦

    3 pub/sub 广播问题

    • 对集群所有的主节点执行subscribe订阅test频道
    • 连接集群发布test频道的消息
    • 集群内所有节点订阅test频道的都收到了消息

    集群模式下的publish广播问题,也会消耗集群内的网络带宽。这种情况建议使用sentinel来专门用于pub/sub来规避

    4 集群倾斜

    4.1 数据倾斜

    一般分为以下几种情况

    1. 节点和槽分配不均匀
    2. 不同的槽对应的键值差异较大
      1. case:大量使用hash_tag,会产生不同的键映射到同一个槽
      2. 可以使用command: cluster countkeysinslot {slot}获取slot的键值数目,然后使用command: cluster getkeysinslot {slot} {count} 循环迭代出槽下所有的键
    3. 集合对象包含大量元素(大key)
      1. 可以使用命令:redis-cli --bigkeys
      2. 大key,比如几百M,可能会在migrate过程中超时失败
    4. 内存配置不一致
      1. 有些压缩数据结构的配置不一致(hash-max-ziplist-value、set-max-intset-entries等)

    4.2 请求倾斜

    可能情况:热key对应高算法复杂度的命令,或者大对象操作hegtall、smembers

    建议:使用hmget代替hgetall避免全部读取

    5 读写分离

    5.1 只读连接

    1. 集群模式下,从节点是不接受任何读写请求的,从节点接收到的读写命令会重定向到负责槽的主节点。
    2. 需要使用从节点分担读压力,需要连接从节点,输入readonly,从节点接收到的读命令,如果key属于自己正在复制的主节点则直接返回,否则返回重定向信息
      1. readonly是连接级别生效的,每次新建连接都需要执行readonly
      2. readwrite命令可以关闭连接只读状态
      3. 可以通过client list查看客户端连接的状态:flags=N表示普通客户端
    /data # redis-cli
    127.0.0.1:6379> client list
    id=28 addr=10.0.129.186:6379 fd=21 name= age=19730 idle=5 flags=M db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping
    id=20058 addr=127.0.0.1:34716 fd=23 name= age=8 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=32742 obl=0 oll=0 omem=0 events=r cmd=client
    127.0.0.1:6379> get 100
    (error) MOVED 339 10.0.129.186:6379
    127.0.0.1:6379> readonly
    OK
    127.0.0.1:6379> client list
    id=28 addr=10.0.129.186:6379 fd=21 name= age=19753 idle=8 flags=M db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping
    id=20058 addr=127.0.0.1:34716 fd=23 name= age=31 idle=0 flags=r db=0 sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=32742 obl=0 oll=0 omem=0 events=r cmd=client
    127.0.0.1:6379> get 100
    "100"
    127.0.0.1:6379> readwrite
    OK
    127.0.0.1:6379> get 100
    (error) MOVED 339 10.0.129.186:6379
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    5.2 读写分离

    1. 实现方式,需要对客户端进行以下修改
      1. 维护每个主节点可用的从节点列表(cluster slave master-nodeId 可以获取到主节点下所有从节点的信息)
      2. 读命令进行路由到从节点
      3. 从节点新建的连接开启readonly状态
    2. 其他替代方案
      1. 部署多套redis集群,客户端多写来维护;读选择距离最近的redis集群

    6 手动故障转移

    6.1 操作方式

    在从节点上执行cluster failover,客户端请求会有短暂的阻塞,但不会丢失数据。流程如下:

    1. 从节点通知主节点停止处理请求
    2. 主节点发送给对应从节点,延迟复制的数据
    3. 从节点同步了该数据,直到主从复置的偏移量一致【保证数据不丢失】
    4. 从节点立刻发起投票选举(因为这个执行cluster failover的从节点是唯一的,所以不需要延迟选举)。选举成功之后变成主节点,并向集群广播
    5. 旧节点收到消息后更新自身配置为从节点,解除客户端请求阻塞,这些请求会被重定向到新的主节点上
    6. 旧节点向新的主节点发起全量复制流程

    6.2 应用场景

    1. 主节点所在机器要迁移到新的机器
    2. 自动故障转移失败,通过手动转移故障可以恢复部分节点。自动故障转移失败的场景:
      1. 主节点和对应的从节点全部故障了
      2. 健康的从节点没有故障转移的资格,主从复制的短线时间超过:(server.repl_ping_slave_period * 1000) +
        (server.cluster_node_timeout * server.cluster_slave_validity_factor)
      3. 从节点的故障选举失败,没有在cluster_node_timeout*2时间内完成
      4. 集群内超过半数以上的主节点同时故障了

    6.3 操作命令

    1. cluster failover:没有实际的failure,但是希望master发生主从切换(具体哪一个从提升为主,看在哪一个主上执行cluster failover),切换是一个安全的方式,没有任何数据丢失。工作的流程见下。最终,旧的master的client连接会自动地转移到新的master;只有旧master的所有replication stream同步完成,副本才会变成新的master。

      1. step1 副本告诉主:停止接受来自client的查询请求
      2. step2 主回复副本当前的replication offset
      3. step3 副本等待replication offset和主的一致,保证主的数据都被同步过来了
      4. step4 副本开始failover,包含一个来自多数master的configuration epoch,然后广播新的配置文件
      5. step5 老的master 收到配置文件的更新:解锁他的client,开始回复redirection messages告诉client去找新的master
    2. cluster failover force:

      1. 从节点收到该请求,直接发起选举,不需要跟主确定主从偏移量(从节点复制延迟的数据会丢失)
      2. 从节点选举成功之后成为主并开启广播
    3. cluster failover takeover:

      1. 集群内超过半数以上的主都挂了,没法投票。从节点收到该请求,不再进行选举流程而是直接更新本地配置纪元并替换主节点

      2. 因为takeover模式没有通过领导选举,可能配置纪元会存在冲突

        1. 如果有冲突,集群会以nodeId更大的为准,可能会导致丢数据(网络分区恢复后,会出现拥有相同slot的两个主节点)

    附录

    不好意思,我又要推荐了哈哈

    发现github上一个分析的很细致的资料,这个人总结的东西有点深度和不同,喜欢的可以自取:

    https://github.com/moooofly/MarkSomethingDown

  • 相关阅读:
    Leetcode(81)——搜索旋转排序数组 II
    pdf文件属性的删除
    【Python 千题 —— 基础篇】判断布尔值
    zookeeper:Unexpected exception, exiting abnormally ::java.io.EOFException
    制作一个模板
    pandas--->CSV / JSON
    Dockerfile文件解释
    Android 多线程的几种实现方式(二)-- Executor
    el-dialog 实现可以拖动的弹窗
    人工智能基础第三次作业
  • 原文地址:https://blog.csdn.net/Alexia23/article/details/125550491