谈谈redis缓存三大问题(三)- 缓存击穿

首先还是先看下缓存击穿的一个基本概念:
在这里插入图片描述
如上图,这个图应该在其他博文中出现过很多次了,同样,缓存击穿就是在某个时刻。当某个热点key失效的瞬间,大批量请求进来,造成数据库压力太大导致数据库服务宕机。

当然关于缓存击穿也是有对应的解决方法:
1,设置热点key用不过期
2,使用分布式锁
备注:关于redis实现分布式锁,可以参考我的另一篇博客:https://blog.csdn.net/baomw/article/details/84931234

当然当有了分布式锁之后,我们该如何实现呢?可以看下如下案例:

 public User queryById(String id) {
        Jedis jedis = jedisPool.getResource();
        String userStr = jedis.get(id);
        //先查缓存,查到直接返回
        if (StringUtils.isNotEmpty(userStr)) {
            return JSONObject.parseObject(userStr, User.class);
        }
        //模拟并发效果这边在查缓存和数据库之间延时50ms
        try {
            Thread.sleep(50);
        } catch (InterruptedException ignored) {
        }
        //查不到差数据库
        System.out.println("==========开始查数据库============");
        User user = dao.queryById(id);
        jedis.setex(user.getId(), 10, JSONObject.toJSONString(user));
        return user;
    }
	
	public static void main(String[] args) {
	    ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
	    final BaomwTestService cityService = ac.getBean(BaomwTestService.class);
	    //模拟并发效果这边启动10个线程同时做数据库查询操作
        for (int i=0;i<10;i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(cityService.queryById("2"));
                }
            }).start();
        }
    }

在这里插入图片描述
可以看到,十次都查询了数据库,如果是高并发场景下,成千上万个线程同时进来,那么数据库压力显然是受不住的。

当然解决方案就是加分布式锁,这边可以看下案例

public User queryById(String id) {
        Jedis jedis = jedisPool.getResource();
        //先查缓存,查到直接返回
        String userStr = jedis.get(id);
        if (StringUtils.isNotEmpty(userStr)) {
            return JSONObject.parseObject(userStr, User.class);
        }
        try {
            Thread.sleep(50);
        } catch (InterruptedException ignored) {
        }
        //对查询健加锁,使锁的粒度最小
        lock.lock("locak"+id);
        //这里需要再次查询缓存,因为可能会出现第一波并发线程同时到达:lock.lock("locak"+id);的
        //位置,虽然是每次只有一个线程会拿到锁并执行,但是已经到这个位置的线程还是都会去查询一遍数据库
        //所以这边必须加一个查询缓存的操作
        userStr = jedis.get(id);
        if (StringUtils.isNotEmpty(userStr)) {
            return JSONObject.parseObject(userStr, User.class);
        }
        try {
            //查不到差数据库
            System.out.println("==========开始查数据库============");
            User user = dao.queryById(id);
            jedis.setex(user.getId(), 100, JSONObject.toJSONString(user));
            return user;
        }finally {
            //为了预防死锁,这边再finally加解锁操作
            lock.unlock("locak"+id);
        }
    }

在这里插入图片描述
可以看到现在是只查询一次数据库了。

发布了47 篇原创文章 · 获赞 97 · 访问量 6万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章