https://cloud.tencent.com/developer/article/1986819
Redis 最为突出的特性就是:执行命令的速度非常快(原因是所有数据都存放在内存中)。但是单机 Redis 总会遇到瓶颈的,比如:并发、流量、内存等。在 Redis 3.0 之前,官方并没有提供集群方案,在访问量比较大的情况,基本使用的是 Twemproxy、Codis 等集群方案。直到 Redis 3.0,官方推出了 Redis Cluster,实现了切片集群方案。今天就来聊聊 Redis Cluster。
Redis Cluster 采用虚拟槽分区,一个集群有 16384 个虚拟槽,这些虚拟槽类似数据分区,每个键值对都会根据它的 key,按照 CRC16 算法计算一个 16bit 的值,然后再用这个 16bit 的值对 16384 取模,通过模映射到一个虚拟槽中。
Redis Cluster 使用虚拟槽,可以解耦数据和节点之间的关系,大大简化了节点扩缩容的难度,并且重要的是扩缩容不影响数据一致性。
安装 6 个 Redis 实例(本节的版本为:5.0.7,6 个实例在同一台 Centos7 上部署的,端口分配是:7001 到 7006),Redis 安装可以参考官方文档(https://redis.io/download)。
在 Redis 的配置文件中,加入如下参数,开启 Redis 集群功能。
cluster-enabled yes
cluster-config-file /data/redis{port}/data/nodes{port}.conf
cluster-node-timeout 5000
简单介绍上面三个参数的含义:
部署 Redis Cluster 方案时,可以使用 cluster create 命令创建集群,此时,Redis 会自动把这些槽平均分布在集群实例上。
当然,如果想自定义每个实例的槽分配,也可以使用 cluster meet 命令手动建立实例间的连接,形成集群,再使用 cluster addslots 命令,指定每个实例上的哈希槽个数(比如集群中配置不统一的场景)。要注意的是,在手动分配哈希槽时,需要把 16384 个槽都分配完,否则 Redis 集群无法正常工作。
本节内容就只讲使用 cluster create 命令创建集群的过程。
首先执行集群创建命令:
redis-cli --cluster create 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006 --cluster-replicas 1
这里的 --cluster-replicas 1 表示每个主节点都分配一个从节点,如果为 2,则表示每个主节点分配两个从节点。 生产环境也建议至少配置一个从节点。
如图,在下方输入 yes 确认
出现如下信息,则表示集群创建完毕
查看集群状态
redis-cli --cluster check 127.0.0.1:7001
可以看到,槽位分配和主从配置已经自动完成。
继续安装两个 Redis 节点 7007 和 7008,然后按照 2.2 的方式在配置文件中加入集群相关参数。
增加节点,命令如下:
redis-cli --cluster add-node 127.0.0.1:7007 127.0.0.1:7001
127.0.0.1:7007 表示新增节点 127.0.0.1:7001 表示集群中任意节点
查看集群状态
redis-cli --cluster check 127.0.0.1:7001
发现 127.0.0.1:7007 已经加入到集群中了
再添加从节点:
redis-cli --cluster add-node 127.0.0.1:7008 127.0.0.1:7001 --cluster-slave --cluster-master-id c9d5817021fe802c094806a79c3c52a37b8b15f5
查看集群状态
redis-cli --cluster check 127.0.0.1:7001
如图,框中的就是新增加的一组主从。
然后进行槽迁移
redis-cli --cluster reshard 127.0.0.1:7001
在最后一行,显示要分配多少槽
How many slots do you want to move (from 1 to 16384)?
因为扩容后是 4 个主实例,因此这个位置输入 4096,表示每个实例分配 4096 个槽
随后会出现
其中:
What is the receiving node ID?
这里输入新增节点 7007 的 node-id
Source node #1:
这里输入 all,表示所有节点平均分配
或者手动输入 node-id 分配,然后输入 done 结束
查看集群状态
redis-cli --cluster check 127.0.0.1:7001
发现会从每个老节点上抽取一些槽,转移到新的节点上。
如果需要进行缩容,首先要下线缩容节点的槽,执行以下命令,可以将 7007 节点上的 1364 个槽转移到 7001 节点上
redis-cli --cluster reshard --cluster-from c9d5817021fe802c094806a79c3c52a37b8b15f5 --cluster-to 99d09213eab4bd707e4923d6574182e23384b765 --cluster-slots 1364 127.0.0.1 7001
按照同样的方式,将其余的槽迁移到 7002 和 7003 两个节点。
查看集群信息,发现 7007 节点没任何槽了
然后删除节点
redis-cli --cluster del-node 127.0.0.1:7001 c9d5817021fe802c094806a79c3c52a37b8b15f5
该操作会将删除的节点服务关闭。
同样的办法,把从节点也删除
redis-cli --cluster del-node 127.0.0.1:7001 81d26724de7961a22a93cf64901a68b40c1f90cd
其实,Redis Cluster 在进行类似上面的扩缩容操作时,客户端依然可以进行数据改查,那么 Redis Cluster 是怎样实现这一动态扩缩容特性的呢?
这里就来聊聊 Redis Cluster 的迁移原理:
比如需要将 masterA 节点中编号为 1、2、3 的 slot 迁移到 masterB 节点上,在slot 迁移的中间状态,slot 1、2、3 在 masterA 节点的状态表现为 MIGRATING 状态,在 masterB 节点的状态表现为 IMPORTING。
如果 key 存在,则成功处理
如果 key 不存在,则返回客户端 ASK,客户端根据 ASK 首先发送 ASKING 命令到目标节点,然后发送请求的命令到目标节点。
假如 key 包含多个命令,如果都存在,则成功处理,如果都不存在,则返回客户端 ASK,如果一部分存在,则返回客户端 TRYAGAIN,通知客户端稍后重试,这样当所有的 key 都迁移完毕的时候,客户端重试请求的时候会得到 ASK,然后经过一次重定向就可以获取这一批键了。
正常命令会被 MOVED 重定向,如果是 ASKING 命令,则命令会被执行,从而 key 没在被迁移的节点,已经被迁移到目标节点的情况命令可以被顺利执行;如果 key 不存在,则新建;如果 key 不在该节点上,命令会被 MOVED 重定向,刷新客户端中 node 的映射关系。
为什么客户端在访问任何一个实例时,都能获得其他槽的数据呢?
因为 Redis 实例会把自己的虚拟槽信息发给同集群下的其它实例,当集群创建完成后,每个实例就有所有哈希槽的映射关系了。
集群中的每个节点都会定期的向集群中其他节点发送 ping 消息,以此交换各个节点状态信息。
这里再总结下 Redis cluster 的一些优势
Redis Cluster 与 Redis 单机版相比,存在一些限制,我们在运维或者开发过程,应该提前了解的,这里就总结几点限制:
尽量避免大 key、热 key,因为这可能导致某一个节点成为系统的短板。
不建议使用事务、避免使用阻塞操作(比如 save、flushall、keys * 等)