目 录CONTENT

文章目录

docker搭建Redis分片集群

zhouzz
2024-10-08 / 0 评论 / 0 点赞 / 6 阅读 / 14043 字
温馨提示:
本文最后更新于 2024-10-09,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

1.主从/哨兵不足

主从模式可以解决高可用、高并发读的问题。但依然有两个问题没有解决:

  • 海量数据存储
  • 高并发写

要解决这两个问题就需要用到分片集群了。分片的意思,就是把数据拆分存储到不同节点,这样整个集群的存储数据量就更大了。

Redis分片集群的结构如图

20241009010034.png

分片集群特征:

  • 集群中有多个master,每个master保存不同分片数据 ,解决海量数据存储问题
  • 每个master都可以有多个slave节点 ,确保高可用
  • master之间通过ping监测彼此健康状态 ,类似哨兵作用
  • 客户端请求可以访问集群任意节点,最终都会被转发到数据所在节点

2.搭建分片集群

Redis分片集群最少也需要3个master节点,由于我们的机器性能有限,我们只给每个master配置1个slave,形成最小的分片集群:

计划部署的节点信息如下:

容器名角色IP映射端口
r1master192.168.150.1017001
r2master192.168.150.1017002
r3master192.168.150.1017003
r4slave192.168.150.1017004
r5slave192.168.150.1017005
r6slave192.168.150.1017006

集群配置

分片集群中的Redis节点必须开启集群模式,一般在配置文件中添加下面参数:

port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes

其中有3个我们没见过的参数:

  • cluster-enabled:是否开启集群模式
  • cluster-config-file:集群模式的配置文件名称,无需手动创建,由集群自动维护
  • cluster-node-timeout:集群中节点之间心跳超时时间

一般搭建部署集群肯定是给每个节点都配置上述参数,不过考虑到我们计划用docker-compose部署,因此可以直接在启动命令中指定参数,偷个懒。

在虚拟机的/root目录下新建一个redis-cluster目录,然后在其中新建一个docker-compose.yaml文件,内容如下:

vim docker-compose.yaml
version: "3.2"

services:
  redis_cluster1:
    image: redis:6.2.6
    container_name: redis_cluster1
    network_mode: "host"
    entrypoint: ["redis-server", "--port", "7001", "--cluster-enabled", "yes", "--cluster-config-file", "node.conf"]
  redis_cluster2:
    image: redis:6.2.6
    container_name: redis_cluster2
    network_mode: "host"
    entrypoint: ["redis-server", "--port", "7002", "--cluster-enabled", "yes", "--cluster-config-file", "node.conf"]
  redis_cluster3:
    image: redis:6.2.6
    container_name: redis_cluster3
    network_mode: "host"
    entrypoint: ["redis-server", "--port", "7003", "--cluster-enabled", "yes", "--cluster-config-file", "node.conf"]
  redis_cluster4:
    image: redis:6.2.6
    container_name: redis_cluster4
    network_mode: "host"
    entrypoint: ["redis-server", "--port", "7004", "--cluster-enabled", "yes", "--cluster-config-file", "node.conf"]
  redis_cluster5:
    image: redis:6.2.6
    container_name: redis_cluster5
    network_mode: "host"
    entrypoint: ["redis-server", "--port", "7005", "--cluster-enabled", "yes", "--cluster-config-file", "node.conf"]
  redis_cluster6:
    image: redis:6.2.6
    container_name: redis_cluster6
    network_mode: "host"
    entrypoint: ["redis-server", "--port", "7006", "--cluster-enabled", "yes", "--cluster-config-file", "node.conf"]

注意:使用Docker部署Redis集群,network模式必须采用host

3.启动集群

进入/root/redis-cluster目录,使用命令启动redis:

3.1 启动

docker-compose up -d

3.2 停止

docker-compose stop

如果你只想停止特定的服务,可以指定服务名:

docker-compose stop <service_name>

3.3 停止并移除

停止并移除容器、网络和卷(会删除所有生成的资源)

docker-compose down

启动成功,可以通过命令查看启动进程:

ps -ef | grep redis

结果如下:

