1. 缓存改善网站性能概述

缓存是提升网站性能的重要手段,通过将频繁访问的数据存储在高速存储介质中,可以显著减少数据库访问次数,提高响应速度,降低服务器负载。本文将详细介绍缓存改善网站性能的原理、策略以及在运维实战中的最佳实践。

1.1 缓存核心价值

  1. 性能提升: 显著提高网站响应速度
  2. 负载降低: 减少数据库和服务器负载
  3. 用户体验: 改善用户访问体验
  4. 成本优化: 降低服务器资源消耗
  5. 可扩展性: 支持高并发访问
  6. 稳定性: 提高系统稳定性

1.2 缓存类型

  • 浏览器缓存: HTTP缓存、本地存储
  • CDN缓存: 静态资源缓存
  • 反向代理缓存: Nginx缓存
  • 应用缓存: 内存缓存、分布式缓存
  • 数据库缓存: 查询缓存、结果缓存

1.3 缓存策略

  • Cache-Aside: 旁路缓存模式
  • Write-Through: 写透模式
  • Write-Behind: 写回模式
  • Refresh-Ahead: 预刷新模式

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
/**
* Redis缓存配置类
* @author Java实战
*/
@Configuration
@EnableConfigurationProperties(RedisCacheProperties.class)
public class RedisCacheConfig {

@Autowired
private RedisCacheProperties properties;

/**
* Redis连接工厂
* @return Redis连接工厂
*/
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
config.setHostName(properties.getHost());
config.setPort(properties.getPort());
config.setPassword(properties.getPassword());
config.setDatabase(properties.getDatabase());

LettuceConnectionFactory factory = new LettuceConnectionFactory(config);
factory.setTimeout(Duration.ofMillis(properties.getTimeout()));
return factory;
}

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

// 序列化配置
GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer();
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);

template.afterPropertiesSet();
return template;
}

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

return builder.build();
}

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

private static final Logger logger = LoggerFactory.getLogger(RedisCacheConfig.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
/**
* Redis缓存属性配置
* @author Java实战
*/
@Data
@ConfigurationProperties(prefix = "redis.cache")
public class RedisCacheProperties {

/**
* Redis主机
*/
private String host = "localhost";

/**
* Redis端口
*/
private int port = 6379;

/**
* Redis密码
*/
private String password = "";

/**
* Redis数据库
*/
private int database = 0;

/**
* 连接超时时间(毫秒)
*/
private int timeout = 2000;

/**
* 默认TTL(分钟)
*/
private int defaultTtl = 30;

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

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

/**
* 是否启用缓存
*/
private boolean enable = true;

/**
* 缓存键前缀
*/
private String keyPrefix = "cache:";

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

/**
* 压缩阈值(字节)
*/
private int compressionThreshold = 1024;
}

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
/**
* Redis缓存服务
* @author Java实战
*/
@Service
@Slf4j
public class RedisCacheService {

@Autowired
private RedisTemplate<String, Object> redisTemplate;

@Autowired
private RedisCacheProperties properties;

/**
* 设置缓存
* @param key 键
* @param value 值
* @param ttl 过期时间(秒)
*/
public void set(String key, Object value, int ttl) {
log.info("设置缓存,键: {}, TTL: {}", key, ttl);

try {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("缓存键不能为空");
}

if (value == null) {
throw new IllegalArgumentException("缓存值不能为空");
}

String fullKey = buildKey(key);
redisTemplate.opsForValue().set(fullKey, value, Duration.ofSeconds(ttl));

log.info("设置缓存成功,键: {}", key);

} catch (Exception e) {
log.error("设置缓存异常,键: {}", key, e);
throw new RuntimeException("设置缓存失败: " + e.getMessage());
}
}

/**
* 设置缓存(使用默认TTL)
* @param key 键
* @param value 值
*/
public void set(String key, Object value) {
set(key, value, properties.getDefaultTtl() * 60);
}

/**
* 获取缓存
* @param key 键
* @return
*/
public Object get(String key) {
log.info("获取缓存,键: {}", key);

try {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("缓存键不能为空");
}

String fullKey = buildKey(key);
Object value = redisTemplate.opsForValue().get(fullKey);

if (value != null) {
log.info("获取缓存成功,键: {}", key);
} else {
log.warn("缓存不存在,键: {}", key);
}

return value;

} catch (Exception e) {
log.error("获取缓存异常,键: {}", key, e);
return null;
}
}

