Redis Cluster 学习笔记
编者注:本文为历史博文归档;涉及 JDK、框架与工具链版本请以当前官方文档为准。引用外链图片可能失效,阅读时请注意时效性。
简介
- Redis Cluster 是 Redis 提供的分布式数据库方案,通过在多个 Redis 节点之间进行分片(Sharding)来实现数据共享,并能在部分节点故障的情况下继续运行。
- Redis 集群的基本存储单位是槽(Slot)。一个集群共有 $2^{14}=16384$ 个槽。每一个槽的 Key 存放在集群中唯一的 Master 节点中,每一个槽还可以拥有 0~N 个 Slave 节点。
集群简介
Redis 集群的每个节点都与其他节点保持连接,需要占用两个端口:
- 数据端口(例如 7000):用来为客户端提供服务以及同其他服务端交换数据。
- 总线端口(数据端口号 + 10000,其中偏移量 10000 是固定的):用来在节点之间传送控制信息。
要让 Redis 集群系统正常运行,需要满足以下网络可见性要求:
- 每个节点的数据端口对 Client 以及 其他集群节点 可见。
- 每个节点的总线端口对 其他节点 可见。
数据分片(Sharding)
- Redis 集群有 16384 个哈希槽(Hash Slot),散列算法是简单的取 Key 的
CRC16模 16384。 - 集群中的每一个节点负责哈希槽的一个子集。
- Slot 可以动态地迁移、删除和分配。
Slot 的设计使得集群中能动态地添加和删除节点,例如:
- 扩容:当添加新的节点 NodeD 时,只需要从节点 NodeA、NodeB、NodeC 中移动一些 Slot 到 NodeD 即可。
- 缩容:当需要删除节点 NodeA 时,将 NodeA 中的 Slot 移动到 NodeB 和 NodeC 中。当 NodeA 为空之后,可以从集群中删除 NodeA。
Redis Cluster 支持多键(
MGET、MSET等)操作,但前提是单次命令执行(Command Execution)或者整个事务(Transaction)中的 Key 属于同一个哈希槽。我们可以使用 Hash Tag 来强制多个 Key 使用同一个哈希槽。- Hash Tag 的简单语法是:只有 Key 中
{}中的部分才被用来做 Hash。 - 例如:Key
{user1000}.following和{user1000}.followers会被 Hash 到同一个哈希槽中去,因为{}中的内容相同。
- Hash Tag 的简单语法是:只有 Key 中
主从模式
- Redis Cluster 采用主从模式,其中每一个槽有 1(主节点本身)~ N(N-1 个从节点)。
- 故障转移:当主节点故障之后,系统会从该主节点的从节点中选举出一个从节点作为新的主节点,接管故障主节点负责处理的槽。当故障的节点恢复后,自动变为从节点。
- 不可用情况:当一个哈希槽的所有节点(主节点和从节点)都故障之后,系统不能正常运行。
配置参数
在 Redis 集群配置文件中,需要修改的最小配置项包括:
port: 端口cluster-enabled: 开启 Cluster 模式cluster-config-file: 集群配置文件(节点配置文件)cluster-node-timeout: 节点超时时间 (ms)
集群搭建
Redis 的
src目录下提供了create-cluster脚本来创建简单的 Demo:create-cluster startcreate-cluster create// 命令 1 和 2 开启集群create-cluster stop// 停止集群
手动创建 Redis Cluster
创建目录结构:
mkdir cluster-test cd cluster-test mkdir 7000 7001 7002 7003 7004 7005- 在每一个文件夹中创建一个
redis.conf,替换其中的端口号为 7000~7005,修改 Cluster 相关配置。 创建启动脚本
start_cluster.sh:#!/bin/bash cd 7000/ nohup ./redis-server redis.conf & cd ../7001 nohup ./redis-server redis.conf & cd ../7002 nohup ./redis-server redis.conf & cd ../7003 nohup ./redis-server redis.conf & cd ../7004 nohup ./redis-server redis.conf & cd ../7005 nohup ./redis-server redis.conf & cd ../ ps -aux | grep redis关闭集群(重启集群需要 kill 掉所有进程):
ps -aux | grep redis-server | grep -v grep | cut -c 9-15 | xargs kill -9创建集群(使用
redis-trib.rb工具):./redis-trib.rb create --replicas 1 127.0.0.1:7000 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
集群管理
redis-trib.rb是 Redis 作者用 Ruby 实现的 Redis Cluster 管理工具。用此工具,我们能轻松地完成节点的添加、删除、Slot 的管理等。常用命令如下:create host1:port1 ... hostN:portN // 创建集群 check host:port // 检查集群 info host:port // 查看集群 fix host:port // 修复集群 reshard host:port // 迁移 slot rebalance host:port // 平衡节点 slot 数量 add-node new_host:new_port existing_host:existing_port // 添加节点 del-node host:port node_id // 删除节点 set-timeout host:port milliseconds // 设置节点心跳超时时间此外也能通过
CLUSTER相关命令来管理集群,使用这些命令需要登录节点:// 集群 (cluster) CLUSTER INFO // 打印集群的信息 CLUSTER NODES // 列出集群当前已知的所有节点(node),以及这些节点的相关信息 // 节点 (node) CLUSTER MEET <ip> <port> // 将 ip 和 port 所指定的节点添加到集群当中,让它成为集群的一份子 CLUSTER FORGET <node_id> // 从集群中移除 node_id 指定的节点 CLUSTER REPLICATE <node_id> // 将当前节点设置为 node_id 指定的节点的从节点 CLUSTER SAVECONFIG // 将节点的配置文件保存到硬盘里面 // 槽 (slot) CLUSTER ADDSLOTS <slot> [slot ...] // 将一个或多个槽(slot)指派(assign)给当前节点 CLUSTER DELSLOTS <slot> [slot ...] // 移除一个或多个槽对当前节点的指派 CLUSTER FLUSHSLOTS // 移除指派给当前节点的所有槽,让当前节点变成一个没有指派任何槽的节点 CLUSTER SETSLOT <slot> NODE <node_id> // 将槽 slot 指派给 node_id 指定的节点,如果槽已经指派给另一个节点,那么先让另一个节点删除该槽,然后再进行指派 CLUSTER SETSLOT <slot> MIGRATING <node_id> // 将本节点的槽 slot 迁移到 node_id 指定的节点中 CLUSTER SETSLOT <slot> IMPORTING <node_id> // 从 node_id 指定的节点中导入槽 slot 到本节点 CLUSTER SETSLOT <slot> STABLE // 取消对槽 slot 的导入(import)或者迁移(migrate) // 键 (key) CLUSTER KEYSLOT <key> // 计算键 key 应该被放置在哪个槽上 CLUSTER COUNTKEYSINSLOT <slot> // 返回槽 slot 目前包含的键值对数量 CLUSTER GETKEYSINSLOT <slot> <count> // 返回 count 个 slot 槽中的键
客户端使用原理
Redis Cluster 需要客户端能够解析 Cluster 协议,主要包括:
MOVE和ASK命令的重定向,连接超时的处理。- 槽、节点缓存的维护,连接的管理等。
向 Redis 集群发送的任何含 Key 命令(如
GET,SET,LLEN,MGET,MSET,RENAME,RPOPLPUSH等)时,先计算 Key 的槽位编号,将指令发送给对应槽位的 Master 节点。- 如果 Key 存在,返回结果。
- 如果指令发送到了错误的节点,该节点并不会处理请求,而是会返回重定向(
MOVED,ASK)错误信息。
客户端内部结构示例(Go 语言示意):
const kClusterSlots = 16384 type Cluster struct { slots [kClusterSlots]*redisNode // 槽对应的节点信息 nodes map[string]*redisNode // ... }重定向错误示例:
MOVED 16384 127.0.0.1:7001 ASK 16384 127.0.0.1:7001- MOVED:代表槽 i 的负责权已经从节点 A 转移到节点 B。当客户端收到了槽的
MOVED错误之后,应该将本地缓存的节点和槽的对应信息也更新。即下次遇到槽 i 的命令请求,直接向 Slot-B 发送命令。 - ASK:代表槽 i 正在从节点 A 转移到节点 B。所以当客户端收到了
ASK之后,只是这次命令向节点 B 请求,而且必须先发送ASKING命令。接下来槽 i 的命令仍然向节点 A 请求。(注:原文误写为 ACK,此处已修正为 ASK) - 超时:超时后随机向新的节点更新槽的信息。
使用示例
- 当搭建好 Redis Cluster 后,使用 Redis Cluster 就变得很简单。当前有多种 Client Libraries 实现,以
redis-go-cluster为例: 安装
redis-go-cluster:go get github.com/chasex/redis-go-cluster使用示例:
import "github.com/chasex/redis-go-cluster" cluster, err := redis.NewCluster( &redis.Options{ StartNodes: []string{"127.0.0.1:7000", "127.0.0.1:7001", "127.0.0.1:7002"}, ConnTimeout: 50 * time.Millisecond, ReadTimeout: 50 * time.Millisecond, WriteTimeout: 50 * time.Millisecond, KeepAlive: 16, AliveTime: 60 * time.Second, }) _, err := cluster.Do("SET", key, value) value, err := redis.Int(cluster.Do("GET", key)) // ...
参考文档
- https://redis.io/topics/cluster-tutorial 最好的文档永远是官方的文档
- http://blog.csdn.net/dc_726/article/details/48552531 全面剖析 Redis Cluster 原理和应用
- https://github.com/chasex/redis-go-cluster redis-go-cluster,使用 redigo
- http://weizijun.cn/2016/01/08/redis%20cluster%E7%AE%A1%E7%90%86%E5%B7%A5%E5%85%B7redis-trib-rb%E8%AF%A6%E8%A7%A3/ redis-trib.rb 的介绍
- http://blog.51yip.com/nosql/1726.html redis cluster 节点管理
说明
版本时效性:本文内容基于较早期的 Redis Cluster 版本整理。
- 管理工具:
redis-trib.rb已在 Redis 5.0 后被移除,官方推荐使用内置的redis-cli --cluster命令替代。 - 客户端库:文中提到的
redis-go-cluster库可能已不再维护,生产环境建议使用官方推荐或社区活跃更新的 Redis 客户端库。 - 配置与命令:具体配置参数与命令行为请以当前使用的 Redis 版本官方文档为准。
- 管理工具:
版权声明:本文为原创文章,版权归 戴老师的博客 所有,转载请联系博主获得授权。
本文地址:https://1diff.fun/archives/redis-cluster-xue-xi-bi-ji.html
如果对本文有什么问题或疑问都可以在评论区留言,我看到后会尽量解答。

