用互斥锁解决缓存击穿问题
创作时间:
作者:
@小白创作中心
用互斥锁解决缓存击穿问题
引用
CSDN
1.
https://blog.csdn.net/hguhbh/article/details/139521951
缓存击穿是高并发系统中常见的问题之一,它通常发生在热点数据的缓存失效时,导致大量请求直接打到数据库,造成数据库压力剧增。本文将详细介绍如何使用互斥锁(Mutex)来解决缓存击穿问题,并通过实际代码和性能测试验证解决方案的有效性。
在高并发场景下,一个被频繁访问的缓存数据(热点key)突然失效,如果没有有效的防护措施,大量请求会直接冲击数据库,导致数据库负载激增,甚至引发系统崩溃。为了解决这一问题,本文将介绍使用互斥锁(Mutex)的解决方案。
缓存击穿问题概述
缓存击穿问题也叫热点key问题,就是一个被高并发访问并且缓存重建业务复杂的存储在redis中的key突然失效,无数请求就会瞬间打到数据库造成巨大冲击。
解决方案:互斥锁
互斥锁是一种常用的解决方案,其核心思想是:当一个线程获取到互斥锁时,其他线程必须等待,直到锁被释放。这样可以确保在缓存重建期间,只有一个线程负责查询数据库并更新缓存,其他线程则等待或返回旧数据。
代码实现
我们使用Redis的setnx
命令来实现互斥锁,该命令只有在key不存在时才会创建成功,若key已存在就会创建失败。
private boolean tryLock(String key) {
//参数分别是,key,value,过期时间,过期时间的单位
//这里过期时间用事先写的静态变量,10L
Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", LOCK_SHOP_TTL, TimeUnit.SECONDS);
return BooleanUtil.isTrue(flag); //如果直接返回flag,当flag为null时,会做拆箱,报错空指针。
}
private void UnLock(String key) {
stringRedisTemplate.delete(key);
}
接下来,我们在服务层实现具体的缓存重建逻辑:
@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {
@Resource
private StringRedisTemplate stringRedisTemplate;
public Result queryById(Long id) {
//用互斥锁解决缓存击穿
Shop shop = queryWithMutex(id);
if (shop == null) {
return Result.fail("店铺不存在");
}
return Result.ok(shop);
}
public Shop queryWithMutex(Long id) {
//1.从redis查询数据缓存
String key = CACHE_SHOP_KEY + id;
String shopJson = stringRedisTemplate.opsForValue().get(key);
//2.判断是否存在
if (StrUtil.isNotBlank(shopJson)) { //isNotBlank方法只有有值字符串才会返回true,null和空值都会返回false
//3.存在,返回
Shop shop = JSONUtil.toBean(shopJson, Shop.class);
return shop;
}
//shopJson不存在
//判断查到的数据是否为空值(这个空值指的不是null,是空字符串)
if (shopJson != null) {
//返回错误信息
return null;
}
//4实现缓存重建
//4.1获取互斥锁
String lockKey = LOCK_SHOP_KEY + id;
boolean lock = tryLock(lockKey);
//4.2判断是否获取成功
Shop shop = null;
try {
if (!lock) {
//4.3失败,休眠并重试
Thread.sleep(50);
return queryWithMutex(id);
}
//4.4成功,根据id查询数据库
shop = getById(id);
//模拟数据库重建的延时
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, TimeUnit.MINUTES);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
//7.释放互斥锁
UnLock(lockKey);
}
//8.返回
return shop;
}
}
性能测试
为了验证解决方案的有效性,我们使用JMeter进行压力测试。开启100个线程进行测试,结果所有请求都成功了。通过查看控制台日志,可以发现只查询了一次数据库,这说明互斥锁机制成功地防止了缓存击穿。
总结
通过使用互斥锁,我们可以有效地解决缓存击穿问题。虽然这种方法可能会带来一定的性能开销,但在高并发场景下,它能够显著降低数据库的压力,保障系统的稳定运行。在实际应用中,还可以结合其他策略(如逻辑过期时间)来进一步优化解决方案。
热门推荐
区块链技术的核心:共识机制详解
T5和T8灯管的区别:灯管t5和t8有什么区别?五大差异看这篇就够了
五岁儿童喜欢听的法律故事:法治启蒙与社会责任
东西边户大不同:光照、温差与价格全方位对比
养鱼新手必看:如何通过水草装饰提升鱼缸美观与生态平衡
打破粗心 “魔咒”:解锁孩子细心成长密码
高强度间歇训练带来3益处 提升跑步表现
中医视角下的腹痛:深入解析与全面调治
四川绵阳十大必去景区,类型多样,你去过几个?
长三角这条城际铁路传来两个好消息!项目总投资334亿元
乡镇公务员考试科目概览:考试科目及备考重点解读
武狮不是舞狮?安徽牛官堡八旬武狮传承人揭秘:玩狮须有牛门洪拳武术功底
高净值人群财富管理的时代新需求
阿根廷足球巨星——梅西资料详情一览
研究揭秘气候变化和地貌对古人类迁徙的重要影响
《斗罗大陆Ⅱ绝世唐门》:玄幻世界的热血与传承
老外真的喜欢吃中餐?在美华人辟谣:想多了,都是骗人的!
劳动合同中未缴纳五险一金的法律风险及合规建议
2024年考研“内卷”加剧,各学科硕士毕业生就业质量如何?| 数说
中高职一体化培养:春潮涌动聚势起
潮州青龙庙会:千年文化传承与侨心凝聚
沭阳:强化地名文化遗产保护与传承
如何设计具有竞争力的工资薪酬体系?
国风漫画&《三体》:为讲好中国故事提供新思路
警校2025年重大变化!分数要暴涨?考警校更难了吗?
三个在心仪对象面前失语的星座男
如何建立高效学习系统:从目标设定到反馈调整的完整指南
网上注销个体工商户及公司所需材料详解
详细揭示藻类细胞分裂:比勒菲尔德大学研究团队取得突破性进展
张红甫教你做香辣入味的红烧鸡块