[Redis][集群][下]详细讲解

目录

  • 1.集群搭建(基于 Docker)
  • 2.主节点宕机
    • 1.宕机后会发生什么?
    • 2.处理流程
      • 1.故障判定
      • 2.故障迁移
  • 3.集群扩容
    • 0.前言
    • 1.把新的主节点加入到集群
    • 2.重新分配slots
    • 3.给新的主节点添加从节点


1.集群搭建(基于 Docker)

  • 拓扑结构如下
    请添加图片描述

  • 创建目录和配置:创建redis-cluster⽬录,内部创建两个⽂件

    redis-cluster/
    ├── docker-compose.yml 
    └── generate.sh
    
  • generate.sh

    for port in $(seq 1 9); \
    do \
        mkdir -p redis${port}/
        touch redis${port}/redis.conf
        cat << EOF > redis${port}/redis.conf
        port 6379
        bind 0.0.0.0
        protected-mode no
        appendonly yes
        cluster-enabled yes
        cluster-config-file nodes.conf
        cluster-node-timeout 5000
        cluster-announce-ip 172.30.0.10${port}
        cluster-announce-port 6379
        cluster-announce-bus-port 16379
        EOF
    done
    
    # 注意cluster-announce-ip的值有变化
    for port in $(seq 10 11); \
    do \
        mkdir -p redis${port}/
        touch redis${port}/redis.conf
        cat << EOF > redis${port}/redis.conf
        port 6379
        bind 0.0.0.0
        protected-mode no
        appendonly yes
        cluster-enabled yes
        cluster-config-file nodes.conf
        cluster-node-timeout 5000
        cluster-announce-ip 172.30.0.1${port}
        cluster-announce-port 6379
        cluster-announce-bus-port 16379
        EOF
    done
    
    • 配置说明
      • cluster-enabled yes:开启集群
      • cluster-config-file nodes.conf:集群节点⽣成的配置
      • cluster-node-timeout 5000:节点失联的超时时间
      • cluster-announce-ip 172.30.0.101:节点⾃⾝ip
      • cluster-announce-port 6379:节点⾃⾝的业务端⼝
      • cluster-announce-bus-port 16379节点⾃⾝的总线端⼝(管理端口),集群管理的信息交互是通过这个端⼝进⾏的
  • docker-compose.yml

    • 先创建networks,并分配网段172.30.0.0/24
    • 配置每个节点,注意配置⽂件映射,端⼝映射,以及容器的ip地址,设定成固定ip⽅便后续的观察和操作
      version: '3.7'
      networks:
      	mynet:
              ipam:
                  config:
                      - subnet: 172.30.0.0/24
                      
      services:
          redis1:
              image: 'redis:5.0.9'
              container_name: redis1
              restart: always
              volumes:
              	- ./redis1/:/etc/redis/
              ports:
              	- 6371:6379
              	- 16371:16379
              command:
              	redis-server /etc/redis/redis.conf
              networks:
              	mynet:
              		ipv4_address: 172.30.0.101
          redis2:
              image: 'redis:5.0.9'
              container_name: redis2
              restart: always
              volumes:
              	- ./redis1/:/etc/redis/
              ports:
              	- 6372:6379
              	- 16372:16379
              command:
              	redis-server /etc/redis/redis.conf
              networks:
              	mynet:
              		ipv4_address: 172.30.0.102
      
      # redis3 ~ redis11
      
  • 启动容器docker-compose up -d

  • 构建集群:此处把前9个主机构成集群,3主6从,后两个主机暂时不用

    • --cluster create:表⽰建⽴集群.后⾯填写每个节点的ip和地址
    • --cluster-replicas 2:表⽰每个主节点需要两个从节点备份
      redis-cli --cluster create 172.30.0.101:6379 172.30.0.102:6379 172.30.0.103:6379 172.30.0.104:6379 172.30.0.105:6379 172.30.0.106:6379 172.30.0.107:6379 172.30.0.108:6379 172.30.0.109:6379 --cluster-replicas 2
      
    • 日志中会描述哪些是主节点,哪些从节点跟随哪个主节点
      >>> Performing hash slots allocation on 9 nodes...
      Master[0] -> Slots 0 - 5460
      Master[1] -> Slots 5461 - 10922
      Master[2] -> Slots 10923 - 16383
      Adding replica 172.30.0.105:6379 to 172.30.0.101:6379
      Adding replica 172.30.0.106:6379 to 172.30.0.101:6379
      Adding replica 172.30.0.107:6379 to 172.30.0.102:6379
      Adding replica 172.30.0.108:6379 to 172.30.0.102:6379
      Adding replica 172.30.0.109:6379 to 172.30.0.103:6379
      Adding replica 172.30.0.104:6379 to 172.30.0.103:6379
      M: e4f37f8f0ea0dafc584349999795716613910e51 172.30.0.101:6379
         	slots:[0-5460] (5461 slots) master
      M: 5f71983ad52cc7077ce8874ae1c4f9c23d9f502c 172.30.0.102:6379
      	slots:[5461-10922] (5462 slots) master
      M: b3c0a96f6a206088ecea639147b6fcf903afe872 172.30.0.103:6379
      	slots:[10923-16383] (5461 slots) master
      S: 85025819223f12615046c54d89f510e9cd0444a1 172.30.0.104:6379
      	replicates b3c0a96f6a206088ecea639147b6fcf903afe872
      S: 2e5dc211288784ba55d554a377b87bfe2b5398db 172.30.0.105:6379
      	replicates e4f37f8f0ea0dafc584349999795716613910e51
      S: 29f05d98982bd3df05d0222091e4b8ef9569f424 172.30.0.106:6379
      	replicates e4f37f8f0ea0dafc584349999795716613910e51
      S: 3584840ac704c3ee016f3bdcca3f7ebe6f6e8e80 172.30.0.107:6379
      	replicates 5f71983ad52cc7077ce8874ae1c4f9c23d9f502c
      S: 0a889103b35db2a6e82e8c09904bbef310cff3b1 172.30.0.108:6379
      	replicates 5f71983ad52cc7077ce8874ae1c4f9c23d9f502c
      S: 00ba82bed6abeb015116d51d1af7fcb1609d03ad 172.30.0.109:6379
      	replicates b3c0a96f6a206088ecea639147b6fcf903afe872
      Can I set the above configuration? (type 'yes' to accept): yes
      
    • Redis在构建集群时,谁是主节点,谁是从节点,谁和谁是一个分片,都是不固定的
      • 理论上说,从集群角度看,提供的所有节点本就应该是等价的
  • 此时,使用客户端连上集群中的任何一个节点,都相当于连上了整个集群

    • 客⼾端后⾯要加上-c选项,否则如果key没有落到当前节点上,是不能操作的
      • -c会自动把请求重定向到对应节点
    • 使⽤cluster nodes可以查看到整个集群的情况
  • 使用集群之后,之前的那些Redis命令,基本都能使用,除了一些操作多个key,如mget
    ![[Pasted image 20240911210424.png]]


