用互斥锁解决缓存击穿问题
创作时间:
作者:
@小白创作中心
用互斥锁解决缓存击穿问题
引用
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个线程进行测试,结果所有请求都成功了。通过查看控制台日志,可以发现只查询了一次数据库,这说明互斥锁机制成功地防止了缓存击穿。
总结
通过使用互斥锁,我们可以有效地解决缓存击穿问题。虽然这种方法可能会带来一定的性能开销,但在高并发场景下,它能够显著降低数据库的压力,保障系统的稳定运行。在实际应用中,还可以结合其他策略(如逻辑过期时间)来进一步优化解决方案。
热门推荐
苦尽甘来怎么接下一句?了解这句诗的后续和含义
开车耳鸣的解决方法是什怎么?这种解决方法有哪些实用技巧?
不伤肾的止痛药
五一广场简牍:见证长沙历史变迁的珍贵文献
每天晚上吃安眠药有什么影响?
Cell重磅发现:安眠药物会扰乱睡眠期间大脑的废物清除,可能导致阿尔茨海默病
如何为硬盘或U盘选择最佳格式化方式?
李白:融汇百川的"思想家"
易白:探索现代诗歌与流行音乐融合之路,用音乐唤醒诗歌
信息系统项目管理师证书对职业发展有什么帮助?
6大简单培养孩子专注力的方法
汇丰银行账户注销流程全解析:一步步指南及注意事项
pandas中的关系型连接操作详解
这种豆子春天吃正好,这类人除外,有致命风险!
华为“元老级功臣”徐文伟退休:33年见证华为从创业到崛起
Rtools下载、安装、配置教程
如何写出产品需求曲线表
水文循环:过程、特征和重要性
患“三高”的张家界,苦等“降压”良药
资本充足率的计算方法及其对金融机构的影响
英语里及物动词和不及物动词的本质区别是什么,是跟不跟宾语吗?
温泉设计中的水元素:如何利用水流创造动态美感
六版红衣展昭,焦恩俊最俊美,何家劲最经典,至今无人可替代
手工云吞,自制美味(用手包云吞)
数学学习之道:掌握方法,超越学会
恭喜!国羽又一17岁新星崛起:新版林丹连夺两冠,谌龙调教有方
南红是什么材质的宝石?了解南红宝石的特点与价值
南红玛瑙和蜜蜡哪个价值更高?全方位对比分析
如何通过日常饮食调整?提升整体活力与精神状态?
音乐理论入门:首调与固定调的对比分析