
在分布式系统中,缓存是提升性能的关键组件。Redisson 作为 Redis 的 Java 客户端,提供了丰富的分布式数据结构,其中 RMapCache 是一个支持过期时间的分布式 Map,广泛应用于各类业务场景。然而,在实际开发中,我们经常需要了解当前有效(未过期)的缓存数据量,例如:
但 Redisson 的 RMapCache.size() 返回的是所有键值对的数量(包括已过期的),这与我们的实际需求不符。本文将深入探讨如何准确获取未失效的键值对数量,并结合真实业务案例说明其应用价值。
size() 不能满足需求?size() 的陷阱Redis 的过期机制是惰性删除 + 定期删除,这意味着已过期的键可能仍存在于 Redis 中,直到被访问或被定期清理。因此:
RMapCache<String, User> userCache = redissonClient.getMapCache("userCache");
int totalSize = userCache.size(); // 错误!包含已过期的键
size() 方法返回的是 Redis 中该 Map 的总键数,而非当前有效的键数。这会导致我们无法准确了解缓存的健康状况。
Redisson 的 size() 方法返回的是 Redis 中该 Map 的总条目数,不考虑过期状态。而 Redis 采用惰性删除 + 定期删除机制,过期键在被访问前仍存在于内存中。
关键区别: size() → Redis 内存中存储的总条目数 keySet().size() → 当前有效(未过期)的条目数
Redisson 的 RMapCache 在访问键时会自动过滤已过期的键,因此我们可以通过以下方法获取当前有效的键值对数量:
keySet().size()long validCount = userCache.keySet().size();
原理:
keySet() 方法在获取键集合时,Redisson 会自动过滤已过期的键(通过 Redis 的 TTL 命令检查),确保返回的是当前有效的键集合。
readAllEntrySet().size()Set<Map.Entry<String, User>> entries = userCache.readAllEntrySet();
long validCount = entries.size();
优势: 同时获取键和值,适用于需要遍历有效条目的场景。
RFuture<Set<String>> future = userCache.keySetAsync();
future.whenComplete((keys, exception) -> {
if (exception == null) {
long validCount = keys.size();
// 业务逻辑:如触发缓存监控告警
}
});
适用场景: 在 Web 服务中避免阻塞主线程,提升接口响应速度。
当调用 keySet() 或 readAllEntrySet() 时,Redisson 会从 Redis 获取当前存在的键,而 Redis 在返回键时会自动检查并排除已过期的键。因此,返回的集合大小就是当前有效的键值对数量。
某电商平台使用 Redisson 的 RMapCache 缓存商品库存信息,每个商品库存设置 30 秒的 TTL(过期时间):
MapCache<String, Integer> stockCache = redissonClient.getMapCache("stockCache");
stockCache.put("product_1001", 100, 30, TimeUnit.SECONDS);
public class StockCacheMonitor {
private final RMapCache<String, Integer> stockCache;
private final int threshold = 50; // 有效缓存阈值
public StockCacheMonitor(RMapCache<String, Integer> stockCache) {
this.stockCache = stockCache;
}
public void monitorCacheValidity() {
int validStockCount = stockCache.keySet().size();
if (validStockCount < threshold) {
// 触发缓存预热或告警
log.warn("警告:有效库存缓存数量不足,当前有效数量: {}", validStockCount);
// 这里可以添加缓存预热逻辑
}
}
}
size() 方法当 RMapCache 包含大量数据时,keySet() 会将所有键加载到内存,可能影响性能:
// 不推荐:大数据量下性能较差
int validCount = redissonClient.getMapCache("largeCache").keySet().size();
// 推荐:分页遍历
int count = 0;
Iterator<String> iterator = redissonClient.getMapCache("largeCache").keySet().iterator();
while (iterator.hasNext()) {
iterator.next();
count++;
}
RFuture<Set<String>> futureKeys = redissonClient.getMapCache("userCache").keySetAsync();
futureKeys.whenComplete((keys, exception) -> {
if (exception == null) {
int validCount = keys.size();
log.info("有效缓存数量: {}", validCount);
} else {
log.error("获取缓存数量失败", exception);
}
});
A: Redis 的过期删除是惰性的,可能在获取时某些键已经过期但尚未被清理。Redisson 的 keySet() 会自动过滤这些键,所以返回的是当前实际有效的数量。
A:
size() 方法的性能对比方法 | 性能 | 准确性 | 适用场景 |
|---|---|---|---|
size() | 高(直接返回 Redis 计数) | 低(包含过期键) | 仅需知道总键数 |
keySet().size() | 中(需遍历键) | 高(仅有效键) | 需要准确有效数量 |
readAllEntrySet().size() | 低(需遍历键和值) | 高 | 需要键值对信息 |
在 Redisson 的 RMapCache 中,不要使用 size() 方法,而应使用 keySet().size() 或 readAllEntrySet().size() 来获取未失效的键值对数量。这种方法简单有效,能准确反映当前缓存的有效状态。