用互斥锁解决缓存击穿问题
创作时间:
作者:
@小白创作中心
用互斥锁解决缓存击穿问题
引用
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个线程进行测试,结果所有请求都成功了。通过查看控制台日志,可以发现只查询了一次数据库,这说明互斥锁机制成功地防止了缓存击穿。
总结
通过使用互斥锁,我们可以有效地解决缓存击穿问题。虽然这种方法可能会带来一定的性能开销,但在高并发场景下,它能够显著降低数据库的压力,保障系统的稳定运行。在实际应用中,还可以结合其他策略(如逻辑过期时间)来进一步优化解决方案。
热门推荐
如何用人工智能助手联网
乾隆皇帝的幽默天赋:一位风趣帝王的文学情趣
诺氟沙星胶囊是治拉肚子的药吗
如何从期货和股票市场的联动中分析投资机会
家庭环境是什么?从物理空间到情感支持的全方位解析
无线广播仍将坐镇车机
显卡超频完全指南:原理、步骤与风险提示
长春地铁最新进展:5号线、7号线、9号线建设提速
《鸣潮》珂莱塔角色攻略:优雅与强大的完美结合
PPR管的寿命是多少年?如何延长其使用寿命?
潮汕五日游攻略:从潮州韩文公祠到汕头礐石风景区,感受历史与自然的双重魅力
AI赋能前端创造力:效率提升与设计思维的碰撞
AI“伴侣”,所托非人
房产交易中的证件问题处理指南
河北保定白石山旅游攻略,穿越万千奇峰,寻觅太行仙境!
周瑜在历史上的影响:一位军事天才的早逝如何改变了三国格局
7部豆瓣高分动画电影推荐,每一部都值得N刷!
事故责任判定标准如何确定
《白鸟与蝙蝠》:一部深刻探讨人性与正义的悬疑小说
平行宇宙探秘:那些不同时空中的“另一个我”,生活如何?
你是不是因为可回收包装袋警示语而困扰,这篇文章教会你
解锁有氧运动之谜:跑步、游泳还是骑行?
星座表十二星座查询,老板要求用Excel做出表格,我讲讲该怎么做
ESFJ人格特质及其理想伴侣类型分析
安邦保险:昔日金融巨擘的陨落之路
控糖期间不能吃的食物有哪些?控糖期间饮食禁忌!
吲哚-3-甲醇(I3C)研发热情高涨 主要用于制造抗肿瘤药物
早餐新选择:告别单调,开启活力一天的美食
抗血管生成与ICI联合疗法临床试验的经验教训
13个技巧,教你轻松应对暗恋