第25集Redis Cluster多实例部署实战指南

引言

Redis Cluster是Redis官方提供的分布式解决方案,通过多实例部署可以实现数据的高可用、负载均衡和水平扩展。在实际生产环境中,Redis Cluster的部署和管理是一个复杂的系统工程,需要从架构设计、配置管理、监控告警等多个维度进行综合考虑。

本文将深入探讨Redis Cluster多实例部署的完整流程,从基础架构设计到实际部署实施,从集群管理到性能优化,提供企业级的Redis集群解决方案。

Redis Cluster架构设计

1. 集群架构概述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// Redis Cluster架构设计
public class RedisClusterArchitecture {

/**
* Redis Cluster架构特点
*/
public static void explainClusterArchitecture() {
System.out.println("=== Redis Cluster架构特点 ===");

System.out.println("1. 无中心化设计:");
System.out.println(" - 所有节点地位平等");
System.out.println(" - 无单点故障");
System.out.println(" - 节点间相互通信");

System.out.println("\n2. 数据分片存储:");
System.out.println(" - 16384个槽位");
System.out.println(" - 数据按槽位分布");
System.out.println(" - 支持动态扩容");

System.out.println("\n3. 高可用保证:");
System.out.println(" - 主从复制");
System.out.println(" - 自动故障转移");
System.out.println(" - 故障检测");

System.out.println("\n4. 客户端路由:");
System.out.println(" - 客户端直连");
System.out.println(" - 自动重定向");
System.out.println(" - 智能路由");
}

/**
* 集群节点类型
*/
public static void explainNodeTypes() {
System.out.println("\n=== Redis Cluster节点类型 ===");

System.out.println("1. 主节点(Master):");
System.out.println(" - 负责数据读写");
System.out.println(" - 管理槽位");
System.out.println(" - 参与故障转移");

System.out.println("\n2. 从节点(Slave):");
System.out.println(" - 复制主节点数据");
System.out.println(" - 提供读服务");
System.out.println(" - 故障时提升为主节点");

System.out.println("\n3. 哨兵节点(Sentinel):");
System.out.println(" - 监控节点状态");
System.out.println(" - 执行故障转移");
System.out.println(" - 配置管理");
}

/**
* 槽位分配策略
*/
public static void explainSlotAllocation() {
System.out.println("\n=== 槽位分配策略 ===");

System.out.println("1. 槽位数量:");
System.out.println(" - 总共16384个槽位");
System.out.println(" - 每个主节点管理部分槽位");
System.out.println(" - 槽位均匀分布");

System.out.println("\n2. 槽位计算:");
System.out.println(" - 使用CRC16算法");
System.out.println(" - 对key进行哈希计算");
System.out.println(" - 取模得到槽位号");

System.out.println("\n3. 槽位迁移:");
System.out.println(" - 支持在线迁移");
System.out.println(" - 数据一致性保证");
System.out.println(" - 迁移过程透明");
}
}

2. 部署架构设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// Redis Cluster部署架构设计
public class ClusterDeploymentArchitecture {

/**
* 生产环境部署架构
*/
public static void designProductionArchitecture() {
System.out.println("=== 生产环境部署架构 ===");

System.out.println("1. 三主三从架构:");
System.out.println(" - 3个主节点,3个从节点");
System.out.println(" - 每个主节点有1个从节点");
System.out.println(" - 支持1个主节点故障");

System.out.println("\n2. 六主六从架构:");
System.out.println(" - 6个主节点,6个从节点");
System.out.println(" - 每个主节点有1个从节点");
System.out.println(" - 支持2个主节点故障");

System.out.println("\n3. 跨机房部署:");
System.out.println(" - 主节点分布在不同机房");
System.out.println(" - 从节点跨机房部署");
System.out.println(" - 保证机房级容灾");

System.out.println("\n4. 网络拓扑:");
System.out.println(" - 使用内网通信");
System.out.println(" - 配置防火墙规则");
System.out.println(" - 优化网络延迟");
}

/**
* 服务器配置建议
*/
public static void suggestServerConfiguration() {
System.out.println("\n=== 服务器配置建议 ===");

System.out.println("1. CPU配置:");
System.out.println(" - 最少4核CPU");
System.out.println(" - 推荐8核以上");
System.out.println(" - 支持多线程处理");

System.out.println("\n2. 内存配置:");
System.out.println(" - 最少8GB内存");
System.out.println(" - 推荐16GB以上");
System.out.println(" - 预留系统内存");

System.out.println("\n3. 存储配置:");
System.out.println(" - 使用SSD存储");
System.out.println(" - 配置RAID1或RAID10");
System.out.println(" - 预留足够空间");

System.out.println("\n4. 网络配置:");
System.out.println(" - 千兆网卡");
System.out.println(" - 低延迟网络");
System.out.println(" - 稳定网络连接");
}
}