root      15281  15225  0 10:23 ?        00:00:00 redis-server *:7003 [cluster]
root      15341  15261  0 10:23 ?        00:00:00 redis-server *:7001 [cluster]
root      15382  15258  0 10:23 ?        00:00:00 redis-server *:7004 [cluster]
root      15405  15339  0 10:23 ?        00:00:00 redis-server *:7002 [cluster]
root      15414  15327  0 10:23 ?        00:00:00 redis-server *:7006 [cluster]
root      15424  15323  0 10:23 ?        00:00:00 redis-server *:7005 [cluster]

可以发现每个redis节点都以cluster模式运行。不过节点与节点之间并未建立连接。

接下来,我们使用命令创建集群:

# 进入任意节点容器
docker exec -it redis_cluster1 bash

执行分配命令:

redis-cli --cluster create --cluster-replicas 1 \
192.168.44.100:7001 192.168.44.100:7002 192.168.44.100:7003 \
192.168.44.100:7004 192.168.44.100:7005 192.168.44.100:7006

命令说明:

  • redis-cli --cluster:代表集群操作命令
  • create:代表是创建集群
  • --cluster-replicas 1 :指定集群中每个master的副本个数为1
    • 此时节点总数 ÷ (replicas + 1) 得到的就是master的数量n。因此节点列表中的前n个节点就是master,其它节点都是slave节点,随机分配到不同master

输入命令后控制台会弹出下面的信息:

>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 192.168.44.100:7005 to 192.168.44.100:7001
Adding replica 192.168.44.100:7006 to 192.168.44.100:7002
Adding replica 192.168.44.100:7004 to 192.168.44.100:7003
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 3eb131a80c225342f7fdbd384db974f6ef09658a 192.168.44.100:7001
   slots:[0-5460] (5461 slots) master
M: e3e5da5dd235fdb0ec7721a365ecd714b9544bf1 192.168.44.100:7002
   slots:[5461-10922] (5462 slots) master
M: 7d71a3f97b11be9a4748af1c61e50e4579a9acea 192.168.44.100:7003
   slots:[10923-16383] (5461 slots) master
S: a1a89cb8bd552107e38003fe0010fd18da381197 192.168.44.100:7004
   replicates 3eb131a80c225342f7fdbd384db974f6ef09658a
S: 9d52c3a2b087f08a43567081ff5411e25b4bf38f 192.168.44.100:7005
   replicates e3e5da5dd235fdb0ec7721a365ecd714b9544bf1
S: 68fc9cca08fd8e544c2fb660748dcfef9470354b 192.168.44.100:7006
   replicates 7d71a3f97b11be9a4748af1c61e50e4579a9acea
Can I set the above configuration? (type 'yes' to accept):

这里展示了集群中master与slave节点分配情况,并询问你是否同意。

输入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 192.168.44.100:7001)
M: 3eb131a80c225342f7fdbd384db974f6ef09658a 192.168.44.100:7001
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
S: 9d52c3a2b087f08a43567081ff5411e25b4bf38f 192.168.44.100:7005
   slots: (0 slots) slave
   replicates e3e5da5dd235fdb0ec7721a365ecd714b9544bf1
M: 7d71a3f97b11be9a4748af1c61e50e4579a9acea 192.168.44.100:7003
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
S: 68fc9cca08fd8e544c2fb660748dcfef9470354b 192.168.44.100:7006
   slots: (0 slots) slave
   replicates 7d71a3f97b11be9a4748af1c61e50e4579a9acea
S: a1a89cb8bd552107e38003fe0010fd18da381197 192.168.44.100:7004
   slots: (0 slots) slave
   replicates 3eb131a80c225342f7fdbd384db974f6ef09658a
M: e3e5da5dd235fdb0ec7721a365ecd714b9544bf1 192.168.44.100:7002
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

接着,我们可以通过命令查看集群状态:

redis-cli -p 7001 cluster nodes

结果如下:

9d52c3a2b087f08a43567081ff5411e25b4bf38f 192.168.44.100:7005@17005 slave e3e5da5dd235fdb0ec7721a365ecd714b9544bf1 0 1728408523368 2 connected
7d71a3f97b11be9a4748af1c61e50e4579a9acea 192.168.44.100:7003@17003 master - 0 1728408527000 3 connected 10923-16383
3eb131a80c225342f7fdbd384db974f6ef09658a 192.168.44.100:7001@17001 myself,master - 0 1728408526000 1 connected 0-5460
68fc9cca08fd8e544c2fb660748dcfef9470354b 192.168.44.100:7006@17006 slave 7d71a3f97b11be9a4748af1c61e50e4579a9acea 0 1728408527391 3 connected
a1a89cb8bd552107e38003fe0010fd18da381197 192.168.44.100:7004@17004 slave 3eb131a80c225342f7fdbd384db974f6ef09658a 0 1728408526000 1 connected
e3e5da5dd235fdb0ec7721a365ecd714b9544bf1 192.168.44.100:7002@17002 master - 0 1728408528396 2 connected 5461-10922

