这是 Beta 探索课程,内容结构、实验步骤和示例可能会继续调整。
悲观锁:使用数据库悲观锁保证一致性
问题:50万人抢100台手机,如何保证不超卖?😰
想象一下这个场景:
秒杀开始:
- 库存:100台iPhone
- 参与用户:50万人
- 并发请求:瞬间涌入50万次
- 你的任务:保证不多卖一台,不少卖一台
传统方案的问题:
超卖现象演示:
时间线:
T1: 线程A读取库存 = 1
T2: 线程B读取库存 = 1 ← 都看到还有1台
T3: 线程A判断库存充足,下单
T4: 线程B判断库存充足,下单 ← 都认为可以买
T5: 线程A扣减库存:stock = 0
T6: 线程B扣减库存:stock = -1 ← 超卖了!
结果:库存1台,卖出2台,超卖!解决方案:悲观锁 🔒
悲观锁的核心思想:假设冲突一定会发生,操作前先加锁。
数据库悲观锁的实现
数据设计要点
- 查询目标是快速定位状态、任务或资源,避免在关键路径上做大范围扫描。
完整方案落地:
并发测试:验证悲观锁的正确性
测试输出:
🚀 开始并发测试:20个用户同时抢购
============================================================
🔒 用户1尝试获取商品1的锁...
✅ 用户1获得锁,当前库存:10
🔒 用户2尝试获取商品1的锁... ← 等待中...
🔒 用户3尝试获取商品1的锁... ← 等待中...
...
🎉 用户1购买成功,剩余库存:9
✅ 用户2获得锁,当前库存:9
🎉 用户2购买成功,剩余库存:8
✅ 用户3获得锁,当前库存:8
...
❌ 用户11:库存不足,stock=0
❌ 用户12:库存不足,stock=0
...
============================================================
📊 测试结果:
总请求数:20
成功数:10
失败数:10
耗时:245.32ms
平均响应时间:12.27ms悲观锁的工作原理 🔍
让我们深入理解悲观锁的底层机制:
悲观锁的优缺点分析
优点 ✅
缺点 ❌
性能对比表
| 并发量 | 悲观锁表现 | 平均响应时间 | 数据库负载 | 用户体验 |
|---|---|---|---|---|
| 100 | ✅ 优秀 | 10ms | 20% | ⭐⭐⭐⭐⭐ |
| 500 | ✅ 良好 | 50ms | 40% | ⭐⭐⭐⭐ |
| 1000 | ✅ 可接受 | 100ms | 60% | ⭐⭐⭐ |
| 2000 | ⚠️ 一般 | 200ms | 80% | ⭐⭐ |
| 5000 | ⚠️ 较慢 | 500ms | 95% | ⭐ |
| 10000+ | ❌ 不可用 | 1s+ | 100% | 崩溃 💥 |
最佳实践建议 💡
想一想
问题 1:悲观锁能支撑多少QPS?
根据我们的测试和经验数据:
- 单机MySQL:最多支撑 1000-5000 QPS
- 主从架构:悲观锁只能走主库,无法分担压力
- 秒杀场景:50万QPS远远超出能力范围
你的思考:如果秒杀QPS是50万,悲观锁够用吗?
问题 2:如何优化悲观锁性能?
提示方向:
- 能否减少锁的粒度?(行锁 vs 表锁)
- 能否缩短事务时间?
- 能否用缓存分担压力?
问题 3:什么场景适合用悲观锁?
考虑因素:
- 并发量级
- 一致性要求
- 开发成本
- 系统复杂度