Redis(十六) 集群

news2025/1/11 7:15:46

在这里插入图片描述

文章目录

  • 前言
  • 什么是集群
  • 集群模式基本原理
    • 哈希求余
    • 一致性哈希算法
    • 哈希槽分区算法
  • docker模拟出一个集群
    • 集群中节点挂了会怎么办
      • 故障判定
      • 故障迁移
    • 集群扩容

前言

前面我们学习了 redis 哨兵机制,哨兵机制是为了解决当主节点挂了之后,能够自动进行故障转移的机制,该机制提高了系统的可用性,它并不能帮助 redis 节点存储更多的数据,要想使得我们的 redis 节点能够存储更多的数据,就需要用到集群。

什么是集群

这里集群有两种含义,一种是广义的集群,一种就是狭义的集群。只要你是多个机器构成的分布式系统,都可以称作是一个“集群”,这就是广义的集群,而狭义的集群则是 redis 提供的集群模式,在这个集群模式下主要解决的就是存储空间不足的情况。

Redis集群实现了对Redis的水平扩容,即启动多个Redis节点,将整个数据库分布存储在多个节点中,每个节点存储总数据的一部分。Redis集群将数据分成多个槽(slot),每个节点负责处理其中的一部分槽,通过哈希算法将键分配到不同的槽中,这样可以实现数据的分布式存储和负载均衡。

集群模式基本原理

既然集群模式是将数据分为多个部分,分别存储在多个 redis 节点中的,那么如何将数据分为多个部分呢?redis 是如何实现数据的分片的呢?

哈希求余

哈希求余的思想很简单,我们都知道任何一个数据经过哈希函数的计算都能得到一个整数,当我们插入数据的时候,因为 redis 中 key 是唯一的,所以就将 key 经过哈希函数的计算得到一个整数,然后再针对 redis 主节点的个数进行求余,然后根据这个余数讲数据插入到对应编号的 redis 节点中,这样就简单的实现了数据的分片。

当查询 key 的值的时候,还是首先将 key 经过哈希函数的计算得到一个整数,然后还是将这个整数与 redis 主节点的个数进行去余的操作,根据这个余数和 redis 节点的编号的对应关系就知道这个 key 存储在哪个 redis 节点上了。

在这里插入图片描述

通过哈希求余可以很简单的首先数据分片操作,但是这个做法有一个致命的缺点,就是当进行扩容操作,增加 redis 节点的时候,对应的取余的操作数就发生了变化了,对于扩容之前的数据进行查询操作,因为 redis 节点的数量变化了,所以取模之后得到的结果就不一样了,那么在该节点对应编号的 redis 节点上查询该数据就无法得到该 key 的值。

为了解决这个问题,就需要对不应该待在当前节点的数据进行重新分配:

在这里插入图片描述
可以发现,当进行扩容操作的时候,大部分的数据都需要进行数据迁移的操作,这是一个很大的工作量。所以为了解决这个问题,又出现了第二种分片的方式 —— 一致性哈希算法。

一致性哈希算法

相较于普通的哈希算法,一致性哈希算法在进行哈希之后当前 key 属于有哪个分片,这个交替出现的情况改为了连续的情况,比如一段数据经过哈希之后的结果是 0 1 2 3,那么这四个数据就分别属于 0、1、2、3 号编号所对应的 redis 节点,而一致性哈希算法将这种交替出现的情况改为了连续出现,一致性哈希算法,会将一个圆分成例如 2^ 32 -1 个哈希槽,当 key 经过哈希函数计算之后,拿这个计算之后的结果去余上 2^ 32 -1,然后按照顺时针或者逆时针去找分片。

在这里插入图片描述
当进行扩容操作新增加 redis 节点之后,通过这个一致性哈希算法进行数据重新分配的时候就只用迁移小部分数据了:

在这里插入图片描述
一致性哈希算法虽然解决了普通哈希算法扩容的时候需要重新分配大量数据的问题,但是这样又出现了新的问题,就是出现了可能各个 redis 节点上存储的数据量存在较大差异的情况,这样就导致了数据倾斜的问题,这就导致了某个服务器承受的压力很大而导致挂掉,而有节点中存储的数据太少,干的工作很少的情况。这样有解决方法吗?有的,可以在扩容的时候,一次添加多个 redis 节点,使得新数据在迁移到新的 redis 节点之后各个 redis 节点中存储的数据量基本相同的情况,但是这样做的话又存在不确定性,因为实际工作中可能一次扩容老板不会给你这么多机器。所以为了解决这个问题,又出现了另外一种算法 —— 哈希槽分区算法。

哈希槽分区算法

哈希槽分区算法才是 redis 真正采用的分片算法,这个算法本质上结合了普通哈希求余和一致性哈希算法。该算法会将一致性哈希算法中的哈希槽分配到不同的分片上。

该算法会将哈希槽的个数默认为 16384 个,这个数字是如何来的呢?8 * 2 * 1024,也就是 2kb 的大小,为什么会设置为 2kb 呢?我们都知道判断 redis 节点是否存活的方式是定时向这个 redis 节点发送心跳包,而这个心跳包则是通过网络来传输的,既然是通过网络传输的,就不可避免的需要网络带宽,而网络带宽是网络中最重要也是最贵的资源,所以心跳包的大小越大就需要占用越多的网络带宽,虽然 2 kb 不算大,但是顶不住多了 redis 节点每一段时间都需要这个心跳包,这个 2 kb 大小的槽位是符合现在的业务需求的,所以为了避免占用过多的网络带宽又保证能够满足需求,所以哈希槽分区算法的槽位就设置成了 16384 个。