/**
* 获取缓存(指定类型)
* @param key 键
* @param clazz 类型
* @return
*/
@SuppressWarnings("unchecked")
public <T> T get(String key, Class<T> clazz) {
Object value = get(key);
if (value != null && clazz.isAssignableFrom(value.getClass())) {
return (T) value;
}
return null;
}

/**
* 删除缓存
* @param key 键
* @return 是否成功
*/
public boolean delete(String key) {
log.info("删除缓存,键: {}", key);

try {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("缓存键不能为空");
}

String fullKey = buildKey(key);
boolean result = Boolean.TRUE.equals(redisTemplate.delete(fullKey));

log.info("删除缓存结果,键: {}, 结果: {}", key, result);

return result;

} catch (Exception e) {
log.error("删除缓存异常,键: {}", key, e);
return false;
}
}

/**
* 检查缓存是否存在
* @param key 键
* @return 是否存在
*/
public boolean exists(String key) {
log.info("检查缓存是否存在,键: {}", key);

try {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("缓存键不能为空");
}

String fullKey = buildKey(key);
boolean result = Boolean.TRUE.equals(redisTemplate.hasKey(fullKey));

log.info("缓存存在检查结果,键: {}, 结果: {}", key, result);

return result;

} catch (Exception e) {
log.error("检查缓存是否存在异常,键: {}", key, e);
return false;
}
}

/**
* 设置过期时间
* @param key 键
* @param ttl 过期时间(秒)
* @return 是否成功
*/
public boolean expire(String key, int ttl) {
log.info("设置缓存过期时间,键: {}, TTL: {}", key, ttl);

try {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("缓存键不能为空");
}

String fullKey = buildKey(key);
boolean result = Boolean.TRUE.equals(redisTemplate.expire(fullKey, Duration.ofSeconds(ttl)));

log.info("设置缓存过期时间结果,键: {}, 结果: {}", key, result);

return result;

} catch (Exception e) {
log.error("设置缓存过期时间异常,键: {}", key, e);
return false;
}
}

/**
* 获取过期时间
* @param key 键
* @return 过期时间(秒)
*/
public long getExpire(String key) {
log.info("获取缓存过期时间,键: {}", key);

try {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("缓存键不能为空");
}

String fullKey = buildKey(key);
Long expire = redisTemplate.getExpire(fullKey);

long result = expire != null ? expire : -1;

log.info("获取缓存过期时间结果,键: {}, 过期时间: {}", key, result);

return result;

} catch (Exception e) {
log.error("获取缓存过期时间异常,键: {}", key, e);
return -1;
}
}

/**
* 批量删除缓存
* @param keys 键列表
* @return 删除数量
*/
public long delete(String... keys) {
log.info("批量删除缓存,键数量: {}", keys.length);

try {
if (keys == null || keys.length == 0) {
throw new IllegalArgumentException("缓存键列表不能为空");
}

String[] fullKeys = Arrays.stream(keys)
.map(this::buildKey)
.toArray(String[]::new);

Long result = redisTemplate.delete(Arrays.asList(fullKeys));

long deleteCount = result != null ? result : 0;

log.info("批量删除缓存结果,请求数量: {}, 删除数量: {}", keys.length, deleteCount);

return deleteCount;

} catch (Exception e) {
log.error("批量删除缓存异常,键数量: {}", keys.length, e);
return 0;
}
}

/**
* 获取缓存统计信息
* @return 统计信息
*/
public CacheStatistics getStatistics() {
log.info("获取缓存统计信息");

try {
CacheStatistics statistics = new CacheStatistics();

// 获取Redis信息
Properties info = redisTemplate.getConnectionFactory()
.getConnection()
.info();

// 设置统计信息
statistics.setTotalKeys(Long.parseLong(info.getProperty("db0", "0")));
statistics.setUsedMemory(Long.parseLong(info.getProperty("used_memory", "0")));
statistics.setConnectedClients(Long.parseLong(info.getProperty("connected_clients", "0")));
statistics.setTotalCommandsProcessed(Long.parseLong(info.getProperty("total_commands_processed", "0")));

statistics.setLastUpdateTime(LocalDateTime.now());

log.info("获取缓存统计信息成功");

return statistics;

} catch (Exception e) {
log.error("获取缓存统计信息异常", e);
return null;
}
}

