为什么MySQL中的悲观锁会导致系统吞吐量下降_改用乐观锁版本号机制

浅磊姑娘_7423

浅磊姑娘_7423

2026-05-19

552人浏览

原创

select for update会拖慢系统,因其在未走索引、主键不明确或扫描范围大时升级为表锁,且事务越长阻塞越严重,易引发死锁;而update where version=?靠原子性实现无锁更新,但需严格满足version字段约束与原子执行条件。

为什么mysql中的悲观锁会导致系统吞吐量下降_改用乐观锁版本号机制

为什么 SELECT ... FOR UPDATE 会拖慢整个系统

因为它是真锁,且默认行级锁的生效前提很苛刻。一旦没走索引、主键不明确、或扫描范围过大,InnoDB 就会升级为表锁——整张表卡住,所有后续的 SELECT ... FOR UPDATE、UPDATE、DELETE 都得排队等。更麻烦的是,事务越长,锁持有时间越久,阻塞链越容易形成死锁,触发 innodb_deadlock_detect 回滚后还要重试,实际吞吐反而更低。

常见踩坑点:

  • SELECT * FROM orders WHERE status = 'pending' FOR UPDATE —— 如果 status 没索引,直接表锁
  • 在事务里做 HTTP 调用、日志写入、复杂计算,导致锁持有几十毫秒甚至几百毫秒
  • 多个 FOR UPDATE 语句顺序不一致(比如 A 先锁 id=1 再锁 id=2,B 反过来),极易死锁

UPDATE ... WHERE version = ? 为什么能绕过锁竞争

它根本不加锁,纯靠数据库原子性 + 条件匹配完成冲突检测。执行时只做一次快照读(MVCC),然后尝试更新:若 version 值已变,UPDATE 影响行为 0,业务立刻感知失败;若匹配成功,一行内完成 stock = stock - 1 和 version = version + 1,无锁、无等待、无死锁风险。

但要注意几个硬约束:

MySQL
MySQL

编写正确的MySQL查询,避免字符集、索引和锁方面的常见陷阱。

下载
  • 必须把 version 字段设为 INT UNSIGNED NOT NULL DEFAULT 0,避免空值导致条件永远不成立
  • 初始插入时要显式写 version = 0,不能依赖 DEFAULT 在某些 ORM 中可能不生效
  • 不能在同一个事务里先 SELECT ... version 再 UPDATE —— 这样还是可能被其他事务抢先更新,必须保证“读-判-更”是原子的,即业务层拿到 version 后立即发 UPDATE

什么时候换乐观锁反而更危险

不是所有场景都适合砍掉悲观锁。如果业务逻辑中「读」和「更新」之间存在不可省略的中间判断(比如库存扣减前要查用户等级、校验优惠券、调第三方风控接口),那 version 机制就扛不住:别人可能在你调接口时改了数据,而你 UPDATE 还是会成功(因为 version 没变),导致超卖。

这种情况下,要么保留悲观锁(但缩短事务范围,把外部调用挪到事务外),要么改用带条件的乐观锁增强版:

  • 把关键业务状态也纳入 WHERE 条件,例如 UPDATE goods SET stock = stock - 1, version = version + 1 WHERE id = 1 AND version = 5 AND stock >= 1
  • 或者用 SELECT ... FOR UPDATE NOWAIT(MySQL 8.0+)快速失败,避免无限等待
  • 再或者,对热点商品 ID 做分段(如 shard_id = id % 16),把单行锁分散成 16 行锁,降低冲突概率

版本号字段本身带来的隐性成本

很多人忽略一点:version 字段会让每次 UPDATE 都变成“非覆盖写”——即使只改一个字段,也必须带上 version = version + 1,这会导致:

  • 二级索引更新更频繁(因为主键索引改了,所有二级索引都要更新)
  • binlog 日志体积增大(每行变更多记一个字段)
  • 如果用了 MySQL Group Replication 或 Canal 做订阅,下游解析压力上升
  • 某些 ORM(如 MyBatis-Plus)自动生成的 updateById 默认不带 version 条件,容易漏写,上线即出 bug

真正落地时,version 不是加个字段就能跑,得配合 DAO 层强约束、SQL 审计规则、以及监控 UPDATE 返回影响行数是否为 0 的告警——否则问题暴露出来,已经是在凌晨三点的订单超卖现场了。

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

mysql

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
mysql修改数据表名
mysql修改数据表名

MySQL修改数据表:1、首先查看数据库中所有的表,代码为:‘SHOW TABLES;’;2、修改表名,代码为:‘ALTER TABLE 旧表名 RENAME [TO] 新表名;’。php中文网还提供MySQL的相关下载、相关课程等内容,供大家免费下载使用。

2023.06.20

2253

6

MySQL创建存储过程
MySQL创建存储过程

存储程序可以分为存储过程和函数,MySQL中创建存储过程和函数使用的语句分别为CREATE PROCEDURE和CREATE FUNCTION。使用CALL语句调用存储过程智能用输出变量返回值。函数可以从语句外调用(通过引用函数名),也能返回标量值。存储过程也可以调用其他存储过程。php中文网还提供MySQL创建存储过程的相关下载、相关课程等内容,供大家免费下载使用。

2023.06.21

1379

5

mongodb和mysql的区别
mongodb和mysql的区别

mongodb和mysql的区别:1、数据模型;2、查询语言;3、扩展性和性能;4、可靠性。本专题为大家提供mongodb和mysql的区别的相关的文章、下载、课程内容,供大家免费下载体验。

2023.07.18

815

5

mysql密码忘了怎么查看
mysql密码忘了怎么查看

MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS 应用软件之一。那么mysql密码忘了怎么办呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.07.19

3112

5

mysql创建数据库
mysql创建数据库

MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS 应用软件之一。那么mysql怎么创建数据库呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.07.25

5248

4

mysql默认事务隔离级别
mysql默认事务隔离级别

MySQL是一种广泛使用的关系型数据库管理系统,它支持事务处理。事务是一组数据库操作,它们作为一个逻辑单元被一起执行。为了保证事务的一致性和隔离性,MySQL提供了不同的事务隔离级别。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

2023.08.08

1179

3

sqlserver和mysql区别
sqlserver和mysql区别

SQL Server和MySQL是两种广泛使用的关系型数据库管理系统。它们具有相似的功能和用途,但在某些方面存在一些显著的区别。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.11

5511

4

mysql忘记密码
mysql忘记密码

MySQL是一种关系型数据库管理系统,关系数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性。那么忘记mysql密码我们该怎么解决呢?php中文网给大家带来了相关的教程以及其他关于mysql的文章,欢迎大家前来学习阅读。

2023.08.14

4842

7

mysql事务隔离级别
mysql事务隔离级别

mysql规范中定义了四种事务隔离级别,不同的隔离级别对事务的处理有所不同。本专题为大家提供mysql事务隔离级别相关的文章内容,大家可以免费体验。

2023.08.16

6414

11

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PostgreSQL vs MySQL
PostgreSQL vs MySQL

共1课时 | 188人学习