4.散列插槽

数据要分片存储到不同的Redis节点,肯定需要有分片的依据,这样下次查询的时候才能知道去哪个节点查询。很多数据分片都会采用一致性hash算法。而Redis则是利用散列插槽(hash slot)的方式实现数据分片
在Redis集群中,共有16384个hash slots,集群中的每一个master节点都会分配一定数量的hash slots。具体的分配在集群创建时就已经指定了:

>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383

如上所示:

  • Master[0],本例中就是7001节点,分配到的插槽是0~5460
  • Master[1],本例中就是7002节点,分配到的插槽是5461~10922
  • Master[2],本例中就是7003节点,分配到的插槽是10923~16383

当我们读写数据时,Redis基于CRC16 算法对key做hash运算,得到的结果与16384取余,就计算出了这个key的slot值。然后到slot所在的Redis节点执行读写操作。

不过hash slot的计算也分两种情况:

  • 当key中包含{}时,根据{}之间的字符串计算hash slot
  • 当key中不包含{}时,则根据整个key字符串计算hash slot

例如:

  • key是user,则根据user来计算hash slot
  • key是user:{age},则根据age来计算hash slot

我们来测试一下,先于7001建立连接:

# 进入容器
docker exec -it redis_cluster1 bash
# 进入redis-cli
redis-cli -c -p 7001
# 测试
set user jack

注意: 集群连接需要加 -c 参数

现在,我们添加一个新的key,这次加上{}:

# 试一下key中带{}
set user:{age} 21

# 再试一下key中不带{}
set age 20

可以看到结果:

192.168.44.100:7002> set user:{age} 21
-> Redirected to slot [741] located at 192.168.44.100:7001
OK
192.168.44.100:7001> get user
-> Redirected to slot [5474] located at 192.168.44.100:7002
"jack"
192.168.44.100:7002> set age 20
-> Redirected to slot [741] located at 192.168.44.100:7001
OK

5.故障转移

分片集群的节点之间会互相通过ping的方式做心跳检测,超时未回应的节点会被标记为下线状态。当发现master下线时,会将这个master的某个slave提升为master。

我们先打开一个控制台窗口,利用命令监测集群状态:

watch docker exec -it redis_cluster1 redis-cli -p 7001 cluster nodes

命令前面的watch可以每隔一段时间刷新执行结果,方便我们实时监控集群状态变化。
接着,我们故技重施,利用命令让某个master节点休眠。比如这里我们让7002节点休眠,打开一个新的ssh控制台,输入下面命令:

docker exec -it redis_cluster2 redis-cli -p 7002 DEBUG sleep 30

可以观察到,集群发现7002宕机,标记为下线,过了一段时间后,7002原本的从节点7005变成了master。

而7002被标记为slave,而且其master正好是7005,主从地位互换。

6.Java客户端连接分片集群

RedisTemplate底层同样基于lettuce实现了分片集群的支持,而使用的步骤与哨兵模式基本一致:
1)引入redis的starter依赖

2)配置分片集群地址

3)配置读写分离

与哨兵模式相比,其中只有分片集群的配置方式略有差异,如下:

spring:
  redis:
    cluster:
      nodes:
        - 192.168.44.100:7001
        - 192.168.44.100:7002
        - 192.168.44.100:7003
        - 192.168.44.100:7005
        - 192.168.44.100:7006
        - 192.168.44.100:7007

7. 小结

Redis分片集群如何判断某个key应该在哪个实例?

  • 将16384个插槽分配到不同的实例
  • 根据key计算哈希值,对16384取余
  • 余数作为插槽,寻找插槽所在实例即可

如何将同一类数据固定的保存在同一个Redis实例?

  • Redis计算key的插槽值时会判断key中是否包含{},如果有则基于{}内的字符计算插槽
  • 数据的key中可以加入{类型},例如key都以{typeId}为前缀,这样同类型数据计算的插槽一定相同
0

评论区