Redis Cluster部署配置

1. 基础配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# Redis Cluster配置文件模板
# redis-cluster-7000.conf

# 基础配置
port 7000
bind 0.0.0.0
protected-mode no

# 集群配置
cluster-enabled yes
cluster-config-file nodes-7000.conf
cluster-node-timeout 15000
cluster-announce-ip 192.168.1.100
cluster-announce-port 7000
cluster-announce-bus-port 17000

# 持久化配置
save 900 1
save 300 10
save 60 10000
rdbcompression yes
rdbchecksum yes
dbfilename dump-7000.rdb
dir /data/redis-cluster/7000

# 内存配置
maxmemory 8gb
maxmemory-policy allkeys-lru
maxmemory-samples 5

# 网络配置
tcp-backlog 511
timeout 0
tcp-keepalive 300

# 日志配置
loglevel notice
logfile /var/log/redis/redis-7000.log

# 安全配置
requirepass cluster_password
masterauth cluster_password

# 其他配置
daemonize yes
pidfile /var/run/redis/redis-7000.pid

2. 部署脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
#!/bin/bash
# Redis Cluster部署脚本
# deploy-redis-cluster.sh

set -e

# 配置变量
REDIS_VERSION="6.2.7"
REDIS_USER="redis"
REDIS_HOME="/opt/redis"
REDIS_DATA_DIR="/data/redis-cluster"
REDIS_LOG_DIR="/var/log/redis"
REDIS_CONF_DIR="/etc/redis"

# 节点配置
NODES=(
"192.168.1.100:7000:17000"
"192.168.1.100:7001:17001"
"192.168.1.100:7002:17002"
"192.168.1.101:7000:17000"
"192.168.1.101:7001:17001"
"192.168.1.101:7002:17002"
)

# 创建用户和目录
create_user_and_directories() {
echo "创建Redis用户和目录..."

# 创建用户
if ! id -u $REDIS_USER > /dev/null 2>&1; then
useradd -r -s /bin/false $REDIS_USER
fi

# 创建目录
mkdir -p $REDIS_HOME
mkdir -p $REDIS_DATA_DIR
mkdir -p $REDIS_LOG_DIR
mkdir -p $REDIS_CONF_DIR
mkdir -p /var/run/redis

# 设置权限
chown -R $REDIS_USER:$REDIS_USER $REDIS_HOME
chown -R $REDIS_USER:$REDIS_USER $REDIS_DATA_DIR
chown -R $REDIS_USER:$REDIS_USER $REDIS_LOG_DIR
chown -R $REDIS_USER:$REDIS_USER /var/run/redis
}

# 安装Redis
install_redis() {
echo "安装Redis $REDIS_VERSION..."

cd /tmp
wget http://download.redis.io/releases/redis-$REDIS_VERSION.tar.gz
tar xzf redis-$REDIS_VERSION.tar.gz
cd redis-$REDIS_VERSION

make && make install

# 创建软链接
ln -sf /usr/local/bin/redis-server /usr/bin/redis-server
ln -sf /usr/local/bin/redis-cli /usr/bin/redis-cli

# 清理
cd /
rm -rf /tmp/redis-$REDIS_VERSION*
}

