事务未生效主因是autocommit未关闭或连接被重置;select ... for update锁不住行多因where未走索引导致表锁;死锁需通过show engine innodb status分析锁持有与等待关系。

事务没生效,COMMIT 后数据还是回滚了?
根本原因往往是自动提交(autocommit)没关,或者连接被框架/驱动悄悄重置了。MySQL 默认开启 autocommit,执行单条 INSERT/UPDATE 会立刻落盘,BEGIN 和 COMMIT 形同虚设。
实操建议:
- 显式关闭 autocommit:
SET autocommit = 0;(MySQL),或在连接字符串里加?autocommit=false - 确认事务边界是否被 ORM 干预:Django 的
transaction.atomic()、Spring 的@Transactional都可能覆盖手动控制 - 检查客户端是否异常断连——未收到
COMMIT确认就断开,服务端通常会回滚活跃事务 - 用
SELECT @@autocommit;和SELECT TRX_ID, TRX_STATE FROM INFORMATION_SCHEMA.INNODB_TRX;实时验证状态
SELECT ... FOR UPDATE 锁不住行?
常见于 WHERE 条件没走索引,导致升级为表锁,或者锁范围超出预期(比如范围查询锁住间隙)。InnoDB 的行锁依赖索引,没索引=全表扫描=锁表。
实操建议:
- 先
EXPLAIN SELECT ...确认是否命中索引;主键、唯一索引最安全,普通索引需注意type是ref而非index或ALL - 避免在
FOR UPDATE查询中用函数或表达式过滤,如WHERE YEAR(create_time) = 2024会失效索引 - 注意隔离级别影响:READ COMMITTED 下,非唯一条件的
FOR UPDATE可能只锁查到的行;REPEATABLE READ 下还会锁间隙(防止幻读) - 测试时用两个会话:一个执行
SELECT ... FOR UPDATE不提交,另一个尝试更新同一行,看是否阻塞
死锁日志里出现 WAITING FOR THIS LOCK TO BE GRANTED 怎么定位?
MySQL 的 SHOW ENGINE INNODB STATUS\G 输出里,死锁段落会明确列出两个事务各自持有什么锁、等待什么锁。关键不是看谁“先”执行,而是看加锁顺序是否构成环路。
篇文章是针对git版本控制和工作流的总结,如果有些朋友之前还没使用过git,对git的基本概念和命令不是很熟悉,可以从以下基本教程入手: Git是分布式版本控制系统,与SVN类似的集中化版本控制系统相比,集中化版本控制系统虽然能够令多个团队成员一起协作开发,但有时如果中央服务器宕机的话,谁也无法在宕机期间提交更新和协同开发。甚至有时,中央服务器磁盘故障,恰巧又没有做备份或备份没及时,那就可能有丢失数据的风险。感兴趣的朋友可以过来看看
实操建议:
- 重点看
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:和*** (2) HOLDS THE LOCK(S):两段,对照 SQL 判断访问顺序 - 典型模式:事务 A 先锁 ID=1 再锁 ID=2,事务 B 反过来——只要两个事务并发执行,就必然死锁
- 统一 DML 顺序是根治法:比如所有业务都按
ORDER BY id ASC更新一批记录 - 避免在事务里做 RPC、文件读写等长耗时操作,拉长持有锁的时间窗口
高并发下 INSERT ... ON DUPLICATE KEY UPDATE 为什么还报唯一键冲突?
这不是事务问题,而是 INSERT 操作本身在重复键时触发了唯一约束检查,而该检查发生在语句执行期,和事务隔离级别无关。即使你包在 BEGIN/COMMIT 里,也拦不住这个校验。
实操建议:
- 确认
ON DUPLICATE KEY UPDATE的“key”确实是唯一索引或主键——普通索引不触发此逻辑 - 如果想避免报错,必须用
INSERT IGNORE或捕获1062 Duplicate entry错误码后重试 - 注意:该语句在 REPEATABLE READ 下可能引发间隙锁竞争,尤其在插入不存在的范围时,容易卡住其他插入
- 替代方案:先
SELECT判断是否存在,再决定INSERT或UPDATE——但要加FOR UPDATE,否则并发下仍有竞态
锁的粒度、时机、释放点,全都藏在存储引擎实现和 SQL 执行计划里。调参改配置不如先 EXPLAIN 一眼看清实际走了哪条路。









