MySQL悲观锁是一种基于“先加锁再操作”理念的并发控制机制,它假设并发场景下数据冲突发生的概率较高,因此在操作数据前会先对数据加锁,直到事务提交或回滚才释放锁,以此保证同一时间只有一个事务能操作目标数据。

悲观锁的核心实现方式
在MySQL中,悲观锁主要通过SELECT ... FOR UPDATE语句实现,该语句会对查询到的行加上排他锁,其他事务无法对这些行执行更新、删除操作,也无法再次加排他锁,只能等待当前事务释放锁。
使用悲观锁需要注意以下几点:
- 必须在事务中使用,否则加锁语句不会生效
- 锁的范围由查询条件决定,如果查询条件命中索引,会加行锁,否则会升级为表锁,严重影响并发性能
- 锁的持有时间和事务时长一致,事务提交或回滚后锁会自动释放
悲观锁使用示例
以下是一个典型的库存扣减场景的悲观锁使用代码:
-- 开启事务 START TRANSACTION; -- 查询商品库存并加排他锁,假设商品id为1001 SELECT stock FROM product WHERE id = 1001 FOR UPDATE; -- 判断库存是否充足,充足则扣减库存 UPDATE product SET stock = stock - 1 WHERE id = 1001; -- 提交事务,释放锁 COMMIT;
高并发场景使用悲观锁的问题
虽然悲观锁能完全避免数据冲突,但在高并发场景下会暴露出明显的短板:
- 锁竞争严重:高并发下大量事务会同时尝试获取同一把锁,未获取到锁的事务会进入等待状态,导致大量线程阻塞,系统吞吐量下降
- 死锁风险升高:如果多个事务加锁的顺序不一致,很容易出现死锁,虽然MySQL有死锁检测机制,但检测和处理死锁也会消耗额外的性能
- 长事务隐患:如果事务中包含耗时操作,锁的持有时间会被拉长,进一步加剧锁竞争,甚至可能导致数据库连接耗尽
高并发场景的选型建议
是否在高并发场景使用悲观锁,需要结合业务特性判断:
| 场景类型 | 推荐方案 | 原因说明 |
|---|---|---|
| 冲突频率高、数据一致性要求极高 | 悲观锁 | 比如金融转账场景,数据错误会导致严重损失,悲观锁能完全避免冲突 |
| 冲突频率低、并发量高 | 乐观锁 | 通过版本号或时间戳实现,无锁竞争,性能更高,冲突时重试即可 |
| 读多写少的高并发场景 | 读写分离+乐观锁 | 读操作不需要加锁,写操作冲突概率低,用乐观锁减少性能损耗 |
总结
MySQL悲观锁并不是高并发场景的禁用方案,而是需要结合业务需求合理选用。如果业务对数据一致性要求极高且冲突频率高,悲观锁是可靠的选择;如果高并发下冲突频率较低,优先选择乐观锁能获得更好的性能表现。实际开发中也可以结合两种方案,针对不同业务模块做差异化设计,平衡一致性和性能。