# 生成配置文件
generate_config_files() {
echo "生成Redis配置文件..."

for node in "${NODES[@]}"; do
IFS=':' read -r ip port bus_port <<< "$node"

# 创建数据目录
mkdir -p $REDIS_DATA_DIR/$port

# 生成配置文件
cat > $REDIS_CONF_DIR/redis-cluster-$port.conf << EOF
# Redis Cluster配置文件 - 端口 $port
port $port
bind 0.0.0.0
protected-mode no

# 集群配置
cluster-enabled yes
cluster-config-file nodes-$port.conf
cluster-node-timeout 15000
cluster-announce-ip $ip
cluster-announce-port $port
cluster-announce-bus-port $bus_port

# 持久化配置
save 900 1
save 300 10
save 60 10000
rdbcompression yes
rdbchecksum yes
dbfilename dump-$port.rdb
dir $REDIS_DATA_DIR/$port

# 内存配置
maxmemory 8gb
maxmemory-policy allkeys-lru
maxmemory-samples 5

# 网络配置
tcp-backlog 511
timeout 0
tcp-keepalive 300

# 日志配置
loglevel notice
logfile $REDIS_LOG_DIR/redis-$port.log

# 安全配置
requirepass cluster_password
masterauth cluster_password

# 其他配置
daemonize yes
pidfile /var/run/redis/redis-$port.pid
EOF

# 设置权限
chown $REDIS_USER:$REDIS_USER $REDIS_CONF_DIR/redis-cluster-$port.conf
chown -R $REDIS_USER:$REDIS_USER $REDIS_DATA_DIR/$port
done
}

# 启动Redis实例
start_redis_instances() {
echo "启动Redis实例..."

for node in "${NODES[@]}"; do
IFS=':' read -r ip port bus_port <<< "$node"

# 启动Redis实例
sudo -u $REDIS_USER redis-server $REDIS_CONF_DIR/redis-cluster-$port.conf

echo "Redis实例 $port 已启动"
done

# 等待实例启动
sleep 5
}

# 创建集群
create_cluster() {
echo "创建Redis Cluster..."

# 构建节点列表
cluster_nodes=""
for node in "${NODES[@]}"; do
IFS=':' read -r ip port bus_port <<< "$node"
cluster_nodes="$cluster_nodes $ip:$port"
done

# 创建集群
redis-cli --cluster create $cluster_nodes --cluster-replicas 1 --cluster-yes -a cluster_password

echo "Redis Cluster创建完成"
}

# 验证集群
verify_cluster() {
echo "验证Redis Cluster..."

# 检查集群状态
redis-cli -h 192.168.1.100 -p 7000 -a cluster_password cluster nodes
redis-cli -h 192.168.1.100 -p 7000 -a cluster_password cluster info

echo "集群验证完成"
}

# 主函数
main() {
echo "开始部署Redis Cluster..."

create_user_and_directories
install_redis
generate_config_files
start_redis_instances
create_cluster
verify_cluster

echo "Redis Cluster部署完成!"
}

# 执行主函数
main "$@"

3. 集群管理脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#!/bin/bash
# Redis Cluster管理脚本
# manage-redis-cluster.sh

# 配置变量
REDIS_USER="redis"
REDIS_CONF_DIR="/etc/redis"
REDIS_LOG_DIR="/var/log/redis"

# 节点配置
NODES=(
"192.168.1.100:7000"
"192.168.1.100:7001"
"192.168.1.100:7002"
"192.168.1.101:7000"
"192.168.1.101:7001"
"192.168.1.101:7002"
)

# 启动集群
start_cluster() {
echo "启动Redis Cluster..."

for node in "${NODES[@]}"; do
IFS=':' read -r ip port <<< "$node"

if [ "$ip" = "$(hostname -I | awk '{print $1}')" ]; then
sudo -u $REDIS_USER redis-server $REDIS_CONF_DIR/redis-cluster-$port.conf
echo "启动节点 $ip:$port"
fi
done
}

# 停止集群
stop_cluster() {
echo "停止Redis Cluster..."

for node in "${NODES[@]}"; do
IFS=':' read -r ip port <<< "$node"

if [ "$ip" = "$(hostname -I | awk '{print $1}')" ]; then
redis-cli -h $ip -p $port -a cluster_password shutdown
echo "停止节点 $ip:$port"
fi
done
}

