Redis 集群部署方案全解析
Redis 提供了多种部署方案,从单机到集群,每种方案都有其适用场景。本文将全面解析各种部署方案,帮助你选择最适合的架构。
一、部署方案对比
1.1 方案总览
Redis 部署方案演进:
单机版 → 主从复制 → 哨兵模式 → Cluster 集群
┌─────────────────────────────────────────┐
│ 方案 │ 可用性 │ 扩展性 │ 复杂度 │
├─────────────────────────────────────────┤
│ 单机版 │ 低 │ 无 │ 低 │
│ 主从复制 │ 中 │ 低 │ 中 │
│ 哨兵模式 │ 高 │ 中 │ 高 │
│ Cluster │ 高 │ 高 │ 高 │
└─────────────────────────────────────────┘
1.2 详细对比
| 特性 | 单机 | 主从 | 哨兵 | Cluster |
|---|---|---|---|---|
| 节点数 | 1 | 2+ | 3+ | 6+ |
| 故障转移 | 手动 | 手动 | 自动 | 自动 |
| 读写分离 | 不支持 | 支持 | 支持 | 支持 |
| 水平扩展 | 不支持 | 不支持 | 不支持 | 支持 |
| 数据分片 | 无 | 无 | 无 | 有 |
| 适用 QPS | <1 万 | <5 万 | <10 万 | >10 万 |
| 适用数据量 | <10GB | <20GB | <50GB | 不限 |
二、单机版部署
2.1 适用场景
- 开发测试环境
- 个人项目
- 低并发应用(QPS < 1 万)
- 数据量小(< 10GB)
2.2 部署配置
# redis.conf 基础配置
bind 127.0.0.1
port 6379
daemonize yes
pidfile /var/run/redis/redis-server.pid
logfile /var/log/redis/redis-server.log
# 数据目录
dir /var/lib/redis
# 持久化
save 900 1
save 300 10
save 60 10000
# 内存限制
maxmemory 2gb
maxmemory-policy allkeys-lru
# 安全配置
requirepass your_password
2.3 启动脚本
#!/bin/bash
# start_redis.sh
REDIS_HOME="/opt/redis"
REDIS_CONF="$REDIS_HOME/redis.conf"
# 创建目录
mkdir -p /var/run/redis
mkdir -p /var/log/redis
mkdir -p /var/lib/redis
# 启动 Redis
$REDIS_HOME/bin/redis-server $REDIS_CONF
# 验证启动
sleep 2
$REDIS_HOME/bin/redis-cli ping
三、主从复制部署
3.1 适用场景
- 读多写少的应用
- 需要数据备份
- QPS < 5 万
- 数据量 < 20GB
3.2 架构设计
主从复制架构:
┌─────────────┐
│ Master │ 192.168.1.10:6379
│ (读写) │
└──────┬──────┘
│ 复制
├─────────────┐
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Slave 1 │ │ Slave 2 │
│ (只读) │ │ (只读) │
│ 192.168.1.11│ │ 192.168.1.12│
└─────────────┘ └─────────────┘
3.3 主节点配置
# Master 配置 (redis-master.conf)
bind 0.0.0.0
port 6379
daemonize yes
pidfile /var/run/redis/redis-master.pid
logfile /var/log/redis/redis-master.log
dir /var/lib/redis/master
# 密码
requirepass master_password
# 复制认证密码
masterauth master_password
# 持久化
save 900 1
save 300 10
save 60 10000
# 内存限制
maxmemory 4gb
maxmemory-policy allkeys-lru
3.4 从节点配置
# Slave 配置 (redis-slave.conf)
bind 0.0.0.0
port 6379
daemonize yes
pidfile /var/run/redis/redis-slave.pid
logfile /var/log/redis/redis-slave.log
dir /var/lib/redis/slave
# 密码
requirepass slave_password
# 复制认证
masterauth master_password
# 指定主节点
replicaof 192.168.1.10 6379
# 只读模式
replica-read-only yes
# 持久化(可选)
save 900 1
3.5 启动顺序
#!/bin/bash
# start_master_slave.sh
# 1. 启动主节点
redis-server /etc/redis/redis-master.conf
sleep 2
# 2. 启动从节点 1
redis-server /etc/redis/redis-slave1.conf
sleep 2
# 3. 启动从节点 2
redis-server /etc/redis/redis-slave2.conf
sleep 2
# 4. 验证复制状态
redis-cli -h 192.168.1.10 -a master_password INFO replication
3.6 读写分离实现
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.util.List;
import java.util.Random;
public class ReadWriteSeparation {
private JedisPool masterPool;
private List<JedisPool> slavePools;
private Random random = new Random();
public ReadWriteSeparation(JedisPool masterPool, List<JedisPool> slavePools) {
this.masterPool = masterPool;
this.slavePools = slavePools;
}
/**
* 写操作(路由到主节点)
*/
public void set(String key, String value) {
try (Jedis jedis = masterPool.getResource()) {
jedis.set(key, value);
}
}
/**
* 读操作(随机路由到从节点)
*/
public String get(String key) {
if (slavePools.isEmpty()) {
try (Jedis jedis = masterPool.getResource()) {
return jedis.get(key);
}
}
// 随机选择一个从节点
int index = random.nextInt(slavePools.size());
try (Jedis jedis = slavePools.get(index).getResource()) {
return jedis.get(key);
}
}
/**
* 删除操作(路由到主节点)
*/
public void delete(String key) {
try (Jedis jedis = masterPool.getResource()) {
jedis.del(key);
}
}
}
四、哨兵模式部署
4.1 适用场景
- 高可用要求
- 自动故障转移
- QPS < 10 万
- 数据量 < 50GB
4.2 架构设计
哨兵模式架构(3 哨兵 + 1 主 2 从):
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Sentinel 1 │ │ Sentinel 2 │ │ Sentinel 3 │
│ 192.168.1.20│ │ 192.168.1.21│ │ 192.168.1.22│
│ port 26379│ │ port 26379│ │ port 26379│
└─────────────┘ └─────────────┘ └─────────────┘
│ │ │
└──────────────────┼──────────────────┘
│ 监控
┌──────────────────┼──────────────────┐
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Master │ │ Slave 1 │ │ Slave 2 │
│ 192.168.1.10│ │ 192.168.1.11│ │ 192.168.1.12│
└─────────────┘ └─────────────┘ └─────────────┘
4.3 哨兵配置
# Sentinel 配置 (redis-sentinel.conf)
port 26379
daemonize yes
pidfile /var/run/redis/redis-sentinel.pid
logfile /var/log/redis/redis-sentinel.log
# 监控主节点
# sentinel monitor <master-name> <ip> <port> <quorum>
sentinel monitor mymaster 192.168.1.10 6379 2
# 认证密码
sentinel auth-pass mymaster master_password
# 故障判定时间(毫秒)
sentinel down-after-milliseconds mymaster 5000
# 故障转移超时时间(毫秒)
sentinel failover-timeout mymaster 60000
# 同时复制的从节点数
sentinel parallel-syncs mymaster 1
# 通知脚本(可选)
# sentinel notification-script mymaster /var/redis/notify.sh
# 客户端重新连接配置
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
4.4 启动哨兵集群
#!/bin/bash
# start_sentinel_cluster.sh
# 1. 启动主从节点(参考前面)
# ...
# 2. 启动哨兵 1
redis-sentinel /etc/redis/redis-sentinel1.conf
sleep 2
# 3. 启动哨兵 2
redis-sentinel /etc/redis/redis-sentinel2.conf
sleep 2
# 4. 启动哨兵 3
redis-sentinel /etc/redis/redis-sentinel3.conf
sleep 2
# 5. 验证哨兵状态
redis-cli -h 192.168.1.20 -p 26379 INFO sentinel
4.5 Java 连接哨兵
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisSentinelPool;
import redis.clients.jedis.JedisPoolConfig;
import java.util.HashSet;
import java.util.Set;
public class SentinelExample {
public static JedisSentinelPool createSentinelPool() {
// 哨兵节点
Set<String> sentinels = new HashSet<>();
sentinels.add("192.168.1.20:26379");
sentinels.add("192.168.1.21:26379");
sentinels.add("192.168.1.22:26379");
// 连接池配置
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(50);
poolConfig.setMaxIdle(20);
poolConfig.setMinIdle(5);
poolConfig.setMaxWaitMillis(3000);
// 创建哨兵连接池
return new JedisSentinelPool(
"mymaster", // 主节点名称
sentinels, // 哨兵节点集合
poolConfig, // 连接池配置
"master_password" // 密码
);
}
public static void main(String[] args) {
JedisSentinelPool pool = createSentinelPool();
try (Jedis jedis = pool.getResource()) {
// 自动连接到主节点
jedis.set("test", "value");
System.out.println(jedis.get("test"));
}
pool.close();
}
}
4.6 故障转移测试
# 1. 查看当前主节点
redis-cli -h 192.168.1.20 -p 26379 sentinel get-master-addr-by-name mymaster
# 2. 模拟主节点故障
redis-cli -h 192.168.1.10 -a master_password DEBUG sleep 1000
# 3. 观察哨兵日志
tail -f /var/log/redis/redis-sentinel.log
# 4. 查看新的主节点
redis-cli -h 192.168.1.20 -p 26379 sentinel get-master-addr-by-name mymaster
# 5. 验证数据完整性
redis-cli -h <new-master-ip> -a master_password keys "*" | wc -l
五、Cluster 集群部署
5.1 适用场景
- 大数据量(> 50GB)
- 高并发(QPS > 10 万)
- 需要水平扩展
- 高可用要求
5.2 架构设计
Cluster 集群架构(6 节点,3 主 3 从):
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Master 1 │ │ Master 2 │ │ Master 3 │
│ 0-5460 │ │ 5461-10922 │ │ 10923-16383 │
│ 192.168.1.10│ │ 192.168.1.11│ │ 192.168.1.12│
│ port 7000 │ │ port 7001 │ │ port 7002 │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
┌──────┴──────┐ ┌──────┴──────┐ ┌──────┴──────┐
│ Slave 1 │ │ Slave 2 │ │ Slave 3 │
│ 192.168.1.13│ │ 192.168.1.14│ │ 192.168.1.15│
│ port 7003 │ │ port 7004 │ │ port 7005 │
└─────────────┘ └─────────────┘ └─────────────┘
5.3 节点配置
# Cluster 节点配置 (redis-cluster-node.conf)
bind 0.0.0.0
port 7000
daemonize yes
pidfile /var/run/redis/redis-7000.pid
logfile /var/log/redis/redis-7000.log
dir /var/lib/redis/7000
# 开启集群模式
cluster-enabled yes
# 集群配置文件(自动生成)
cluster-config-file nodes-7000.conf
# 节点超时时间(毫秒)
cluster-node-timeout 5000
# 密码
requirepass cluster_password
masterauth cluster_password
# 持久化
save 900 1
save 300 10
save 60 10000
# 内存限制
maxmemory 4gb
maxmemory-policy allkeys-lru
5.4 创建集群
# 1. 启动所有节点
for port in 7000 7001 7002 7003 7004 7005; do
redis-server /etc/redis/redis-cluster-$port.conf
done
sleep 3
# 2. 创建集群(3 主 3 从)
redis-cli --cluster create \
192.168.1.10:7000 192.168.1.11:7001 192.168.1.12:7002 \
192.168.1.13:7003 192.168.1.14:7004 192.168.1.15:7005 \
--cluster-replicas 1 \
--cluster-yes \
-a cluster_password
# 3. 查看集群状态
redis-cli -h 192.168.1.10 -p 7000 -a cluster_password cluster info
# 4. 查看节点信息
redis-cli -h 192.168.1.10 -p 7000 -a cluster_password cluster nodes
5.5 Java 连接 Cluster
import redis.clients.jedis.ClusterCommandArguments;
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 ClusterExample {
public static JedisCluster createCluster() {
// 集群节点
Set<HostAndPort> nodes = new HashSet<>();
nodes.add(new HostAndPort("192.168.1.10", 7000));
nodes.add(new HostAndPort("192.168.1.11", 7001));
nodes.add(new HostAndPort("192.168.1.12", 7002));
nodes.add(new HostAndPort("192.168.1.13", 7003));
nodes.add(new HostAndPort("192.168.1.14", 7004));
nodes.add(new HostAndPort("192.168.1.15", 7005));
// 连接池配置
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(100);
poolConfig.setMaxIdle(50);
poolConfig.setMinIdle(10);
// 创建集群连接
return new JedisCluster(
nodes,
5000, // 超时时间
5000, // 连接超时
5, // 最大重试次数
"cluster_password",
poolConfig
);
}
public static void main(String[] args) {
JedisCluster cluster = createCluster();
// 自动路由到正确节点
cluster.set("user:1001:name", "张三");
String value = cluster.get("user:1001:name");
System.out.println(value);
cluster.close();
}
}
六、方案选型建议
6.1 选型决策树
选择部署方案决策树:
1. 是否需要高可用?
├── 否 → 单机版
└── 是 → 2
2. 数据量是否 > 50GB?
├── 否 → 3
└── 是 → Cluster
3. QPS 是否 > 10 万?
├── 否 → 4
└── 是 → Cluster
4. 是否需要自动故障转移?
├── 否 → 主从复制
└── 是 → 哨兵模式
5. 预算是否充足?
├── 否 → 主从复制(2 节点)
└── 是 → 哨兵模式(3 哨兵 + 1 主 2 从)
6.2 推荐配置
| 规模 | 推荐方案 | 节点数 | 内存 | 预估成本 |
|---|---|---|---|---|
| 小型 | 单机版 | 1 | 4GB | 低 |
| 中型 | 主从复制 | 3 | 8GB | 中 |
| 大型 | 哨兵模式 | 6 | 16GB | 高 |
| 超大型 | Cluster | 6+ | 32GB+ | 很高 |
七、总结
7.1 各方案核心特点
-
单机版
- 优点:简单、成本低
- 缺点:单点故障、无法扩展
- 适用:开发测试、小规模应用
-
主从复制
- 优点:读写分离、数据备份
- 缺点:手动故障转移
- 适用:读多写少、中等规模
-
哨兵模式
- 优点:自动故障转移、高可用
- 缺点:无法水平扩展
- 适用:高可用要求、中大规模
-
Cluster 集群
- 优点:水平扩展、自动分片
- 缺点:复杂度高、运维成本高
- 适用:大数据量、高并发
参考资料
- Redis 官方文档 - Deployment
- Redis 高可用架构
- 《Redis 实战》第 10 章