/**
* 构建缓存键
* @param key 原始键
* @return 完整键
*/
private String buildKey(String key) {
return properties.getKeyPrefix() + key;
}
}

3. 内存缓存实现

3.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
/**
* 内存缓存配置
* @author Java实战
*/
@Configuration
@EnableConfigurationProperties(MemoryCacheProperties.class)
public class MemoryCacheConfig {

@Autowired
private MemoryCacheProperties properties;

/**
* Caffeine缓存管理器
* @return 缓存管理器
*/
@Bean
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(caffeineCacheBuilder());
return cacheManager;
}

/**
* Caffeine缓存构建器
* @return 缓存构建器
*/
private Caffeine<Object, Object> caffeineCacheBuilder() {
return Caffeine.newBuilder()
.maximumSize(properties.getMaximumSize())
.expireAfterWrite(properties.getExpireAfterWrite(), TimeUnit.MINUTES)
.expireAfterAccess(properties.getExpireAfterAccess(), TimeUnit.MINUTES)
.recordStats();
}

/**
* 内存缓存服务
* @return 内存缓存服务
*/
@Bean
public MemoryCacheService memoryCacheService() {
return new MemoryCacheService();
}
}

3.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
/**
* 内存缓存属性配置
* @author Java实战
*/
@Data
@ConfigurationProperties(prefix = "memory.cache")
public class MemoryCacheProperties {

/**
* 最大缓存数量
*/
private long maximumSize = 10000;

/**
* 写入后过期时间(分钟)
*/
private int expireAfterWrite = 30;

/**
* 访问后过期时间(分钟)
*/
private int expireAfterAccess = 10;

/**
* 是否启用统计
*/
private boolean enableStats = true;

/**
* 是否启用缓存
*/
private boolean enable = true;

/**
* 缓存键前缀
*/
private String keyPrefix = "memory:";

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

/**
* 压缩阈值(字节)
*/
private int compressionThreshold = 1024;
}

3.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
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
/**
* 内存缓存服务
* @author Java实战
*/
@Service
@Slf4j
public class MemoryCacheService {

@Autowired
private MemoryCacheProperties properties;

private final Cache<String, Object> cache;

public MemoryCacheService() {
this.cache = Caffeine.newBuilder()
.maximumSize(10000)
.expireAfterWrite(30, TimeUnit.MINUTES)
.expireAfterAccess(10, TimeUnit.MINUTES)
.recordStats()
.build();
}

/**
* 设置缓存
* @param key 键
* @param value 值
* @param ttl 过期时间(分钟)
*/
public void set(String key, Object value, int ttl) {
log.info("设置内存缓存,键: {}, TTL: {}", key, ttl);

try {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("缓存键不能为空");
}

if (value == null) {
throw new IllegalArgumentException("缓存值不能为空");
}

String fullKey = buildKey(key);
cache.put(fullKey, value);

log.info("设置内存缓存成功,键: {}", key);

} catch (Exception e) {
log.error("设置内存缓存异常,键: {}", key, e);
throw new RuntimeException("设置内存缓存失败: " + e.getMessage());
}
}

/**
* 设置缓存(使用默认TTL)
* @param key 键
* @param value 值
*/
public void set(String key, Object value) {
set(key, value, properties.getExpireAfterWrite());
}

/**
* 获取缓存
* @param key 键
* @return
*/
public Object get(String key) {
log.info("获取内存缓存,键: {}", key);

try {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("缓存键不能为空");
}

String fullKey = buildKey(key);
Object value = cache.getIfPresent(fullKey);

if (value != null) {
log.info("获取内存缓存成功,键: {}", key);
} else {
log.warn("内存缓存不存在,键: {}", key);
}

return value;

} catch (Exception e) {
log.error("获取内存缓存异常,键: {}", key, e);
return null;
}
}

/**
* 获取缓存(指定类型)
* @param key 键
* @param clazz 类型
* @return
*/
@SuppressWarnings("unchecked")
public <T> T get(String key, Class<T> clazz) {
Object value = get(key);
if (value != null && clazz.isAssignableFrom(value.getClass())) {
return (T) value;
}
return null;
}