# 重启集群
restart_cluster() {
echo "重启Redis Cluster..."
stop_cluster
sleep 5
start_cluster
}

# 检查集群状态
check_cluster_status() {
echo "检查Redis Cluster状态..."

for node in "${NODES[@]}"; do
IFS=':' read -r ip port <<< "$node"

if redis-cli -h $ip -p $port -a cluster_password ping > /dev/null 2>&1; then
echo "节点 $ip:$port 状态正常"
else
echo "节点 $ip:$port 状态异常"
fi
done

# 显示集群信息
redis-cli -h 192.168.1.100 -p 7000 -a cluster_password cluster info
}

# 添加节点
add_node() {
local new_node=$1
local existing_node=$2

if [ -z "$new_node" ] || [ -z "$existing_node" ]; then
echo "用法: add_node <新节点> <现有节点>"
echo "示例: add_node 192.168.1.102:7000 192.168.1.100:7000"
return 1
fi

echo "添加节点 $new_node 到集群..."
redis-cli --cluster add-node $new_node $existing_node -a cluster_password
}

# 删除节点
remove_node() {
local node_to_remove=$1

if [ -z "$node_to_remove" ]; then
echo "用法: remove_node <节点>"
echo "示例: remove_node 192.168.1.102:7000"
return 1
fi

echo "从集群中删除节点 $node_to_remove..."
redis-cli --cluster del-node $node_to_remove -a cluster_password
}

# 重新分片
reshard_cluster() {
local source_node=$1

if [ -z "$source_node" ]; then
echo "用法: reshard_cluster <源节点>"
echo "示例: reshard_cluster 192.168.1.100:7000"
return 1
fi

echo "重新分片集群..."
redis-cli --cluster reshard $source_node -a cluster_password
}

# 显示帮助信息
show_help() {
echo "Redis Cluster管理脚本"
echo ""
echo "用法: $0 <命令> [参数]"
echo ""
echo "命令:"
echo " start 启动集群"
echo " stop 停止集群"
echo " restart 重启集群"
echo " status 检查集群状态"
echo " add-node <节点> 添加节点"
echo " remove-node <节点> 删除节点"
echo " reshard <节点> 重新分片"
echo " help 显示帮助信息"
}

# 主函数
main() {
case "$1" in
start)
start_cluster
;;
stop)
stop_cluster
;;
restart)
restart_cluster
;;
status)
check_cluster_status
;;
add-node)
add_node "$2" "$3"
;;
remove-node)
remove_node "$2"
;;
reshard)
reshard_cluster "$2"
;;
help|--help|-h)
show_help
;;
*)
echo "未知命令: $1"
show_help
exit 1
;;
esac
}

# 执行主函数
main "$@"

Java客户端配置

