Redis 集群是 Redis 官方提供的分布式数据库解决方案,在 3.0 版本中被正式引入。它的主要目标是解决单点 Redis 实例在数据存储容量、高可用性和并发处理能力上的瓶颈。
一、为什么需要 Redis 集群?
想象一下,你只有一个 Redis 实例:
- 数据容量瓶颈:所有数据都存在这一台服务器的内存里。当数据量超过服务器物理内存时,就无法再存储新数据了。
- 性能瓶颈:所有读写请求都由这一台服务器处理。当并发请求数量巨大时,CPU、网络带宽和内存都会成为性能瓶颈,导致响应变慢。
- 可用性瓶颈:这是最致命的问题。如果这台服务器宕机或者网络中断,那么整个 Redis 服务就不可用了,所有依赖它的应用都会瘫痪。这就是“单点故障”(Single Point of Failure)。
Redis 集群就是为了解决以上三个核心问题而生的。
二、Redis 集群的核心概念
理解 Redis 集群,首先要掌握以下几个关键概念:
1. 数据分片
这是解决容量和性能瓶颈的核心思想。Redis 集群将整个数据集自动分割成 16384 个哈希槽。
- 什么是哈希槽? 你可以把它想象成一个虚拟的“格子”或“桶”,整个集群的数据都存放在这些格子里。集群固定有 16384 个槽(编号 0-16383)。
- 如何分配? 这些哈希槽会被平均分配到集群中的各个主节点上。
- 例如,一个有 3 个主节点的集群:
- 节点 A 可能包含槽 0 到 5460。
- 节点 B 可能包含槽 5461 到 10922。
- 节点 C 可能包含槽 10923 到 16383。
- 例如,一个有 3 个主节点的集群:
- 数据如何存储? 当你要存储一个键值对(如
SET mykey "hello"
)时,Redis 会执行以下步骤:- 计算键
mykey
的 CRC16 校验和。 - 将校验和对 16384 取模,得到一个数字
HASH_SLOT = CRC16("mykey") % 16384
。 - 根据计算出的
HASH_SLOT
,找到负责这个槽的节点。 - 将该键值对存储到对应的节点上。
- 计算键
好处:
- 水平扩展:当数据量增大时,只需向集群中添加新的主节点,并将一部分哈希槽从旧节点迁移到新节点,即可实现扩容。
- 负载均衡:读写请求被分散到不同的节点上,充分利用了多台服务器的计算能力。
2. 主从复制
这是解决高可用性的核心思想。Redis 集群采用主从复制模型,但它是基于分片的主从复制。
- 结构:集群中的每个主节点都可以有一个或多个从节点。
- 数据同步:从节点会实时复制其主节点上的所有数据和操作。数据是异步复制的。
- 故障转移:这是关键。当一个主节点宕机时:
- 集群中的其他节点会检测到该主节点失联。
- 该主节点的从节点之间会进行选举(基于 Raft 算法的共识机制)。
- 其中一个从节点会被选举为新的主节点。
- 新的主节点会接管原来主节点负责的所有哈希槽,并继续提供服务。
- 整个过程对客户端是自动、透明的,通常只需要几秒钟到几十秒。
好处:
- 高可用:即使某个主节点挂了,它的从节点能立即顶上,保证服务不中断。
- 数据冗余:数据在主从节点上都有备份,避免了单点数据丢失的风险。
3. 一个典型的 Redis 集群架构
一个最基本、高可用的 Redis 集群通常需要 6 个节点:3 个主节点 + 3 个从节点(每个主节点配 1 个从节点)。
+-------------+ +-------------+
| Master A | ----> | Slave A1 |
| (Slots 0-5460) | +-------------+
+-------------+
|
| (Gossip Protocol)
|
+-------------+ +-------------+
| Master B | ----> | Slave B1 |
| (Slots 5461-10922) | +-------------+
+-------------+
|
|
+-------------+ +-------------+
| Master C | ----> | Slave C1 |
| (Slots 10923-16383)| +-------------+
+-------------+
4. Gossip 协议
集群中的节点之间需要通信,以交换状态信息(如节点是否在线、负责哪些槽等)。Redis 集群使用 Gossip 协议来完成这件事。
- 工作方式:每个节点都会定期与其他几个随机节点交换信息。
- 传播内容:节点会把自己所知道的关于集群的信息(包括其他节点的状态)告诉对方。
- 效果:就像谣言一样,信息会迅速、可靠地传播到整个集群中的所有节点,最终所有节点都能达成一致的集群视图。这使得集群能够快速发现故障节点和新的节点。
三、Redis 集群的特性与限制
主要特性
- 自动分片:数据自动分割到多个节点。
- 高可用:主节点故障时,从节点能自动提升为主节点,继续服务。
- 在线扩容/缩容:可以动态地添加或删除节点,并重新分配哈希槽,不影响服务(或影响很小)。
- 部分可用性:即使集群中有一部分节点(例如一个主节点和它的所有从节点)发生故障,只要大部分主节点还在,集群仍然可以处理那些指向可用节点的请求。
重要限制(使用时必须注意)
-
不支持多数据库空间:单机 Redis 默认有 16 个数据库(db0-db15)。但在集群模式下,只能使用
db0
。SELECT
命令被禁用。这是因为集群需要明确知道每个键属于哪个哈希槽,而多数据库空间会使这个映射变得复杂。 -
涉及多个键的操作受限:由于一个键可能分布在不同的节点上,所以那些需要同时操作多个键的命令会受到限制。
- 不允许:跨槽位的
MGET
,MSET
,SUNION
等操作。 - 允许:如果所有键都在同一个哈希槽中,那么多键操作是允许的。如何保证?使用哈希标签。
- 哈希标签:如果一个键包含
{}
,例如user{1000}:profile
和user{1000}:orders
,Redis 只会计算{}
内部字符串(这里是1000
)的哈希槽。因此,这两个键会被存储在同一个节点上,可以安全地进行多键操作。
- 哈希标签:如果一个键包含
- 不允许:跨槽位的
-
事务是单节点的:
MULTI
/EXEC
事务中的所有命令都必须在同一个节点上执行(即所有键的哈希槽相同)。否则事务会失败。 -
Keys 命令的性能问题:
KEYS *
命令在集群中变得非常危险和低效。因为它需要在每个节点上执行,然后合并结果,可能会阻塞整个集群。在生产环境中,应该使用SCAN
命令代替。 -
客户端需要支持集群模式:普通的 Redis 客户端无法直接连接 Redis 集群。客户端必须实现“集群感知”(Cluster-aware)功能,能够:
- 连接到集群中的任意一个节点。
- 发送命令后,如果收到
-MOVED
或-ASK
重定向错误,能自动解析错误信息,找到正确的节点,并重新发送命令。 - 大多数现代编程语言的 Redis 客户端库(如 Jedis, Lettuce for Java; redis-py for Python)都提供了对集群的支持。
四、如何搭建和管理 Redis 集群?
1. 搭建方式
- 手动搭建(不推荐):通过配置文件和
redis-cli
命令手动创建节点、分配槽、建立主从关系。过程繁琐,容易出错。 - 使用
redis-trib.rb
(旧版):在 Redis 5.0 之前,官方推荐使用 Ruby 脚本redis-trib.rb
来创建和管理集群。 - 使用
redis-cli
(推荐):从 Redis 5.0 开始,集群管理功能被集成到了原生的redis-cli
中,不再依赖 Ruby。这是目前最标准、最简单的方式。
2. 使用 redis-cli
创建集群的简要步骤
假设你有 6 台机器(或一台机器的 6 个不同端口),已经启动了 6 个 Redis 实例。
# 假设 6 个节点的 IP 和端口如下
# 192.168.1.10:7000, 192.168.1.10:7001
# 192.168.1.11:7002, 192.168.1.11:7003
# 192.168.1.12:7004, 192.168.1.12:7005
# 1. 创建集群
# --cluster-replicas 1 表示为每个主节点创建 1 个从节点
# 命令会自动将前 3 个节点作为主节点,后 3 个作为它们的从节点
redis-cli --cluster create 192.168.1.10:7000 192.168.1.10:7001 192.168.1.11:7002 192.168.1.11:7003 192.168.1.12:7004 192.168.1.12:7005 --cluster-replicas 1
# 2. 检查集群状态
redis-cli --cluster check 192.168.1.10:7000
# 3. 查看集群信息
redis-cli -c -p 7000 cluster info
redis-cli -c -p 7000 cluster nodes
3. 扩容示例:添加一个新主节点
# 1. 启动一个新的 Redis 实例,端口为 7006
# 2. 将新节点添加到集群中
redis-cli --cluster add-node 192.168.1.13:7006 192.168.1.10:7000
# 3. 重新分配哈希槽
# 将一部分槽从旧节点迁移到新节点
redis-cli --cluster reshard 192.168.1.10:7000
# 然后根据提示输入要迁移的槽数量、接收槽的目标节点ID等
五、总结:单机、主从、哨兵、集群如何选择?
方案 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
单机 | 部署简单,性能高 | 存在单点故障,容量和性能有上限 | 开发、测试环境,或数据量极小、可用性要求不高的场景。 |
主从复制 | 读写分离,提高了读性能,数据有备份 | 故障转移需要手动干预,写操作依然是单点 | 读多写少,且对自动故障转移要求不高的场景。 |
哨兵 | 自动故障转移,系统高可用 | 写操作依然是单点,难以实现水平扩容 | 对高可用有要求,但数据量不大,写并发不高的场景。 |
集群 | 数据分片,解决了容量和性能瓶颈;高可用,自动故障转移 | 架构复杂,有功能限制(多键操作等),运维成本高 | 生产环境首选。数据量大、并发高、对可用性和扩展性有严格要求的场景。 |
简单来说:
- 如果你的数据量在单机内存范围内,且对高可用有要求,用 哨兵。
- 如果你的数据量超过了单机内存,或者并发量极高,那么 集群 是你唯一的选择。