一、Redis集群(cluster)
1.1、定义:
由于数据量过大,单个master复制集难以承担,因此需要对多个复制集进行集群,每个集群只负责整个数据集中的一小部分,在多个redis节点间共享数据的程序集。
1.2、集群能干什么?
- 支持多个master,每个master可以有多个slave
- 集群自带sentinel的故障转移机制,内置高可用的支持,无需再去使用哨兵功能
- 客户端与redis节点连接时,不再需要连接集群中所有节点,只需要任意连接集群中的一个可用节点即可
- slot槽位负责分配到各个物理节点,由对应的集群来负责维护节点、插槽和数据之间的关系
1.3、槽位slot
集群并未使用一致性hash算法,而是使用了槽位slot的概念。
以哈希槽分区算法为例,集群有16384个槽位,每个key通过CRC16校验后对16384取模来决定映射到哪一个槽位上。
HASH_SLOT = CRC16(key) mod 16384
***注意:***虽然集群节点上限为16384个,但建议最大节点约1000个
1.4、redis分片
- 定义:使用redis集群时,会将数据分散到多个节点上,每个节点负责一部分数据的存储和查询。即每个节点上存储的数据是整个数据集的一部分。
- 如何找到分片:以哈希槽分区算法为例,HASH_SLOT = CRC16(key) mod 16384
1.5、使用槽位和分片的优势
- 方便扩容和缩容,且不会造成集群成不可用状态
- 方便数据分派查找
1.6、注意
- redis集群是不保证强一致性,即在特定情况下,redis集群会丢失一些被系统收到的写入命令。
二、集群算法
2.1、哈希取余分区
假设3台redis构成一个集群,用户每次读写都是根据公式hash(key)%3,来决定数据映射在哪台机器上。
- 优点:
-
- 简单粗暴,直接有效
-
- 缺点:
-
- 由于分母写死了,扩容/缩容困难,一旦节点数量发生变化,所有的映射规则都需要重新计算。例如,如果某台redis机器出故障,那么分母会进行变化,所有的映射规则都需要重新计算,此时有很大可能造成数据混乱。
-
2.2、一致性哈希分区
2.2.1、为了弥补哈希取余分区的缺点,引入一致性哈希分区。
当redis服务器数量发生变化时,尽量减少客户端到服务器的映射关系。
2.2.2、具体步骤
-
- 算法构建一致性哈希环
- 构成hash空间[0,2^32-1],将线性空间变为环形空间。
- 之前的hash取余分区,是将key通过hash函数映射到[0,232-1]区间内,然后对**节点数量**进行取模。而一致性哈希分区则是***对232取模***,然后将结果映射到环形空间上。
-
- 服务器IP节点映射
- 将集群中每个服务器的IP地址映射到环形空间上。
-
- key落到服务器的落键规则
- 将key通过hash函数映射到环形空间上,然后顺时针查找距离最近的服务器节点。
2.2.3、优点
-
- 具有容错性,当一台服务器宕机后,key映射到这台服务器的数据,会转移到顺时针方向的下一台服务器上。
-
- 具备扩展性,当新增一台服务器时,会将该台服务器与逆时针上一台服务器间的数据转移到该服务器上,不会导致hash取余全部重新洗牌。
2.2.4、缺点
-
- 数据倾斜问题,当服务器节点数量较少时,会导致部分服务器的负载过大,大部分缓存的数据全部集中在一台机器上-----头重脚轻
2.3、哈希槽分区算法
2.3.1、为了解决一致性哈希分区的数据倾斜问题,引入了哈希槽分区的概念。
- 为了解决数据倾斜问题,在数据与redis之间增加了一层映射关系,即哈希槽,用于管理数据与节点的关系。即节点上放的是槽,槽里面放的是数据。
- 哈希槽实质是一个数组[0, 2^14-1],即16384个槽位。
三、Redis集群搭建(采用哈希槽分区算法)
3.1、 三主三从集群配置(硬件够,可以多开,四主四从,五主五从)
-
- 在自己的redis目录下新建一个文件夹,命名为cluster
- 在自己的redis目录下新建一个文件夹,命名为cluster
-
- 根据上图配置,设置相关配置文件,注意IP地址
vim redisCluster6381.conf
daemonize yes
protected-mode no
port 6381
logfile "./cluster6381.log"
pidfile "./cluster6381.pid"
dir "./"
dbfilename dump6381.rdb
appendonly yes
appendfilename "appendonly6381.aof"
requirepass "123456"
masterauth "123456"
cluster-enabled yes
cluster-config-file nodes6381.conf
cluster-node-timeout 5000
其余6382,6383,6384,6385,6386.conf文件都一样,注意端口号和该机上的redis密码
至少是六个节点
本机硬件不够,就记录下相关配置操作
3.2、启动集群
-
- 启动6381,6382,6383,6384,6385,6386六个节点
#机器1
redis-server /path/redisCluster6381.conf
redis-server /path/redisCluster6382.conf
#机器2
redis-server /path/redisCluster6383.conf
redis-server /path/redisCluster6384.conf
#机器3
redis-server /path/redisCluster6385.conf
redis-server /path/redisCluster6386.conf
-
- 通过redis-cli为这6台机器构建集群关系
redis-cli -a 123456 --cluster create --cluster-replicas 1 192.168.127.132:6381 192.168.127.132:6382 192.168.127.138:6383 192.168.127.138:6384 192.168.127.142:6385 192.168.127.142:6386
–cluster-replicas 1 表示每个master都有一个slave
如果创建成功,不仅会显示集群信息,还会在每个节点的目录下生成一个nodes.conf文件
-
- 以6381为切入点,查看集群信息
redis-cli -a 123456 -p 6381
cluster nodes #查看集群信息
三主三从集群搭建成功,值得注意的是和理论上建立的集群可能对不上,比如6381为主,6382为从,但实际上却是6384为从机,以实际为准
3.3、集群读写测试
- 开启集群,要注意路由到位,即槽位的范围区间,否则会出现下面这个错误
k1要跑到6385的机器上,k2在本机可以
- 如何解决
退出,重连redis
redis-cli -a 123456 -p 6381 -c #-c 表示集群模式
- 查看某个key所属的槽位
cluster keyslot k1
3.4、主从容错切换迁移
- 以6381主机为例,down掉6381,看从机是否会上位
6381down掉,6384自动上位
- 6381王者归来,能否重新上位?
和哨兵模式一样,重新上线后,不会自动上位
3.5、节点从属调整
cluster failover #手动触发故障转移
3.6、集群扩容
-
- 新增一台机器,填写相关配置文件,如redisCluster6387,6388.conf文件
-
- 启动新增的节点,方法和上面启动集群一样
-
- 新机器加入原有集群
redis-cli -a 123456 --cluster add-node 192.168.127.144:6387 192.168.127.132:6381
注意最后面是6381,不是6388,代表6381是新机6387的领路人
-
- 槽位重新洗牌,重新分配
redis-cli -a 123456 --cluster reshard 192.168.127.132:6381
-
- 为6388分配从节点,即6387的从节点
redis-cli -a 123456 --cluster add-node 192.168.127.144:6388 192.168.127.144:6387 --cluster-slave --cluster-master-id <6387的节点ID>
3.7、集群缩容
-
- 将6387,6388下线
#先获取6388的节点ID
redis-cli -a 123456 --cluster check 192.168.127.144:6388
#下线6388,删除该节点
redis-cli -a 123456 --cluster del-node 192.168.127.144:6388 <节点ID>
#下线6387,先将数据归还
redis-cli -a 123456 --cluster reshard 192.168.127.132:6381
redis-cli -a 123456 --cluster del-node 192.168.127.144:6387 <节点ID>
四、总结
-
- 不在同一槽位上的数据,无法使用mset,mget等操作
- 不在同一槽位上的数据,无法使用mset,mget等操作
-
- 如果在集群模式下,要使用mset,mget等操作
mset k1{z} v1 k2{z} v2 k3{z} v3
mget k1{z} k2{z} k3{z}
-
- 配置文件中参数cluster-require-full-coverage yes,代表集群最小必须是三主三从才能对外提供服务,如果任意一个一主一从挂了,那么集群就不能对外提供服务了。
-
- 集群算法分为三种:哈希取余分区、一致性哈希分区、哈希槽分区
-
- 哈希取余分区,扩容缩容困难
-
- 一致性哈希分区,解决了扩容缩容困难的问题,但是存在数据倾斜问题
-
- 哈希槽分区,解决了数据倾斜问题,但对硬件存在一定的要求,至少三主三从。
五、问题
1. 哈希槽分区算法为什么要用16384个槽位?
- CRC16用2^16,应该是65536,为什么不用这么多,而是用了16384个槽位?
-
- 如果槽位数量达到65536,那么发送心跳信息的消息头达到8k(65536 / 8 / 1024),心跳包过于庞大,浪费带宽
-
- redis集群主节点数量不可能超过1000个,如果超过1000个,会造成网络拥塞。
-
- 槽位越少,节点越少的情况下,压缩比高,容易传输。
-