使用Redisson实现高并发场景下的缓存穿透、缓存击穿、缓存雪崩以及缓存数据不一致性的问题
- 缓存击穿:同一时间进行查询,缓存中没有找到,查询数据库,可以通过设置不同的过期时间解决
- 缓存穿透:同一时间进行查询,缓存中没有找到,数据库中也没有查到,可以通过设置空对象解决
- 缓存雪崩:缓存宕机了,加一个map保存对象,从缓存中查询前先从map中查询
- 缓存与数据库不一致:对更新数据加写锁,对读数据加读锁解决
以下核心代码涉及到商品的更新以及查看,解决高并发下的缓存的问题
private static Map<String,Product> productMap = new ConcurrenHashMap<>();// 更新@Transactionalpublic 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;}