当前位置: 首页 > news >正文

《Redis》集群

文章目录

  • 集群概念
  • 哈希槽分区算法
  • 基于Docker搭建集群环境
    • 搭建集群关系
  • 故障处理机制
  • 集群扩容


集群概念

在这里插入图片描述

哈希槽分区算法

hash_slot = crc16(key) % 16384
16384 其实是 16 * 1024, 也就是 2^14.

相当于是把整个哈希值, 映射到 16384 个槽位上, 也就是 [0, 16383].
然后再把这些槽位⽐较均匀的分配给每个分⽚. 每个分⽚的节点都需要记录⾃⼰持有哪些分⽚

假设当前有三个分⽚, ⼀种可能的分配⽅式:
• 0 号分⽚: [0, 5461], 共 5462 个槽位
• 1 号分⽚: [5462, 10923], 共 5462 个槽位
• 2 号分⽚: [10924, 16383], 共 5460 个槽位

这里的分片规则是很灵活的,每个分片持有的槽位也不一定是连续的。
每个分片的节点使用位图来表示自己持有哪些槽位,对于16384个槽位来说,需要2048字节(2KB)大小的空间来表示。

如果需要扩容,比如新增3号分片,就可以针对原来的槽位进行重新分配,比如把之前分片持有的槽位,各自拿出一点来分给3号分片。
一种可能的分配方式:
• 0 号分⽚: [0, 4095], 共 4096 个槽位
• 1 号分⽚: [5462, 9557], 共 4096 个槽位
• 2 号分⽚: [10924, 15019], 共 4096 个槽位
• 3 号分⽚: [4096, 5461] + [9558, 10923] + [15019, 16383], 共 4096 个槽位

我们在实际使⽤ Redis 集群分⽚的时候, 不需要⼿动指定哪些槽位分配给某个分⽚, 只需要告
诉某个分⽚应该持有多少个槽位即可, Redis 会⾃动完成后续的槽位分配, 以及对应的 key 搬
运的⼯作


问题1::Redis 集群是最多有 16384 个分⽚吗?

并⾮如此. 如果⼀个分⽚只有⼀个槽位, 这对于集群的数据均匀其实是难以保证的.
实际上 Redis 的作者建议集群分⽚数不应该超过 1000.
分片数量越多,系统越复杂,出故障的概率也越高。

问题2:为什么是 16384 个槽位?

节点之间通过⼼跳包通信. ⼼跳包中包含了该节点持有哪些槽位, 这个是使⽤位图这样的数据结构
表⽰的. 表⽰ 16384 (16k) 个 槽位, 需要的位图⼤⼩是 2KB. 如果给定的槽位数更多了, ⽐如 65536
个了, 此时就需要消耗更多的空间, 8 KB 位图表⽰了. 8 KB, 对于内存来说不算什么, 但是在频繁的⽹
络⼼跳包中, 还是⼀个不⼩的开销的。
• 另⼀⽅⾯, Redis 集群⼀般不建议超过 1000 个分⽚. 所以 16k 对于最⼤ 1000 个分⽚来说是⾜够⽤
的, 同时也会使对应的槽位配置位图体积不⾄于很⼤。


所以,哈希槽分片算法就解决了搬运成本⾼ 和 数据分配不均匀的问题。
这两个问题分别是哈希求余和一致性哈希算法无法解决的问题。

基于Docker搭建集群环境

在这里插入图片描述
要搭建的集群如上,由于机器有限,这里就使用docker来模拟多台机器实现redis集群的搭建。

创建目录:

mkdir -p redis-cluster/
cd redis-cluster
touch docker-compose.yml generate.sh
后面这个文件是一个shell脚本文件,为了同时执行多条命令,同时还能加入像条件,循环,函数等机制,这样这个shell脚本就像一门编程语言一样了。因为要批量生成11个redis节点(9个是集群,2个是扩容用的,使用脚本会很快)

注意!在此之间一定要关闭之前开启的redis服务器。
进入对应的redis目录,如redis-data/ ,redis-sentinel/

执行:docker-compose down
因为我这些redis的数据节点,redis的哨兵节点都是在一个个docker容器中启动的,所以要用docker-compose命令来停止。
docker ps -a,就能看到没有正在运行的容器了。

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

执行bash generate.sh命令后,就生成了多个配置文件

接下来创建redis容器
在docker-compose.yml文件中内容如下

