事务没生效的首要原因是表引擎为MyISAM,它不支持事务;其次常见于autocommit=0未配合BEGIN、连接池复用导致事务状态残留、隔离级别与锁冲突等。

事务没生效,先确认是不是用了 MyISAM
MySQL 默认安装可能带 MyISAM 引擎,它根本不支持事务。执行 BEGIN 或 START TRANSACTION 看似成功,但 ROLLBACK 会静默失败,COMMIT 也毫无意义。
检查方式很简单:
- 查当前表引擎:
SHOW CREATE TABLE `your_table`;,看输出里是ENGINE=InnoDB还是ENGINE=MyISAM - 批量查库中所有非 InnoDB 表:
SELECT table_schema, table_name, engine FROM information_schema.tables WHERE engine != 'InnoDB' AND table_schema NOT IN ('information_schema','mysql','performance_schema','sys'); - 建表时务必显式指定:
CREATE TABLE t (...) ENGINE=InnoDB;,别依赖默认值
autocommit=0 不等于开启事务
autocommit 控制的是“每条语句是否自动提交”,设为 0 只是让后续语句进入隐式事务模式,但真正启动事务仍需 BEGIN 或 START TRANSACTION。很多人误以为只要 SET autocommit = 0 就万事大吉,结果 DML 语句执行后没 COMMIT,连接一断就全丢。
常见陷阱:
- 应用框架(如 Spring)可能自动管理事务边界,手动设
autocommit=0反而干扰其逻辑 - 连接池复用连接时,上一个请求留下的
autocommit=0状态可能影响下一个请求 - 高并发下若忘记
COMMIT或ROLLBACK,会长时间持有锁,拖慢整个表
高并发下事务“失效”的真实原因常是隔离级别与锁冲突
看起来事务没回滚、没生效,大概率不是事务机制坏了,而是:
- 用了
READ COMMITTED或更低隔离级别,其他连接提前读到了未提交变更(其实是幻读或不可重复读,不是事务失效) -
SELECT ... FOR UPDATE在非唯一索引上加锁,导致锁升级成表级锁,阻塞严重 - 长事务没及时结束,
undo log被复用,导致一致性读视图异常 - 应用层没捕获异常,
ROLLBACK根本没执行,比如 PHP 中try/catch漏了数据库异常分支
验证方法:在事务中执行 SELECT @@tx_isolation; 和 SELECT * FROM information_schema.INNODB_TRX;,看是否有长时间运行的事务卡住。
连接池配置不当会让事务行为变得不可预测
很多 ORM 或中间件(如 Druid、HikariCP)默认开启 autoCommit=true,且连接归还池时不会重置事务状态。如果某次请求开了事务但没正确关闭,下个用户拿到这个连接,autocommit 仍是 0,SELECT 都可能被挂起等锁。
必须检查并设置:
- HikariCP:
connection-init-sql=SET autocommit=1 - Druid:
connectionInitSqls=["SET autocommit=1"] - Spring Boot:
spring.datasource.hikari.connection-init-sql=SET autocommit=1 - 避免在业务代码里直接调用
connection.setAutoCommit(false),优先走声明式事务
最隐蔽的问题是:事务失效往往不报错,只表现为数据不一致或响应变慢——得从连接状态、锁视图、引擎类型三层交叉验证,缺一不可。











