首页 > 其他分享 >如何使用互斥锁解决缓存击穿(热点Key)

如何使用互斥锁解决缓存击穿(热点Key)

时间:2024-06-09 16:34:14浏览次数:23  
标签:shop 缓存 return Key 互斥 key null

还是基于上一篇的代码。当热点缓存失效时,会出现大量请求直接打到数据库,可能会导致数据库压力过大,甚至崩溃。为了解决这一问题,我们可以使用互斥锁(Mutex)来控制缓存重建过程。

改造代码如下:

public Shop queryWithMutex(Long id) {
    String key = CACHE_SHOP_KEY + id;
    // 1.从Redis中查询商铺缓存
    String shopJson = stringRedisTemplate.opsForValue().get(key);
    // 2.判断缓存是否存在
    if (StrUtil.isNotBlank(shopJson)) {
        // 3.如果存在,直接返回
        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);
        // 4.5模拟查询数据库耗时
        Thread.sleep(200);
        // 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_SHOP_TTL + ThreadLocalRandom.current().nextInt(-5, 6), TimeUnit.MINUTES);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    } finally {
        // 7.释放互斥锁
        unlock(lockKey);
    }

    // 8.返回结果
    return shop;
}

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);
}
代码解析
  1. 从缓存中查询数据

    首先从Redis缓存中查询数据,如果数据存在则直接返回。

  2. 判断缓存是否存在

    如果缓存存在且不为空,直接返回缓存数据。如果缓存存在但为空(之前缓存过空值),返回null。

  3. 获取互斥锁

    尝试获取互斥锁,如果获取失败,则休眠一段时间后重试。

  4. 查询数据库并更新缓存

    获取锁成功后,从数据库中查询数据,并将结果写入缓存。

  5. 释放互斥锁

    最终释放互斥锁,保证锁的释放以防止死锁。

标签:shop,缓存,return,Key,互斥,key,null
From: https://blog.csdn.net/Takumilove/article/details/139563038

相关文章

  • redis做为缓存,mysql的数据如何与redis进行同步呢?
    让我们一步步来实现如何让MySQL数据库的数据和Redis缓存保持同步。想象一下,MySQL是一个大仓库,存放着所有重要的货物(数据),而Redis则像是一个快速取货窗口,让你能更快拿到常用的东西。为了让两者保持一致,我们需要搭建一座桥梁,让仓库里的变动能及时反映到取货窗口。第一步:理解同步......
  • Redis-6-三种缓存读写策略
    2.1旁路缓存CacheAsidePattern(旁路缓存)适合读请求比较多的场景CacheAsidePattern中服务端需要同时维系db和cache,并且是以db的结果为准。2.1.1写先更新db直接删除缓存2.1.2读先读缓存有,则从缓存返回。没有,从db中读取返回。再将读取的数据写入缓存......
  • MaxKey本地运行实战指南
    MaxKey本地运行总结概述开发环境准备主页传送门:......
  • [Tkey] CodeForces 1267G Game Relics
    太神了这题,膜拜出题人orz。思考一首先是大家都提到的一点,先抽卡再买。这里来做个数学分析。假设我们还剩\(k\)种没有买,其实我们是有式子来算出它的花费期望的。WIKI上提到,假设一个事件的概率为\(p\),则遇到它的期望为\(\frac{1}{p}\),因此,对于这个题,抽到一个新物品的概率显......
  • 多级缓存同步更新操作的优化策略——MQ
    多级缓存同步更新操作的优化策略——MQ文章部分内容参考京东技术文章,在文章的基础上添加自己的见解,如有侵权请告知删除https://mp.weixin.qq.com/s/2SQP1c-zMWdkIu4XRnBV0A参考资料:R2M分布式锁原理——https://www.cnblogs.com/Jcloud/p/17098240.html技术背景在像......
  • 使用Redis优化Java应用的性能——高效的缓存实践
    引言:在现代应用开发中,高效的缓存策略是提升性能和用户体验的关键。Redis作为一个高性能的键值存储系统,提供了一种快速存取数据的方式,帮助Java应用处理大量动态信息而无需频繁查询数据库。什么是Redis?Redis是一个开源的键值存储系统,它支持多种类型的数据结构如字符串、哈希、......
  • 谈谈Redis缓存中MySQL的数据如何与Redis同步
    在现代应用程序中,性能和响应速度是至关重要的。为了提高数据访问速度,常常会使用缓存技术。Redis作为一种高性能的内存数据库,常被用作缓存层,而MySQL则作为持久化存储层。如何有效地将MySQL数据与Redis缓存进行同步,是一个关键问题。本文将详细探讨Redis作为缓存时,http://ww......
  • hive性能优化系列-distinct-有限key
    案例方式1使用groupbyselects_age,count(1)from(selects_agefromstudent_tb_orcgroupbys_age)b方式2使用distinctselects_age,count(distincts_age)fromstudent_tb_orc分析数据量特别大方式1的代码在数据量特别大的情况下能够有效避免Reduce端的数据......
  • 互联网大厂的缓存策略:抵抗超高并发的秘密武器,已开源!
    大家好,我是冰河~~最近,有小伙伴私信我:冰哥,我最近出去面试,面试官问我如何设计缓存能让系统在百万级别流量下仍能平稳运行,我当时没回答上来。接着,面试官问我之前的项目是怎么使用缓存的,我说只是缓存了一些数据。当时确实想不到缓存还有哪些用处,估计这次面试是挂了。冰哥,你可以给我讲......
  • 亲,你有多久没有清理过你电脑的 DNS 缓存了?
    最近明月因为工作关系更换了几次使用的电脑,期间就发现明明另一台电脑访问某个网址是正常,换一台电脑后就会出现无法访问的现象,并且用的还是同一个宽带网络,实在是太诡异了!后来还是突然想起来DNS缓存这个问题,立马清除了那台电脑的DNS缓存后,打不开的网址顺利的呈现出来了。DNS......