version: '3.7'
networks:mynet:ipam:config:- subnet: 172.30.0.0/24
services:redis1:image: 'redis:5.0.9'container_name: redis1restart: alwaysvolumes:- ./redis1/:/etc/redis/ports:- 6371:6379- 16371:16379command:redis-server /etc/redis/redis.confnetworks:mynet:ipv4_address: 172.30.0.101redis2:image: 'redis:5.0.9'container_name: redis2restart: alwaysvolumes:- ./redis2/:/etc/redis/ports:- 6372:6379- 16372:16379command:redis-server /etc/redis/redis.confnetworks:mynet:ipv4_address: 172.30.0.102redis3:image: 'redis:5.0.9'container_name: redis3restart: alwaysvolumes:- ./redis3/:/etc/redis/ports:- 6373:6379- 16373:16379command:redis-server /etc/redis/redis.confnetworks:mynet:ipv4_address: 172.30.0.103redis4:image: 'redis:5.0.9'container_name: redis4restart: alwaysvolumes:- ./redis4/:/etc/redis/ports:- 6374:6379- 16374:16379command:redis-server /etc/redis/redis.confnetworks:mynet:ipv4_address: 172.30.0.104redis5:image: 'redis:5.0.9'container_name: redis5restart: alwaysvolumes:- ./redis5/:/etc/redis/ports:- 6375:6379- 16375:16379command:redis-server /etc/redis/redis.confnetworks:mynet:ipv4_address: 172.30.0.105redis6:image: 'redis:5.0.9'container_name: redis6restart: alwaysvolumes:- ./redis6/:/etc/redis/ports:- 6376:6379- 16376:16379command:redis-server /etc/redis/redis.confnetworks:mynet:ipv4_address: 172.30.0.106redis7:image: 'redis:5.0.9'container_name: redis7restart: alwaysvolumes:- ./redis7/:/etc/redis/ports:- 6377:6379- 16377:16379command:redis-server /etc/redis/redis.confnetworks:mynet:ipv4_address: 172.30.0.107redis8:image: 'redis:5.0.9'container_name: redis8restart: alwaysvolumes:- ./redis8/:/etc/redis/ports:- 6378:6379- 16378:16379command:redis-server /etc/redis/redis.confnetworks:mynet:ipv4_address: 172.30.0.108redis9:image: 'redis:5.0.9'container_name: redis9restart: alwaysvolumes:- ./redis9/:/etc/redis/ports:- 6379:6379- 16379:16379command:redis-server /etc/redis/redis.confnetworks:mynet:ipv4_address: 172.30.0.109redis10:image: 'redis:5.0.9'container_name: redis10restart: alwaysvolumes:- ./redis10/:/etc/redis/ports:- 6380:6379- 16380:16379command:redis-server /etc/redis/redis.confnetworks:mynet:ipv4_address: 172.30.0.110redis11:image: 'redis:5.0.9'container_name: redis11restart: alwaysvolumes:- ./redis11/:/etc/redis/ports:- 6381:6379- 16381:16379command:redis-server /etc/redis/redis.confnetworks:mynet:ipv4_address: 172.30.0.111

随后执行命令:
docker-compose up -a
记住,生成新的容器前,一定要看看之前的redis容器是否关闭了,否则可能造成端口冲突!

搭建集群关系

接下来搭建集群关系:

此处是把前 9 个主机构建成集群, 3 主 6 从. 后 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

  • –cluster create 表⽰建⽴集群. 后⾯填写每个节点的 ip 和端口.
    –cluster-replicas 2 表⽰每个主节点需要两个从节点备份

这里就是为了建立三主六从的集群结构。

但是这里,谁是主节点,谁是谁的从节点,不一定的。谁和谁是一组也不确定的。
因为节点之间没有优劣之分。

然后输入yes,才会真正构建集群。


从101-109这九个节点,此时是一个整体,使用客户端操作任意一个节点,本质上都是等价的。

redis-cli -c -h 172.30.0.101 -p 6379 # 使用静态IP连接redis1容器的redis服务器。
redis-cli -c -p 6371 # 直接连接映射到容器外的端口6372(也就是宿主机的端口)来操作redis1容器。
上面两者都是一样的。
-c选项是,如果连接了该容器的redis服务器,并且在里面新增数据(set key1 11)
可能会出现这个key1的哈希映射槽不属于我当前的redis1服务器,导致新增数据失败。
-c选项就是为了就算我新增数据不在当前槽,这个数据也会自动通过上面讲的哈希槽分片算法,自动映射到正确的槽上,也就是直接新增进持有key1对应的哈希槽的redis服务器中了。

使用redis-cli客户端连接上某一个服务器节点后,
使用

cluster nodes命令,即可查看整个集群的节点信息。

接下来就可以使用集群存储数据了


故障处理机制

docker stop redis1
模拟主节点redis1故障
docker start redis1后,发现它不再是主节点了。

可以执行cluster failover命令进行集群恢复,也可以主动登录到redis1服务器,执行slaveof no one命令, 直接把自己升级成主节点。

