缓存统计与监控
2026/1/15大约 4 分钟JavaGuava缓存本地缓存后端
缓存统计与监控
开启统计
使用 recordStats() 开启缓存统计功能。
Cache<String, Object> cache = CacheBuilder.newBuilder()
.maximumSize(1000)
.recordStats() // 开启统计
.build();CacheStats 详解
CacheStats 包含了缓存的各种统计指标。
CacheStats stats = cache.stats();
// 命中统计
long hitCount = stats.hitCount(); // 命中次数
long missCount = stats.missCount(); // 未命中次数
double hitRate = stats.hitRate(); // 命中率
double missRate = stats.missRate(); // 未命中率
long requestCount = stats.requestCount(); // 总请求次数
// 加载统计
long loadCount = stats.loadCount(); // 加载次数
long loadSuccessCount = stats.loadSuccessCount(); // 加载成功次数
long loadExceptionCount = stats.loadExceptionCount(); // 加载异常次数
double loadExceptionRate = stats.loadExceptionRate(); // 加载异常率
long totalLoadTime = stats.totalLoadTime(); // 总加载时间(纳秒)
double averageLoadPenalty = stats.averageLoadPenalty(); // 平均加载时间(纳秒)
// 淘汰统计
long evictionCount = stats.evictionCount(); // 淘汰次数统计指标说明
| 指标 | 说明 | 计算方式 |
|---|---|---|
| hitRate | 命中率 | hitCount / requestCount |
| missRate | 未命中率 | missCount / requestCount |
| loadExceptionRate | 加载异常率 | loadExceptionCount / loadCount |
| averageLoadPenalty | 平均加载时间 | totalLoadTime / loadCount |
统计信息输出
基本输出
public void printStats(Cache<?, ?> cache) {
CacheStats stats = cache.stats();
System.out.println("=== 缓存统计 ===");
System.out.println("请求总数: " + stats.requestCount());
System.out.println("命中次数: " + stats.hitCount());
System.out.println("命中率: " + String.format("%.2f%%", stats.hitRate() * 100));
System.out.println("未命中次数: " + stats.missCount());
System.out.println("加载次数: " + stats.loadCount());
System.out.println("加载成功: " + stats.loadSuccessCount());
System.out.println("加载异常: " + stats.loadExceptionCount());
System.out.println("平均加载时间: " + String.format("%.2fms", stats.averageLoadPenalty() / 1_000_000));
System.out.println("淘汰次数: " + stats.evictionCount());
}输出示例:
=== 缓存统计 ===
请求总数: 10000
命中次数: 8500
命中率: 85.00%
未命中次数: 1500
加载次数: 1500
加载成功: 1498
加载异常: 2
平均加载时间: 5.23ms
淘汰次数: 500JSON 格式输出
public String statsToJson(Cache<?, ?> cache) {
CacheStats stats = cache.stats();
Map<String, Object> map = new LinkedHashMap<>();
map.put("requestCount", stats.requestCount());
map.put("hitCount", stats.hitCount());
map.put("hitRate", stats.hitRate());
map.put("missCount", stats.missCount());
map.put("missRate", stats.missRate());
map.put("loadCount", stats.loadCount());
map.put("loadSuccessCount", stats.loadSuccessCount());
map.put("loadExceptionCount", stats.loadExceptionCount());
map.put("averageLoadPenaltyMs", stats.averageLoadPenalty() / 1_000_000);
map.put("evictionCount", stats.evictionCount());
return new ObjectMapper().writeValueAsString(map);
}集成监控系统
集成 Micrometer
@Component
public class CacheMetrics {
private final Cache<String, Object> cache;
private final MeterRegistry meterRegistry;
public CacheMetrics(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
this.cache = CacheBuilder.newBuilder()
.maximumSize(10000)
.recordStats()
.build();
// 注册指标
registerMetrics();
}
private void registerMetrics() {
// 命中率
Gauge.builder("cache.hit.rate", cache, c -> c.stats().hitRate())
.description("Cache hit rate")
.register(meterRegistry);
// 请求数
Gauge.builder("cache.requests", cache, c -> c.stats().requestCount())
.description("Total cache requests")
.register(meterRegistry);
// 命中数
Gauge.builder("cache.hits", cache, c -> c.stats().hitCount())
.description("Cache hits")
.register(meterRegistry);
// 未命中数
Gauge.builder("cache.misses", cache, c -> c.stats().missCount())
.description("Cache misses")
.register(meterRegistry);
// 淘汰数
Gauge.builder("cache.evictions", cache, c -> c.stats().evictionCount())
.description("Cache evictions")
.register(meterRegistry);
// 缓存大小
Gauge.builder("cache.size", cache, c -> c.size())
.description("Cache size")
.register(meterRegistry);
// 平均加载时间
Gauge.builder("cache.load.time.avg", cache,
c -> c.stats().averageLoadPenalty() / 1_000_000)
.description("Average load time in ms")
.register(meterRegistry);
}
}集成 Prometheus
@RestController
@RequestMapping("/metrics")
public class CacheMetricsController {
@Autowired
private Cache<String, Object> cache;
@GetMapping("/cache")
public String getCacheMetrics() {
CacheStats stats = cache.stats();
StringBuilder sb = new StringBuilder();
sb.append("# HELP cache_hit_rate Cache hit rate\n");
sb.append("# TYPE cache_hit_rate gauge\n");
sb.append("cache_hit_rate ").append(stats.hitRate()).append("\n");
sb.append("# HELP cache_requests_total Total cache requests\n");
sb.append("# TYPE cache_requests_total counter\n");
sb.append("cache_requests_total ").append(stats.requestCount()).append("\n");
sb.append("# HELP cache_hits_total Cache hits\n");
sb.append("# TYPE cache_hits_total counter\n");
sb.append("cache_hits_total ").append(stats.hitCount()).append("\n");
sb.append("# HELP cache_misses_total Cache misses\n");
sb.append("# TYPE cache_misses_total counter\n");
sb.append("cache_misses_total ").append(stats.missCount()).append("\n");
sb.append("# HELP cache_evictions_total Cache evictions\n");
sb.append("# TYPE cache_evictions_total counter\n");
sb.append("cache_evictions_total ").append(stats.evictionCount()).append("\n");
sb.append("# HELP cache_size Current cache size\n");
sb.append("# TYPE cache_size gauge\n");
sb.append("cache_size ").append(cache.size()).append("\n");
return sb.toString();
}
}统计信息差值
CacheStats 支持计算两个时间点之间的差值。
// 记录初始统计
CacheStats startStats = cache.stats();
// 执行一些操作...
Thread.sleep(60000);
// 记录结束统计
CacheStats endStats = cache.stats();
// 计算差值
CacheStats deltaStats = endStats.minus(startStats);
System.out.println("过去1分钟:");
System.out.println("请求数: " + deltaStats.requestCount());
System.out.println("命中率: " + deltaStats.hitRate());
System.out.println("加载次数: " + deltaStats.loadCount());定时统计报告
@Component
public class CacheStatsReporter {
private final Cache<String, Object> cache;
private CacheStats lastStats;
@Scheduled(fixedRate = 60000) // 每分钟
public void reportStats() {
CacheStats currentStats = cache.stats();
if (lastStats != null) {
CacheStats delta = currentStats.minus(lastStats);
log.info("缓存统计(过去1分钟): 请求={}, 命中率={:.2f}%, 加载={}, 淘汰={}",
delta.requestCount(),
delta.hitRate() * 100,
delta.loadCount(),
delta.evictionCount()
);
// 命中率告警
if (delta.requestCount() > 100 && delta.hitRate() < 0.5) {
log.warn("缓存命中率过低: {:.2f}%", delta.hitRate() * 100);
}
}
lastStats = currentStats;
}
}多缓存统计汇总
@Component
public class CacheStatsAggregator {
private final Map<String, Cache<?, ?>> caches = new ConcurrentHashMap<>();
public void register(String name, Cache<?, ?> cache) {
caches.put(name, cache);
}
public Map<String, CacheStats> getAllStats() {
return caches.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
e -> e.getValue().stats()
));
}
public void printAllStats() {
System.out.println("=== 所有缓存统计 ===");
System.out.printf("%-20s %10s %10s %10s %10s%n",
"缓存名称", "请求数", "命中率", "大小", "淘汰数");
System.out.println("-".repeat(70));
caches.forEach((name, cache) -> {
CacheStats stats = cache.stats();
System.out.printf("%-20s %10d %9.2f%% %10d %10d%n",
name,
stats.requestCount(),
stats.hitRate() * 100,
cache.size(),
stats.evictionCount()
);
});
}
}输出示例:
=== 所有缓存统计 ===
缓存名称 请求数 命中率 大小 淘汰数
----------------------------------------------------------------------
userCache 50000 92.50% 1000 500
configCache 10000 99.00% 100 0
productCache 30000 85.00% 5000 1500小结
Guava Cache 提供了完善的统计功能,通过 recordStats() 开启后可以获取命中率、加载时间、淘汰次数等关键指标。结合监控系统可以实现缓存的实时监控和告警。
面试题预览
常见面试题
- Guava Cache 提供了哪些统计指标?
- 如何监控 Guava Cache 的命中率?
- 缓存命中率低可能是什么原因?如何优化?
