这是 Beta 探索课程,内容结构、实验步骤和示例可能会继续调整。
乐观锁:使用版本号实现乐观锁
问题:悲观锁太慢,如何提升性能?🤔
上一节我们学习了悲观锁,它有一个致命问题:
悲观锁性能测试结果:
- 1000 QPS:响应时间 100ms
- 5000 QPS:响应时间 500ms
- 10000 QPS:响应时间 1000ms+
- 秒杀场景需要 500000 QPS ❌ 完全不够用!问题根源:
所有请求排队等待锁 🔒
线程A ━━━━━━━━━━━━━━┫
线程B ⏳ ━━━━━━━━━━━━━━┫
线程C ⏳ ━━━━━━━━━━━━━━┫
线程D ⏳ ━━━━━━━━━━━━━━┫
每个请求必须等前一个请求完全完成才能开始新的思路:
- 悲观锁:假设一定会冲突,先加锁再操作
- 乐观锁:假设大概率不冲突,先操作,冲突时重试
乐观锁的核心思想 💡
生活中的乐观锁例子
数据库乐观锁实现
方案1:版本号机制
数据设计要点
- 这是一次表结构演进:随着业务能力增加,把新状态、新时间点或新归属关系补进数据模型。
- 查询目标是快速定位状态、任务或资源,避免在关键路径上做大范围扫描。
完整方案落地:
方案2:CAS(Compare And Swap)
并发测试:对比乐观锁和悲观锁
测试结果对比:
============================================================
开始对比测试:100个用户抢购
============================================================
🔒 测试悲观锁...
悲观锁结果:
成功:10/100
耗时:1234.56ms
平均响应:12.35ms
🔓 测试乐观锁...
乐观锁结果:
成功:10/100
耗时:456.78ms
平均响应:4.57ms
============================================================
性能对比:
============================================================
悲观锁:耗时 1234ms
乐观锁:耗时 456ms
性能提升:2.7倍 🚀乐观锁的性能分析
为什么乐观锁更快?
乐观锁的优缺点
优点 ✅
| 优点 | 说明 | 适用场景 |
|---|---|---|
| 并发性能高 | 读取不加锁,并发度高 | 读多写少 |
| 无死锁 | 不需要获取锁,不会死锁 | 复杂事务 |
| 实现简单 | 只需添加版本号字段 | 快速开发 |
| 数据库压力小 | 不需要维护锁 | 高并发 |
缺点 ❌
| 缺点 | 说明 | 影响 |
|---|---|---|
| ABA问题 | 值从A改成B又改成A,无法感知 | 部分场景 |
| 冲突重试 | 高冲突时大量重试,性能下降 | 秒杀场景严重 |
| 用户体验 | 冲突时需要重试,可能失败 | 用户感知 |
| 业务侵入 | 需要处理重试逻辑 | 代码复杂度 |
ABA问题详解
乐观锁的最佳实践
性能对比总结
不同场景的性能对比
| 场景 | 并发量 | 库存 | 悲观锁性能 | 乐观锁性能 | 推荐 |
|---|---|---|---|---|---|
| 后台管理 | 10 | 充足 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 乐观锁 |
| 普通电商 | 100 | 充足 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 乐观锁 |
| 促销活动 | 1000 | 100 | ⭐⭐⭐ | ⭐⭐⭐⭐ | 乐观锁 |
| 秒杀活动 | 10000 | 100 | ⭐⭐ | ⭐⭐ | 都不够 |
| 超级秒杀 | 100000 | 100 | ⭐ | ⭐ | 都不够 |
关键结论
想一想
问题 1:乐观锁为什么在秒杀场景性能下降?
提示:
- 秒杀:50万人抢100台手机
- 冲突率 = 并发数 / 库存 = 500000 / 100 = 5000倍
- 意味着什么?
问题 2:如何优化乐观锁在秒杀场景的表现?
提示:
- 能否减少并发数?(限流)
- 能否减少冲突?(队列)
- 能否提前筛选?(缓存)
问题 3:还有什么更好的方案?
提示:
- 数据库是瓶颈
- 能否不用数据库?
- Redis、消息队列?