该算法是如何将槽位分配到不同的分片中的呢?给大家举个例子:

假设有三个分片,可能的分配方式:

  • 0号分片:[0, 5461],共 5462 个槽位
  • 1号分片:[5462, 10923],共 5462 个槽位
  • 2号分片:[10924, 16383],共 5460 个槽位

虽然不能做到绝对平均,但是每个节点所分得的槽位数量是大致相同的,并且每个分片为了区分哪个槽位是属于自己的,使用了位图这样的数据结构来记录。每个分片持有的槽位号可以是不连续的。

如果此时进行了扩容,有四个分片,那么每个分片所持有的槽位就会发生变化:

  • 0号分片:[0, 4095],共 4096个槽位
  • 1号分片:[5462, 9557],共 4096个槽位
  • 2号分片:[10924, 15019],共 4096个槽位
  • 3号分片:[4096, 5461] + [9558, 10923] + [15020, 16383]共 4096 个槽位

当进行扩容的时候,只是将每个分片上的槽位分一点给新的分片,这样就减少了大量的重新分配工作。

docker模拟出一个集群

同样,因为我条件有限,只有一个云服务器,而使用虚拟机的话,也需要创建出很多个虚拟机,就很吃主机的内存,所以这里就选择使用 docker 来模拟出来集群模式。

准备模拟出三个分片,也就是三个 redis 主节点,每个主节点有两个从节点,也就是说一共需要创建出 9 个 redis 节点,一个 redis 节点需要一个配置文件,这里一个一个手动创建的话就很麻烦,所以我们选择使用 shell 脚本的方式来创建多个 redis 配置文件。

在这里插入图片描述

先创建出下面目录结构的文件:

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-anounce-port 6379
cluster-announce-bus-poet 16379
EOF
done
  • seq 1 9:生成[1,9]之间的数字
  • ${}:表示得到执行 {} 中的命令的返回值
  • for port in ${seq 1 9};:循环 port 的取值为1-9
  • \:续航符,将下面一行的内容将当前一行的内容合并为一行
  • do:循环开始的标志,Linux shell 中不是用 {} 来表示循环体
  • EOF:文件内容结束的标志
  • cat >:将下面的内容重定向到文件中
  • done:循环结束的标志

写完 shell 脚本之后,使用 bash .sh 来运行 shell 脚本,运行之后当前目录结构就变成了:

redis-cluster
├── docker-compose.yml
├── generate.sh
├── redis1
│   └── redis.conf
├── redis2
│   └── redis.conf
├── redis3
│   └── redis.conf
├── redis4
│   └── redis.conf
├── redis5
│   └── redis.conf
├── redis6
│   └── redis.conf
├── redis7
│   └── redis.conf
├── redis8
│   └── redis.conf
└── redis9
    └── redis.conf

