关于 Redis 运维 系列其他文章的传送门
-
《 手把手教你理解 Redis Cluster 》 您当前所在位置
关于 Redis 开发 系列其他文章的传送门

一、Redis 主从复制架构面临的问题¶
当 Redis 是主从复制架构时,客户端需要知道在读、写时语句分别发往哪个节点,并且写语句要均衡的发往各个从节点。如果客户端无法做到这点,这时就要使用一个可以理解语句含义的代理,也就是“语句路由器”来帮助客户端完成请求工作,就像 ProxySQL一样。
以 MariaDB 举例,为了防止单点故障,前端的语句路由器就要使用 Keepalived 等工具实现高可用,这就增加了集群架构的复杂度。所以很多中小企业在使用 MariaDB 时,会部署为 Galera Cluster 的方式,使得每个节点都是主节点,都可读可写,然后前端只需要加一个简单的反向代理即可,例如 Nginx 或者 Proxy。不过很遗憾 Redis 不支持这种多主模型。
一些 Redis 可以使用的“语句路由器”¶
-
Twemproxy (Twitter):即能代理 Redis,也能代理 Memcached;
- 使用代理分片机制;
- 优点:
- 非常稳定,企业级方案;
- 缺点:
- 单点故障;
- 需依賴第三方软件,如 Keepalived;
- 无法平滑地橫向扩展;
- 沒有后台界面;
- 代理分片机制引入更多的来回次数并提高延迟;
- 单核模式,无法充份利用多核,除非多实例;
- Twitter 官方內部不再继续使用 Twemproxy;
-
Codis (豌豆荚):
- 代理分片机制;
- 2014年11月开源;
- 基于 Go 以及 C 语言开发;
- 优点:
- 非常穩定,企业级方案;
- 数据自动平衡;
- 高性能;
- 简单的测试显示较 Twemproxy 快一倍;
- 善用多核 CPU;
- 简单;
- 沒有 Paxos 类的协调机制;
- 沒有主从复制;
- 有后台界面;
- 缺点:
- 代理分片机制引入更多的来回次数并提高延迟;
- 需要第三方软件支持协调机制;目前支持 Zookeeper 及 Etcd;
- 不支持主从复制,需要另外实现;
- Codis 采用了 Proxy 的方案,所以必然会带来单机性能的损;
二、什么是 Redis Cluster?¶
Redis Cluster(官方)¶
为了解决主从复制的瓶颈,Redis 官方发布了一种分布式集群,填补了主从架构的不足,以下是 Redis Cluster 的一些特性:
- 官方实现;
- 需要 Redis 3.0 或更高版本;
- 优点:
- 无中心的 P2P Gossip 分散式模式;
- 更少的來回次数并降低延迟;
- 自动于多个 Redis 节点进行分片;
- 不需要第三方软件支持协调机制;
- 缺点:
- 依赖于 Redis 3.0 或更高版本;
- 需要时间验正其稳定性;
- 沒有后台界面;
- 需要智能客戶端;
- Redis 客戶端必须支持 Redis Cluster 架构;
- 较 Codis 有更多的维护升级成本;
有中心节点 和 无中心节点 的区别¶
首先要了解,一个完整的数据结构是由 2 部分组成,元数据和对应的数据本身。客户端在获取数据时,会先读取元数据,再根据元数据中保存的数据的实际存放位置,去读取数据本身。
在 Redis Cluster 分布式存储系统中,元数据就是一个 key,而数据本身是一个对应的 value,而 key 就保存在 Leader 节点上,当客户端访问数据时会先从 Leader 节点读取元数据,再根据元数据去对应的数据节点读取数据本身。
而 Leader 节点在分布式系统中有两种实现方式:
- 一种是有中心节点,即集群中只有一台 Leader,保存了整个集群的元数据;
- 另一种是无中心节点,即每个节点都是 Leader,都保存了整个集群的元数据,并且会相互同步。客户端可以向任意一个 Leader 节点请求
key;
Redis Cluster 就属于无中心节点的。
Redis Cluster 在生产环境的使用(关于智能客户端)¶
然而,在 Redis Cluster 中虽然每个 Leader 节点都保存着集群全部的元数据,有冗余效果,但是每个数据节点却只保存着一部分数据,如果宕机的话数据就会丢失。所以要为每个数据节点构建从节点,并且能在主节点宕机时自动把从节点提升为主节点,这就要引入 Sentinel 集群来监控主从的状态。
在这样的架构下,就要求客户端必须是一个智能客户端,能够从 Leader 节点获取到 key,然后自行去对应的数据节点读取数据,并且在集群状态发生变化后能自动找到新的节点。
Cerberus (芒果TV) 就是这样的一个客户端。
Cerberus (芒果TV) 的特性:
- 优点:
- 数据自动平衡;
- 本身实现了 Redis 的 Smart Client;
- 支持读写分离;
- 缺点:
- 依赖 Redis 3.0 或更高版本;
- 代理分片机制引入更多的來回次数并增大延迟;
- 需要时间验正其稳定性;
- 没有后台界面;
本文不展开讲解客户端,主要讲解 Redis Cluster 的原理和部署方法。
三、Redis Cluster 集群简介¶
Redis 面临和 MySQL 一样的问题:主从复制这种集群扩展方式,只能缓解对读请求压力的均衡,而对写请求没有任何均衡的作用。每一个节点都要写所有的数据,每一个节点都是整个集群数据集的一个副本。总之,主从这种方式能够降低读的压力,能够提供冗余的效果,但是并不能确保数据是分散的,不能对写操作带来任何压力的分散。
Redis Cluster 是一个可以在多个 Redis 节点之间进行数据共享的设施(installation)。
Redis 集群不支持那些需要同时处理多个键的 Redis 命令,因为执行这些命令需要在多个 Redis 节点之间移动数据,并且在高负载的情况下,这些命令将降低 Redis 集群的性能,并导致不可预测的行为。
Redis 集群通过分区(partition)来提供一定程度的可用性(availability):即使集群中有一部分节点失效或者无法进行通讯,集群也可以继续处理命令请求。
Redis 集群提供了以下两个好处:
- 将数据自动切分(split)到多个节点的能力。
- 当集群中的一部分节点失效或者无法进行通讯时,仍然可以继续处理命令请求的能力。
Redis Cluster 实现原理¶
Redis 集群是两级路由,使用了数据分片(sharding)而非一致性哈希(consistency hashing)来实现:一个 Redis 集群包含 16384 个哈希槽(hash slot),数据库中的每个键都属于这 16384 个哈希槽的其中一个,集群使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽,其中 CRC16(key) 语句用于计算键 key 的 CRC16 校验和。
集群中的每个节点负责处理一部分哈希槽。举个例子,一个集群可以有三个哈希槽,其中:
- 节点 A 负责处理
0号至5500号哈希槽。 - 节点 B 负责处理
5501号至11000号哈希槽。 - 节点 C 负责处理
11001号至16384号哈希槽。
Redis 集群 不保证数据的强一致性(strong consistency):在特定条件下,Redis 集群可能会丢失已经被执行过的写命令。