集群机制,也能处理故障处理。(不需要哨兵的情况下)
但是这里集群的故障处理和哨兵的故障处理流程不太一样:

1.先进行故障判定:
在这里插入图片描述

2.进行故障处理
在这里插入图片描述

集群扩容

集群的扩容,是一件成本高,风险大的工作。

第⼀步: 把新的主节点加⼊到集群
上⾯已经把 redis1 - redis9 重新构成了集群. 接下来把 redis10 和 redis11 也加⼊集群.

redis-cli --cluster add-node 172.30.0.110:6379 172.30.0.101:6379

第一个IP+端口表示要新增的节点,第二个IP+端口表示集群中的任意一个节点(哪个都行,这里我挑了第一个),表示要将新增的节点加入第二个节点所在的集群中。

第二步:重新分配哈希槽(slots)

redis-cli --cluster reshard 172.30.0.101:6379
resard后的地址是集群中的任意节点地址.
另外, 注意单词拼写, 是 reshard (重新切分), 不是 reshared (重新分享) , 不要多写个 e.

执⾏之后, 会进⼊交互式操作, redis 会提⽰⽤⼾输⼊以下内容:
• 多少个 slots 要进⾏ reshard ? (此处我们填写 4096,其实也就是本来是一个主节点占1/3的哈希槽的,现在有4个主节点了,那就每个主节点占有1/4哈希槽吧,16384/4 那就是4096了)
• 哪个节点来接收这些 slots ? (此处我们填写 172.30.0.110 这个节点的集群节点对应的id,进入任意一个节点,然后执行cluster nodes命令查看)
• 这些 slots 从哪些节点搬运过来? (此处我们填写 all, 表⽰从其他所有的节点都进⾏搬运)

之后会询问你上面的搬运方案是否合适,然后输入yes。

之后就会进⾏集群的 key 搬运⼯作. 这个过程涉及到数据搬运. 可能需要消耗⼀定的时间。
📌 在搬运 key 的过程中, 对于那些不需要搬运的 key, 访问的时候是没有任何问题的. 但是对于需
要搬运的 key, 进⾏访问可能会出现短暂的访问错误 (key 的位置出现了变化)
随着搬运完成, 这样的错误⾃然就恢复了

不过,搬运的过程不仅仅是对key的重新划分,还有对数据的重新搬运,是比较重量的操作。

第三步: 给新的主节点添加从节点
光有主节点了, 此时扩容的⽬标已经初步达成. 但是为了保证集群可⽤性, 还需要给这个新的主节点添加从节点, 保证该主节点宕机之后, 有从节点能够顶上.

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]
执⾏完毕后, 从节点就已经被添加完成了.

http://www.dtcms.com/a/267837.html

相关文章:

  • 【Note】《Kafka: The Definitive Guide》 第二章 Installing Kafka:Kafka 安装与运行
  • Redis--主从复制详解
  • 【Docker基础】Docker容器挂载方式深度解析:--volume与--mount参数对比
  • QT6 源(155)模型视图架构里的列表视图 QListView:接着学习成员函数,信号函数,附上本类的源代码带注释。
  • HCIA-网络地址转换(NAT)
  • CppCon 2018 学习:Woes of Scope Guards and Unique_Resource
  • 抖音小游戏(IAA)巨量引擎投放指南
  • [shadPS4] 内存管理 | 权限管理 |文件系统 | 挂载和句柄
  • 【BTC】数据结构
  • 7,TCP服务器
  • JavaScript基础语法之运算符和控制流
  • 李宏毅NLP-8-语音模型
  • 【管理学】组织纪律性与创新性的失衡导致的问题
  • Redis事务机制
  • [论文阅读]VGGFace2: A dataset for recognising faces across pose and age
  • Linux-磁盘管理
  • 【前端工程化】前端工作中的业务规范有哪些
  • 基于评估方法论评估一个大模型的准确度
  • 文心开源大模型ERNIE-4.5-0.3B-Paddle私有化部署保姆级教程及技术架构探索
  • Java面试宝典:网络编程
  • 基于Pandas和FineBI的昆明职位数据分析与可视化实现(五) - 基于随机森林算法预测职位分类
  • 【星闪】Hi2821 | Pinctrl、GPIO + LED灯和按键输入例程
  • 字符函数和字符串函数(下)- 暴力匹配算法
  • python pip 下载慢
  • 在 Dokploy 中为 PostgreSQL 搭建 PgBouncer 数据库连接池(图文)
  • 【influxdb3】如何使用 SQL 对时间序列数据进行聚合查询
  • Golang读取ZIP压缩包并显示Gin静态html网站
  • 51c大模型~合集150
  • 大型语言模型中的自动化思维链提示
  • unity校招岗面试题 天津某场 深圳某场