1. Spring Boot配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// Redis Cluster Spring Boot配置
@Configuration
@EnableCaching
public class RedisClusterConfig {

private static final Logger logger = LoggerFactory.getLogger(RedisClusterConfig.class);

@Value("${redis.cluster.nodes}")
private String clusterNodes;

@Value("${redis.cluster.password}")
private String password;

@Value("${redis.cluster.max-redirects:3}")
private int maxRedirects;

@Value("${redis.cluster.timeout:2000}")
private int timeout;

@Value("${redis.cluster.pool.max-total:20}")
private int maxTotal;

@Value("${redis.cluster.pool.max-idle:10}")
private int maxIdle;

@Value("${redis.cluster.pool.min-idle:5}")
private int minIdle;

/**
* Redis Cluster配置
*/
@Bean
public RedisClusterConfiguration redisClusterConfiguration() {
logger.info("配置Redis Cluster...");

// 解析集群节点
Set<RedisNode> nodes = parseClusterNodes(clusterNodes);

RedisClusterConfiguration clusterConfig = new RedisClusterConfiguration(nodes);
clusterConfig.setPassword(password);
clusterConfig.setMaxRedirects(maxRedirects);

logger.info("Redis Cluster节点数量: {}", nodes.size());
return clusterConfig;
}

/**
* 解析集群节点
*/
private Set<RedisNode> parseClusterNodes(String clusterNodes) {
Set<RedisNode> nodes = new HashSet<>();

String[] nodeStrings = clusterNodes.split(",");
for (String nodeString : nodeStrings) {
String[] parts = nodeString.trim().split(":");
if (parts.length >= 2) {
String host = parts[0];
int port = Integer.parseInt(parts[1]);
nodes.add(new RedisNode(host, port));
}
}

return nodes;
}

/**
* 连接池配置
*/
@Bean
public GenericObjectPoolConfig<?> poolConfig() {
GenericObjectPoolConfig<?> poolConfig = new GenericObjectPoolConfig<>();
poolConfig.setMaxTotal(maxTotal);
poolConfig.setMaxIdle(maxIdle);
poolConfig.setMinIdle(minIdle);
poolConfig.setMaxWaitMillis(3000);
poolConfig.setTestOnBorrow(true);
poolConfig.setTestOnReturn(true);
poolConfig.setTestWhileIdle(true);
return poolConfig;
}

/**
* Lettuce连接工厂
*/
@Bean
public LettuceConnectionFactory lettuceConnectionFactory() {
LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
.commandTimeout(Duration.ofMillis(timeout))
.poolConfig(poolConfig())
.build();

return new LettuceConnectionFactory(redisClusterConfiguration(), clientConfig);
}

/**
* Redis模板
*/
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(lettuceConnectionFactory());

// 设置序列化器
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());

template.afterPropertiesSet();
return template;
}

/**
* 缓存管理器
*/
@Bean
public CacheManager cacheManager() {
RedisCacheManager.Builder builder = RedisCacheManager
.RedisCacheManagerBuilder
.fromConnectionFactory(lettuceConnectionFactory())
.cacheDefaults(cacheConfiguration());

return builder.build();
}

/**
* 缓存配置
*/
private RedisCacheConfiguration cacheConfiguration() {
return RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(60))
.serializeKeysWith(RedisSerializationContext.SerializationPair
.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer()));
}
}

