当前位置: 首页 > news >正文

《高并发优化方案一》:本地锁 + 分布式锁实战详解

高并发优化方案一:本地锁 + 分布式锁实战详解🚀

    • 一、 🌐场景背景
    • 二、🧠 为什么使用本地 + 分布式锁?
    • 三、🔒 本地锁和分布式锁做什么?
    • 四、🧰 实战:商品库存扣减代码
    • 五、📈 多级锁处理流程图
    • 六、⚠️ 注意事项
    • 七、❌ 不推荐用`Synchronized(productId.intern() )`代替锁池
    • 八、🧠 总结

在高并发场景下,如何保障系统的一致性同时提升性能,一直是后端工程师绕不开的核心问题。今天我们来聊一个实用而高效的优化方案 —— 本地锁 + 分布式锁 的双层锁机制。

这是高并发分布式系统中一种优化在高并发场景下使用Redis分布锁所产生的网络抖动、延迟增加的手段之一,适用于 秒杀库存、订单幂等等保证线程安全的分布式高并发资源场景


一、 🌐场景背景

你可能遇到过这样的情况:

  • 处于分布式系统
  • 多个用户几乎同时抢购同一商品
  • 因为分布式系统,使用了 Redis 分布式锁来控制并发,但高并发下频繁 Redis 请求导致网络抖动、延迟增加,积累的请求越多,网络请求越来越阻塞,最后甚至可能导致网络奔溃。
  • Redis 一旦慢了,整个业务都被拖住

那么,有没有办法减少 Redis 的压力,同时又能保证线程互斥?

答案就是 —— 本地锁 + 分布式锁 多级联动!


二、🧠 为什么使用本地 + 分布式锁?

✅ 优点总结:

优点说明
🚀 降低 Redis 压力本地锁先阻挡大部分请求,只有一个线程发起 Redis 锁请求
📉 减少网络开销减少 Redis I/O 消耗,提高响应速度
⚡ 提升吞吐性能实现资源级别的并发处理(不同商品并发互不干扰)
🧩 可组合扩展支持 tryLock、超时、限流等控制策略
🧷 提高系统健壮性Redis 故障时仍可依靠本地限流部分挡住请求,防止系统雪崩

三、🔒 本地锁和分布式锁做什么?

  • 本地锁(JVM 内存锁):挡住同一节点上的并发请求。一般使用 ReentrantLocksynchronized,但是不推荐synchronized,因为这个锁的条件一般只能是实例和calss对象,导致锁的粒度下降,性能降低。
  • 分布式锁(如 Redis 锁):用于集群中多个节点之间的互斥。推荐使用 Redisson。

两者搭配使用,可以实现既快又安全的高并发处理方案


四、🧰 实战:商品库存扣减代码

下面是一个库存扣减逻辑的完整双锁实现。

@Service
public class StockService {private final RedissonClient redissonClient;// 本地锁池(用于按商品ID管理锁)private final Cache<String, ReentrantLock> localLockPool =CacheBuilder.newBuilder().maximumSize(10000).expireAfterAccess(5, TimeUnit.MINUTES).build();@Autowiredpublic StockService(RedissonClient redissonClient) {this.redissonClient = redissonClient;}public boolean decreaseStock(String productId, int quantity) {String redisKey = "lock:stock:" + productId;ReentrantLock localLock = localLockPool.get(productId, ReentrantLock::new);localLock.lock();try {RLock redisLock = redissonClient.getLock(redisKey);if (redisLock.tryLock(5, 10, TimeUnit.SECONDS)) {try {// 查询库存int stock = getStock(productId);if (stock >= quantity) {updateStock(productId, stock - quantity);return true;} else {System.out.println("库存不足");return false;}} finally {redisLock.unlock();}} else {System.out.println("获取分布式锁失败");return false;}} catch (Exception e) {e.printStackTrace();return false;} finally {localLock.unlock();}}private int getStock(String productId) {return 10; // 模拟数据库}private void updateStock(String productId, int newStock) {System.out.printf("商品 %s 库存更新为:%d%n", productId, newStock);}
}