关于 Redis Cluster 的更多细节,建议读:
四、配置 Redis Cluster¶
首先,如果要构建集群,节点就不能是主从复制架构,需要删除配置文件中与主从复制相关的配置。
Redis Cluster 集群由多个运行在集群模式(cluster mode)下的 Redis 程序组成,而集群模式需要通过配置来开启,开启集群模式的 Redis 服务将可以使用集群特有的功能和命令。
官方提供了自动部署 Redis Cluster 的教程,为了更好的理解,这里采用手动创建的方式部署集群。
1. 设置配置文件,启用集群功能¶
以下是一个包含了最少选项的集群配置文件示例,在配置文件 /etc/redis.conf 中的 ##### REDIS CLUSTER ##### 配置段中启用:
port 7000:定义 Redis 监听的端口;cluster-enabled yes:用于开实例的集群模式;cluster-config-file nodes.conf:设定了保存节点配置文件的路径,节点配置文件无须人为修改,它由 Redis 集群在启动时创建,并在有需要时自动进行更新,配置时避免名称冲突,默认保存在/var/lib/redis下;cluster-node-timeout 5000:集群节点互联超时的阈值,单位毫秒;cluster-slave-validity-factor<factor>:每个从节点都要检查最后与主节点断线时间,判断是否有资格替换故障的主节点。如果从节点与主节点断线时间超过cluster-node-time * cluster-slave-validity-factor,则当前从节点不具备故障转移资格。
PS:要让集群正常运作至少需要三个主节点,不过在生产环境中使用集群功能时,强烈建议使用六个节点:其中三个为主节点,而其余三个则是各个主节点的从节点。
启动 Redis 服务:
1 | systemctl start redis |
查看监听的端口:
1 | ss -tnl |
每个 Redis Cluster node 需要打开两个 TCP 端口。一个 TCP 端口为 clients 提供服务,例如这个端口为
6379,那么该端口增加10000就是 Cluster 的 node 之间数据端口,例如16379。端口
16379是 Redis Cluster 的 node-to-node 使用二进制协议通信的端口。
因此,Redis Cluster 每个 node 都必须打开两个 TCP 端口,否则整个 Redis Cluster 是无法正常工作的。
使用客户端连入,输入 CLUSTER INFO 可以查看集群状态:
1 | -> |
2. 为每个节点分配哈希槽(hash slot)¶
使用 CLUSTER ADDSLOTS slot [slot ...] 为当前节点添加 slot,需要一个一个指明,比如要添加 5000 个,就需要写成 CLUSTER ADDSLOTS 1 2 3 ... 5498 5499 5500,太恐怖了。
好在 Redis 的客户端工具支持不进入交互模式而直接执行命令,所以可以在 shell 下以命令行展开的方式添加 slot,如下:
1 | ~] # redis-cli -h 192.168.50.11 -a monster -c CLUSTER ADDSLOTS {0..5500} |
3. 让节点之间互相发现¶
现在集群都配置好了,但各自都是独立在运行,最后一步在任意节点上使用 CLUSTER MEET ip port 来和其他的所有节点“认识”一下,集群就可以正常工作了。
这里使用 192.168.50.11 去“认识” 12 和 13 就行了,12、13 之间会间接认识。
1 | 127.0.0.1:6379> CLUSTER MEET 192.168.50.12 6379 |
之后在所有的节点上看到的都应该是如下状态:
1 | ~] # redis-cli -h 192.168.50.13 -a monster CLUSTER INFO | grep -E 'cluster_state|cluster_slots_assigned' |
验证¶
在 redis-cli 客户端内插入键值:
1 | 127.0.0.1:6379> SET NAME tom |
可以看到,在插入 NAME tom 时是正常的,但是当插入 ID 1 时,却提示说这个键经过 hash 计算并取模后应该落在第 16148 号哈希槽上,但是这个槽不在本机,而是在 192.168.50.13 这个节点。
说明了每个节点都可以帮你路由到你要的数据放在哪,但可能本节点并不是存放这个数据的地方。
此时应该去 192.168.50.13 再次插入 ID 1 就会成功。
这一系列操作需要一个智能客户端来自动实现,而 redis-cli 是不支持的。