Redis Cluster 中文教程
1. 核心概念
Redis Cluster 是 Redis 官方提供的分布式解决方案,用于构建高可用、高扩展性的 Redis 集群。它通过数据分片和自动故障转移机制,提供了比单机 Redis 更高的可用性和扩展性。
1.1 主要特点
- 数据分片:将数据自动分布到多个节点上,每个节点负责一部分数据
- 自动故障转移:当主节点发生故障时,自动将从节点提升为新的主节点
- 高可用性:即使部分节点故障,集群仍然可以正常工作
- 水平扩展:可以通过添加节点来扩展集群容量和性能
- 无中心架构:所有节点平等,没有中心节点
1.2 架构组成
- 节点:Redis Cluster 中的每个 Redis 实例称为一个节点
- 槽位:Redis Cluster 将数据分为 16384 个槽位,每个节点负责一部分槽位
- 主从复制:每个主节点可以有多个从节点,用于提供高可用性
- Gossip 协议:节点之间通过 Gossip 协议交换信息,保持集群状态同步
- 集群总线:节点之间通过专门的集群总线(端口为 Redis 端口 + 10000)进行通信
1.3 数据分布
Redis Cluster 使用哈希槽(Hash Slot)来分布数据:
- 将键通过 CRC16 算法计算哈希值
- 对哈希值取模 16384,得到槽位号
- 根据槽位号确定数据存储在哪个节点
2. 安装与配置
2.1 安装 Redis
Redis Cluster 是 Redis 3.0+ 版本的内置功能,安装 Redis 后即可使用。
# Ubuntu/Debian 系统
apt update
apt install redis-server
# CentOS/RHEL 系统
yum install epel-release
yum install redis
# 从源码编译安装
wget https://download.redis.io/redis-stable.tar.gz
tar -xzf redis-stable.tar.gz
cd redis-stable
make
make install2.2 配置 Redis Cluster
2.2.1 准备配置文件
创建 6 个 Redis 实例的配置文件(3 主 3 从):
redis-7000.conf:
# 端口
port 7000
# 绑定地址
bind 0.0.0.0
# 保护模式
protected-mode no
# 后台运行
daemonize yes
# 日志文件
logfile "redis-7000.log"
# 数据目录
dir "./data/7000"
# 启用集群模式
cluster-enabled yes
# 集群配置文件
cluster-config-file nodes-7000.conf
# 集群节点超时时间(毫秒)
cluster-node-timeout 15000
# 集群复制因子
cluster-replica-factor 1
# 密码(可选)
requirepass your_strong_password
# 从节点连接主节点的密码
masterauth your_strong_passwordredis-7001.conf:
# 端口
port 7001
# 绑定地址
bind 0.0.0.0
# 保护模式
protected-mode no
# 后台运行
daemonize yes
# 日志文件
logfile "redis-7001.log"
# 数据目录
dir "./data/7001"
# 启用集群模式
cluster-enabled yes
# 集群配置文件
cluster-config-file nodes-7001.conf
# 集群节点超时时间(毫秒)
cluster-node-timeout 15000
# 集群复制因子
cluster-replica-factor 1
# 密码(可选)
requirepass your_strong_password
# 从节点连接主节点的密码
masterauth your_strong_passwordredis-7002.conf:
# 端口
port 7002
# 绑定地址
bind 0.0.0.0
# 保护模式
protected-mode no
# 后台运行
daemonize yes
# 日志文件
logfile "redis-7002.log"
# 数据目录
dir "./data/7002"
# 启用集群模式
cluster-enabled yes
# 集群配置文件
cluster-config-file nodes-7002.conf
# 集群节点超时时间(毫秒)
cluster-node-timeout 15000
# 集群复制因子
cluster-replica-factor 1
# 密码(可选)
requirepass your_strong_password
# 从节点连接主节点的密码
masterauth your_strong_passwordredis-7003.conf:
# 端口
port 7003
# 绑定地址
bind 0.0.0.0
# 保护模式
protected-mode no
# 后台运行
daemonize yes
# 日志文件
logfile "redis-7003.log"
# 数据目录
dir "./data/7003"
# 启用集群模式
cluster-enabled yes
# 集群配置文件
cluster-config-file nodes-7003.conf
# 集群节点超时时间(毫秒)
cluster-node-timeout 15000
# 集群复制因子
cluster-replica-factor 1
# 密码(可选)
requirepass your_strong_password
# 从节点连接主节点的密码
masterauth your_strong_passwordredis-7004.conf:
# 端口
port 7004
# 绑定地址
bind 0.0.0.0
# 保护模式
protected-mode no
# 后台运行
daemonize yes
# 日志文件
logfile "redis-7004.log"
# 数据目录
dir "./data/7004"
# 启用集群模式
cluster-enabled yes
# 集群配置文件
cluster-config-file nodes-7004.conf
# 集群节点超时时间(毫秒)
cluster-node-timeout 15000
# 集群复制因子
cluster-replica-factor 1
# 密码(可选)
requirepass your_strong_password
# 从节点连接主节点的密码
masterauth your_strong_passwordredis-7005.conf:
# 端口
port 7005
# 绑定地址
bind 0.0.0.0
# 保护模式
protected-mode no
# 后台运行
daemonize yes
# 日志文件
logfile "redis-7005.log"
# 数据目录
dir "./data/7005"
# 启用集群模式
cluster-enabled yes
# 集群配置文件
cluster-config-file nodes-7005.conf
# 集群节点超时时间(毫秒)
cluster-node-timeout 15000
# 集群复制因子
cluster-replica-factor 1
# 密码(可选)
requirepass your_strong_password
# 从节点连接主节点的密码
masterauth your_strong_password2.2.2 创建数据目录
mkdir -p data/7000 data/7001 data/7002 data/7003 data/7004 data/70052.2.3 启动 Redis 实例
redis-server redis-7000.conf
redis-server redis-7001.conf
redis-server redis-7002.conf
redis-server redis-7003.conf
redis-server redis-7004.conf
redis-server redis-7005.conf2.2.4 创建集群
使用 redis-cli --cluster create 命令创建集群:
redis-cli --cluster create \
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 \
--cluster-replicas 1 \
-a your_strong_password--cluster-replicas 1:表示每个主节点有 1 个从节点-a your_strong_password:指定 Redis 密码
执行命令后,会提示输入 yes 确认集群配置:
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0-5460
Master[1] -> Slots 5461-10922
Master[2] -> Slots 10923-16383
Adding replica 127.0.0.1:7003 to 127.0.0.1:7000
Adding replica 127.0.0.1:7004 to 127.0.0.1:7001
Adding replica 127.0.0.1:7005 to 127.0.0.1:7002
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 8b3e6a7e5b9a4c8a1c5a5b6a7b8a9b0a1b2a3b4a 127.0.0.1:7000
slots:[0-5460] (5461 slots) master
M: 8b3e6a7e5b9a4c8a1c5a5b6a7b8a9b0a1b2a3b4b 127.0.0.1:7001
slots:[5461-10922] (5462 slots) master
M: 8b3e6a7e5b9a4c8a1c5a5b6a7b8a9b0a1b2a3b4c 127.0.0.1:7002
slots:[10923-16383] (5461 slots) master
S: 8b3e6a7e5b9a4c8a1c5a5b6a7b8a9b0a1b2a3b4d 127.0.0.1:7003
replicates 8b3e6a7e5b9a4c8a1c5a5b6a7b8a9b0a1b2a3b4a
S: 8b3e6a7e5b9a4c8a1c5a5b6a7b8a9b0a1b2a3b4e 127.0.0.1:7004
replicates 8b3e6a7e5b9a4c8a1c5a5b6a7b8a9b0a1b2a3b4b
S: 8b3e6a7e5b9a4c8a1c5a5b6a7b8a9b0a1b2a3b4f 127.0.0.1:7005
replicates 8b3e6a7e5b9a4c8a1c5a5b6a7b8a9b0a1b2a3b4c
Can I set the above configuration? (type 'yes' to accept): yes输入 yes 后,集群会自动完成配置并启动:
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
...
>>> Performing Cluster Check (using node 127.0.0.1:7000)
M: 8b3e6a7e5b9a4c8a1c5a5b6a7b8a9b0a1b2a3b4a 127.0.0.1:7000
slots:[0-5460] (5461 slots) master
1 additional replica(s)
M: 8b3e6a7e5b9a4c8a1c5a5b6a7b8a9b0a1b2a3b4b 127.0.0.1:7001
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
M: 8b3e6a7e5b9a4c8a1c5a5b6a7b8a9b0a1b2a3b4c 127.0.0.1:7002
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: 8b3e6a7e5b9a4c8a1c5a5b6a7b8a9b0a1b2a3b4d 127.0.0.1:7003
slots: (0 slots) slave
replicates 8b3e6a7e5b9a4c8a1c5a5b6a7b8a9b0a1b2a3b4a
S: 8b3e6a7e5b9a4c8a1c5a5b6a7b8a9b0a1b2a3b4e 127.0.0.1:7004
slots: (0 slots) slave
replicates 8b3e6a7e5b9a4c8a1c5a5b6a7b8a9b0a1b2a3b4b
S: 8b3e6a7e5b9a4c8a1c5a5b6a7b8a9b0a1b2a3b4f 127.0.0.1:7005
slots: (0 slots) slave
replicates 8b3e6a7e5b9a4c8a1c5a5b6a7b8a9b0a1b2a3b4c
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.3. 基本使用
3.1 连接集群
使用 redis-cli 连接 Redis Cluster:
# 连接到集群中的任意节点
redis-cli -c -p 7000 -a your_strong_password
# -c 参数表示以集群模式连接3.2 查看集群信息
# 连接到集群
redis-cli -c -p 7000 -a your_strong_password
# 查看集群信息
127.0.0.1:7000> CLUSTER INFO
# 查看集群节点
127.0.0.1:7000> CLUSTER NODES
# 查看槽位分配
127.0.0.1:7000> CLUSTER SLOTS3.3 数据操作
在 Redis Cluster 中执行数据操作与单机 Redis 基本相同,但需要注意以下几点:
- 键会自动路由到对应的节点
- 不支持跨槽位的命令(如 MSET 多个键)
- 可以使用
HASHSLOT命令查看键对应的槽位
# 连接到集群
redis-cli -c -p 7000 -a your_strong_password
# 设置键值对
127.0.0.1:7000> SET foo bar
-> Redirected to slot [12182] located at 127.0.0.1:7002
OK
# 获取键值
127.0.0.1:7002> GET foo
"bar"
# 查看键对应的槽位
127.0.0.1:7002> CLUSTER KEYSLOT foo
(integer) 12182
# 使用哈希标签(Hash Tag)将多个键映射到同一个槽位
127.0.0.1:7002> SET {user:1}:name "张三"
OK
127.0.0.1:7002> SET {user:1}:age 25
OK
127.0.0.1:7002> MGET {user:1}:name {user:1}:age
1) "张三"
2) "25"3.4 客户端连接
示例代码(Node.js):
const redis = require('redis');
// 创建 Redis 客户端,连接到集群
const redisClient = redis.createClient({
url: 'redis://:your_strong_password@127.0.0.1:7000',
socket: {
reconnectStrategy: (retries) => Math.min(retries * 100, 3000)
}
});
// 监听错误事件
redisClient.on('error', (err) => {
console.error('Redis error:', err);
});
// 监听连接事件
redisClient.on('connect', () => {
console.log('Connected to Redis Cluster');
});
// 连接到集群
async function connectRedis() {
try {
await redisClient.connect();
console.log('Successfully connected to Redis Cluster');
} catch (err) {
console.error('Failed to connect to Redis:', err);
}
}
// 使用示例
async function testCluster() {
await connectRedis();
// 设置键值对
await redisClient.set('user:1', JSON.stringify({ id: 1, name: '张三', email: 'zhangsan@example.com' }));
// 获取键值
const user = await redisClient.get('user:1');
console.log('User:', JSON.parse(user));
// 关闭连接
await redisClient.disconnect();
}
testCluster();4. 高级功能
4.1 添加节点
4.1.1 添加主节点
准备新节点配置:
# 创建配置文件 redis-7006.conf echo "port 7006 bind 0.0.0.0 protected-mode no daemonize yes logfile \"redis-7006.log\" dir \"../data/7006\" cluster-enabled yes cluster-config-file nodes-7006.conf cluster-node-timeout 15000 requirepass your_strong_password masterauth your_strong_password" > redis-7006.conf创建数据目录:
mkdir -p data/7006启动新节点:
redis-server redis-7006.conf将新节点加入集群:
redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000 -a your_strong_password重新分配槽位:
redis-cli --cluster reshard 127.0.0.1:7000 -a your_strong_password执行后会提示输入:
- 需要移动的槽位数
- 接收槽位的节点 ID
- 从哪些节点获取槽位(输入
all表示从所有主节点获取)
4.1.2 添加从节点
准备新节点配置:
# 创建配置文件 redis-7007.conf echo "port 7007 bind 0.0.0.0 protected-mode no daemonize yes logfile \"redis-7007.log\" dir \"../data/7007\" cluster-enabled yes cluster-config-file nodes-7007.conf cluster-node-timeout 15000 requirepass your_strong_password masterauth your_strong_password" > redis-7007.conf创建数据目录:
mkdir -p data/7007启动新节点:
redis-server redis-7007.conf将新节点作为从节点加入集群:
redis-cli --cluster add-node 127.0.0.1:7007 127.0.0.1:7000 --cluster-slave --cluster-master-id <master-node-id> -a your_strong_password其中
<master-node-id>是要复制的主节点 ID。
4.2 移除节点
4.2.1 移除从节点
redis-cli --cluster del-node 127.0.0.1:7000 <node-id> -a your_strong_password4.2.2 移除主节点
先将主节点的槽位迁移到其他节点:
redis-cli --cluster reshard 127.0.0.1:7000 -a your_strong_password然后移除节点:
redis-cli --cluster del-node 127.0.0.1:7000 <node-id> -a your_strong_password
4.3 手动故障转移
# 连接到要提升为新主节点的从节点
redis-cli -c -p 7003 -a your_strong_password
# 执行手动故障转移
127.0.0.1:7003> CLUSTER FAILOVER
OK4.4 集群备份与恢复
4.4.1 备份
Redis Cluster 的备份需要在每个节点上单独进行:
# 连接到每个节点执行备份
redis-cli -c -p 7000 -a your_strong_password BGSAVE
redis-cli -c -p 7001 -a your_strong_password BGSAVE
redis-cli -c -p 7002 -a your_strong_password BGSAVE
# ... 其他节点4.4.2 恢复
停止所有节点:
redis-cli -c -p 7000 -a your_strong_password SHUTDOWN redis-cli -c -p 7001 -a your_strong_password SHUTDOWN # ... 其他节点复制备份文件到对应的数据目录:
cp /path/to/backup/dump.rdb data/7000/ cp /path/to/backup/dump.rdb data/7001/ # ... 其他节点启动所有节点:
redis-server redis-7000.conf redis-server redis-7001.conf # ... 其他节点
5. 最佳实践
5.1 部署建议
- 节点数量:建议至少部署 3 个主节点,每个主节点至少有 1 个从节点
- 硬件配置:根据数据量和访问量选择合适的硬件配置,主节点和从节点配置应相同
- 网络配置:确保节点之间网络畅通,网络延迟应尽可能低
- 防火墙:确保开放必要的端口(Redis 端口和集群总线端口)
- 部署位置:将节点部署在不同的物理机器或虚拟机上,避免单点故障
5.2 性能优化
- 内存配置:根据数据量合理设置
maxmemory参数 - 持久化配置:根据业务需求选择合适的持久化方式(RDB、AOF 或两者结合)
- 复制配置:合理设置
repl-backlog-size参数,加速从节点重连后的同步 - 连接管理:使用连接池管理客户端连接,避免频繁创建和销毁连接
- 批量操作:使用
MSET、MGET等批量命令减少网络往返时间 - 使用哈希标签:对于需要一起操作的多个键,使用哈希标签将它们映射到同一个槽位
5.3 安全配置
- 设置密码:为所有节点设置强密码
- 限制访问:通过
bind参数限制只接受特定 IP 的连接 - 使用 TLS:在生产环境中启用 TLS 加密
- 定期更新:及时更新 Redis 版本,修复安全漏洞
- 监控权限:限制
CONFIG等敏感命令的访问权限
5.4 监控与维护
- 监控指标:监控节点状态、内存使用、CPU 使用率、网络流量等指标
- 日志分析:定期分析 Redis 日志,及时发现问题
- 定期检查:使用
redis-cli --cluster check命令定期检查集群状态 - 备份策略:制定合理的备份策略,确保数据安全
- 容量规划:根据数据增长趋势,提前规划集群容量
6. 故障排查
6.1 常见问题
- 集群无法启动:检查配置文件是否正确,端口是否被占用
- 节点无法加入集群:检查网络连接,确保所有节点的密码一致
- 槽位分配不均:使用
redis-cli --cluster rebalance命令重新平衡槽位分配 - 故障转移失败:检查从节点是否正常,网络延迟是否过高
- 数据不一致:检查主从复制状态,确保从节点已同步主节点数据
- 内存不足:增加节点内存或使用
maxmemory策略
6.2 排查工具
- redis-cli --cluster:提供了多种集群管理命令
- CLUSTER 命令:在 Redis 客户端中执行的集群管理命令
- Redis 日志:包含详细的集群操作和错误信息
- 监控工具:如 Redis Exporter + Prometheus + Grafana
6.3 示例排查过程
问题:集群中某个主节点故障,无法自动故障转移
排查步骤:
检查节点状态:
redis-cli --cluster check 127.0.0.1:7000 -a your_strong_password查看日志:
tail -f data/7000/redis-7000.log检查从节点状态:
redis-cli -c -p 7003 -a your_strong_password INFO replication手动触发故障转移:
redis-cli -c -p 7003 -a your_strong_password CLUSTER FAILOVER检查网络连接:
ping 127.0.0.1 telnet 127.0.0.1 7000
7. 实用案例
7.1 构建高可用 Redis Cluster
场景:构建一个高可用的 Redis Cluster,用于存储用户会话数据。
解决方案:
部署架构:
- 3 个主节点,每个主节点 1 个从节点
- 节点分布在 3 台不同的物理机器上
- 每台机器运行 2 个 Redis 实例(1 主 1 从)
配置步骤:
- 在每台机器上创建 Redis 配置文件
- 启动所有 Redis 实例
- 使用
redis-cli --cluster create创建集群 - 配置客户端连接
客户端配置:
const redis = require('redis'); // 创建 Redis 客户端 const redisClient = redis.createClient({ url: 'redis://:your_strong_password@127.0.0.1:7000', socket: { reconnectStrategy: (retries) => Math.min(retries * 100, 3000) } }); // 监听错误事件 redisClient.on('error', (err) => { console.error('Redis error:', err); }); // 连接到 Redis async function connectRedis() { try { await redisClient.connect(); console.log('Successfully connected to Redis Cluster'); } catch (err) { console.error('Failed to connect to Redis:', err); } } // 存储会话数据 async function storeSession(sessionId, sessionData, expiration = 3600) { try { await redisClient.set(`session:${sessionId}`, JSON.stringify(sessionData), { EX: expiration }); return true; } catch (err) { console.error('Error storing session:', err); return false; } } // 获取会话数据 async function getSession(sessionId) { try { const sessionData = await redisClient.get(`session:${sessionId}`); return sessionData ? JSON.parse(sessionData) : null; } catch (err) { console.error('Error getting session:', err); return null; } } // 导出函数 module.exports = { connectRedis, storeSession, getSession };使用示例:
const sessionService = require('./session-service'); // 启动时连接 Redis sessionService.connectRedis(); // 存储会话 await sessionService.storeSession('sess_123', { userId: 1, username: 'zhangsan', lastActive: Date.now() }); // 获取会话 const session = await sessionService.getSession('sess_123'); console.log('Session data:', session);
7.2 使用 Redis Cluster 作为缓存
场景:使用 Redis Cluster 作为应用的分布式缓存,提高系统性能。
解决方案:
部署架构:
- 3 个主节点,每个主节点 1 个从节点
- 节点分布在不同的可用区
缓存策略:
- 热点数据缓存:将频繁访问的数据缓存到 Redis Cluster
- 缓存过期策略:使用
EX参数设置合理的过期时间 - 缓存预热:系统启动时预加载热点数据
- 缓存穿透:使用布隆过滤器防止缓存穿透
实现代码:
const redis = require('redis'); // 创建 Redis 客户端 const redisClient = redis.createClient({ url: 'redis://:your_strong_password@127.0.0.1:7000', socket: { reconnectStrategy: (retries) => Math.min(retries * 100, 3000) } }); // 连接到 Redis async function connectRedis() { try { await redisClient.connect(); console.log('Successfully connected to Redis Cluster'); } catch (err) { console.error('Failed to connect to Redis:', err); } } // 设置缓存 async function setCache(key, value, expiration = 3600) { try { await redisClient.set(key, JSON.stringify(value), { EX: expiration }); return true; } catch (err) { console.error('Error setting cache:', err); return false; } } // 获取缓存 async function getCache(key) { try { const value = await redisClient.get(key); return value ? JSON.parse(value) : null; } catch (err) { console.error('Error getting cache:', err); return null; } } // 删除缓存 async function deleteCache(key) { try { await redisClient.del(key); return true; } catch (err) { console.error('Error deleting cache:', err); return false; } } // 缓存预热 async function warmupCache() { // 预加载热点数据 const hotData = [ { key: 'product:1', value: { id: 1, name: '产品 1', price: 100 } }, { key: 'product:2', value: { id: 2, name: '产品 2', price: 200 } }, { key: 'product:3', value: { id: 3, name: '产品 3', price: 300 } } ]; for (const item of hotData) { await setCache(item.key, item.value); } console.log('Cache warmup completed'); } // 导出函数 module.exports = { connectRedis, setCache, getCache, deleteCache, warmupCache };使用示例:
const cacheService = require('./cache-service'); // 启动时连接 Redis 和预热缓存 cacheService.connectRedis(); cacheService.warmupCache(); // 获取产品信息 async function getProduct(productId) { // 尝试从缓存获取 const cacheKey = `product:${productId}`; const cachedProduct = await cacheService.getCache(cacheKey); if (cachedProduct) { console.log('Getting product from cache'); return cachedProduct; } // 缓存未命中,从数据库获取 console.log('Getting product from database'); const product = await db.query('SELECT * FROM products WHERE id = ?', [productId]); // 存入缓存 if (product) { await cacheService.setCache(cacheKey, product); } return product; } // 使用示例 const product = await getProduct(1); console.log('Product:', product);
7. 总结
Redis Cluster 是构建高可用、高扩展性 Redis 集群的理想解决方案,通过数据分片和自动故障转移机制,提供了比单机 Redis 更高的可用性和扩展性。本教程详细介绍了 Redis Cluster 的核心概念、部署配置、使用方法和最佳实践,帮助开发者快速掌握 Redis Cluster 的使用技巧,构建稳定可靠的分布式 Redis 集群。
在实际应用中,应根据具体需求和场景,合理配置 Redis Cluster 参数,确保系统的稳定性和性能。同时,定期进行集群维护和监控,及时发现和解决问题,确保 Redis Cluster 持续稳定运行。