利用互斥锁解决缓存击穿问题

2.9 利用互斥锁解决缓存击穿问题

核心思路:相较于原来从缓存中查询不到数据后直接查询数据库而言,现在的方案是 进行查询之后,如果从缓存没有查询到数据,则进行互斥锁的获取,获取互斥锁后,判断是否获得到了锁,如果没有获得到,则休眠,过一会再进行尝试,直到获取到锁为止,才能进行查询

如果获取到了锁的线程,再去进行查询,查询后将数据写入redis,再释放锁,返回数据,利用互斥锁就能保证只有一个线程去执行操作数据库的逻辑,防止缓存击穿

操作锁的代码:

核心思路就是利用redis的setnx方法来表示获取锁,该方法含义是redis中如果没有这个key,则插入成功,返回1,在stringRedisTemplate中返回true, 如果有这个key则插入失败,则返回0,在stringRedisTemplate返回false,我们可以通过true,或者是false,来表示是否有线程成功插入key,成功插入的key的线程我们认为他就是获得到锁的线程。

java 复制代码
private boolean tryLock(String key) {
    Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
    return BooleanUtil.isTrue(flag);
}

private void unlock(String key) {
    stringRedisTemplate.delete(key);
}

操作代码:

java 复制代码
 public Shop queryWithMutex(Long id)  {
        String key = CACHE_SHOP_KEY + id;
        // 1、从redis中查询商铺缓存
        String shopJson = stringRedisTemplate.opsForValue().get("key");
        // 2、判断是否存在
        if (StrUtil.isNotBlank(shopJson)) {
            // 存在,直接返回
            return JSONUtil.toBean(shopJson, Shop.class);
        }
        //判断命中的值是否是空值
        if (shopJson != null) {
            //返回一个错误信息
            return null;
        }
        // 4.实现缓存重构
        //4.1 获取互斥锁
        String lockKey = "lock:shop:" + id;
        Shop shop = null;
        try {
            boolean isLock = tryLock(lockKey);
            // 4.2 判断否获取成功
            if(!isLock){
                //4.3 失败,则休眠重试
                Thread.sleep(50);
                return queryWithMutex(id);
            }
            //4.4 成功,根据id查询数据库
             shop = getById(id);
            // 5.不存在,返回错误
            if(shop == null){
                 //将空值写入redis
                stringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL,TimeUnit.MINUTES);
                //返回错误信息
                return null;
            }
            //6.写入redis
            stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),CACHE_NULL_TTL,TimeUnit.MINUTES);

        }catch (Exception e){
            throw new RuntimeException(e);
        }
        finally {
            //7.释放互斥锁
            unlock(lockKey);
        }
        return shop;
    }
相关推荐
Sharewinfo_BJ17 分钟前
Power BI沿袭视图详解:从看懂链路到做影响分析
数据库·数据分析·智信bi·上北智信
l1t21 分钟前
DeepSeek总结的 DuckDB 2.0 中更简洁的变量语法
数据库·sql
bug菌21 分钟前
一个周末就想把短链服务推到 10 万 QPS,真能靠加 Redis 做到吗?
java·redis·go
sunshine8851 小时前
集成电路(IC)设计与封测行业10大常用ERP系统分析
大数据·数据库·人工智能
fen_fen1 小时前
Jmeter5.1.1连接oracle数据库的错误复盘
数据库·jmeter·oracle
黎相思2 小时前
LRU Cache
c++·缓存
imDwAaY2 小时前
MySQL 存储引擎是什么?
android·数据库·mysql
星云低代码开发平台2 小时前
ERP 与 CRM 客户信用核对:业务键、时点和权限验收
大数据·数据库·人工智能
xiaogai_gai2 小时前
销售出库单同步实战:旺店通→金蝶云星空的单一策略深度拆解
大数据·前端·数据库
下页、再停留2 小时前
【C#桌面客户端系列学习-11】将SQLite数据库打包到exe里
数据库·c#·visual studio