/**
* 删除缓存
* @param key 键
* @return 是否成功
*/
public boolean delete(String key) {
log.info("删除内存缓存,键: {}", key);

try {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("缓存键不能为空");
}

String fullKey = buildKey(key);
cache.invalidate(fullKey);

log.info("删除内存缓存成功,键: {}", key);

return true;

} catch (Exception e) {
log.error("删除内存缓存异常,键: {}", key, e);
return false;
}
}

/**
* 检查缓存是否存在
* @param key 键
* @return 是否存在
*/
public boolean exists(String key) {
log.info("检查内存缓存是否存在,键: {}", key);

try {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("缓存键不能为空");
}

String fullKey = buildKey(key);
boolean result = cache.getIfPresent(fullKey) != null;

log.info("内存缓存存在检查结果,键: {}, 结果: {}", key, result);

return result;

} catch (Exception e) {
log.error("检查内存缓存是否存在异常,键: {}", key, e);
return false;
}
}

/**
* 清空所有缓存
*/
public void clear() {
log.info("清空所有内存缓存");

try {
cache.invalidateAll();

log.info("清空所有内存缓存成功");

} catch (Exception e) {
log.error("清空所有内存缓存异常", e);
}
}

/**
* 获取缓存统计信息
* @return 统计信息
*/
public MemoryCacheStatistics getStatistics() {
log.info("获取内存缓存统计信息");

try {
MemoryCacheStatistics statistics = new MemoryCacheStatistics();

CacheStats stats = cache.stats();

// 设置统计信息
statistics.setHitCount(stats.hitCount());
statistics.setMissCount(stats.missCount());
statistics.setHitRate(stats.hitRate());
statistics.setEvictionCount(stats.evictionCount());
statistics.setAverageLoadPenalty(stats.averageLoadPenalty());
statistics.setSize(cache.estimatedSize());

statistics.setLastUpdateTime(LocalDateTime.now());

log.info("获取内存缓存统计信息成功");

return statistics;

} catch (Exception e) {
log.error("获取内存缓存统计信息异常", e);
return null;
}
}

/**
* 构建缓存键
* @param key 原始键
* @return 完整键
*/
private String buildKey(String key) {
return properties.getKeyPrefix() + key;
}
}

4. 缓存策略实现

4.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
/**
* 缓存策略接口
* @author Java实战
*/
public interface CacheStrategy {

/**
* 获取缓存
* @param key 键
* @return
*/
Object get(String key);

/**
* 设置缓存
* @param key 键
* @param value 值
* @param ttl 过期时间(秒)
*/
void set(String key, Object value, int ttl);

/**
* 删除缓存
* @param key 键
* @return 是否成功
*/
boolean delete(String key);

/**
* 检查缓存是否存在
* @param key 键
* @return 是否存在
*/
boolean exists(String key);
}

4.2 Cache-Aside策略实现

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
/**
* Cache-Aside策略实现
* @author Java实战
*/
@Service
@Slf4j
public class CacheAsideStrategy implements CacheStrategy {

@Autowired
private RedisCacheService redisCacheService;

@Autowired
private MemoryCacheService memoryCacheService;

@Autowired
private CacheStrategyProperties properties;

/**
* 获取缓存
* @param key 键
* @return
*/
@Override
public Object get(String key) {
log.info("Cache-Aside策略获取缓存,键: {}", key);

try {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("缓存键不能为空");
}

// 先从内存缓存获取
Object value = memoryCacheService.get(key);
if (value != null) {
log.info("从内存缓存获取成功,键: {}", key);
return value;
}

// 再从Redis缓存获取
value = redisCacheService.get(key);
if (value != null) {
log.info("从Redis缓存获取成功,键: {}", key);
// 回写到内存缓存
memoryCacheService.set(key, value);
return value;
}

log.warn("缓存不存在,键: {}", key);
return null;

} catch (Exception e) {
log.error("Cache-Aside策略获取缓存异常,键: {}", key, e);
return null;
}
}

