一般缓存设计都是使用缓存+失效时间的模式,这种模式存在一种问题。对于一些热点key的数据,当该key的缓存失效,需要重新从DB数据库获取。这时,涌入大量请求,所有请求并发地访问DB数据库,可能会压垮数据库,这种情况叫做缓存击穿。
解决方案如下
- 使用互斥分布式锁
在请求并发访问数据库时,对查询数据库操作进行加锁,同一时间只有一个请求操作数据库。示例代码如下:
public Goods getById(Long id) {
if (Objects.isNull(id)) {
return null;
}
String key = GOODS + id;
String goodsJson = redisTemplate.opsForValue().get(key);
if (StringUtils.isNotBlank(goodsJson)) {
return JSON.parseObject(goodsJson, WinemallGoods.class);
}
String owner = String.valueOf(Thread.currentThread().getId());
// 使用分布式锁,同一时间只有一个请求成功访问数据库
redisLock.lock(key, owner, 10000L, 1000L);
try {
Goods goods = goodsMapper.selectByPrimaryKey(id);
if (Objects.nonNull(goods)) {
redisTemplate.opsForValue().set(key, JSON.toJSONString(goods), RedisConst.DEFAULT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
return goods;
} else {
// 穿透优化
redisTemplate.opsForValue().set(key, JSON.toJSONString(new WinemallGoods()), RedisConst.DEFAULT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
return null;
}
} finally {
redisLock.unlock(key, owner);
}
}
- 设置热点key数据永不过期