【并发编程】java 如何解决redis缓存穿透、缓存雪崩(高性能示例代码)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/seesun2012/article/details/84344600

缓存穿透

  • 1、什么是缓存穿透:
    从字面上理解,缓存穿透就是运行程序击穿了你的Redis缓存服务器,去访问MySQL数据库;
    由于Redis存在一定的命中概率,进来的请求发现Redis中并没有相关数据或者是没有命中指定数据,会去数据库查询。
  • 2、缓存穿透有什么危害:
    如果大量请求进来,直接去访问数据库服务查询,数据库服务器CPU短时间内会超负载运行,致使数据库服务宕机。

解决思路:

  • 1、简单:加synchronized关键字(同步锁);
  • 2、推荐:使用Lock对象的tryLock()方法(定时锁);
package com.cn.seesun2012.cache;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import com.cn.seesun2012.mapper.ProductInfoMapper;
import com.cn.seesun2012.util.JedisClient;

/**
 * 
 *           ┏┓    ┏┓+ +
 *        ┏┛┻━━━┛┻┓ + +
 *        ┃       ┃
 *        ┃   ━   ┃ ++ + + +
 *        ████━████ ┃+
 *        ┃       ┃ +
 *        ┃   ┻   ┃
 *        ┃       ┃ + +
 *        ┗━┓   ┏━┛
 *          ┃   ┃
 *          ┃   ┃ + + + +
 *          ┃   ┃			God beast body, code no BUG
 *          ┃   ┃ +			神兽护体,代码无BUG
 *          ┃   ┃
 *          ┃   ┃  +
 *          ┃    ┗━━━┓ + +
 *          ┃        ┣┓
 *          ┃        ┏┛
 *          ┗┓┓┏━┳┓┏┛ + + + +
 *           ┃┫┫ ┃┫┫
 *           ┗┻┛ ┗┻┛+ + + +
 *
 * @title:	商品信息-模块
 * @version	v1.0.0
 * @author	csdn:seesun2012
 * @date	2018年11月22日  下午17:21:33  周四
 *
 */
public class ProductInfoCache {
	
	// Jedis 缓存对象
	private JedisClient jedisClient;
	// 商品信息XML映射类
	private ProductInfoMapper productInfoMapper;
	// 缓存业务KEY前缀
	private static final String PRODUCT_KEY = "项目名.模块名.业务名.";
	// 锁-实例
	private Lock lock = new ReentrantLock();
	
	/**
	 * 获取商品图片路径
	 * @param id
	 */
	public synchronized String getProductImgUrlById(String id){
		// 获取缓存
		String product = jedisClient.get(PRODUCT_KEY + id);
		if (null == product) {
			// 如果没有获取锁等待3秒,SECONDS代表:秒
			try {
				if (lock.tryLock(3, TimeUnit.SECONDS)) {
					try {
						// 获取锁后再查一次,查到了直接返回结果
						product = jedisClient.get(PRODUCT_KEY + id);
						if (null == product) {
							// 查询数据库
							product = productInfoMapper.getProductInfoById(id);
							if (null == product) {
								// 假设有10000人的并发量,第一次查也没有数据,
								// 那么设定为"null",加入有效期6秒
								jedisClient.setex((PRODUCT_KEY+id), "null", 6);
								return product;
							}
							jedisClient.set((PRODUCT_KEY + id), product);
							return product;
						}
						return product;
					} catch (Exception e) {
						product = jedisClient.get(PRODUCT_KEY + id);
					} finally {
						// 释放锁(成功、失败都必须释放,如果是lock.tryLock()方法会一直阻塞在这)
						lock.unlock();
					}
				} else {
					product = jedisClient.get(PRODUCT_KEY + id);
				}
			} catch (InterruptedException e) {
				product = jedisClient.get(PRODUCT_KEY + id);
			}
		}
		return product;
	}
	
}

tryLock(long time, TimeUnit unit)方法和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false;如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。

Lock的使用法方法: https://blog.csdn.net/zengmingen/article/details/53259394?utm_source=blogxgwz7


缓存雪崩

  • 1、什么是缓存雪崩: 缓存在同一时间内大量键过期(失效),接着来的一大波请求瞬间都落在了数据库中导致连接异常。

解决思路:

  • 1、也是像解决缓存穿透一样加锁排队,实现同上;
  • 2、建立备份缓存,缓存A和缓存B,A设置超时时间,B不设值超时时间,先从A读缓存,A没有读B,并且更新A缓存和B缓存;



布隆过滤器

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。








持续更新中…

如有对思路不清晰或有更好的解决思路,欢迎与本人交流,QQ群:273557553,个人微信:seesun2012

你的提问是小编创作灵感的来源!
































没有更多推荐了,返回首页