/**
* 设置缓存
* @param key 键
* @param value 值
* @param ttl 过期时间(秒)
*/
@Override
public void set(String key, Object value, int ttl) {
log.info("Cache-Aside策略设置缓存,键: {}, TTL: {}", key, ttl);

try {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("缓存键不能为空");
}

if (value == null) {
throw new IllegalArgumentException("缓存值不能为空");
}

// 设置到Redis缓存
redisCacheService.set(key, value, ttl);

// 设置到内存缓存
memoryCacheService.set(key, value, ttl / 60);

log.info("Cache-Aside策略设置缓存成功,键: {}", key);

} catch (Exception e) {
log.error("Cache-Aside策略设置缓存异常,键: {}", key, e);
throw new RuntimeException("设置缓存失败: " + e.getMessage());
}
}

/**
* 删除缓存
* @param key 键
* @return 是否成功
*/
@Override
public boolean delete(String key) {
log.info("Cache-Aside策略删除缓存,键: {}", key);

try {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("缓存键不能为空");
}

// 删除Redis缓存
boolean redisResult = redisCacheService.delete(key);

// 删除内存缓存
boolean memoryResult = memoryCacheService.delete(key);

boolean result = redisResult && memoryResult;

log.info("Cache-Aside策略删除缓存结果,键: {}, 结果: {}", key, result);

return result;

} catch (Exception e) {
log.error("Cache-Aside策略删除缓存异常,键: {}", key, e);
return false;
}
}

/**
* 检查缓存是否存在
* @param key 键
* @return 是否存在
*/
@Override
public boolean exists(String key) {
log.info("Cache-Aside策略检查缓存是否存在,键: {}", key);

try {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("缓存键不能为空");
}

// 检查内存缓存
if (memoryCacheService.exists(key)) {
return true;
}

// 检查Redis缓存
return redisCacheService.exists(key);

} catch (Exception e) {
log.error("Cache-Aside策略检查缓存是否存在异常,键: {}", key, e);
return false;
}
}
}

4.3 Write-Through策略实现

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
/**
* Write-Through策略实现
* @author Java实战
*/
@Service
@Slf4j
public class WriteThroughStrategy implements CacheStrategy {

@Autowired
private RedisCacheService redisCacheService;

@Autowired
private MemoryCacheService memoryCacheService;

@Autowired
private CacheStrategyProperties properties;

/**
* 获取缓存
* @param key 键
* @return
*/
@Override
public Object get(String key) {
log.info("Write-Through策略获取缓存,键: {}", key);

try {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("缓存键不能为空");
}

// 先从内存缓存获取
Object value = memoryCacheService.get(key);
if (value != null) {
log.info("从内存缓存获取成功,键: {}", key);
return value;
}

// 再从Redis缓存获取
value = redisCacheService.get(key);
if (value != null) {
log.info("从Redis缓存获取成功,键: {}", key);
// 回写到内存缓存
memoryCacheService.set(key, value);
return value;
}

log.warn("缓存不存在,键: {}", key);
return null;

} catch (Exception e) {
log.error("Write-Through策略获取缓存异常,键: {}", key, e);
return null;
}
}

/**
* 设置缓存
* @param key 键
* @param value 值
* @param ttl 过期时间(秒)
*/
@Override
public void set(String key, Object value, int ttl) {
log.info("Write-Through策略设置缓存,键: {}, TTL: {}", key, ttl);

try {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("缓存键不能为空");
}

if (value == null) {
throw new IllegalArgumentException("缓存值不能为空");
}

// 先设置到Redis缓存
redisCacheService.set(key, value, ttl);

// 再设置到内存缓存
memoryCacheService.set(key, value, ttl / 60);

log.info("Write-Through策略设置缓存成功,键: {}", key);

} catch (Exception e) {
log.error("Write-Through策略设置缓存异常,键: {}", key, e);
throw new RuntimeException("设置缓存失败: " + e.getMessage());
}
}

/**
* 删除缓存
* @param key 键
* @return 是否成功
*/
@Override
public boolean delete(String key) {
log.info("Write-Through策略删除缓存,键: {}", key);

try {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("缓存键不能为空");
}

// 删除Redis缓存
boolean redisResult = redisCacheService.delete(key);

// 删除内存缓存
boolean memoryResult = memoryCacheService.delete(key);

boolean result = redisResult && memoryResult;

log.info("Write-Through策略删除缓存结果,键: {}, 结果: {}", key, result);

return result;

} catch (Exception e) {
log.error("Write-Through策略删除缓存异常,键: {}", key, e);
return false;
}
}

