【Redis】分别从互斥锁与逻辑过期两个方面来解决缓存击穿问题

前言

身逢乱世,未雨绸缪

一.什么是缓存击穿

说直白点,就是一个被非常频繁使用的key突然失效了请求没命中缓存,而因此造成了无数的请求落到数据库上,瞬间将数据库拖垮。而这样的key也被叫做热key!

在这里插入图片描述
可以直观地看到,要想解决缓存击穿绝对不能让这么多线程的请求在某一时段大量去访问到数据库。
以此为基础,针对访问数据库的限制有两种解决方案:

二.基于互斥锁解决缓存击穿

对于一个访问频繁的id查询接口,可能会发生缓存击穿问题,下面通过互斥锁的方式来解决
在这里插入图片描述
在以前,id查询信息的接口里一般将查询的信息写到缓存里,针对是否命中缓存再去做对应的处理。而在并发的情况下,对于热Key失效的情况,大量的请求则会直接打到数据库上并试图重建缓存,很有可能打停数据库,导致服务中断。对于这样的情况往往是在未命中缓存时,最佳的处理点就在于业务中判断缓存是否命中之后的那一步操作,即“多余”的请求对数据库的访问与否。
其他线程的请求能不能去访问数据库?什么时候才能去访问数据库?
其他的线程能不能去访问数据库?——加锁,有锁才能
什么时候才能去访问数据库?——等主线程释放锁
那其他线程拿不到锁的时间该干嘛?——睡吧,等会再来

为了实现在多个线程并行的情况下只能有一个线程获得锁,我们可以使用Redis自带的setnx
在这里插入图片描述
他可以保证在key不存在时可以进行写的操作,key存在时无法进行写的操作,这就完美地保证了在并发情况下只有第一个拿到锁的线程才能去写,并且他写完了之后(在不释放的前提下)别人就写不了了。
如何去获取?写个Key—Value进去
如何释放?把Key删了 del lock (通常设置一个有效期,避免长时间未释放的情况)

这样我就可以以此为条件封装两个方法,一个写key来尝试获取锁另一个删key来释放锁。就像这样:

/**
 * 尝试获取锁
 *
 * @param key
 * @return
 */
private boolean tryLock(String key) {
   
   
    Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
    return BooleanUtil.isTrue(flag);
}

/**
 * 释放锁
 *
 * @param key
 */
private void unlock(String key) {
   
   
    stringRedisTemplate.delete(key);
}

在并行情况下每当其他线程想要获取锁,来访问缓存都要通过将自己的key写到tryLock()方法里,setIfAbsent()返回false则说明有线程在在更新缓存数据,锁未释放。若返回true则说明当前线程拿到锁了可以访问缓存甚至操作缓存。
我们在下面一个热门的查询场景中用代码用代码来实现互斥锁解决缓存击穿
在这里插入图片描述

    /**
     * 解决缓存击穿的互斥锁
     * @param id
     * @return
     */
    public Shop queryWithMutex(Long id) {
   
   
        String key = CACHE_SHOP_KEY + id;
        //1.从Redis查询缓存
        String shopJson = stringRedisTemplate.opsForValue().get(key);  //JSON格式
        //2.判断是否存在
评论 39
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

懒羊羊.java

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值