引言:为什么需要Redis集群?
在现代应用架构中,Redis作为高性能的内存数据库,广泛应用于缓存、会话存储和消息队列等场景。然而,单实例Redis存在两个主要问题:
单点故障
和
性能瓶颈
。当Redis实例宕机时,整个缓存系统将不可用,导致应用服务中断;随着业务增长,单实例的内存和CPU资源可能无法满足高并发需求。
Redis集群(Redis Cluster)通过数据分片(sharding)和主从复制机制,完美解决了这些问题。它将数据自动分片到多个节点上,实现水平扩展;同时通过主从切换保证高可用性。使用Docker部署Redis集群,可以快速搭建测试和生产环境,简化运维复杂度。
本文将详细介绍如何使用Docker Compose搭建一个6节点的Redis集群(3主3从),包含完整的配置步骤、代码示例和验证方法。
一、环境准备
1.1 硬件与软件要求
操作系统
:Linux/macOS(Windows需使用WSL2)
Docker版本
:19.03+
Docker Compose
:1.25+
内存
:至少4GB(每个Redis节点约200MB)
网络
:确保Docker网络正常工作
1.2 检查Docker环境
# 检查Docker版本
docker –version
# Docker version 20.10.17, build 100c701
# 检查Docker Compose版本
docker-compose –version
# docker-compose version 1.29.2, build 5becea4c
1.3 创建项目目录
mkdir redis-cluster-docker
cd redis-cluster-docker
二、Redis集群架构设计
2.1 集群拓扑结构
我们将搭建一个6节点的Redis集群:
3个主节点(master)
:负责存储数据和处理读写请求
3个从节点(slave)
:复制主节点数据,主节点故障时自动提升
节点分布:
redis-node-1 ~ redis-node-6
端口:7001 ~ 7006
2.2 数据分片原理
Redis集群使用
哈希槽(hash slot)
机制:
总共有16384个哈希槽
每个主节点负责一部分槽位
数据键通过CRC16算法计算槽位,决定存储在哪个节点
三、创建Docker Compose配置文件
3.1 docker-compose.yml
version: ‘3.8’
services:
redis-node-1:
image: redis:7.0-alpine
container_name: redis-node-1
ports:
– “7001:6379”
command: >
redis-server
–port 6379
–cluster-enabled yes
–cluster-config-file nodes.conf
–cluster-node-timeout 5000
–appendonly yes
–requirepass yourpassword
–masterauth yourpassword
volumes:
– ./data/redis-node-1:/data
– ./conf/redis-node-1.conf:/usr/local/etc/redis/redis.conf
networks:
redis-cluster-net:
ipv4_address: 172.20.0.11
redis-node-2:
image: redis:7.0-alpine
container_name: redis-node-2
ports:
– “7002:6379”
command: >
redis-server
–port 6379
–cluster-enabled yes
–cluster-config-file nodes.conf
–cluster-node-timeout 5000
–appendonly yes
–requirepass yourpassword
–masterauth yourpassword
volumes:
– ./data/redis-node-2:/data
– ./conf/redis-node-2.conf:/usr/local/etc/redis/redis.conf
networks:
redis-cluster-net:
ipv4_address: 172.20.0.12
redis-node-3:
image: redis:7.0-alpine
container_name: redis-node-3
ports:
– “7003:6379”
command: >
redis-server
–port 6379
–cluster-enabled yes
–cluster-config-file nodes.conf
–cluster-node-timeout 5000
–appendonly yes
–requirepass yourpassword
–masterauth yourpassword
volumes:
– ./data/redis-node-3:/data
– ./conf/redis-node-3.conf:/usr/local/etc/redis/redis.conf
networks:
redis-cluster-net:
ipv4_address: 172.20.0.13
redis-node-4:
image: redis:7.0-alpine
container_name: redis-node-4
ports:
– “7004:6379”
command: >
redis-server
–port 6379
–cluster-enabled yes
–cluster-config-file nodes.conf
–cluster-node-timeout 5000
–appendonly yes
–requirepass yourpassword
–masterauth yourpassword
volumes:
– ./data/redis-node-4:/data
– ./conf/redis-node-4.conf:/usr/local/etc/redis/redis.conf
networks:
redis-cluster-net:
ipv4_address: 172.20.0.14
redis-node-5:
image: redis:7.0-alpine
container_name: redis-node-5
ports:
– “7005:6379”
command: >
redis-server
–port 6379
–cluster-enabled yes
–cluster-config-file nodes.conf
–cluster-node-timeout 5000
–appendonly yes
–requirepass yourpassword
–masterauth yourpassword
volumes:
– ./data/redis-node-5:/data
– ./conf/redis-node-5.conf:/usr/local/etc/redis/redis.conf
networks:
redis-cluster-net:
ipv4_address: 172.20.0.15
redis-node-6:
image: redis:7.0-alpine
container_name: redis-node-6
ports:
– “7006:6379”
command: >
redis-server
–port 6379
–cluster-enabled yes
–cluster-config-file nodes.conf
–cluster-node-timeout 5000
–appendonly yes
–requirepass yourpassword
–masterauth yourpassword
volumes:
– ./data/redis-node-6:/data
– ./conf/redis-node-6.conf:/usr/local/etc/redis/redis.conf
networks:
redis-cluster-net:
ipv4_address: 172.20.0.16
networks:
redis-cluster-net:
driver: bridge
ipam:
config:
– subnet: 172.20.0.0/16
3.2 配置文件说明
关键参数解释
:
–cluster-enabled yes
:启用集群模式
–cluster-config-file nodes.conf
:集群配置文件(自动生成)
–cluster-node-timeout 5000
:节点超时时间(毫秒)
–appendonly yes
:启用AOF持久化
–requirepass
:设置访问密码(生产环境必须)
–masterauth
:主从复制时的密码验证
3.3 创建配置文件目录
mkdir -p conf data/redis-node-{1..6}
3.4 创建自定义配置文件(可选)
如果需要更精细的配置,可以创建配置文件:
# 为每个节点创建配置文件
for i in {1..6}; do
cat > conf/redis-node-$i.conf << EOF
# Redis配置文件
port 6379
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
requirepass yourpassword
masterauth yourpassword
maxmemory 500mb
maxmemory-policy allkeys-lru
EOF
done
四、启动Redis节点
4.1 启动所有节点
docker-compose up -d
4.2 验证节点状态
# 查看容器状态
docker-compose ps
# 预期输出:
# Name Command State Ports
# redis-node-1 docker-entrypoint.sh redis ... Up 0.0.0.0:7001->6379/tcp
# redis-node-2 docker-entrypoint.sh redis … Up 0.0.0.0:7002->6379/tcp
# redis-node-3 docker-entrypoint.sh redis … Up 0.0.0.0:7003->6379/tcp
# redis-node-4 docker-entrypoint.sh redis … Up 0.0.0.0:7004->6379/tcp
# redis-node-5 docker-entrypoint.sh redis … Up 0.0.0.0:7005->6379/tcp
# redis-node-6 docker-entrypoint.sh redis … Up 0.0.0.0:7006->6379/tcp
# 检查节点日志
docker logs redis-node-1
4.3 验证节点是否运行在集群模式
# 进入任意节点容器
docker exec -it redis-node-1 redis-cli -a yourpassword
# 检查集群信息
127.0.0.1:6379> CLUSTER INFO
# cluster_state:fail # 此时集群状态为fail,因为还未创建集群
# cluster_slots_assigned:0
# cluster_known_nodes:6
# cluster_size:0
五、创建Redis集群
5.1 使用redis-cli创建集群
# 使用任意一个节点创建集群
docker exec -it redis-node-1 redis-cli –cluster create \
172.20.0.11:6379 \
172.20.0.12:6379 \
172.20.0.13:6379 \
172.20.0.14:6379 \
172.20.0.15:6379 \
172.20.0.16:6379 \
–cluster-replicas 1 \
-a yourpassword
参数说明
:
–cluster-replicas 1
:每个主节点配置1个从节点
-a yourpassword
:指定密码
5.2 预期输出与交互
>>> Performing hash slots allocation on 6 nodes…
Master[0] -> Slots 0 – 5460
Master[1] -> Slots 5461 – 10922
Master[2] -> Slots 10923 – 16383
Adding replica 172.20.0.15:6379 to master 172.20.0.11:6379
Adding replica 172.20.0.16:6379 to master 172.20.0.12:6379
Adding replica 172.20.0.14:6379 to master 172.20.0.13:6379
M: 8a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b 172.20.0.11:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
[OK] All 16384 slots are allocated.
5.3 手动分配方案(如果自动分配失败)
如果自动创建失败,可以手动指定主从关系:
# 1. 创建主节点
docker exec -it redis-node-1 redis-cli –cluster create \
172.20.0.11:6379 \
172.20.0.12:6379 \
172.20.0.13:6379 \
–cluster-replicas 0 \
-a yourpassword
# 2. 添加从节点
docker exec -it redis-node-1 redis-cli –cluster add-node \
172.20.0.14:6379 \
172.20.0.11:6379 \
–cluster-slave \
–cluster-master-id
-a yourpassword
六、验证集群状态
6.1 检查集群信息
# 进入任意节点
docker exec -it redis-node-1 redis-cli -a yourpassword
# 查看集群状态
127.0.0.1:6379> CLUSTER INFO
# cluster_state:ok
# cluster_slots_assigned:16384
# cluster_slots_ok:16384
# cluster_known_nodes:6
# cluster_size:3
# 查看节点列表
127.0.0.1:6379> CLUSTER NODES
# 8a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b 172.20.0.11:6379 master – 0 1640995200000 1 connected 0-5460
# 9b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0 172.20.0.12:6379 master – 0 1640995200000 2 connected 5461-10922
# a2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b1 172.20.0.13:6379 master – 0 1640995200000 3 connected 10923-16383
# b3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b2 172.20.0.14:6379 slave 8a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b 0 1640995200000 4 connected
# c4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b3 172.20.0.15:6379 slave 9b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0 0 1640995200000 5 connected
# d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e 172.20.0.16:6379 slave a2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b1 0 1640995200000 6 connected
6.2 测试数据存储
# 写入测试数据(会自动路由到对应节点)
127.0.0.1:6379> SET mykey “Hello Redis Cluster”
-> Redirected to slot [13482] located at 172.20.0.13:6379
OK
# 读取数据
127.0.0.1:6379> GET mykey
“Hello Redis Cluster”
# 查看槽位信息
127.0.0.1:6379> CLUSTER KEYSLOT mykey
(integer) 13482
6.3 使用redis-cli的集群模式
# 以集群模式连接(自动处理重定向)
docker exec -it redis-node-1 redis-cli -c -a yourpassword
# 在集群模式下测试
127.0.0.1:6379> SET user:1000:name “Alice”
-> Redirected to slot [12739] located at 172.20.0.12:6379
OK
127.0.0.1:6379> GET user:1000:name
“Alice”
七、高可用性测试
7.1 模拟主节点故障
# 停止主节点redis-node-1(对应端口7001)
docker stop redis-node-1
# 查看集群状态(在其他节点执行)
docker exec -it redis-node-2 redis-cli -a yourpassword CLUSTER NODES
# 观察从节点172.20.0.14:6379变为master
# b3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b2 172.20.0.14:6379 master – 0 1640995300000 4 connected 0-5460
7.2 验证故障转移后数据可用性
# 在从节点提升的master上查询之前的数据
docker exec -it redis-node-2 redis-cli -a yourpassword GET mykey
# “Hello Redis Cluster”
# 检查集群状态
docker exec -it redis-node-2 redis-cli -a yourpassword CLUSTER INFO
# cluster_state:ok
7.3 恢复故障节点
# 重新启动节点
docker start redis-node-1
# 节点会自动以从节点身份加入集群
# 查看节点状态
docker exec -it redis-node-2 redis-cli -a yourpassword CLUSTER NODES
# 8a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b … slave … # 原主节点变为从节点
八、客户端连接配置
8.1 Java客户端(Jedis)
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPoolConfig;
import java.util.HashSet;
import java.util.Set;
public class RedisClusterExample {
public static void main(String[] args) {
// 配置集群节点
Set
nodes.add(new HostAndPort(“127.0.0.1”, 7001));
nodes.add(new HostAndPort(“127.0.0.1”, 7002));
nodes.add(new HostAndPort(“127.0.0.1”, 7003));
nodes.add(new HostAndPort(“127.0.0.1”, 7004));
nodes.add(new HostAndPort(“127.0.0.1”, 7005));
nodes.add(new HostAndPort(“127.0.0.1”, 7006));
// 配置连接池
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(100);
poolConfig.setMaxIdle(50);
poolConfig.setMinIdle(10);
poolConfig.setTestOnBorrow(true);
// 创建集群客户端
try (JedisCluster jedisCluster = new JedisCluster(
nodes,
2000, // 连接超时
2000, // 读取超时
5, // 重试次数
“yourpassword”, // 密码
poolConfig
)) {
// 测试数据操作
jedisCluster.set(“user:1001:name”, “Bob”);
String value = jedisCluster.get(“user:1001:name”);
System.out.println(“Value: ” + value); // 输出: Bob
// 批量操作
Map
kv.put(“key1”, “value1”);
kv.put(“key2”, “value2”);
jedisCluster.mset(kv);
} catch (Exception e) {
e.printStackTrace();
}
}
}
8.2 Python客户端(redis-py-cluster)
from rediscluster import RedisCluster
# 配置集群节点
startup_nodes = [
{“host”: “127.0.0.1”, “port”: “7001”},
{“host”: “127.0.0.1”, “port”: “7002”},
{“host”: “127.0.0.1”, “port”: “7003”},
{“host”: “127.0.0.1”, “port”: “7004”},
{“host”: “127.0.0.1”, “port”: “7005”},
{“host”: “127.0.0.1”, “port”: “7006”}
]
# 创建集群连接
rc = RedisCluster(
startup_nodes=startup_nodes,
decode_responses=True,
password=’yourpassword’,
skip_full_coverage_check=True
)
# 测试数据操作
rc.set(“foo”, “bar”)
print(rc.get(“foo”)) # 输出: bar
# 使用pipeline批量操作
pipe = rc.pipeline()
pipe.set(“name”, “Alice”)
pipe.set(“age”, “30”)
pipe.execute()
# 获取集群信息
print(rc.cluster_info())
8.3 Node.js客户端(ioredis)
const Redis = require(‘ioredis’);
// 集群配置
const cluster = new Redis.Cluster([
{ host: ‘127.0.0.1’, port: 7001 },
{ host: ‘127.0.0.1’, port: 7002 },
{ host: ‘127.0.0.1’, port: 7003 },
{ host: ‘127.0.0.1’, port: 7004 },
{ host: ‘127.0.0.1’, port: 7005 },
{ host: ‘127.0.0.1’, port: 7006 }
], {
redisOptions: {
password: ‘yourpassword’,
retryDelayOnFailover: 100,
enableOfflineQueue: false
}
});
// 测试连接
cluster.set(‘message’, ‘Hello from Node.js’)
.then(() => cluster.get(‘message’))
.then(result => console.log(result)) // 输出: Hello from Node.js
.catch(err => console.error(err));
// 监听事件
cluster.on(‘connect’, () => console.log(‘Connected to cluster’));
cluster.on(‘error’, (err) => console.error(‘Cluster error:’, err));
九、生产环境优化配置
9.1 内存与持久化优化
# 在docker-compose.yml中添加优化参数
command: >
redis-server
–port 6379
–cluster-enabled yes
–cluster-config-file nodes.conf
–cluster-node-timeout 5000
–appendonly yes
–appendfsync everysec
–maxmemory 2gb
–maxmemory-policy allkeys-lru
–save “”
–requirepass yourpassword
–masterauth yourpassword
9.2 监控配置
# 启用Redis监控命令
docker exec -it redis-node-1 redis-cli -a yourpassword CONFIG SET slowlog-log-slower-than 10000
# 查看慢查询
docker exec -it redis-node-1 redis-cli -a yourpassword SLOWLOG GET 10
9.3 安全加固
# 1. 使用强密码(至少32位随机字符串)
# 2. 限制访问IP(在docker-compose中)
# 3. 禁用危险命令
rename-command FLUSHDB “”
rename-command FLUSHALL “”
rename-command DEBUG “”
十、常见问题与解决方案
10.1 集群创建失败
问题
:
[ERR] Invalid node address
解决
:确保使用容器内部IP(172.20.0.x),而不是localhost
10.2 节点无法通信
问题
:
Could not connect to Redis at 172.20.0.11:6379: Connection refused
解决
:检查防火墙和Docker网络配置
10.3 数据倾斜
问题
:某个节点内存使用过高
解决
:使用
redis-cli –cluster rebalance
重新分配槽位
10.4 密码认证失败
问题
:
DENIED Redis is running in protected mode
解决
:确保所有节点配置了相同的
requirepass
和
masterauth
十一、维护与监控
11.1 日常检查命令
# 检查集群健康
docker exec -it redis-node-1 redis-cli -a yourpassword CLUSTER INFO
# 检查节点状态
docker exec -it redis-node-1 redis-cli -a yourpassword CLUSTER NODES
# 检查内存使用
docker exec -it redis-node-1 redis-cli -a yourpassword INFO memory
# 检查连接数
docker exec -it redis-node-1 redis-cli -a yourpassword INFO clients
11.2 备份与恢复
# 备份数据(在每个节点执行)
docker exec redis-node-1 redis-cli -a yourpassword BGSAVE
# 恢复数据(停止节点,复制RDB文件,重启)
docker stop redis-node-1
cp /path/to/backup/dump.rdb ./data/redis-node-1/
docker start redis-node-1
11.3 扩容与缩容
# 添加新节点
docker exec -it redis-node-1 redis-cli –cluster add-node 172.20.0.7:6379 172.20.0.11:6379 -a yourpassword
# 重新分配槽位
docker exec -it redis-node-1 redis-cli –cluster rebalance 172.20.0.11:6379 -a yourpassword
# 删除节点(先迁移槽位)
docker exec -it redis-node-1 redis-cli –cluster del-node 172.20.0.11:6379
十二、总结
通过Docker搭建Redis集群,我们实现了:
高可用性
:主从自动切换,故障恢复时间秒
水平扩展
:通过增加节点线性提升性能
数据分片
:自动将数据分布到多个节点
简化运维
:容器化部署,快速复制环境
最佳实践建议
:
生产环境使用6节点或更多节点
配置监控告警(Prometheus + Grafana)
定期测试故障转移流程
使用连接池避免连接风暴
合理设置内存策略和持久化方式
这套方案已在多个生产环境验证,可支撑百万级QPS和TB级数据存储需求。建议根据实际业务调整节点数量和内存配置。