/**
* 检查缓存是否存在
* @param key 键
* @return 是否存在
*/
@Override
public boolean exists(String key) {
log.info("Write-Through策略检查缓存是否存在,键: {}", key);

try {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("缓存键不能为空");
}

// 检查内存缓存
if (memoryCacheService.exists(key)) {
return true;
}

// 检查Redis缓存
return redisCacheService.exists(key);

} catch (Exception e) {
log.error("Write-Through策略检查缓存是否存在异常,键: {}", key, e);
return false;
}
}
}

5. 缓存监控管理

5.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
/**
* 缓存监控服务
* @author Java实战
*/
@Service
@Slf4j
public class CacheMonitorService {

@Autowired
private RedisCacheService redisCacheService;

@Autowired
private MemoryCacheService memoryCacheService;

@Autowired
private CacheStrategyProperties properties;

/**
* 获取缓存状态
* @return 缓存状态
*/
public CacheStatus getCacheStatus() {
log.info("获取缓存状态");

try {
CacheStatus status = new CacheStatus();

// 设置Redis缓存状态
CacheStatistics redisStats = redisCacheService.getStatistics();
if (redisStats != null) {
status.setRedisStatus("RUNNING");
status.setRedisTotalKeys(redisStats.getTotalKeys());
status.setRedisUsedMemory(redisStats.getUsedMemory());
status.setRedisConnectedClients(redisStats.getConnectedClients());
} else {
status.setRedisStatus("ERROR");
}

// 设置内存缓存状态
MemoryCacheStatistics memoryStats = memoryCacheService.getStatistics();
if (memoryStats != null) {
status.setMemoryStatus("RUNNING");
status.setMemoryHitCount(memoryStats.getHitCount());
status.setMemoryMissCount(memoryStats.getMissCount());
status.setMemoryHitRate(memoryStats.getHitRate());
status.setMemorySize(memoryStats.getSize());
} else {
status.setMemoryStatus("ERROR");
}

// 设置总体状态
if ("RUNNING".equals(status.getRedisStatus()) && "RUNNING".equals(status.getMemoryStatus())) {
status.setOverallStatus("HEALTHY");
} else {
status.setOverallStatus("UNHEALTHY");
}

status.setLastCheckTime(LocalDateTime.now());

log.info("获取缓存状态成功");

return status;

} catch (Exception e) {
log.error("获取缓存状态异常", e);
return null;
}
}

/**
* 获取缓存性能指标
* @return 性能指标
*/
public CachePerformanceMetrics getPerformanceMetrics() {
log.info("获取缓存性能指标");

try {
CachePerformanceMetrics metrics = new CachePerformanceMetrics();

// 获取Redis性能指标
CacheStatistics redisStats = redisCacheService.getStatistics();
if (redisStats != null) {
metrics.setRedisTotalCommands(redisStats.getTotalCommandsProcessed());
metrics.setRedisUsedMemory(redisStats.getUsedMemory());
metrics.setRedisConnectedClients(redisStats.getConnectedClients());
}

// 获取内存缓存性能指标
MemoryCacheStatistics memoryStats = memoryCacheService.getStatistics();
if (memoryStats != null) {
metrics.setMemoryHitCount(memoryStats.getHitCount());
metrics.setMemoryMissCount(memoryStats.getMissCount());
metrics.setMemoryHitRate(memoryStats.getHitRate());
metrics.setMemoryEvictionCount(memoryStats.getEvictionCount());
metrics.setMemoryAverageLoadPenalty(memoryStats.getAverageLoadPenalty());
}

metrics.setLastUpdateTime(LocalDateTime.now());

log.info("获取缓存性能指标成功");

return metrics;

} catch (Exception e) {
log.error("获取缓存性能指标异常", e);
return null;
}
}

/**
* 健康检查
* @return 健康状态
*/
public boolean healthCheck() {
log.info("缓存健康检查");

try {
// 检查Redis连接
boolean redisHealthy = checkRedisHealth();

// 检查内存缓存
boolean memoryHealthy = checkMemoryHealth();

boolean healthy = redisHealthy && memoryHealthy;

log.info("缓存健康检查结果: {}", healthy);

return healthy;

} catch (Exception e) {
log.error("缓存健康检查异常", e);
return false;
}
}

/**
* 检查Redis健康状态
* @return 是否健康
*/
private boolean checkRedisHealth() {
try {
// 尝试获取Redis统计信息
CacheStatistics stats = redisCacheService.getStatistics();
return stats != null;
} catch (Exception e) {
log.error("检查Redis健康状态异常", e);
return false;
}
}

/**
* 检查内存缓存健康状态
* @return 是否健康
*/
private boolean checkMemoryHealth() {
try {
// 尝试获取内存缓存统计信息
MemoryCacheStatistics stats = memoryCacheService.getStatistics();
return stats != null;
} catch (Exception e) {
log.error("检查内存缓存健康状态异常", e);
return false;
}
}
}