2.主节点宕机

1.宕机后会发生什么?

  • 此处集群做的工作,和之前哨兵做的类似,会主动从该主节点旗下从节点中挑一个出来,提拔为主节点
    • master提示fail后,原本的slave会变成新的master
    • 如果重新启动原master,该节点会作为slave存在
  • 可以使⽤cluster failover进⾏集群恢复,也就是把原master重新设定成·master
    • 需要登录到原master机器上执行

2.处理流程

1.故障判定

  • 集群中的所有节点,都会周期性的使⽤⼼跳包进⾏通信
    • 节点A给节点B发送ping包,B就会给A返回⼀个pong
      • pingpong除了message type属性之外,其他部分都是⼀样的
      • 这⾥包含了集群的配置信息(该节点的id,该节点从属于哪个分⽚,是主节点还是从节点,从属于谁,持有哪些slots的位图…)
    • 每个节点,每秒钟,都会给⼀些随机的节点发起ping,⽽不是全发⼀遍
      • 这样设定是为了避免在节点很多的时候,⼼跳包也⾮常多
      • ⽐如有9个节点,如果全发,就是 9 ∗ 8 9*8 98有72组⼼跳了,⽽且这是按照 N 2 N^2 N2这样的级别增⻓的
    • 当节点A给节点B发起ping包,B不能如期回应的时候,此时A就会尝试重置和B的TCP连接,看能否连接成功,如果仍然连接失败,A就会把B设为PFAIL状态(相当于主观下线).
    • A判定B为PFAIL之后,会通过Redis内置的Gossip协议,和其他节点进⾏沟通,向其他节点确认B的状态
      • 每个节点都会维护⼀个⾃⼰的"下线列表",由于视⻆不同,每个节点的下线列表也不⼀定相同
    • 此时A发现其他很多节点,也认为B为PFAIL,并且数⽬超过总集群个数的⼀半,那么A就会把B标记成FALL(相当于客观下线),并且把这个消息同步给其他节点(其他节点收到之后,也会把B标记成FAIL)
    • 至此,B是否下线,就已经明确了
  • 某个或者某些节点宕机,有的时候会引起整个集群都宕机,称为fail状态
    • 某个分⽚,所有的主节点和从节点都挂了
      • 此时,该分片就无法提供数据服务了,相当于数据缺失
    • 某个分⽚,主节点挂了,但是没有从节点
    • 超过半数的master节点都挂了
      • 如果突然一系列的master都挂了,说明集群遇到了非常严重的问题,此时就需要赶紧停下来,检查检查是不是有什么问题
    • 核心原则:保证每个slots都能正常工作(存取数据)

