1.集群的基本介绍
- 集群 这个词.
- 广义的集群,只要你是多个机器,构成了分布式系统, 都可以称为是一个"集群"前面主从结构,哨兵模式,也可以称为是"广义的集群”
- 狭义的集群,redis 提供的集群模式, 这个集群模式之下,主要是要解决,存储空间不足的问题(拓展存储空间)
哨兵模式提高了系统的可用性
哨兵模式中,本质上还是 redis 主/从节点(内存,256G,512G.上 TB 的服务器也不是没有(贵))存储数据,其中就要求一个主节点/从节点,就得存储整个数据的"全集
- 此处关键问题,就是引入多台机器,每台机器存储一部分数据,
- 设 有 1TB 的数据需要存储.
- 拿两台机器来存,每个机器只需要存 512GB 即可.
- 拿三台机器来存,每个机器只需要存 300 多 GB 即可,
- 拿四台机,器来存,每个机器只需要存 256GB.....
- 随着机器数目的增加,每个机器存储的数据量就减少了
- 【只要机器的规模足够多,就可以存储任意大小的数据了,(公司的实力)】
- 【不是说光搞3个机器就够了每个存储数据的机器还需要搭配若于个从节点】
2.如何把数据分成多分?
- 把数据分成多份,应该怎么分?
- 是一个 分片(sharding).
- 三种主流的分片方式,
-
1.哈希求余
- 借鉴了哈希表的基本思想.
借助 hash 函数,把一个 key 映射到整数,再针对数组的长度
求余,就可以得到一个数组下标. - 比如有三个分片,编号 012
此时就可以针对要插入的数据的 key(redis 都是 键值对 结构的数据)计算 hash 值.(比如,使用 md5)【md5 本身就是一个计算 hash 值的算法针对一个字符串,里面的内容进行一系列的数学变换 =>整数.】再把这个 hash 值余上分片个数,就得到了一个下标,此时就可以把这个数据放到该下标对应的分片中了. - 【缺陷】
- 一旦服务器集群需要扩容,就需要更高的成本了
分片主要目的就是为了能提高存储能力.
分片越多,能存的数据越多,成本也更高, - 一般都是先少搞几个分片.(3 个)
但是随着业务逐渐增长,数据变多了~~3 个分片就已经不足以保存了 - 就需要"扩容”
引入新的分片,N 就改变了
hash(key)% N >=0 - 当 hash 函数和 key 都不变的情况下,如果 N 改变了整体的分片结果仍然会变!!
- 如果发现某个数据,在扩容之后,不应该待在当前的分片中了,就需要重新进行分配 (搬运数据)
- 此处列出的这些值,就可以脑补成 hash(key)假设当前计算完 hash 值之后,得到的 数值正好是 100-120
- 共 20 个数据,只有 3 个数据不需要搬运~~ 搬运了 17 个数据如果是 20 亿 个数据呢?? 17 亿数据就要搬运...这就是一个大活了!!
- 上述级别的扩容,开销极大的,往往是不能直接在生产环境上操作的,只能通过"替换"的方式来实现扩容。
- 依赖的机器更多了.成本更高,操作步骤非常复杂!!!
-
2.一致性哈希算法
- 会导致数据不均匀的问题
-
此时, 只需要把 0 号分⽚上的部分数据, 搬运给 3 号分⽚即可. 1 号分⽚和 2 号分⽚管理的区间都是不变的.
-
优点: ⼤⼤降低了扩容时数据搬运的规模, 提⾼了扩容操作的效率.
-
缺点: 数据分配不均匀 (有的多有的少, 数据倾斜).
-
3.哈希槽分区算法
- 【问题1】
- Redis 集群是最多有 16384 个分片吗?
- 每个分片上就只有一个槽位.(此时很难保证数据在各个分片上的均衡性~~)(有的槽位可能是 有多个 key,有的槽位可能是 没有 key ~)
- key 是要先映射到槽位,再映射到分片的
- 如果每个分片包含的槽位比较多,如果槽位个数相当, 就可以认为是包含的 key 数量相当的:
- 如果每个分片包含的槽位非常少,槽位个数不一定能直观的反应到 key 的数目
- 实际上 Redis 的作者建议集群分片数不应该超过(1000.
- 如果真是1.6w 个分片,整个数据服务器的集群规模就太可怕了
- 几万台主机构成的集群了.整个集群的可用性是非常堪忧的!!
- 【问题2】
- 问题二: 为什么是 16384 个槽位?
- 心跳包中包含了该节点持有哪些 slots.
- 需要表示出该节点持有哪些槽位
- 虽然 8kb 比2kb 也大不了多少, 但是心跳包,周期性通信的.(非常频繁 & 网络带宽
- 这个值个数上基本够用了.同时占用的硬件资源(网络带宽)又不是很大
3.搭建集群环境
3.1 生成配置文件
- 基于 docker 在咱们云服务器上搭建出一个 redis 集群出来.
- 当前阶段,主要是因为咱们只有一个 云服务器,搞分布式系统, 就比较麻烦
- 实际工作中,一般是通过多个主机的方式,来搭建集群的,
第⼀步: 创建⽬录和配置
一定要记得,把之前启动 redis 容器,给停止掉!!
- 在 linux 上,以 .sh 后缀结尾的文件,称为"shell(脚本)【剧本,也可以称为脚本,】
- 使用 linux 的时候, 都是通过一些命令来进行操作的.
- 使用命令操作,就非常适合把命令给写到一个文件中,批量化执行,
- 同时,还能加入, 条件,循环,函数 等机制
- 因此,就可以基于这些来完成更复杂的工作了
- 需要创建 11 个 redis 节点.这些 redis 的配置文件内容,大同小异.
- 此时就可以使用脚本来批量生成.(也可以不使用脚本,手动一个一个改)
-
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
- 成功!!!
3.2 创建容器
编写 docker-compose.yml
version: '3.3' 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: - ./redis2/:/etc/redis/ ports: - 6372:6379 - 16372:16379 command: redis-server /etc/redis/redis.conf networks: mynet: ipv4_address: 172.30.0.102 redis3: image: 'redis:5.0.9' container_name: redis3 restart: always volumes: - ./redis3/:/etc/redis/ ports: - 6373:6379 - 16373:16379 command: redis-server /etc/redis/redis.conf networks: mynet: ipv4_address: 172.30.0.103 redis4: image: 'redis:5.0.9' container_name: redis4 restart: always volumes: - ./redis4/:/etc/redis/ ports: - 6374:6379 - 16374:16379 command: redis-server /etc/redis/redis.conf networks: mynet: ipv4_address: 172.30.0.104 redis5: image: 'redis:5.0.9' container_name: redis5 restart: always volumes: - ./redis5/:/etc/redis/ ports: - 6375:6379 - 16375:16379 command: redis-server /etc/redis/redis.conf networks: mynet: ipv4_address: 172.30.0.105 redis6: image: 'redis:5.0.9' container_name: redis6 restart: always volumes: - ./redis6/:/etc/redis/ ports: - 6376:6379 - 16376:16379 command: redis-server /etc/redis/redis.conf networks: mynet: ipv4_address: 172.30.0.106 redis7: image: 'redis:5.0.9' container_name: redis7 restart: always volumes: - ./redis7/:/etc/redis/ ports: - 6377:6379 - 16377:16379 command: redis-server /etc/redis/redis.conf networks: mynet: ipv4_address: 172.30.0.107 redis8: image: 'redis:5.0.9' container_name: redis8 restart: always volumes: - ./redis8/:/etc/redis/ ports: - 6378:6379 - 16378:16379 command: redis-server /etc/redis/redis.conf networks: mynet: ipv4_address: 172.30.0.108 redis9: image: 'redis:5.0.9' container_name: redis9 restart: always volumes: - ./redis9/:/etc/redis/ ports: - 6379:6379 - 16379:16379 command: redis-server /etc/redis/redis.conf networks: mynet: ipv4_address: 172.30.0.109 redis10: image: 'redis:5.0.9' container_name: redis10 restart: always volumes: - ./redis10/:/etc/redis/ ports: - 6380:6379 - 16380:16379 command: redis-server /etc/redis/redis.conf networks: mynet: ipv4_address: 172.30.0.110 redis11: image: 'redis:5.0.9' container_name: redis11 restart: always volumes: - ./redis11/:/etc/redis/ ports: - 6381:6379 - 16381:16379 command: redis-server /etc/redis/redis.conf networks: mynet: ipv4_address: 172.30.0.111
3.3 配置集群关系
1)生成每个 redis 节点的配置文件
2)使用 docker 创建出 11 个 redis 节点, 并且启动容器
3)使用 redis-cli 执行构建集群命令
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
3.4 使用集群
- redis-cli -h 172.30.0.101 -p 6379
-
4.故障处理
5.集群扩容
101-1099个主机,构成了3主,6 从结构的集群了
110 和 111 也加入到集群中
以110为 master, 111为 slave.=>把数据分片从 3->4
集群扩容操作,是一件风险较高,成本较大的操作!!!
1.新的主节点(110) 加入到集群中
redis-cli --cluster add-node 172.30.0.110:6379 172.30.0.101:6379
2.重新分配 slots
redis-cli --cluster reshard 172.30.0.101:6379
3.给新的主节点添加从节点
redis-cli --cluster add-node 172.30.0.111:6379 172.30.0.101:6379 --cluster-slave --cluster-master-id c999d6e82415680b57868cc3c59f9f1a779a59cd
6.集群的缩容
把一些节点拿掉,减少分片的数量,
般都是进行扩容,很少缩容!
7.小结
1)集群是什么,解决了什么问题?
2)数据分片算法[重点/考点]
a)哈希求余
b)一致性哈希算法
c)哈希槽分区算法(redis)
3)搭建 redis 集群,
4)集群容灾.故障转移
5)集群扩容.
6)代码连接集群
使用的库, 得能够支持 集群模式.