搭建redis 集群
- 创建一个网桥
docker network create -d bridge --subnet=192.168.148.0/24 --gateway=192.168.148.1 -o parent=eno1 redis-net
- 通过docker 文件创建redis 集群, 这里注意要不要使用redis 7以上的版本,不然会出问题
version: "3"
services:
redis7001:
image: redis:6.2.14 # 指定redis镜像,可以是name:tag/id
container_name: redis7001 # 启动后的镜像名称,可有可无
ports:
- "7001:7001" # 指定对外端口
- "17001:17001" # 指定集群端口,根据官网一般为对外端口+10000
volumes:
# 挂载主机中的配置文件
- /home/duron/docker/redis_cluster/redis.conf:/conf/redis.conf
# 将数据保存在主机上, 防止丢失
- /home/duron/docker/redis_cluster/7001:/data
command:
# 默认的redis启动命令
- "redis-server"
# 加载指定的配置文件,这里是镜像内的路径
- "/conf/redis.conf"
# 对外端口号,也可以在redistribution.conf中配置
- "--port 7001"
# 开启redis集群模式,也可以在redistribution.conf中配置
- "--cluster-enabled yes"
# 集群节点配置文件名,也可以在redistribution.conf中配置
- "--cluster-config-file nodes-7001.conf"
networks:
extnetwork:
ipv4_address: 192.168.148.71 # 向桥接网络申请ip地址
redis7002:
image: redis:6.2.14
container_name: redis7002
ports:
- "7002:7002"
- "17002:17002"
volumes:
- /home/duron/docker/redis_cluster/redis.conf:/conf/redis.conf
- /home/duron/docker/redis_cluster/7002:/data
command:
- "redis-server"
- "/conf/redis.conf"
- "--port 7002"
- "--cluster-enabled yes"
- "--cluster-config-file nodes-7002.conf"
networks:
extnetwork:
ipv4_address: 192.168.148.72
redis7003:
image: redis:6.2.14
container_name: redis7003
ports:
- "7003:7003"
- "17003:17003"
volumes:
- /home/duron/docker/redis_cluster/redis.conf:/conf/redis.conf
- /home/duron/docker/redis_cluster/7003:/data
command:
- "redis-server"
- "/conf/redis.conf"
- "--port 7003"
- "--cluster-enabled yes"
- "--cluster-config-file nodes-7003.conf"
networks:
extnetwork:
ipv4_address: 192.168.148.73
redis7004:
image: redis:6.2.14
container_name: redis7004
ports:
- "7004:7004"
- "17004:17004"
volumes:
- /home/duron/docker/redis_cluster/redis.conf:/conf/redis.conf
- /home/duron/docker/redis_cluster/7004:/data
command:
- "redis-server"
- "/conf/redis.conf"
- "--port 7004"
- "--cluster-enabled yes"
- "--cluster-config-file nodes-7004.conf"
networks:
extnetwork:
ipv4_address: 192.168.148.74
redis7005:
image: redis:6.2.14
container_name: redis7005
ports:
- "7005:7005"
- "17005:17005"
volumes:
- /home/duron/docker/redis_cluster/redis.conf:/conf/redis.conf
- /home/duron/docker/redis_cluster/7005:/data
command:
- "redis-server"
- "/conf/redis.conf"
- "--port 7005"
- "--cluster-enabled yes"
- "--cluster-config-file nodes-7005.conf"
networks:
extnetwork:
ipv4_address: 192.168.148.75
redis7006:
image: redis:6.2.14
container_name: redis7006
ports:
- "7006:7006"
- "17006:17006"
volumes:
- /home/duron/docker/redis_cluster/redis.conf:/conf/redis.conf
- /home/duron/docker/redis_cluster/7006:/data
command:
- "redis-server"
- "/conf/redis.conf"
- "--port 7006"
- "--cluster-enabled yes"
- "--cluster-config-file nodes-7006.conf"
networks:
extnetwork:
ipv4_address: 192.168.148.76
networks:
extnetwork: # 定义外部桥接网络
external:
name: redis-net
什么是redis cluster的hash slot
redis的hash槽在redis集群中的作用是用来将数据分配到不同的slot,来降低单个节点的压力,来保证redis的工作效率。
redis 是如何将key分配到不同的槽里的呢
redis 是同过CRC16 算法对数据进行hash取值然后在和16384 进行去模的到的slot的结果,我们在查询/存储key的时候会更具slot去获取数据或写入数据。
为什么要分配16384个
Redis 集群使用哈希槽(hash slot)来决定一个给定的键应该被分配到哪个节点。总共有 16384 个哈希槽,这个数字是一个折中的选择。
如果哈希槽的数量太少,例如只有 100 个,那么在大规模集群中,每个节点将需要处理大量的哈希槽,这可能会导致负载分布不均。另一方面,如果哈希槽的数量太多,例如 1 亿,那么管理这些哈希槽的开销将会非常大,尤其是在节点添加或删除时。
因此,16384 是一个在分布均匀性和管理开销之间取得平衡的数字。它足够大,可以在大规模集群中实现均匀的负载分布,同时又足够小,可以在节点变动时快速重新分配哈希槽。
为什么redis hash 槽要选择CRC16算法
-
计算速度快:CRC16 是一种非常简单的算法,计算速度非常快。这对于 Redis 这种需要处理大量键值对的数据库系统来说非常重要。
-
分布均匀:虽然 CRC16 是一种简单的算法,但它生成的哈希值在 0 到 16383 之间的分布是相当均匀的。这意味着键值对在 Redis 集群的各个节点之间的分布也会比较均匀。
redis 是如何获取slot的
1.) 在我们进行查询和写入操作的时候,代码在构建玩cmd的时候我们就进入了process函数,该函数的第一步就是先获取slot,红框中的代码就是获取到hash槽中的slot的值
2.)我们进入到cmdSlot 函数里面我们就会发现,我们马上要开始获取slot了,在进入cmdFirstKeyPos获取到pos值以后我们就可以进入slot函数获取hash slot的值了(keyPos 字段是用来帮助 go-redis 库正确地处理 Redis 集群的一个重要字段。)
3.)我们从这里就看到了hash slot的算法了,他正如我说的那样通过crc16算出hash值和16384 进行取模来获取我们需要通过那个slot来获取/写入数据
获取到slot后我们如何获取连接呢?
1.) 我们通过代码可以看到在获取到slot后,我们会进入一个cmdNode的一个函数里面,我们就是通过slot值在这个函数里面获取到node 节点的。
2.)我们进入cmdNode 可以看出来,这个函数给我们做了读写分离,因为我们没有开启读写分离,所以我们默认会进入slotMasterNode里面
3.) 我们进入这个函数可以看到,我们通过sloaNodes来获取slot 如果nodes获取失败 = 0的情况下 我们默认走了random模式 默认随机了
4. )该函数我们可以看出来,这个函数判断了我们的slot是否在这些nodes 里面,如果在并获取这个节点的节点信息。
注意:
以上代码是在"github.com/redis/go-redis/v9" v9版本的支持下进行的,如果我们使用的是v6版本,我们会发现在我们进行slot 获取的时候会报错,报错会存储到firstErr的一个字段里面,这时候系统监测到该字段有错误,就会默认进行 随机获取slot的一个操作。
redis: got 7 elements in COMMAND reply, wanted 6
所以我们如果想使用hash slot 就需要升级到高一些的版本。