2. 集群监控服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
// Redis Cluster监控服务
@Service
public class RedisClusterMonitorService {

private static final Logger logger = LoggerFactory.getLogger(RedisClusterMonitorService.class);

@Autowired
private LettuceConnectionFactory connectionFactory;

@Autowired
private RedisTemplate<String, Object> redisTemplate;

/**
* 监控集群状态
*/
@Scheduled(fixedRate = 30000) // 每30秒执行一次
public void monitorClusterStatus() {
logger.debug("开始监控Redis Cluster状态...");

try {
// 获取集群信息
ClusterInfo clusterInfo = getClusterInfo();
logClusterInfo(clusterInfo);

// 检查节点状态
checkNodeStatus();

// 检查槽位分布
checkSlotDistribution();

// 检查内存使用
checkMemoryUsage();

} catch (Exception e) {
logger.error("监控Redis Cluster状态失败: {}", e.getMessage(), e);
}
}

/**
* 获取集群信息
*/
private ClusterInfo getClusterInfo() {
RedisConnection connection = connectionFactory.getConnection();
try {
Properties info = connection.info();
return parseClusterInfo(info);
} finally {
connection.close();
}
}

/**
* 解析集群信息
*/
private ClusterInfo parseClusterInfo(Properties info) {
ClusterInfo clusterInfo = new ClusterInfo();

clusterInfo.setClusterEnabled(info.getProperty("cluster_enabled"));
clusterInfo.setClusterKnownNodes(Integer.parseInt(info.getProperty("cluster_known_nodes", "0")));
clusterInfo.setClusterSize(Integer.parseInt(info.getProperty("cluster_size", "0")));
clusterInfo.setClusterCurrentEpoch(Long.parseLong(info.getProperty("cluster_current_epoch", "0")));
clusterInfo.setClusterMyEpoch(Long.parseLong(info.getProperty("cluster_my_epoch", "0")));

return clusterInfo;
}

/**
* 记录集群信息
*/
private void logClusterInfo(ClusterInfo clusterInfo) {
logger.info("=== Redis Cluster信息 ===");
logger.info("集群状态: {}", clusterInfo.getClusterEnabled());
logger.info("已知节点数: {}", clusterInfo.getClusterKnownNodes());
logger.info("集群大小: {}", clusterInfo.getClusterSize());
logger.info("当前纪元: {}", clusterInfo.getClusterCurrentEpoch());
logger.info("我的纪元: {}", clusterInfo.getClusterMyEpoch());
}

/**
* 检查节点状态
*/
private void checkNodeStatus() {
logger.debug("检查节点状态...");

try {
// 获取所有节点信息
Set<RedisNode> nodes = connectionFactory.getClusterConfiguration().getClusterNodes();

for (RedisNode node : nodes) {
try {
RedisConnection connection = connectionFactory.getConnection(node);
Properties info = connection.info();
connection.close();

String role = info.getProperty("role");
String connectedClients = info.getProperty("connected_clients");

logger.debug("节点 {}:{} - 角色: {}, 连接数: {}",
node.getHost(), node.getPort(), role, connectedClients);

} catch (Exception e) {
logger.warn("检查节点 {}:{} 状态失败: {}",
node.getHost(), node.getPort(), e.getMessage());
}
}

} catch (Exception e) {
logger.error("检查节点状态失败: {}", e.getMessage());
}
}

/**
* 检查槽位分布
*/
private void checkSlotDistribution() {
logger.debug("检查槽位分布...");

try {
// 这里需要实现槽位分布检查逻辑
// 实际应用中可以使用Redis Cluster客户端API

logger.debug("槽位分布检查完成");

} catch (Exception e) {
logger.error("检查槽位分布失败: {}", e.getMessage());
}
}

/**
* 检查内存使用
*/
private void checkMemoryUsage() {
logger.debug("检查内存使用...");

try {
Set<RedisNode> nodes = connectionFactory.getClusterConfiguration().getClusterNodes();

for (RedisNode node : nodes) {
try {
RedisConnection connection = connectionFactory.getConnection(node);
Properties info = connection.info();
connection.close();

String usedMemory = info.getProperty("used_memory_human");
String maxMemory = info.getProperty("maxmemory_human");

logger.debug("节点 {}:{} - 已使用内存: {}, 最大内存: {}",
node.getHost(), node.getPort(), usedMemory, maxMemory);

} catch (Exception e) {
logger.warn("检查节点 {}:{} 内存使用失败: {}",
node.getHost(), node.getPort(), e.getMessage());
}
}

} catch (Exception e) {
logger.error("检查内存使用失败: {}", e.getMessage());
}
}

/**
* 集群信息类
*/
public static class ClusterInfo {
private String clusterEnabled;
private int clusterKnownNodes;
private int clusterSize;
private long clusterCurrentEpoch;
private long clusterMyEpoch;

// Getters and setters
public String getClusterEnabled() { return clusterEnabled; }
public void setClusterEnabled(String clusterEnabled) { this.clusterEnabled = clusterEnabled; }
public int getClusterKnownNodes() { return clusterKnownNodes; }
public void setClusterKnownNodes(int clusterKnownNodes) { this.clusterKnownNodes = clusterKnownNodes; }
public int getClusterSize() { return clusterSize; }
public void setClusterSize(int clusterSize) { this.clusterSize = clusterSize; }
public long getClusterCurrentEpoch() { return clusterCurrentEpoch; }
public void setClusterCurrentEpoch(long clusterCurrentEpoch) { this.clusterCurrentEpoch = clusterCurrentEpoch; }
public long getClusterMyEpoch() { return clusterMyEpoch; }
public void setClusterMyEpoch(long clusterMyEpoch) { this.clusterMyEpoch = clusterMyEpoch; }
}
}

性能优化和最佳实践

