主页 > 游戏开发  > 

miqiu的分布式锁(四):MySQL悲观锁解析

miqiu的分布式锁(四):MySQL悲观锁解析
📚miqiu的分布式锁(四):MySQL悲观锁解析
🌟 什么是悲观锁?

“先下手为强!”——这就是悲观锁的核心思想。它默认所有操作都可能发生并发冲突,在操作数据前会先加锁,确保独占访问。在MySQL中,默认的悲观锁是表级锁,但通过特定方式可实现更细粒度的行级锁。


🔑 行级锁开启双条件(缺一不可)

🎯 索引字段操作 必须对建立索引的字段(如product_code)进行查询/更新,否则自动降级为表锁 → EXPLAIN命令可验证是否走索引

🔢 精确值匹配 必须使用=等精确匹配操作符 ❌ 禁用模糊查询(LIKE)和范围查询(!=, >, <)


🔒悲观锁解决的问题: 同一个商品有多个记录的时候无法记录更新前后的变化的问题
💻 核心语法解析 SELECT * FROM tb_stock WHERE product_code = '1002' FOR UPDATE; -- 关键锁定语句

✅ 正确姿势:

确保product_code字段有索引明确指定具体值(‘1002’)

❌ 错误示范:

SELECT * FROM tb_stock WHERE product_code LIKE '100%' FOR UPDATE; -- 触发表锁!
🛠️ 实战代码示例 Java 服务层 @Transactional // 事务注解保证原子性 public void deduct() { // 🚨 注意:必须确保所有查询走同一索引,避免死锁 List<Stock> stocks = stockMapper.queryStock("1002"); Stock stock = stocks.stream() .findFirst() .orElseThrow(() -> new RuntimeException("库存不存在")); if (stock.getCount() > 0) { stock.setCount(stock.getCount() - 1); stockMapper.updateById(stock); } } MyBatis Mapper public interface StockMapper extends BaseMapper<Stock> { @Select("SELECT * FROM tb_stock WHERE product_code = #{productCode} FOR UPDATE") List<Stock> queryStock(@Param("productCode") String productCode); }


⚠️ 悲观锁三大痛点

🐢 性能瓶颈 长时间锁持有导致并发吞吐量下降(实测QPS仅约10)

💀 死锁风险 当多个事务以不同顺序加锁时,可能产生环形等待 👉 解决方案:统一加锁顺序 + 设置锁超时时间

📦 库存热点问题 高频操作同一商品时容易成为系统瓶颈


🧪 压测验证

测试配置

初始库存:5000并发线程:100循环次数:50次/线程

结果分析 ✅ 最终库存准确归零 📉 QPS对比:

锁类型QPS无锁1500+悲观锁~10JVM本地锁~8
💡 最佳实践指南

索引检查双保险

SHOW INDEX FROM tb_stock; -- 查看索引 EXPLAIN SELECT ... FOR UPDATE; -- 验证执行计划

锁超时设置

SET innodb_lock_wait_timeout = 3; -- 设置3秒锁等待超时

监控利器

SHOW ENGINE INNODB STATUS; -- 查看死锁日志

库存操作黄金法则

统一where条件字段更新条件包含版本号/原库存值事务尽量短小精悍
📚 知识扩展

悲观锁 vs 乐观锁

悲观锁乐观锁适用场景高冲突写操作低冲突读多写少实现方式SELECT…FOR UPDATE版本号/CAS性能特点实时性高,吞吐量低延迟检测,吞吐量高

🔚 总结:悲观锁是解决并发问题的重型武器,使用时需精准把控索引条件和事务范围。在高并发场景下,建议结合Redis分布式锁或乐观锁方案,根据业务特点选择最优解!

标签:

miqiu的分布式锁(四):MySQL悲观锁解析由讯客互联游戏开发栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“miqiu的分布式锁(四):MySQL悲观锁解析