2.故障迁移

  • 上述例⼦中,B故障,并且A把BFAIL的消息告知集群中的其他节点
    • 如果B是从节点,那么不需要进行故障迁移
    • 如果B是主节点,那么就会由B的从节点(比如C和D)触发故障转移了
  • 所谓故障转移,就是指把从节点提拔成主节点,继续给整个Redis集群提供支持
  • 具体流程
    • 从节点判定⾃⼰是否具有参选资格,如果从节点和主节点已经太久没通信(此时认为从节点的数据和主节点差异太⼤了),时间超过阈值,就失去竞选资格
    • 具有资格的节点,⽐如C和D,就会先休眠⼀定时间休眠时间 = 500ms基础时间 + [0, 500ms]随机时间 + 排名 * 1000msoffset的值越⼤,则排名越靠前(越⼩)
      • 排名越靠前,休眠时间就越短,谁休眠时间短,大概率就是新的主节点了
    • 比如C的休眠时间到了,C就会给其他所有集群中的节点,进行拉票操作
      • 但是只有主节点才有投票资格
    • 主节点就会把⾃⼰的票投给C(每个主节点只有1票),当C收到的票数超过主节点数⽬的⼀半,C就会晋升成主节点
      • C⾃⼰负责执⾏slaveof no one,并且让D执⾏slaveof C
    • 同时,C还会把⾃⼰成为主节点的消息,同步给其他集群的节点,⼤家也都会更新⾃⼰保存的集群结构信息
  • 上述选举的过程,成为Raft算法,是一种在分布式系统中广泛使用的算法
    • 在随即休眠时间的加持下,基本上就是谁先醒,谁就能竞选成功
    • 更多的时候,是为了选一个节点出来,至于是谁,并不重要
  • 哨兵是先选出leaderleader负责找一个从节点升级成主节点这里是直接投票选出新的主节点

3.集群扩容

0.前言

  • 扩容是⼀个在开发中⽐较常遇到的场景
  • 随着业务的发展,现有集群很可能⽆法容纳⽇益增⻓的数据,此时给集群中加⼊更多新的机器。就可以使存储的空间更⼤了
  • 分布式的本质就是使⽤更多的机器,引⼊更多的硬件资源
  • 明确集群扩容操作,是一件风险较高,成本较大的操作,操作时,需要抱着一个敬畏的心!!!