1. 性能优化配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
// Redis Cluster性能优化
@Service
public class RedisClusterOptimization {

private static final Logger logger = LoggerFactory.getLogger(RedisClusterOptimization.class);

/**
* 性能优化配置
*/
@Configuration
public static class PerformanceOptimizationConfig {

/**
* 优化后的连接池配置
*/
@Bean
public GenericObjectPoolConfig<?> optimizedPoolConfig() {
GenericObjectPoolConfig<?> poolConfig = new GenericObjectPoolConfig<>();

// 连接池大小优化
poolConfig.setMaxTotal(100); // 增加最大连接数
poolConfig.setMaxIdle(50); // 增加最大空闲连接数
poolConfig.setMinIdle(20); // 增加最小空闲连接数

// 连接验证优化
poolConfig.setTestOnBorrow(true); // 借用时测试连接
poolConfig.setTestOnReturn(true); // 归还时测试连接
poolConfig.setTestWhileIdle(true); // 空闲时测试连接

// 超时配置优化
poolConfig.setMaxWaitMillis(1000); // 减少等待时间
poolConfig.setTimeBetweenEvictionRunsMillis(30000); // 30秒检查一次
poolConfig.setMinEvictableIdleTimeMillis(60000); // 60秒空闲时间

return poolConfig;
}

/**
* 优化后的客户端配置
*/
@Bean
public LettuceClientConfiguration optimizedClientConfig() {
return LettuceClientConfiguration.builder()
.commandTimeout(Duration.ofMillis(1000)) // 减少命令超时时间
.poolConfig(optimizedPoolConfig())
.build();
}
}

/**
* 性能监控
*/
public void monitorPerformance() {
logger.info("开始监控Redis Cluster性能...");

// 监控QPS
monitorQPS();

// 监控延迟
monitorLatency();

// 监控连接数
monitorConnections();

// 监控内存使用
monitorMemory();
}

/**
* 监控QPS
*/
private void monitorQPS() {
logger.debug("监控QPS...");

try {
Set<RedisNode> nodes = getClusterNodes();

for (RedisNode node : nodes) {
RedisConnection connection = getConnection(node);
Properties info = connection.info();
connection.close();

String totalCommandsProcessed = info.getProperty("total_commands_processed");
String uptimeInSeconds = info.getProperty("uptime_in_seconds");

if (totalCommandsProcessed != null && uptimeInSeconds != null) {
long commands = Long.parseLong(totalCommandsProcessed);
long uptime = Long.parseLong(uptimeInSeconds);

double qps = commands / (double) uptime;

logger.debug("节点 {}:{} QPS: {:.2f}",
node.getHost(), node.getPort(), qps);
}
}

} catch (Exception e) {
logger.error("监控QPS失败: {}", e.getMessage());
}
}

/**
* 监控延迟
*/
private void monitorLatency() {
logger.debug("监控延迟...");

try {
// 执行延迟测试
long startTime = System.nanoTime();
redisTemplate.opsForValue().set("latency_test", "test");
redisTemplate.opsForValue().get("latency_test");
long endTime = System.nanoTime();

double latency = (endTime - startTime) / 1_000_000.0; // 转换为毫秒

logger.debug("平均延迟: {:.2f} 毫秒", latency);

} catch (Exception e) {
logger.error("监控延迟失败: {}", e.getMessage());
}
}

/**
* 监控连接数
*/
private void monitorConnections() {
logger.debug("监控连接数...");

try {
Set<RedisNode> nodes = getClusterNodes();

for (RedisNode node : nodes) {
RedisConnection connection = getConnection(node);
Properties info = connection.info();
connection.close();

String connectedClients = info.getProperty("connected_clients");
String maxClients = info.getProperty("maxclients");

logger.debug("节点 {}:{} - 连接数: {}/{}",
node.getHost(), node.getPort(), connectedClients, maxClients);
}

} catch (Exception e) {
logger.error("监控连接数失败: {}", e.getMessage());
}
}

/**
* 监控内存使用
*/
private void monitorMemory() {
logger.debug("监控内存使用...");

try {
Set<RedisNode> nodes = getClusterNodes();

for (RedisNode node : nodes) {
RedisConnection connection = getConnection(node);
Properties info = connection.info();
connection.close();

String usedMemory = info.getProperty("used_memory_human");
String maxMemory = info.getProperty("maxmemory_human");
String memoryFragmentationRatio = info.getProperty("mem_fragmentation_ratio");

logger.debug("节点 {}:{} - 内存使用: {}/{}, 碎片率: {}",
node.getHost(), node.getPort(), usedMemory, maxMemory, memoryFragmentationRatio);
}

} catch (Exception e) {
logger.error("监控内存使用失败: {}", e.getMessage());
}
}

/**
* 获取集群节点
*/
private Set<RedisNode> getClusterNodes() {
return connectionFactory.getClusterConfiguration().getClusterNodes();
}

/**
* 获取连接
*/
private RedisConnection getConnection(RedisNode node) {
return connectionFactory.getConnection(node);
}
}

