使用Redisson实现高并发场景下的缓存穿透、缓存击穿、缓存雪崩以及缓存数据不一致性的问题
- 缓存击穿:同一时间进行查询,缓存中没有找到,查询数据库,可以通过设置不同的过期时间解决
- 缓存穿透:同一时间进行查询,缓存中没有找到,数据库中也没有查到,可以通过设置空对象解决
- 缓存雪崩:缓存宕机了,加一个map保存对象,从缓存中查询前先从map中查询
- 缓存与数据库不一致:对更新数据加写锁,对读数据加读锁解决
以下核心代码涉及到商品的更新以及查看,解决高并发下的缓存的问题
private static Map<String,Product> productMap = new ConcurrenHashMap<>();
// 更新
@Transactional
public Product update(Product product) {
Product productResult = null;
// 解决高并发场景下查看和更新是数据库和缓存数据不一致的问题 使用写锁
RReadWriteLock readWriteLock = redisson.geReadWritetLock(LOCK_PRODUCT_UPDATE_PREFIX + product.getId());
RLock writeLock = readWriteLock.writeLock();
writeLock.lock();
try {
productResult = productDao.update(product);
redisutil.set(RedisKeyPrefixConst.PRODUCT_CACHE + productResult.getId(), JSON.toJS0NString(productResult),
genProductcacheTimeout(), TimeUnit.SECONDS);
productMap.put(RedisKeyPrefixConst.PRODUCT_CACHE + productResult.getId(),product);
} finally {
writeLock.unlock();
}
return productResult;
}
// 查看
public Product get(Long productId) {
Product product = null;
String productCacheKey = RedisKeyPrefixConst.PRODUCT_CACHE + productId;
// 从缓存中获取
product = getProductFromCache(productCacheKey);
if (product != null) {
return product;
}
// 高并发分布式锁
RLock hotCacheLock = redisson.getLock(productCacheKey+productId);
hotCacheLock.lock();
// 知道业务处理时间进行优化
// hotCacheLock.tryLock(3, TimeUnit.SECONDS);
try {
product = getProductFromCache(productCacheKey);
if (product != null) {
return product;
}
// 解决高并发场景下查看和更新 数据库和缓存数据不一致的问题 使用读锁
RReadWriteLock readWriteLock = redisson.geReadWritetLock(LOCK_PRODUCT_UPDATE_PREFIX + productId);
RLock readLock = readWriteLock.readLock();
readLock.lock();
try {
product = productDao.get(productId);
if (product != null) {
// 解决缓存穿透问题:缓存过期时间随机
redisUtil.set(productCacheKey, JSON.toJsONString(product), genProductCacheTimeout(), TimeUnit.SECONDS);
// 解决缓存雪崩问题
productMap.put(productCacheKey,product);
} else {
// 解决缓存穿透问题
redisUtil.set(productCacheKey, "{}");
}
}finally {
readLock.unlock();
}
}finally {
hotCacheLock.unlock();
}
return product;
}
//获取随机过期时间
private Integer genProductCacheTimeout() {
return PRODUCT_CACHE_TIMEOUT + new Random().nextInt(5) *60 * 60;
}
// 抽取缓存获取数据方法
private Product getProductFromCache(String productCacheKey) {
Product product = null;
// 从map中取值
product = productMap.get(productCacheKey);
if(Objects.nonNull(product)){
return product;
}
String productstr = redisutil.get(productCacheKey);
if (!stringutils.isEmpty(productStr)) {
if (EMPTY_CACHE.equals(productstr)) {
return new Product();
}
product = JSON.parseObject(productstr, Product.class);
redisUtil.expire(productCacheKey,genProductCacheTimeout(productCacheKey,genProductCacheTimeout(),TimeUnit.SECONDS); //缓存读延期
}
return product;
}