过程:

  1. 对每一个商品ID单独建立一个锁池条件,对于同一个商品ID会产生互斥的作用,从而保证线程安全,以及多个不同商品ID可以并发的情况。
  2. 从锁池中获取对应的商品ID的锁,然后进行根据本地ID上锁。等获取锁成功后再进行redis分布式锁的获取,后续就是业务逻辑,然后先释放 Redis锁 再进行对应的 本地锁 释放。

五、📈 多级锁处理流程图

                 ┌──────────────┐│  接收请求    │└─────┬────────┘▼┌──────────────────────┐│ 获取本地锁(Reentrant)│└────────┬─────────────┘▼┌─────────────────────────────┐│ 获取分布式锁(Redisson)     │└─────┬─────────────┬────────┘▼             ▼┌────────────┐   ┌──────────────┐│ 扣减库存    │   │ 返回失败      │└────┬───────┘   └──────────────┘▼┌────────────────┐│ 解锁(本地+Redis)│└────────────────┘

六、⚠️ 注意事项

风险应对策略
🧠 本地锁池内存泄漏使用 Guava Cache/Caffeine 设置过期时间
⛓ 分布式锁未释放使用 Redisson 自动过期 + WatchDog
🔄 锁冲突过多加指数退避、队列缓存等策略
🧱 粒度太粗全系统串行按资源 ID(如商品ID)拆分锁粒度

七、❌ 不推荐用Synchronized(productId.intern() )代替锁池

虽然可以用 productId.intern() 来实现基于字符串的同步,但这种方式:

  • 会导致字符串常量池污染(内存泄漏):大量不同商品ID都存放在字符冲常量池中时不会回收,最终导致内存溢出
  • 不适用于大规模的动态资源
  • 不易追踪调试

建议始终使用 ConcurrentHashMap 或缓存结构来构建锁池,灵活、安全、可控。
如果一定要使用 Synchronized,可以使用如下方式:

ConcurrentHashMap<String, Object> lockPool = new ConcurrentHashMap<>();
Object lock = lockPool.computeIfAbsent(resourceId, k -> new Object());
synchronized (lock) {// 安全互斥
}

八、🧠 总结

  • 本地锁 + 分布式锁是一种性能与安全兼顾的高并发控制策略。
  • 正确的锁粒度、锁对象复用和生命周期管理,是该策略能否成功落地的关键。
  • 多级锁是构建高可用电商/交易系统的基础能力之一。

http://www.dtcms.com/a/290549.html

相关文章:

  • Excel函数 —— TEXTJOIN 文本连接
  • 支持不限制大小,大文件分段批量上传功能(不受nginx /apache 上传大小限制)
  • Apache Ignite Closure 和 Thread Pool
  • Ubuntu安装k8s集群入门实践-v1.31
  • WinForm-免费,可商用的WinForm UI框架推荐
  • 类似腾讯会议的私有化音视频会议软件,BeeWorks Meet
  • Go语言进阶书籍:Go语言高级编程(第2版)
  • 开源 Arkts 鸿蒙应用 开发(八)多媒体--相册和相机
  • 45.sentinel自定义异常
  • RIQ模型时间管理方法详解
  • Idea或Pycharm上.idea的忽略提交的问题总结
  • go语言八股
  • MySQL(149)如何进行数据清洗?
  • 09_Spring Boot 整合 Freemarker 模板引擎的坑
  • 【C++】stack和queue拓展学习
  • 库卡气体保护焊机器人省气的方法
  • Mac上安装Homebrew的详细步骤
  • 【CNN】卷积神经网络池化- part2
  • Pytorch01:深度学习中的专业名词及基本介绍
  • 有关Maven的个人笔记总结
  • Zetane:让深度学习不再抽象,一键3D可视化
  • SpringSecurity 详细介绍(认证和授权)
  • 直播专用域名租用全解析:开启直播新境界
  • 板凳-------Mysql cookbook学习 (十二--------3_2)
  • 基于 STM32 的数字闹钟系统 Proteus 仿真设计与实现
  • ASP .NET Core 8高效集成Redis缓存实战
  • C++中的deque容器
  • C#/.NET/.NET Core技术前沿周刊 | 第 47 期(2025年7.14-7.20)
  • 解决vscode中vue格式化后缩进太小的问题,并去除分号 - 设置Vetur tabSize从2到4,设置prettier取消分号semi
  • Hyperledger Fabric V2.5 生产环境部署及安装Java智能合约