2. 最佳实践总结

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
// Redis Cluster最佳实践
public class RedisClusterBestPractices {

/**
* 部署最佳实践
*/
public static void deploymentBestPractices() {
System.out.println("=== Redis Cluster部署最佳实践 ===");

System.out.println("1. 节点规划:");
System.out.println(" - 至少3个主节点");
System.out.println(" - 每个主节点配置1个从节点");
System.out.println(" - 主从节点分布在不同服务器");

System.out.println("\n2. 网络配置:");
System.out.println(" - 使用内网通信");
System.out.println(" - 配置防火墙规则");
System.out.println(" - 优化网络延迟");

System.out.println("\n3. 安全配置:");
System.out.println(" - 设置密码认证");
System.out.println(" - 配置访问控制");
System.out.println(" - 启用TLS加密");

System.out.println("\n4. 监控配置:");
System.out.println(" - 配置监控告警");
System.out.println(" - 设置性能指标");
System.out.println(" - 建立日志管理");
}

/**
* 性能优化最佳实践
*/
public static void performanceBestPractices() {
System.out.println("\n=== Redis Cluster性能优化最佳实践 ===");

System.out.println("1. 连接池优化:");
System.out.println(" - 合理设置连接池大小");
System.out.println(" - 启用连接验证");
System.out.println(" - 配置连接超时");

System.out.println("\n2. 内存优化:");
System.out.println(" - 设置合适的内存限制");
System.out.println(" - 配置内存淘汰策略");
System.out.println(" - 监控内存使用情况");

System.out.println("\n3. 网络优化:");
System.out.println(" - 使用批量操作");
System.out.println(" - 减少网络往返");
System.out.println(" - 启用管道技术");

System.out.println("\n4. 数据优化:");
System.out.println(" - 选择合适的数据类型");
System.out.println(" - 避免大键操作");
System.out.println(" - 使用压缩存储");
}

/**
* 运维最佳实践
*/
public static void operationBestPractices() {
System.out.println("\n=== Redis Cluster运维最佳实践 ===");

System.out.println("1. 监控告警:");
System.out.println(" - 监控节点状态");
System.out.println(" - 监控性能指标");
System.out.println(" - 设置告警阈值");

System.out.println("\n2. 备份恢复:");
System.out.println(" - 定期数据备份");
System.out.println(" - 测试恢复流程");
System.out.println(" - 建立备份策略");

System.out.println("\n3. 故障处理:");
System.out.println(" - 建立故障处理流程");
System.out.println(" - 准备应急方案");
System.out.println(" - 定期故障演练");

System.out.println("\n4. 版本管理:");
System.out.println(" - 制定升级计划");
System.out.println(" - 测试新版本");
System.out.println(" - 回滚方案准备");
}
}

总结

Redis Cluster多实例部署是一个复杂的系统工程,需要从架构设计、配置管理、监控告警等多个维度进行综合考虑:

  1. 架构设计:合理的节点规划、网络拓扑、安全配置
  2. 部署实施:自动化部署脚本、配置管理、集群创建
  3. 集群管理:节点管理、槽位分配、故障处理
  4. 性能优化:连接池优化、内存管理、网络优化
  5. 运维监控:状态监控、性能监控、告警管理

通过系统性的部署和管理,可以构建高可用、高性能的Redis Cluster集群,为企业业务发展提供强有力的技术支撑。

参考资料

  1. 《Redis设计与实现》
  2. 《Redis实战》
  3. Redis官方文档
  4. 《分布式系统概念与设计》
  5. 《高性能MySQL》