1.把新的主节点加入到集群

  • 上⾯已经把redis1-redis9重新构成了集群,接下来把redis10和redis11也加⼊集群
    • 此处把redis10作为主机,redis11作为从机
      redis-cli --cluster add-node 172.30.0.110:6379 172.30.0.101:6379
      
  • 说明
    • add-node后的第⼀组地址是新节点的地址
    • 第⼆组地址是集群中的任意节点地址,表示要把新节点加到哪个集群中
  • 执行结果
    >>> Adding node 172.30.0.110:6379 to cluster 172.30.0.101:6379
    >>> Performing Cluster Check (using node 172.30.0.101:6379)
    M: 00d319e23ef76a4d51e74600c42ee2a371ae81f6 172.30.0.101:6379
    	slots:[0-5460] (5461 slots) master
    	2 additional replica(s)
    S: e34911c57d7605903de84ec05b3deac611aaef7e 172.30.0.105:6379
    	slots: (0 slots) slave
    	replicates 00d319e23ef76a4d51e74600c42ee2a371ae81f6
    S: 6cf48cc11d0171b6ab1b418808473167acd7986e 172.30.0.106:6379
    	slots: (0 slots) slave
    	replicates 00d319e23ef76a4d51e74600c42ee2a371ae81f6
    S: fd18c7f164b09ec563f4573ec9d6466e6769221e 172.30.0.108:6379
    	slots: (0 slots) slave
    	replicates b3f2ba758318f4bd54031c98c01d7a6155ff43d3
    M: 579282abe81b3f20ffd17d5a1956cdca3b0e71b0 172.30.0.103:6379
    	slots:[10923-16383] (5461 slots) master
    	2 additional replica(s)
    S: e9ea79b1326ea5a75a1701d5c12a0f6081c1d043 172.30.0.109:6379
    	slots: (0 slots) slave
    	replicates 579282abe81b3f20ffd17d5a1956cdca3b0e71b0
    S: 628d1ec9ceef6760b9038c4fbc83ee92430062ac 172.30.0.107:6379
    	slots: (0 slots) slave
    	replicates b3f2ba758318f4bd54031c98c01d7a6155ff43d3
    M: b3f2ba758318f4bd54031c98c01d7a6155ff43d3 172.30.0.102:6379
    	slots:[5461-10922] (5462 slots) master
    	2 additional replica(s)
    S: 2a248acb47f0036655397897f9800c70ea22514f 172.30.0.104:6379
    	slots: (0 slots) slave
    	replicates 579282abe81b3f20ffd17d5a1956cdca3b0e71b0
    [OK] All nodes agree about slots configuration.
    >>> Check for open slots...
    >>> Check slots coverage...
    [OK] All 16384 slots covered.
    >>> Send CLUSTER MEET to node 172.30.0.110:6379 to make it join the cluster.
    [OK] New node added correctly.
    
  • 此时的集群状态172.30.0.110这个节点已经成为了集群中的主节点,但没有任何slots
    • 需要重新分配slots

2.重新分配slots

  • 示例命令
    redis-cli --cluster reshard 172.30.0.101:6379
    
  • 说明reshard后的地址是集群中的任意节点地址
  • 执行之后,会进入交互式操作,Redis会提示用户输入以下内容
    • 多少个slots要进⾏reshard?
    • 哪个节点来接收这些slots?
    • 这些slots从哪些节点搬运过来?
      • all:从其他每个持有slotsmaster都拿过来点
      • done:从某一个或某几个节点来移动slots,以done为结尾
    How many slots do you want to move (from 1 to 16384)? 4096
    What is the receiving node ID? 522a1bd88a1a9084e6919fa88f4bf1c3655ad837
    Please enter all the source node IDs.
    	Type 'all' to use all the nodes as source nodes for the hash slots.
    	Type 'done' once you entered all the source nodes IDs.
    Source node #1: all
    
  • 如果在搬运slots/key的过程中,此时客户端能否访问Redis集群呢
    • 对于那些不需要搬运的key,访问的时候是没有任何问题的
    • 但是对于需要搬运的key,进⾏访问可能会出现短暂的访问错误(key的位置出现了变化)
    • 随着搬运完成,这样的错误⾃然就恢复了

3.给新的主节点添加从节点

  • 光有主节点了,此时扩容的⽬标已经初步达成,但是为了保证集群可⽤性,还需要给这个新的主节点添加从节点,保证该主节点宕机之后,有从节点能够顶上
  • 示例命令
    redis-cli --cluster add-node 172.30.0.111:6379 172.30.0.101:6379 --cluster-slave --cluster-master-id [172.30.1.110 节点的 nodeId]
    
上一篇:招联金融2025校招内推


下一篇:C语言高阶【2】--动态内存管理【2】--柔性数组(这是个全新的知识点,不想了解一下吗?)