为了展示后面的扩容的情况,我们再创建出两个额外的节点:

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.10${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
EOF
done
redis-cluster
├── docker-compose.yml
├── generate.sh
├── redis1
│   └── redis.conf
├── redis10
│   └── redis.conf
├── redis11
│   └── redis.conf
├── redis2
│   └── redis.conf
├── redis3
│   └── redis.conf
├── redis4
│   └── redis.conf
├── redis5
│   └── redis.conf
├── redis6
│   └── redis.conf
├── redis7
│   └── redis.conf
├── redis8
│   └── redis.conf
└── redis9
    └── redis.conf

当生成这样的目录结构之后,我们来看看 redis.conf 文件中的详细配置项:

  • cluster-enabled yes:表示开启集群模式
  • cluster-config-file nodes.conf:在Redis集群中,节点需要知道其他节点的信息以便能够互相通信和协作。这种信息通常保存在一个配置文件中,该选项指定的就是该配置文件的名称
  • cluster-node-timeout:集群中的节点之间心跳包响应的超时时间
  • cluster-announce-ip:该redis节点所在主机的ip,因为这里使用的是docker容器,所以就写的是对应容器的ip地址
  • cluster-announce-port:该redis节点所在主机的端口号
  • cluster-announce-bus-port:该redis节点的管理端口

说到管理端口就又不得不说业务端口,业务端口就是负责处理客户端请求的端口,而管理端口是指当前服务如果出现问题的话,就需要对其做出维修管理,这个操作就是由这个端口来完成。

当创建出 redis 的对应配置文件之后,接下来就来创建出 redis 容器:

version: '3.7'
networks:
  mynet:
    ipam:
      config:
        - subnet: 172.30.0.0/24
          
services: 
  redis1:
    image: 'redis:6.0.16'
    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
  • networks:为了后续能够创建出静态IP,此时要手动创建出网络,同时给这个网络分配IP
  • 172.30.0.0/24:24 表示子网掩码,此处是 24 个比特位中前 24 位为 1,也就是 172.30.0 为网络号
  • ipv4_address:这个配置配置的就是静态IP,该配置的网络号需要和前面配置的网段号保持一致

配置的网段不可以和已存在的网段冲突,可以使用 ifconfig 来查看当前主机的网段使用情况。

在这里插入图片描述
这里集群中的 redis 节点占用的端口不应该和其他端口冲突,所以我们在配置 redis.conf 和 docker-compose.yml 文件的时候就需要多加注意。

配置完成 docker-compose.yml 文件之后,就可以使用 docker-compose up -d 后台启动容器:

root@iZ2ze5bzkbeuwwqowjzo27Z:~/redis-cluster# docker ps -a
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS                                                                                      NAMES
249ebfca17fb   redis:6.0.16   "docker-entrypoint.s…"   13 seconds ago   Up 11 seconds   0.0.0.0:6376->6379/tcp, :::6376->6379/tcp, 0.0.0.0:16376->16379/tcp, :::16376->16379/tcp   redis6
eea78107ef2f   redis:6.0.16   "docker-entrypoint.s…"   13 seconds ago   Up 11 seconds   0.0.0.0:6377->6379/tcp, :::6377->6379/tcp, 0.0.0.0:16377->16379/tcp, :::16377->16379/tcp   redis7
864d60bc1fc5   redis:6.0.16   "docker-entrypoint.s…"   13 seconds ago   Up 10 seconds   0.0.0.0:6371->6379/tcp, :::6371->6379/tcp, 0.0.0.0:16371->16379/tcp, :::16371->16379/tcp   redis1
f35869951ab7   redis:6.0.16   "docker-entrypoint.s…"   13 seconds ago   Up 10 seconds   0.0.0.0:6374->6379/tcp, :::6374->6379/tcp, 0.0.0.0:16374->16379/tcp, :::16374->16379/tcp   redis4
fe346e709b4d   redis:6.0.16   "docker-entrypoint.s…"   13 seconds ago   Up 11 seconds   0.0.0.0:6372->6379/tcp, :::6372->6379/tcp, 0.0.0.0:16372->16379/tcp, :::16372->16379/tcp   redis2
0e8ddecf23ec   redis:6.0.16   "docker-entrypoint.s…"   13 seconds ago   Up 11 seconds   0.0.0.0:6378->6379/tcp, :::6378->6379/tcp, 0.0.0.0:16378->16379/tcp, :::16378->16379/tcp   redis8
980d6c53a206   redis:6.0.16   "docker-entrypoint.s…"   13 seconds ago   Up 11 seconds   0.0.0.0:6379->6379/tcp, :::6379->6379/tcp, 0.0.0.0:16379->16379/tcp, :::16379->16379/tcp   redis9
bdaf777fa66a   redis:6.0.16   "docker-entrypoint.s…"   13 seconds ago   Up 11 seconds   0.0.0.0:6375->6379/tcp, :::6375->6379/tcp, 0.0.0.0:16375->16379/tcp, :::16375->16379/tcp   redis5
34bc5b70a0c3   redis:6.0.16   "docker-entrypoint.s…"   13 seconds ago   Up 11 seconds   0.0.0.0:6373->6379/tcp, :::6373->6379/tcp, 0.0.0.0:16373->16379/tcp, :::16373->16379/tcp   redis3

redis 容器全部启动成功之后,就可以来搭建集群了,搭建集群需使用 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

创建集群的时候需要列出需要搭建集群的 redis 节点的ip和端口。–cluster-relicas 2 表示每个主节点有两个从节点。

root@iZ2ze5bzkbeuwwqowjzo27Z:~/redis-cluster# 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: 7e572aec6621732f1c2079e67694038045e148d9 172.30.0.101:6379
   slots:[0-5460] (5461 slots) master
M: ca69a1ec58658f16c5cf7514cfe0004c29c0054c 172.30.0.102:6379
   slots:[5461-10922] (5462 slots) master
M: d3b899e31fca22807b12e8f12e552f1ba7469c43 172.30.0.103:6379
   slots:[10923-16383] (5461 slots) master
S: df407eb6c61fc10dddbbecc93b85e9e4f5ffd0ed 172.30.0.104:6379
   replicates d3b899e31fca22807b12e8f12e552f1ba7469c43
S: 6765977995b4c5cacc8fa83a6f332d00c56ccc3a 172.30.0.105:6379
   replicates 7e572aec6621732f1c2079e67694038045e148d9
S: 0489a48c6530d45852afa8a337e234b7d7488835 172.30.0.106:6379
   replicates 7e572aec6621732f1c2079e67694038045e148d9
S: e026a0a37eba8db56e77db443a8d7f56f2f268b5 172.30.0.107:6379
   replicates ca69a1ec58658f16c5cf7514cfe0004c29c0054c
S: b13fe464da77bd5d313debb0dc6bc68226ad221f 172.30.0.108:6379
   replicates ca69a1ec58658f16c5cf7514cfe0004c29c0054c
S: 693d7295247bb56fd27951c46c96c181ce430185 172.30.0.109:6379
   replicates d3b899e31fca22807b12e8f12e552f1ba7469c43
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
...
>>> Performing Cluster Check (using node 172.30.0.101:6379)
M: 7e572aec6621732f1c2079e67694038045e148d9 172.30.0.101:6379
   slots:[0-5460] (5461 slots) master
   2 additional replica(s)
S: 693d7295247bb56fd27951c46c96c181ce430185 172.30.0.109:6379
   slots: (0 slots) slave
   replicates d3b899e31fca22807b12e8f12e552f1ba7469c43
S: 6765977995b4c5cacc8fa83a6f332d00c56ccc3a 172.30.0.105:6379
   slots: (0 slots) slave
   replicates 7e572aec6621732f1c2079e67694038045e148d9
M: ca69a1ec58658f16c5cf7514cfe0004c29c0054c 172.30.0.102:6379
   slots:[5461-10922] (5462 slots) master
   2 additional replica(s)
S: df407eb6c61fc10dddbbecc93b85e9e4f5ffd0ed 172.30.0.104:6379
   slots: (0 slots) slave
   replicates d3b899e31fca22807b12e8f12e552f1ba7469c43
S: b13fe464da77bd5d313debb0dc6bc68226ad221f 172.30.0.108:6379
   slots: (0 slots) slave
   replicates ca69a1ec58658f16c5cf7514cfe0004c29c0054c
S: e026a0a37eba8db56e77db443a8d7f56f2f268b5 172.30.0.107:6379
   slots: (0 slots) slave
   replicates ca69a1ec58658f16c5cf7514cfe0004c29c0054c
S: 0489a48c6530d45852afa8a337e234b7d7488835 172.30.0.106:6379
   slots: (0 slots) slave
   replicates 7e572aec6621732f1c2079e67694038045e148d9
M: d3b899e31fca22807b12e8f12e552f1ba7469c43 172.30.0.103:6379
   slots:[10923-16383] (5461 slots) master
   2 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

slots:表示当前主节点分得的槽位

这里的主从关系是如何形成的呢?前面我们在生成 redis.conf 配置文件的时候不是没有指定主从关系吗?这里搭建集群的时候,主从关系是随机形成的,谁是主节点、谁是从节点、谁和谁属于一个分片都是随机的,因为本身从集群的角度来看,提供的这些节点的价值都是等价的。

集群大家成功之后,我们可以进入对应的 redis 客户端来查看集群的信息:

root@iZ2ze5bzkbeuwwqowjzo27Z:~/redis-cluster# redis-cli -h 172.30.0.101
172.30.0.101:6379> cluster nodes
693d7295247bb56fd27951c46c96c181ce430185 172.30.0.109:6379@16379 slave d3b899e31fca22807b12e8f12e552f1ba7469c43 0 1715223551000 3 connected
6765977995b4c5cacc8fa83a6f332d00c56ccc3a 172.30.0.105:6379@16379 slave 7e572aec6621732f1c2079e67694038045e148d9 0 1715223551000 1 connected
ca69a1ec58658f16c5cf7514cfe0004c29c0054c 172.30.0.102:6379@16379 master - 0 1715223552109 2 connected 5461-10922
df407eb6c61fc10dddbbecc93b85e9e4f5ffd0ed 172.30.0.104:6379@16379 slave d3b899e31fca22807b12e8f12e552f1ba7469c43 0 1715223551107 3 connected
b13fe464da77bd5d313debb0dc6bc68226ad221f 172.30.0.108:6379@16379 slave ca69a1ec58658f16c5cf7514cfe0004c29c0054c 0 1715223551000 2 connected
e026a0a37eba8db56e77db443a8d7f56f2f268b5 172.30.0.107:6379@16379 slave ca69a1ec58658f16c5cf7514cfe0004c29c0054c 0 1715223552008 2 connected
0489a48c6530d45852afa8a337e234b7d7488835 172.30.0.106:6379@16379 slave 7e572aec6621732f1c2079e67694038045e148d9 0 1715223550607 1 connected
7e572aec6621732f1c2079e67694038045e148d9 172.30.0.101:6379@16379 myself,master - 0 1715223551000 1 connected 0-5460
d3b899e31fca22807b12e8f12e552f1ba7469c43 172.30.0.103:6379@16379 master - 0 1715223552000 3 connected 10923-16383

我们在进入 redis 客户端的时候,可以使用 -p 选项指定本机端口来进入,这里的端口就是我们前面 docker-compose.yml 文件中设置的端口映射,也可以使用 -h 选项指定 ip 和端口号来进入对应的 redis 客户端。

然后我们来测试一下集群功能:

172.30.0.101:6379> set key1 111
(error) MOVED 9189 172.30.0.102:6379

这里报了 error 错误是为什么呢?这是因为 key1 经过哈希计算然后求余之后得到的结果对应的不是当前我们进入的这个 101 redis 主机,那么这是否意味着,我们只能进入到对应的客户端才能进行相关槽操作呢?不必这么麻烦,我们只需要在进入客户端的时候加上 -c 选项就可以了:

root@iZ2ze5bzkbeuwwqowjzo27Z:~/redis-cluster# redis-cli -h 172.30.0.101 -c
172.30.0.101:6379> set key1 111
-> Redirected to slot [9189] located at 172.30.0.102:6379
OK
172.30.0.102:6379>

当在进入客户端的时候加上 -c 选项的时候,就算操作的 key 不在当前分片上,这个集群也会自动转换到 key 所在的分片的客户端然后在这个客户端上执行该操作。可以看到,当我们创建 key 的时候,发现 key1 经过哈希然后求余的操作之后,所在的槽位在 9189 这个槽位上,该槽位属于 102 分片上,所以就会自动进入到 102 redis 主机上,然后执行该操作。

101、102和103 的角色都是主节点,那么我们是否能在从节点上设置 key 呢?

root@iZ2ze5bzkbeuwwqowjzo27Z:~/redis-cluster# redis-cli -h 172.30.0.104 -c
172.30.0.104:6379> set key2 222
-> Redirected to slot [4998] located at 172.30.0.101:6379
OK

可以看到,就算我们在从节点的客户端上进行 set 操作,他不会报错,而是会自动转换到对应的主节点上然后执行该操作。

在集群中之前学习的 redis 的命令基本上都可以使用,但是还是有例外:

172.30.0.101:6379> mset key3 333 key4 444
(error) CROSSSLOT Keys in request don't hash to the same slot

当我们进行 mset 和 mget 等一次设置或者查询多个 key 的操作的时候,之后这多个 key 经过哈希计算然后求余之后属于同一个分片的时候才可以执行成功,否则就执行失败。

集群中节点挂了会怎么办

上面为大家演示了设置和查询 key 的操作,那么如果集群中国如果有节点挂了该怎么办呢?挂了的情况分两张:从节点挂了和主节点挂了,从节点挂了影响不大,集群还可以正常工作,而如果是主节点挂了,那么集群就会进行故障转移操作:

我们把 redis1 这个主节点容器给手动停止:
在这里插入图片描述
然后进入集群中的某一个客户端查看当前集群的信息:

root@iZ2ze5bzkbeuwwqowjzo27Z:~/redis-cluster# redis-cli -h 172.30.0.102 -c
172.30.0.102:6379> cluster nodes
ca69a1ec58658f16c5cf7514cfe0004c29c0054c 172.30.0.102:6379@16379 myself,master - 0 1715225034000 2 connected 5461-10922
e026a0a37eba8db56e77db443a8d7f56f2f268b5 172.30.0.107:6379@16379 slave ca69a1ec58658f16c5cf7514cfe0004c29c0054c 0 1715225033000 2 connected
693d7295247bb56fd27951c46c96c181ce430185 172.30.0.109:6379@16379 slave d3b899e31fca22807b12e8f12e552f1ba7469c43 0 1715225034500 3 connected
7e572aec6621732f1c2079e67694038045e148d9 172.30.0.101:6379@16379 master,fail - 1715224900105 1715224897600 1 connected
df407eb6c61fc10dddbbecc93b85e9e4f5ffd0ed 172.30.0.104:6379@16379 slave d3b899e31fca22807b12e8f12e552f1ba7469c43 0 1715225034600 3 connected
b13fe464da77bd5d313debb0dc6bc68226ad221f 172.30.0.108:6379@16379 slave ca69a1ec58658f16c5cf7514cfe0004c29c0054c 0 1715225034000 2 connected
d3b899e31fca22807b12e8f12e552f1ba7469c43 172.30.0.103:6379@16379 master - 0 1715225033000 3 connected 10923-16383
0489a48c6530d45852afa8a337e234b7d7488835 172.30.0.106:6379@16379 slave 6765977995b4c5cacc8fa83a6f332d00c56ccc3a 0 1715225033999 10 connected
6765977995b4c5cacc8fa83a6f332d00c56ccc3a 172.30.0.105:6379@16379 master - 0 1715225033000 10 connected 0-5460

redis1 这里标记的是 fail,也就表示该节点是挂掉了,再看可以发现,之前属于 redis1 分片的从节点 redis5 的身份变成了 master,也就是 redis5 节点由从节点的身份变成了主节点的身份。

再将这个 redis1 节点启动起来,该节点也不会重新恢复为主节点的身份,而是会成为 redis5 节点的从节点:

在这里插入图片描述

那么 redis 集群的这个故障转移是如何实现的呢?

故障判定

首先就是这个集群是如何发现某个主节点挂了呢?

  1. 在集群的节点中,节点 A 会给节点 B 发送一个 ping 包,B 接收到这个 ping 包之后就会向 A 返回一个 pong 包,ping 和 pong 除了 message type 属性之外,其他部分是一样的,这里包含了集群的配置信息(该节点的id,该节点从属于哪个分片,是主节点还是从节点,从属于哪个主节点,持有哪些slots位图)
  2. 每个节点,每秒中国,都会给一些随机的节点发送 ping 包,而不是全部节点都发一遍,这样设定是避免在节点很多的时候,心跳包也非常多(比如如果有9个节点,集群中每秒就会有 9 * 8 72个心跳包在进行网络传输了,而且这是按照N^2这样的级别增长的)
  3. 当节点 A 向节点 B 发送 ping 包之后,节点 B 不能按时返回 pong 包的话,节点 A 就会重置和节点 B 的 tcp 连接,看能否连接成功,如果连接失败,那么 A 就会把 B 的状态设置为 PFAIL(相当于主管下线)
  4. A 判定 B 为 PFAIL 之后,会通过 redis 内置的 Gossip 协议,和其他节点进行沟通,向其他节点确认 B 的状态(每个节点都会维护自己的“下线列表”,由于视角不同,所以每个节点的下线列表可能也不同)
  5. 此时 A 发现其他很多节点也认为 B 节点为 PFAIL 状态,并且数目超过集群节点的一半,那么 A 就会把 B 节点的状态设置为 FAIL(客观下线),并且把这个消息同步给其他节点,其他节点收到这个消息之后也会把 B 的状态设置为 FAIL

故障迁移

如果 B 节点是从节点,那么就不需要进行故障转移,但是如果 B 节点是主节点的话,那么就会由 B 的从节点 C 或者 D 来触发故障转移了。

  1. 从节点会先判断自己是否具有参选资格,如果当前从节点太久没有和自己的主节点进行通信了(也就是此从节点和主节点之间的数据同步差异太大了),时间超过阈值,就会失去竞选资格
  2. 具有资格的节点,比如 C 和 D,就会先休眠一定的时间。休眠时间 = 500ms 基础时间 + [0, 500ms]随机时间 + 排名 * 1000ms。offset 的值越大,排名越靠前,休眠时间就越短,意思就是从节点和主节点的同步程度越高休眠时间越短
  3. 如果某一个节点的休眠时间到了,那么该节点就会给集群中的其他节点进行通信,进行拉票操作,但是只有主节点才具有投票资格
  4. 主节点就会决定是否把票投给该节点,每个主节点只有一票,当该节点收到的票数超过主节点数目的一半的时候,该节点就会晋升为主节点(该节点会自己执行 slaveof no one的操作,然后剩下的从节点就会执行 slaveof 该节点的操作)
  5. 同时该节点会把自己成为主节点的信息同步给集群中的其他节点,大家也就会更新自己保存的集群的结构信息

一般情况下,哪个节点的休眠时间短,那么该节点很大概率就是新的主节点了,而且这个主节点是谁并不重要,重要的是需要选出来一个主节点。这里选出新的主节点的过程跟前面的哨兵选出新的主节点不同,哨兵机制是先在哨兵节点中选出一个 leader,然后由这个 leader 来选出主节点,集群中选出主节点的方式则是从节点自己拉票。

以上是集群能够正常进行故障转移的问题,一下几种情况集群可能无法完成故障转移,而导致集群出现宕机:

  1. 某个分片,所有的主节点和从节点都挂了
  2. 某个分片,主节点挂了,但是该主节点没有从节点
  3. 超过半数的master都挂了

第一种情况和第二种情况其实属于一种情况,就是某一个分片中的所有节点都挂了,那么该分片中的所有数据都无法进行增加和查询了,那么就可能会认为这个集群出现了问题,超过半数的 master 都挂了,很可能是因为比较严重的问题。

集群扩容

前面我们使用 3 个分片,每个分片中一个主节点,两个分分节点构成了一个集群,那么接下来我们再向该集群中添加进来两个节点,实现集群的扩容:


启动完成两个新的 redis 节点之后,使用 redis-cli --cluster add-node 172.30.0.110:6379 172.30.0.101:79 来将新节点添加进去集群中,172.3.0.110:79 是需要添加的节点的 ip 地址和端口号,172.30.0.101:79 是要添加进去的所在集群其中一个节点的 ip 地址和端口号。

在这里插入图片描述

添加完成之后,我们进入任意一个该集群中的客户端,然后查看集群信息:

在这里插入图片描述
虽然 redis10 这个节点成功加入集群中了,并且身份是 master,但是仔细观察可以发现:该主节点没有分配槽点,要想新添加的节点可以分配操作,还需要进行重新分配操作的操作:

redis-cli --cluster reshard 172.30.0.101:6379 后面的 ip 地址和端口号是集群中任何一个节点的 ip 和端口号:

在这里插入图片描述
这里因为是四个分片,所以每个分片的槽位大致就为 16384 / 4 = 4096个:

在这里插入图片描述
输入 redis10 节点的 id:

在这里插入图片描述
输入从哪里哪些节点来移动槽点:

  • all:表示从其他每个持有操槽点的节点分来一点槽点
  • 手动指定:从某一个或者多个节点分来槽点

我们这里选择 all:

在这里插入图片描述
输入 all 之后,会将槽位将要移动的过程显示出来(这里还没有开始移动),输入 yes 之后才开始移动。

完成移动之后,我们再进入集群中某个节点的客户端,然后查看集群信息:

在这里插入图片描述
可以看到新加入的节点从其他的分片中获取到了槽位,所以这时才真正完成了集群的扩容操作。

在搬运 槽位/key 的过程中是否可以进行查询操作呢?

如果这个 key 没有发生搬运,或者已经搬运完成之后,是可以成功查询到的,但是如果该 key 正在搬运的话,就会出现问题。

然后我们再将 redis11 添加到 redis10 这个分片中:

redis-cli --cluster add-node 172.30.0.111:6379 172.30.0.101:6379 --cluster-slave --cluster-master-id 2151637d6e2d5c0c21fb7ec09fcfe9da81cdb276

–cluster-master-id 就是该节点需要加入集群的分片中主节点的 nodeId。

添加完成之后,再查看集群信息:

root@iZ2ze5bzkbeuwwqowjzo27Z:~/redis-cluster/redis11# redis-cli -h 172.30.0.101
172.30.0.101:6379> cluster nodes
2151637d6e2d5c0c21fb7ec09fcfe9da81cdb276 172.30.0.110:6379@16379 master - 0 1715243382093 10 connected 0-1364 5461-6826 10923-12287
3358ff6101429421aba92ae5d69d5d60cdb1b550 172.30.0.108:6379@16379 slave 2151637d6e2d5c0c21fb7ec09fcfe9da81cdb276 0 1715243382000 10 connected
71caee9f98e2d644770d9574cab01d82c851beaf 172.30.0.107:6379@16379 slave e15b8a4873323542b7a5a386229860ba0d6f553f 0 1715243383596 2 connected
e1732ac6f874142bd0a088e1ee90812b95740170 172.30.0.103:6379@16379 master - 0 1715243383000 3 connected 12288-16383
f8787c6909595da33cf14709b097a5040c85c2ed 172.30.0.105:6379@16379 slave 9d37acd0aa7357d09cf5eb2aa368dc02fda877ba 0 1715243383596 1 connected
c1ef5bcc2ca74727d7367f6725226f2cc7560baf 172.30.0.109:6379@16379 slave e1732ac6f874142bd0a088e1ee90812b95740170 0 1715243382000 3 connected
9d37acd0aa7357d09cf5eb2aa368dc02fda877ba 172.30.0.101:6379@16379 myself,master - 0 1715243382000 1 connected 1365-5460
7f2f888320507b03e1266ae8d4e864ee1f14d182 172.30.0.104:6379@16379 slave e1732ac6f874142bd0a088e1ee90812b95740170 0 1715243382000 3 connected
e15b8a4873323542b7a5a386229860ba0d6f553f 172.30.0.102:6379@16379 master - 0 1715243383596 2 connected 6827-10922
9cdaf7b88d7b8655cbf63bf83f97f45fb8151480 172.30.0.111:6379@16379 slave 2151637d6e2d5c0c21fb7ec09fcfe9da81cdb276 0 1715243383096 10 connected
82cd70d87654a133fdf6bd545d0e260288632806 172.30.0.106:6379@16379 slave 9d37acd0aa7357d09cf5eb2aa368dc02fda877ba 0 1715243382000 1 connected

从节点添加成功。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1811475.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JVM对象分配和垃圾回收机制

一、对象创建 1.1 符号引用 new 创建一个对象&#xff0c;需要在JVM创建对象。 符号引用&#xff1a;目标对象采用一个符号表示&#xff0c;类A加载的时候&#xff0c;如果成员变量类B还没有被加载进来&#xff0c;采用一个符号&#xff08;字面量&#xff09;来表示&#x…

Linux C语言:指针的运算

一、指针的算术运算 1、指针运算 指针运算是以指针所存放的地址作为运算量而进行的指针运算的实质就是地址的计算 2、指针的算数运算 指针加上整数&#xff0c;指针减去整数, 指针递增&#xff0c;指针递减和两个指针相减。 指针加减一个n的运算: px n px - n 移动步长…

LeetCode | 2879.显示前三行

在 pandas 中&#xff0c;可以使用 head() 方法来读取 DataFrame 的前几行数据。如果想读取指定数量的行&#xff0c;可以在 head() 方法中传入一个参数 n&#xff0c;读取前 n 行 import pandas as pddef selectFirstRows(employees: pd.DataFrame) -> pd.DataFrame:retur…

mybatisplus(原理)使用方法引用的形式获取实体类对应数据库的列名

我们现在正常来看 一个mybatisplus正常的查询语句 我们可以看到 &#xff0c;再如上的代码中 我们使用了 Address&#xff1a;&#xff1a;getuserId 方法引用&#xff0c;但是我们把方法引用改成lambda表达式的形式的时候不会报错&#xff0c;但是运行的时候报错。为什么…

Tkinter 组件详解之Entry

Tkinter 组件详解之Entry Entry(输入框)组件通常用于获取用户的输入文本。 何时使用 Entry 组件? Entry 组件仅允许用于输入一行文本,如果用于输入的字符串长度比该组件可显示空间更长,那内容将被滚动。这意味着该字符串将不能被全部看到(你可以用鼠标或键盘的方向键调…

[2024-06]-[大模型]-[DEBUG]- ollama webui 11434 connection refused

报错&#xff1a;host.docker.internal:11434 ssl:default [Connection refused] 将/etc/systemd/system/ollama.service中加上如下红框两行 Environment"OLLAMA_HOST0.0.0.0" Environment"OLLAMA_ORIGINS*"然后 systemctl daemon-reload systemctl rest…

速卖通测评攻略:轻松提升店铺曝光度和吸引力

在速卖通平台上&#xff0c;产品排名&#xff0c;店铺曝光的提升无疑是所有卖家追求的目标&#xff0c;因为这直接关联着产品曝光量的增加和潜在销售机会的扩大。然而&#xff0c;提升产品排名并非一蹴而就&#xff0c;它需要一系列的策略和技巧。那么&#xff0c;接下来就让我…

【网络安全】跨站脚本攻击漏洞—HTML前端基础

目录 一、HTML概述 1.1 head部分 1.2 body部分 1.3 HTML特殊符号 二、JavaScript概述 2.1 HTML中JavaScript的存在方式 2.2 DOM操作 2.3 BOM操作 跨站脚本攻击&#xff08;Cross-site scripting&#xff0c;通常缩写为XSS&#xff09;是一种常见的网络安全漏洞&#xff…

区块链实验室(36) - 交叉编译Ethereum的客户端全套工具

停滞了一段时间&#xff0c;重新回到区块链实验。前面在“区块链实验室(31) - 交叉编译Ethereum的客户端Geth”中仅编译出客户端工具geth。编译Ethereum全套工具的代码如下。 #!/bin/bash ## abigen CGO_ENABLED0 GOOSlinux GOARCHarm64 /usr/local/go/bin/go build -ldflags …

成都跃享未来教育抖音小店深度解析靠谱与否

在如今网络购物日益繁荣的时代&#xff0c;抖音小店以其独特的平台优势和庞大的用户基础&#xff0c;吸引了越来越多的商家入驻。成都跃享未来教育咨询有限公司便是其中之一&#xff0c;它的抖音小店究竟靠不靠谱呢&#xff1f;今天&#xff0c;我们就来一起揭开这个谜底。 首…

现货黄金交易多少克一手?国内外情况大不同

如果大家想参与国际市场上的现货黄金交易&#xff0c;就应该从它交易细则的入手&#xff0c;先彻底认识这个品种&#xff0c;因为它是来自欧美市场的投资方式&#xff0c;所以无论是从合约的计的单位&#xff0c;计价的货币&#xff0c;交易的具体时间&#xff0c;以及买卖过程…

乡村振兴的多元化产业发展:推动农村一二三产业融合发展,培育乡村新业态,打造多元化发展的美丽乡村

一、引言 乡村振兴是我国当前及未来一段时间内的重大战略任务&#xff0c;旨在促进农村经济的全面发展&#xff0c;提高农民的生活水平&#xff0c;实现城乡融合发展。在乡村振兴的进程中&#xff0c;推动农村一二三产业融合发展&#xff0c;培育乡村新业态&#xff0c;是打造…

linux安装anconda后,之前的python环境如何加载到anconda环境中

一、问题描述 由于某种原因&#xff0c;我们需要在系统中安装多个环境&#xff0c;我们自然想到安装anconda来解决这个问题。但是当我们安装好anconda后&#xff0c;发现我们未安装anconda之前的python环境使用不了了。那么我们如何将之前的python环境放到conda 环境中呢。 二…

7种常用数据分析方法,建议收藏学习(下)

在上一篇内容里&#xff0c;我们提到了常用的数据分析放法&#xff0c;比如&#xff1a;漏斗分析法、留存分析法、分组分析法、矩阵分析法。没有看到的上篇推送的小伙伴可以回过头去查看我们之前的推送。 今天&#xff0c;我们继续讲后面的内容&#xff1a;关联分析法、指标分…

SpringBoot快速部署(2)—不使用docker的常规方法

一、软件下载和部署前准备 安装软件 获取软件安装包 然后上传到服务器的 /tmp 目录下。 软件&#xff1a;nginx、jdk、mysql 下载 X-shell 和 Xftp 注意&#xff1a;这个页面下载安装的才可以免费使用。家庭/学校免费 - NetSarang Website 安装jdk 1.8 tar -zxvf /tmp/jdk-…

程序猿大战Python——容器——字符串

字符串介绍 什么是Python容器 目标&#xff1a;了解Python容器是什么&#xff1f; 在现实生活中&#xff0c;我们知道容器是用来存放东西的&#xff0c;比如实验室里的烧杯等。 类似的&#xff0c;在Python中的容器是用来存放数据的。 与此同时&#xff0c;为了操作方便&…

springboot与flowable(3):启动、审批、各个Service服务

一、启动流程 流程定义与实例的关系类似于Java的类与对象&#xff0c;通过定义的id创建流程实例&#xff0c;编写测试代码&#xff1a; package org.example.flowabledemo2;import org.flowable.engine.RuntimeService; import org.flowable.engine.runtime.ProcessInst…

[论文阅读] (33)NDSS2024 Summer系统安全和恶意代码分析方向相关论文汇总

《娜璋带你读论文》系列主要是督促自己阅读优秀论文及听取学术讲座&#xff0c;并分享给大家&#xff0c;希望您喜欢。由于作者的英文水平和学术能力不高&#xff0c;需要不断提升&#xff0c;所以还请大家批评指正&#xff0c;非常欢迎大家给我留言评论&#xff0c;学术路上期…

盲盒小程序支付流程优化与风险防控策略

在盲盒小程序中&#xff0c;支付流程的优化和风险防控是提升用户体验和确保交易安全的关键环节。下面我们将深入探讨这两个方面的问题和应对策略。 一、支付流程优化 在支付流程优化方面&#xff0c;我们主要关注以下几点&#xff1a; 简化操作流程&#xff1a;通过减少不必…

男士内裤什么品牌质量好?盘点口碑最好的五款男士内裤

面对市场上琳琅满目的款式与品牌&#xff0c;如何挑选出最适合自己的那一款男士内裤一直是大家最大的疑问&#xff01;今天&#xff0c;我们为您带来实用的选购技巧&#xff0c;并推荐五款备受好评的男士内裤&#xff0c;信儿帮助大家避免选到一些质量不好的男士内裤&#xff0…