5.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
/**
* 缓存监控控制器
* @author Java实战
*/
@RestController
@RequestMapping("/api/cache")
@Slf4j
public class CacheMonitorController {

@Autowired
private CacheMonitorService cacheMonitorService;

@Autowired
private RedisCacheService redisCacheService;

@Autowired
private MemoryCacheService memoryCacheService;

/**
* 获取缓存状态
* @return 缓存状态
*/
@GetMapping("/status")
public ResponseEntity<CacheStatus> getCacheStatus() {
try {
log.info("接收到获取缓存状态请求");

CacheStatus status = cacheMonitorService.getCacheStatus();

if (status != null) {
return ResponseEntity.ok(status);
} else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}

} catch (Exception e) {
log.error("获取缓存状态失败", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

/**
* 获取缓存性能指标
* @return 性能指标
*/
@GetMapping("/metrics")
public ResponseEntity<CachePerformanceMetrics> getPerformanceMetrics() {
try {
log.info("接收到获取缓存性能指标请求");

CachePerformanceMetrics metrics = cacheMonitorService.getPerformanceMetrics();

if (metrics != null) {
return ResponseEntity.ok(metrics);
} else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}

} catch (Exception e) {
log.error("获取缓存性能指标失败", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

/**
* 健康检查
* @return 健康状态
*/
@GetMapping("/health")
public ResponseEntity<Map<String, Object>> healthCheck() {
try {
log.info("接收到缓存健康检查请求");

Map<String, Object> health = new HashMap<>();

boolean healthy = cacheMonitorService.healthCheck();

health.put("status", healthy ? "UP" : "DOWN");
health.put("timestamp", System.currentTimeMillis());
health.put("details", cacheMonitorService.getCacheStatus());

return ResponseEntity.ok(health);

} catch (Exception e) {
log.error("缓存健康检查失败", e);

Map<String, Object> health = new HashMap<>();
health.put("status", "DOWN");
health.put("timestamp", System.currentTimeMillis());
health.put("error", e.getMessage());

return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(health);
}
}

/**
* 清空所有缓存
* @return 是否成功
*/
@PostMapping("/clear")
public ResponseEntity<Boolean> clearAllCache() {
try {
log.info("接收到清空所有缓存请求");

// 清空内存缓存
memoryCacheService.clear();

// 这里可以添加清空Redis缓存的逻辑

return ResponseEntity.ok(true);

} catch (Exception e) {
log.error("清空所有缓存失败", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
}

6. 总结

6.1 缓存改善网站性能总结

  1. 性能提升: 缓存可以显著提高网站响应速度
  2. 负载降低: 减少数据库和服务器负载
  3. 用户体验: 改善用户访问体验
  4. 成本优化: 降低服务器资源消耗
  5. 可扩展性: 支持高并发访问
  6. 稳定性: 提高系统稳定性

6.2 缓存策略选择

  • Cache-Aside: 适合读多写少的场景
  • Write-Through: 适合写多读少的场景
  • Write-Behind: 适合高并发写入场景
  • Refresh-Ahead: 适合预测性访问场景

6.3 最佳实践建议

  • 合理选择缓存类型: 根据业务场景选择合适的缓存类型
  • 设置合理的过期时间: 避免缓存过期和内存浪费
  • 监控缓存性能: 实时监控缓存命中率和性能
  • 缓存预热: 提前加载热点数据
  • 缓存更新策略: 合理处理缓存更新
  • 缓存穿透防护: 防止缓存穿透和雪崩

通过本文的缓存改善网站性能运维实战指南,您可以掌握缓存的核心原理、实现策略、监控管理以及在企业级应用中的最佳实践,构建高效、稳定、可扩展的缓存系统!