1. Redis主备概述

Redis主备是Redis高可用架构的核心技术,通过主从复制实现数据的冗余备份和读写分离。副本数2表示一主一从的配置,实例规格8GB提供了充足的内存空间。Redis主备具备自动故障转移、数据同步、负载均衡、性能优化等功能。本文将详细介绍Redis主备的原理、实现方法、性能优化技巧以及在运维实战中的应用。

1.1 Redis主备核心价值

  1. 高可用性: 通过主从复制实现服务高可用
  2. 数据冗余: 保证数据不丢失
  3. 读写分离: 提高系统性能
  4. 故障转移: 自动故障检测和切换
  5. 负载均衡: 分散读写压力

1.2 Redis主备场景

  • 缓存系统: 分布式缓存系统
  • 会话存储: 用户会话存储
  • 计数器: 分布式计数器
  • 排行榜: 实时排行榜
  • 消息队列: 简单消息队列

1.3 Redis主备特性

  • 主从复制: 异步数据复制
  • 哨兵模式: 自动故障转移
  • 集群模式: 分布式集群
  • 持久化: RDB和AOF持久化
  • 内存管理: 8GB内存优化

2. Redis主备基础实现

2.1 Redis主备配置类

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
/**
* Redis主备配置类
* @author 运维实战
*/
@Configuration
@EnableConfigurationProperties(RedisMasterSlaveProperties.class)
public class RedisMasterSlaveConfig {

@Autowired
private RedisMasterSlaveProperties properties;

/**
* Redis主备服务
* @return Redis主备服务
*/
@Bean
public RedisMasterSlaveService redisMasterSlaveService() {
return new RedisMasterSlaveService();
}

/**
* Redis主从复制服务
* @return Redis主从复制服务
*/
@Bean
public RedisReplicationService redisReplicationService() {
return new RedisReplicationService();
}

/**
* Redis哨兵服务
* @return Redis哨兵服务
*/
@Bean
public RedisSentinelService redisSentinelService() {
return new RedisSentinelService();
}

/**
* Redis集群服务
* @return Redis集群服务
*/
@Bean
public RedisClusterService redisClusterService() {
return new RedisClusterService();
}

/**
* Redis主备监控服务
* @return Redis主备监控服务
*/
@Bean
public RedisMasterSlaveMonitorService redisMasterSlaveMonitorService() {
return new RedisMasterSlaveMonitorService();
}

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

2.2 Redis主备属性配置

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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
/**
* Redis主备属性配置
* @author 运维实战
*/
@Data
@ConfigurationProperties(prefix = "redis.master-slave")
public class RedisMasterSlaveProperties {

/**
* 是否启用Redis主备
*/
private boolean enableMasterSlave = true;

/**
* 主节点配置
*/
private MasterNodeConfig master = new MasterNodeConfig();

/**
* 从节点配置
*/
private SlaveNodeConfig slave = new SlaveNodeConfig();

/**
* 哨兵配置
*/
private SentinelConfig sentinel = new SentinelConfig();

/**
* 集群配置
*/
private ClusterConfig cluster = new ClusterConfig();

/**
* 实例规格配置
*/
private InstanceConfig instance = new InstanceConfig();

/**
* 是否启用高可用
*/
private boolean enableHighAvailability = true;

/**
* 是否启用自动故障转移
*/
private boolean enableAutoFailover = true;

/**
* 是否启用读写分离
*/
private boolean enableReadWriteSeparation = true;

/**
* 是否启用数据持久化
*/
private boolean enablePersistence = true;

/**
* 是否启用监控
*/
private boolean enableMonitoring = true;

/**
* 监控间隔(毫秒)
*/
private long monitorInterval = 30000;

/**
* 是否启用告警
*/
private boolean enableAlert = true;

/**
* 主从延迟告警阈值(毫秒)
*/
private long replicationLagAlertThreshold = 1000;

/**
* 内存使用率告警阈值
*/
private double memoryUsageAlertThreshold = 0.8;

/**
* 主节点配置类
*/
@Data
public static class MasterNodeConfig {
/**
* 主节点地址
*/
private String host = "localhost";

/**
* 主节点端口
*/
private int port = 6379;

/**
* 主节点密码
*/
private String password;

/**
* 连接超时时间(毫秒)
*/
private long connectionTimeout = 3000;

/**
* 读取超时时间(毫秒)
*/
private long readTimeout = 3000;

/**
* 写入超时时间(毫秒)
*/
private long writeTimeout = 3000;

/**
* 最大连接数
*/
private int maxConnections = 20;

/**
* 最小空闲连接数
*/
private int minIdleConnections = 5;
}

/**
* 从节点配置类
*/
@Data
public static class SlaveNodeConfig {
/**
* 从节点地址
*/
private String host = "localhost";

/**
* 从节点端口
*/
private int port = 6380;

/**
* 从节点密码
*/
private String password;

/**
* 连接超时时间(毫秒)
*/
private long connectionTimeout = 3000;

/**
* 读取超时时间(毫秒)
*/
private long readTimeout = 3000;

/**
* 写入超时时间(毫秒)
*/
private long writeTimeout = 3000;

/**
* 最大连接数
*/
private int maxConnections = 20;

/**
* 最小空闲连接数
*/
private int minIdleConnections = 5;

/**
* 是否只读
*/
private boolean readOnly = true;
}

/**
* 哨兵配置类
*/
@Data
public static class SentinelConfig {
/**
* 是否启用哨兵
*/
private boolean enableSentinel = true;

/**
* 哨兵地址列表
*/
private List<String> addresses = Arrays.asList("localhost:26379");

/**
* 主节点名称
*/
private String masterName = "mymaster";

/**
* 哨兵密码
*/
private String password;

/**
* 故障转移超时时间(毫秒)
*/
private long failoverTimeout = 30000;

/**
* 哨兵数量
*/
private int sentinelCount = 3;
}

/**
* 集群配置类
*/
@Data
public static class ClusterConfig {
/**
* 是否启用集群
*/
private boolean enableCluster = false;

/**
* 集群节点地址列表
*/
private List<String> nodes = Arrays.asList("localhost:7000", "localhost:7001", "localhost:7002");

/**
* 集群密码
*/
private String password;

/**
* 最大重定向次数
*/
private int maxRedirects = 3;

/**
* 集群超时时间(毫秒)
*/
private long clusterTimeout = 5000;
}

/**
* 实例规格配置类
*/
@Data
public static class InstanceConfig {
/**
* 实例规格(GB)
*/
private int instanceSize = 8;

/**
* 最大内存使用率
*/
private double maxMemoryUsage = 0.8;

/**
* 是否启用内存淘汰策略
*/
private boolean enableEviction = true;

/**
* 内存淘汰策略
*/
private String evictionPolicy = "allkeys-lru";

/**
* 是否启用压缩
*/
private boolean enableCompression = true;

/**
* 压缩阈值
*/
private int compressionThreshold = 100;
}
}

2.3 Redis主备数据模型类

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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
/**
* Redis主备数据模型类
* @author 运维实战
*/
@Data
public class RedisMasterSlaveData {

/**
* 数据键
*/
private String key;

/**
* 数据值
*/
private Object value;

/**
* 数据类型
*/
private String dataType;

/**
* 过期时间(秒)
*/
private Long expireTime;

/**
* 创建时间
*/
private Long createTime;

/**
* 更新时间
*/
private Long updateTime;

/**
* 访问次数
*/
private Long accessCount;

/**
* 数据大小(字节)
*/
private Integer dataSize;

/**
* 数据状态
*/
private String dataStatus;

/**
* 扩展属性
*/
private Map<String, Object> extendedProperties;

public RedisMasterSlaveData() {
this.createTime = System.currentTimeMillis();
this.updateTime = System.currentTimeMillis();
this.accessCount = 0L;
this.dataStatus = "ACTIVE";
this.extendedProperties = new HashMap<>();
}

public RedisMasterSlaveData(String key, Object value) {
this();
this.key = key;
this.value = value;
this.dataType = value != null ? value.getClass().getSimpleName() : "NULL";
}

/**
* 验证Redis主备数据
* @return 是否有效
*/
public boolean validate() {
if (key == null || key.isEmpty()) {
return false;
}

if (value == null) {
return false;
}

if (createTime == null || createTime <= 0) {
return false;
}

return true;
}

/**
* 计算数据大小
* @return 数据大小(字节)
*/
public int calculateDataSize() {
int size = 0;

if (key != null) {
size += key.getBytes().length;
}

if (value != null) {
if (value instanceof String) {
size += ((String) value).getBytes().length;
} else if (value instanceof byte[]) {
size += ((byte[]) value).length;
} else {
size += value.toString().getBytes().length;
}
}

return size;
}

/**
* 是否过期
* @return 是否过期
*/
public boolean isExpired() {
if (expireTime == null) {
return false;
}
return System.currentTimeMillis() / 1000 > expireTime;
}

/**
* 增加访问次数
*/
public void incrementAccessCount() {
this.accessCount++;
this.updateTime = System.currentTimeMillis();
}

/**
* 添加扩展属性
* @param key 键
* @param value 值
*/
public void addExtendedProperty(String key, Object value) {
if (extendedProperties == null) {
extendedProperties = new HashMap<>();
}
extendedProperties.put(key, value);
}

/**
* 获取扩展属性
* @param key 键
* @return
*/
public Object getExtendedProperty(String key) {
return extendedProperties != null ? extendedProperties.get(key) : null;
}
}

/**
* Redis主备操作结果类
* @author 运维实战
*/
@Data
public class RedisMasterSlaveResult {

private boolean success;
private String operation;
private String key;
private Object result;
private String error;
private long startTime;
private long endTime;

public RedisMasterSlaveResult() {
this.success = false;
}

public RedisMasterSlaveResult(String operation, String key) {
this();
this.operation = operation;
this.key = key;
}

/**
* 获取操作耗时
* @return 操作耗时(毫秒)
*/
public long getDuration() {
return endTime - startTime;
}

/**
* 是否成功
* @return 是否成功
*/
public boolean isSuccess() {
return success;
}
}

/**
* Redis主备状态信息类
* @author 运维实战
*/
@Data
public class RedisMasterSlaveStatus {

/**
* 主节点状态
*/
private String masterStatus;

/**
* 从节点状态
*/
private String slaveStatus;

/**
* 主从复制状态
*/
private String replicationStatus;

/**
* 主从延迟(毫秒)
*/
private Long replicationLag;

/**
* 内存使用情况
*/
private MemoryUsage memoryUsage;

/**
* 连接数
*/
private int connectionCount;

/**
* 命令执行数
*/
private long commandCount;

/**
* 键数量
*/
private long keyCount;

/**
* 运行时间(秒)
*/
private long uptime;

/**
* 最后检查时间
*/
private Long lastCheckTime;

public RedisMasterSlaveStatus() {
this.lastCheckTime = System.currentTimeMillis();
}

/**
* 是否健康
* @return 是否健康
*/
public boolean isHealthy() {
return "UP".equals(masterStatus) &&
"UP".equals(slaveStatus) &&
"CONNECTED".equals(replicationStatus) &&
(replicationLag == null || replicationLag < 1000);
}
}

/**
* 内存使用情况类
* @author 运维实战
*/
@Data
public class MemoryUsage {

/**
* 已使用内存(字节)
*/
private long usedMemory;

/**
* 最大内存(字节)
*/
private long maxMemory;

/**
* 内存使用率
*/
private double memoryUsageRate;

/**
* 内存碎片率
*/
private double memoryFragmentationRatio;

/**
* 计算内存使用率
* @return 内存使用率
*/
public double calculateMemoryUsageRate() {
if (maxMemory > 0) {
return (double) usedMemory / maxMemory;
}
return 0.0;
}
}

2.4 基础Redis主备服务

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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
/**
* 基础Redis主备服务
* @author 运维实战
*/
@Service
public class RedisMasterSlaveService {

@Autowired
private RedisMasterSlaveProperties properties;

@Autowired
private RedisReplicationService redisReplicationService;

@Autowired
private RedisSentinelService redisSentinelService;

@Autowired
private RedisClusterService redisClusterService;

@Autowired
private RedisMasterSlaveMonitorService redisMasterSlaveMonitorService;

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

/**
* 设置数据
* @param key 键
* @param value 值
* @param expireTime 过期时间(秒)
* @return 操作结果
*/
public RedisMasterSlaveResult setData(String key, Object value, Long expireTime) {
logger.info("设置Redis主备数据,键: {}, 过期时间: {}", key, expireTime);

RedisMasterSlaveResult result = new RedisMasterSlaveResult("SET", key);
result.setStartTime(System.currentTimeMillis());

try {
// 验证参数
if (key == null || key.isEmpty()) {
result.setSuccess(false);
result.setError("键不能为空");
result.setEndTime(System.currentTimeMillis());
return result;
}

if (value == null) {
result.setSuccess(false);
result.setError("值不能为空");
result.setEndTime(System.currentTimeMillis());
return result;
}

// 创建数据对象
RedisMasterSlaveData data = new RedisMasterSlaveData(key, value);
data.setExpireTime(expireTime);

// 验证数据
if (!data.validate()) {
result.setSuccess(false);
result.setError("数据验证失败");
result.setEndTime(System.currentTimeMillis());
return result;
}

// 设置数据
boolean success = redisReplicationService.setData(data);

if (success) {
result.setSuccess(true);
result.setResult("OK");
result.setEndTime(System.currentTimeMillis());

// 记录设置成功指标
redisMasterSlaveMonitorService.recordDataOperation("SET", true);

logger.info("Redis主备数据设置成功,键: {}, 耗时: {}ms", key, result.getDuration());
} else {
result.setSuccess(false);
result.setError("设置数据失败");
result.setEndTime(System.currentTimeMillis());

// 记录设置失败指标
redisMasterSlaveMonitorService.recordDataOperation("SET", false);
}

return result;

} catch (Exception e) {
logger.error("Redis主备数据设置异常,键: {}", key, e);
result.setSuccess(false);
result.setError("Redis主备数据设置异常: " + e.getMessage());
result.setEndTime(System.currentTimeMillis());

// 记录设置异常指标
redisMasterSlaveMonitorService.recordDataOperation("SET", false);

return result;
}
}

/**
* 获取数据
* @param key 键
* @return 操作结果
*/
public RedisMasterSlaveResult getData(String key) {
logger.info("获取Redis主备数据,键: {}", key);

RedisMasterSlaveResult result = new RedisMasterSlaveResult("GET", key);
result.setStartTime(System.currentTimeMillis());

try {
// 验证参数
if (key == null || key.isEmpty()) {
result.setSuccess(false);
result.setError("键不能为空");
result.setEndTime(System.currentTimeMillis());
return result;
}

// 获取数据
RedisMasterSlaveData data = redisReplicationService.getData(key);

if (data != null) {
result.setSuccess(true);
result.setResult(data.getValue());
result.setEndTime(System.currentTimeMillis());

// 增加访问次数
data.incrementAccessCount();

// 记录获取成功指标
redisMasterSlaveMonitorService.recordDataOperation("GET", true);

logger.info("Redis主备数据获取成功,键: {}, 耗时: {}ms", key, result.getDuration());
} else {
result.setSuccess(false);
result.setError("数据不存在");
result.setEndTime(System.currentTimeMillis());

// 记录获取失败指标
redisMasterSlaveMonitorService.recordDataOperation("GET", false);
}

return result;

} catch (Exception e) {
logger.error("Redis主备数据获取异常,键: {}", key, e);
result.setSuccess(false);
result.setError("Redis主备数据获取异常: " + e.getMessage());
result.setEndTime(System.currentTimeMillis());

// 记录获取异常指标
redisMasterSlaveMonitorService.recordDataOperation("GET", false);

return result;
}
}

/**
* 删除数据
* @param key 键
* @return 操作结果
*/
public RedisMasterSlaveResult deleteData(String key) {
logger.info("删除Redis主备数据,键: {}", key);

RedisMasterSlaveResult result = new RedisMasterSlaveResult("DELETE", key);
result.setStartTime(System.currentTimeMillis());

try {
// 验证参数
if (key == null || key.isEmpty()) {
result.setSuccess(false);
result.setError("键不能为空");
result.setEndTime(System.currentTimeMillis());
return result;
}

// 删除数据
boolean success = redisReplicationService.deleteData(key);

if (success) {
result.setSuccess(true);
result.setResult("OK");
result.setEndTime(System.currentTimeMillis());

// 记录删除成功指标
redisMasterSlaveMonitorService.recordDataOperation("DELETE", true);

logger.info("Redis主备数据删除成功,键: {}, 耗时: {}ms", key, result.getDuration());
} else {
result.setSuccess(false);
result.setError("删除数据失败");
result.setEndTime(System.currentTimeMillis());

// 记录删除失败指标
redisMasterSlaveMonitorService.recordDataOperation("DELETE", false);
}

return result;

} catch (Exception e) {
logger.error("Redis主备数据删除异常,键: {}", key, e);
result.setSuccess(false);
result.setError("Redis主备数据删除异常: " + e.getMessage());
result.setEndTime(System.currentTimeMillis());

// 记录删除异常指标
redisMasterSlaveMonitorService.recordDataOperation("DELETE", false);

return result;
}
}

/**
* 获取Redis主备状态
* @return 状态信息
*/
public RedisMasterSlaveStatus getRedisMasterSlaveStatus() {
logger.info("获取Redis主备状态");

try {
RedisMasterSlaveStatus status = new RedisMasterSlaveStatus();

// 获取主节点状态
status.setMasterStatus(redisReplicationService.getMasterStatus());

// 获取从节点状态
status.setSlaveStatus(redisReplicationService.getSlaveStatus());

// 获取主从复制状态
status.setReplicationStatus(redisReplicationService.getReplicationStatus());

// 获取主从延迟
status.setReplicationLag(redisReplicationService.getReplicationLag());

// 获取内存使用情况
status.setMemoryUsage(redisReplicationService.getMemoryUsage());

// 获取连接数
status.setConnectionCount(redisReplicationService.getConnectionCount());

// 获取命令执行数
status.setCommandCount(redisReplicationService.getCommandCount());

// 获取键数量
status.setKeyCount(redisReplicationService.getKeyCount());

// 获取运行时间
status.setUptime(redisReplicationService.getUptime());

logger.info("Redis主备状态获取成功,主节点状态: {}, 从节点状态: {}, 复制状态: {}",
status.getMasterStatus(), status.getSlaveStatus(), status.getReplicationStatus());

return status;

} catch (Exception e) {
logger.error("获取Redis主备状态异常", e);
return null;
}
}

/**
* 检查Redis主备健康状态
* @return 健康状态
*/
public boolean checkRedisMasterSlaveHealth() {
logger.info("检查Redis主备健康状态");

try {
RedisMasterSlaveStatus status = getRedisMasterSlaveStatus();

if (status != null) {
boolean healthy = status.isHealthy();

logger.info("Redis主备健康状态检查完成,健康状态: {}", healthy);

return healthy;
} else {
logger.warn("Redis主备状态信息不存在");
return false;
}

} catch (Exception e) {
logger.error("检查Redis主备健康状态异常", e);
return false;
}
}
}

3. 高级功能实现

3.1 Redis主从复制服务

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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
/**
* Redis主从复制服务
* @author 运维实战
*/
@Service
public class RedisReplicationService {

@Autowired
private RedisMasterSlaveProperties properties;

@Autowired
private RedisMasterSlaveMonitorService redisMasterSlaveMonitorService;

private JedisPool masterPool;
private JedisPool slavePool;

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

/**
* 初始化Redis连接池
*/
@PostConstruct
public void initRedisPools() {
try {
// 初始化主节点连接池
JedisPoolConfig masterConfig = createJedisPoolConfig();
masterPool = new JedisPool(masterConfig,
properties.getMaster().getHost(),
properties.getMaster().getPort(),
(int) properties.getMaster().getConnectionTimeout(),
properties.getMaster().getPassword());

// 初始化从节点连接池
JedisPoolConfig slaveConfig = createJedisPoolConfig();
slavePool = new JedisPool(slaveConfig,
properties.getSlave().getHost(),
properties.getSlave().getPort(),
(int) properties.getSlave().getConnectionTimeout(),
properties.getSlave().getPassword());

logger.info("Redis主从复制连接池初始化成功");

} catch (Exception e) {
logger.error("Redis主从复制连接池初始化失败", e);
}
}

/**
* 创建Jedis连接池配置
* @return Jedis连接池配置
*/
private JedisPoolConfig createJedisPoolConfig() {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(20);
config.setMaxIdle(10);
config.setMinIdle(5);
config.setMaxWaitMillis(3000);
config.setTestOnBorrow(true);
config.setTestOnReturn(true);
config.setTestWhileIdle(true);
return config;
}

/**
* 设置数据
* @param data Redis主备数据
* @return 是否成功
*/
public boolean setData(RedisMasterSlaveData data) {
logger.info("设置Redis主从复制数据,键: {}", data.getKey());

try (Jedis jedis = masterPool.getResource()) {
// 设置数据
String result = jedis.set(data.getKey(), data.getValue().toString());

// 设置过期时间
if (data.getExpireTime() != null && data.getExpireTime() > 0) {
jedis.expire(data.getKey(), data.getExpireTime().intValue());
}

boolean success = "OK".equals(result);

if (success) {
logger.info("Redis主从复制数据设置成功,键: {}", data.getKey());
} else {
logger.warn("Redis主从复制数据设置失败,键: {}", data.getKey());
}

return success;

} catch (Exception e) {
logger.error("Redis主从复制数据设置异常,键: {}", data.getKey(), e);
return false;
}
}

/**
* 获取数据
* @param key 键
* @return Redis主备数据
*/
public RedisMasterSlaveData getData(String key) {
logger.info("获取Redis主从复制数据,键: {}", key);

try (Jedis jedis = slavePool.getResource()) {
// 获取数据
String value = jedis.get(key);

if (value != null) {
RedisMasterSlaveData data = new RedisMasterSlaveData(key, value);

// 获取过期时间
long ttl = jedis.ttl(key);
if (ttl > 0) {
data.setExpireTime(System.currentTimeMillis() / 1000 + ttl);
}

logger.info("Redis主从复制数据获取成功,键: {}", key);

return data;
} else {
logger.warn("Redis主从复制数据不存在,键: {}", key);
return null;
}

} catch (Exception e) {
logger.error("Redis主从复制数据获取异常,键: {}", key, e);
return null;
}
}

/**
* 删除数据
* @param key 键
* @return 是否成功
*/
public boolean deleteData(String key) {
logger.info("删除Redis主从复制数据,键: {}", key);

try (Jedis jedis = masterPool.getResource()) {
// 删除数据
long result = jedis.del(key);

boolean success = result > 0;

if (success) {
logger.info("Redis主从复制数据删除成功,键: {}", key);
} else {
logger.warn("Redis主从复制数据删除失败,键: {}", key);
}

return success;

} catch (Exception e) {
logger.error("Redis主从复制数据删除异常,键: {}", key, e);
return false;
}
}

/**
* 获取主节点状态
* @return 主节点状态
*/
public String getMasterStatus() {
try (Jedis jedis = masterPool.getResource()) {
String ping = jedis.ping();
return "PONG".equals(ping) ? "UP" : "DOWN";
} catch (Exception e) {
logger.error("获取主节点状态异常", e);
return "DOWN";
}
}

/**
* 获取从节点状态
* @return 从节点状态
*/
public String getSlaveStatus() {
try (Jedis jedis = slavePool.getResource()) {
String ping = jedis.ping();
return "PONG".equals(ping) ? "UP" : "DOWN";
} catch (Exception e) {
logger.error("获取从节点状态异常", e);
return "DOWN";
}
}

/**
* 获取主从复制状态
* @return 主从复制状态
*/
public String getReplicationStatus() {
try (Jedis jedis = slavePool.getResource()) {
String role = jedis.info("replication");

if (role.contains("role:slave")) {
return "CONNECTED";
} else {
return "DISCONNECTED";
}
} catch (Exception e) {
logger.error("获取主从复制状态异常", e);
return "DISCONNECTED";
}
}

/**
* 获取主从延迟
* @return 主从延迟(毫秒)
*/
public Long getReplicationLag() {
try (Jedis jedis = slavePool.getResource()) {
String info = jedis.info("replication");

// 解析主从延迟
String[] lines = info.split("\r\n");
for (String line : lines) {
if (line.startsWith("master_repl_offset:")) {
String offset = line.split(":")[1];
return Long.parseLong(offset);
}
}

return 0L;
} catch (Exception e) {
logger.error("获取主从延迟异常", e);
return null;
}
}

/**
* 获取内存使用情况
* @return 内存使用情况
*/
public MemoryUsage getMemoryUsage() {
try (Jedis jedis = masterPool.getResource()) {
String info = jedis.info("memory");

MemoryUsage memoryUsage = new MemoryUsage();

// 解析内存信息
String[] lines = info.split("\r\n");
for (String line : lines) {
if (line.startsWith("used_memory:")) {
String usedMemory = line.split(":")[1];
memoryUsage.setUsedMemory(Long.parseLong(usedMemory));
} else if (line.startsWith("maxmemory:")) {
String maxMemory = line.split(":")[1];
memoryUsage.setMaxMemory(Long.parseLong(maxMemory));
} else if (line.startsWith("mem_fragmentation_ratio:")) {
String ratio = line.split(":")[1];
memoryUsage.setMemoryFragmentationRatio(Double.parseDouble(ratio));
}
}

// 计算内存使用率
memoryUsage.setMemoryUsageRate(memoryUsage.calculateMemoryUsageRate());

return memoryUsage;

} catch (Exception e) {
logger.error("获取内存使用情况异常", e);
return null;
}
}

/**
* 获取连接数
* @return 连接数
*/
public int getConnectionCount() {
try (Jedis jedis = masterPool.getResource()) {
String info = jedis.info("clients");

// 解析连接数
String[] lines = info.split("\r\n");
for (String line : lines) {
if (line.startsWith("connected_clients:")) {
String count = line.split(":")[1];
return Integer.parseInt(count);
}
}

return 0;
} catch (Exception e) {
logger.error("获取连接数异常", e);
return 0;
}
}

/**
* 获取命令执行数
* @return 命令执行数
*/
public long getCommandCount() {
try (Jedis jedis = masterPool.getResource()) {
String info = jedis.info("stats");

// 解析命令执行数
String[] lines = info.split("\r\n");
for (String line : lines) {
if (line.startsWith("total_commands_processed:")) {
String count = line.split(":")[1];
return Long.parseLong(count);
}
}

return 0L;
} catch (Exception e) {
logger.error("获取命令执行数异常", e);
return 0L;
}
}

/**
* 获取键数量
* @return 键数量
*/
public long getKeyCount() {
try (Jedis jedis = masterPool.getResource()) {
return jedis.dbSize();
} catch (Exception e) {
logger.error("获取键数量异常", e);
return 0L;
}
}

/**
* 获取运行时间
* @return 运行时间(秒)
*/
public long getUptime() {
try (Jedis jedis = masterPool.getResource()) {
String info = jedis.info("server");

// 解析运行时间
String[] lines = info.split("\r\n");
for (String line : lines) {
if (line.startsWith("uptime_in_seconds:")) {
String uptime = line.split(":")[1];
return Long.parseLong(uptime);
}
}

return 0L;
} catch (Exception e) {
logger.error("获取运行时间异常", e);
return 0L;
}
}
}

3.2 Redis哨兵服务

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
/**
* Redis哨兵服务
* @author 运维实战
*/
@Service
public class RedisSentinelService {

@Autowired
private RedisMasterSlaveProperties properties;

@Autowired
private RedisMasterSlaveMonitorService redisMasterSlaveMonitorService;

private JedisSentinelPool sentinelPool;

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

/**
* 初始化哨兵连接池
*/
@PostConstruct
public void initSentinelPool() {
try {
if (properties.getSentinel().isEnableSentinel()) {
// 创建哨兵连接池配置
JedisPoolConfig config = createJedisPoolConfig();

// 创建哨兵连接池
sentinelPool = new JedisSentinelPool(
properties.getSentinel().getMasterName(),
properties.getSentinel().getAddresses(),
config,
(int) properties.getMaster().getConnectionTimeout(),
properties.getSentinel().getPassword());

logger.info("Redis哨兵连接池初始化成功");
}

} catch (Exception e) {
logger.error("Redis哨兵连接池初始化失败", e);
}
}

/**
* 创建Jedis连接池配置
* @return Jedis连接池配置
*/
private JedisPoolConfig createJedisPoolConfig() {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(20);
config.setMaxIdle(10);
config.setMinIdle(5);
config.setMaxWaitMillis(3000);
config.setTestOnBorrow(true);
config.setTestOnReturn(true);
config.setTestWhileIdle(true);
return config;
}

/**
* 获取主节点地址
* @return 主节点地址
*/
public String getMasterAddress() {
logger.info("获取Redis哨兵主节点地址");

try {
if (sentinelPool != null) {
String masterAddress = sentinelPool.getCurrentHostMaster().toString();

logger.info("Redis哨兵主节点地址获取成功,地址: {}", masterAddress);

return masterAddress;
} else {
logger.warn("Redis哨兵连接池未初始化");
return null;
}

} catch (Exception e) {
logger.error("获取Redis哨兵主节点地址异常", e);
return null;
}
}

/**
* 获取从节点地址列表
* @return 从节点地址列表
*/
public List<String> getSlaveAddresses() {
logger.info("获取Redis哨兵从节点地址列表");

try {
if (sentinelPool != null) {
List<String> slaveAddresses = new ArrayList<>();

// 获取从节点地址
Set<String> slaves = sentinelPool.getSlaves();
slaveAddresses.addAll(slaves);

logger.info("Redis哨兵从节点地址列表获取成功,数量: {}", slaveAddresses.size());

return slaveAddresses;
} else {
logger.warn("Redis哨兵连接池未初始化");
return new ArrayList<>();
}

} catch (Exception e) {
logger.error("获取Redis哨兵从节点地址列表异常", e);
return new ArrayList<>();
}
}

/**
* 检查哨兵状态
* @return 哨兵状态
*/
public String getSentinelStatus() {
logger.info("检查Redis哨兵状态");

try {
if (sentinelPool != null) {
try (Jedis jedis = sentinelPool.getResource()) {
String ping = jedis.ping();
return "PONG".equals(ping) ? "UP" : "DOWN";
}
} else {
return "DOWN";
}

} catch (Exception e) {
logger.error("检查Redis哨兵状态异常", e);
return "DOWN";
}
}

/**
* 执行故障转移
* @return 是否成功
*/
public boolean executeFailover() {
logger.info("执行Redis哨兵故障转移");

try {
if (sentinelPool != null) {
// 执行故障转移
// 这里需要根据具体的哨兵实现来执行故障转移

logger.info("Redis哨兵故障转移执行成功");

return true;
} else {
logger.warn("Redis哨兵连接池未初始化");
return false;
}

} catch (Exception e) {
logger.error("执行Redis哨兵故障转移异常", e);
return false;
}
}
}

4. Redis主备控制器

4.1 Redis主备REST控制器

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
/**
* Redis主备REST控制器
* @author 运维实战
*/
@RestController
@RequestMapping("/api/redis/master-slave")
public class RedisMasterSlaveController {

@Autowired
private RedisMasterSlaveService redisMasterSlaveService;

@Autowired
private RedisMasterSlaveMonitorService redisMasterSlaveMonitorService;

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

/**
* 设置数据
* @param key 键
* @param value 值
* @param expireTime 过期时间(秒)
* @return 操作结果
*/
@PostMapping("/set")
public ResponseEntity<RedisMasterSlaveResult> setData(
@RequestParam String key,
@RequestParam String value,
@RequestParam(required = false) Long expireTime) {
try {
logger.info("接收到Redis主备数据设置请求,键: {}", key);

RedisMasterSlaveResult result = redisMasterSlaveService.setData(key, value, expireTime);

return ResponseEntity.ok(result);

} catch (Exception e) {
logger.error("Redis主备数据设置失败", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

/**
* 获取数据
* @param key 键
* @return 操作结果
*/
@GetMapping("/get")
public ResponseEntity<RedisMasterSlaveResult> getData(@RequestParam String key) {
try {
logger.info("接收到Redis主备数据获取请求,键: {}", key);

RedisMasterSlaveResult result = redisMasterSlaveService.getData(key);

return ResponseEntity.ok(result);

} catch (Exception e) {
logger.error("Redis主备数据获取失败", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

/**
* 删除数据
* @param key 键
* @return 操作结果
*/
@DeleteMapping("/delete")
public ResponseEntity<RedisMasterSlaveResult> deleteData(@RequestParam String key) {
try {
logger.info("接收到Redis主备数据删除请求,键: {}", key);

RedisMasterSlaveResult result = redisMasterSlaveService.deleteData(key);

return ResponseEntity.ok(result);

} catch (Exception e) {
logger.error("Redis主备数据删除失败", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

/**
* 获取Redis主备状态
* @return 状态信息
*/
@GetMapping("/status")
public ResponseEntity<RedisMasterSlaveStatus> getRedisMasterSlaveStatus() {
try {
logger.info("接收到获取Redis主备状态请求");

RedisMasterSlaveStatus status = redisMasterSlaveService.getRedisMasterSlaveStatus();

return ResponseEntity.ok(status);

} catch (Exception e) {
logger.error("获取Redis主备状态失败", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

/**
* 检查Redis主备健康状态
* @return 健康状态
*/
@GetMapping("/health")
public ResponseEntity<Boolean> checkRedisMasterSlaveHealth() {
try {
logger.info("接收到检查Redis主备健康状态请求");

boolean healthy = redisMasterSlaveService.checkRedisMasterSlaveHealth();

return ResponseEntity.ok(healthy);

} catch (Exception e) {
logger.error("检查Redis主备健康状态失败", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

/**
* 获取Redis主备监控指标
* @return 监控指标
*/
@GetMapping("/metrics")
public ResponseEntity<RedisMasterSlaveMetrics> getRedisMasterSlaveMetrics() {
try {
RedisMasterSlaveMetrics metrics = redisMasterSlaveMonitorService.getMetrics();
return ResponseEntity.ok(metrics);
} catch (Exception e) {
logger.error("获取Redis主备监控指标失败", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
}

5. 总结

5.1 Redis主备最佳实践

  1. 合理配置主从: 根据业务需求合理配置主从节点
  2. 优化内存使用: 合理配置8GB内存使用策略
  3. 实现高可用: 部署哨兵模式实现自动故障转移
  4. 监控告警: 实时监控Redis主备状态和性能
  5. 数据持久化: 合理配置RDB和AOF持久化策略

5.2 性能优化建议

  • 内存优化: 合理配置内存淘汰策略
  • 网络优化: 优化网络配置和连接池
  • 持久化优化: 合理配置持久化策略
  • 复制优化: 优化主从复制性能
  • 集群优化: 合理配置集群参数

5.3 运维管理要点

  • 实时监控: 监控Redis主备状态和性能
  • 故障处理: 建立完善的故障处理机制
  • 备份策略: 建立完善的数据备份策略
  • 日志管理: 完善日志记录和分析
  • 性能调优: 根据监控数据优化Redis性能

通过本文的Redis主备(副本数2,实例规格8GB)运维实战指南,您可以掌握Redis主备的原理、实现方法、性能优化技巧以及在企业级应用中的最佳实践,构建高效、可靠的Redis主备系统!