答案是:先查SELECT @@autocommit;确认会话级自动提交状态,返回0表示关闭,需显式COMMIT;再用SELECT @@in_transaction;验证是否处于活跃事务中,避免隐式提交导致数据不可见或锁残留。

MySQL 事务卡住、连接挂起、SELECT 查不到刚 UPDATE 的数据——90% 是 autocommit=0 后忘了 COMMIT,且没配好连接池初始化。
如何确认当前连接的 autocommit 状态
别猜,直接查。同一台 MySQL,不同客户端、不同驱动、不同连接池可能表现完全不同:
-
SELECT @@autocommit;—— 返回1表示自动提交开,0表示关 -
SELECT @@session.autocommit;—— 和上面等价,明确是会话级 -
SELECT @@global.autocommit;—— 全局默认值(需SUPER权限),只影响新连接
注意:SHOW VARIABLES LIKE 'autocommit'; 也行,但返回的是字符串 ON/OFF,不如 @@autocommit 返回整数直观。
为什么 set autocommit = 1; 在应用里经常失效
因为 JDBC、PyMySQL、PDO 等驱动在建连后会「主动覆盖」服务端设置。比如 HikariCP 默认不执行任何 initSQL,Druid 默认也不设,Spring Boot 的 spring.datasource.hikari.connection-init-sql 默认为空。
- MySQL 服务端设了
SET GLOBAL autocommit = 1;,只保新连接默认为 1,但 JDBC 驱动仍可能在连接建立后立刻执行setAutoCommit(false) - 常见现象:你查
SELECT @@autocommit;是 1,但执行完一条UPDATE后,别的会话查不到变更,锁还挂着 —— 实际上驱动已悄悄把 autocommit 改成 0 - 真正可靠的写法是在连接池配置中强制初始化:
– HikariCP:connection-init-sql=SET autocommit = 1
– Druid:connectionInitSqls=["SET autocommit = 1"]
– MyBatis + Spring:spring.datasource.hikari.connection-init-sql同样生效
autocommit=0 时哪些操作会偷偷触发隐式 COMMIT
这是最容易踩的坑:你以为还在事务里,其实刚执行完一条语句,事务已经提交了,后续 ROLLBACK 无效。
-
ALTER TABLE、DROP TABLE、CREATE INDEX等 DDL 语句,执行前会强制COMMIT -
LOCK TABLES、UNLOCK TABLES也会导致隐式提交 -
TRUNCATE TABLE虽是 DML 语法,但在 InnoDB 中实际是 DDL 行为,同样触发隐式 COMMIT - 哪怕
autocommit=0,执行这些语句后,SELECT @@in_transaction;会变成0,且无法回滚前面的修改
验证方法:SELECT @@in_transaction; 比 @@autocommit 更直接反映“当前是否处于活跃事务中”。
应用层该用 START TRANSACTION 还是 SET autocommit = 0
推荐无脑用 START TRANSACTION(或 BEGIN),除非你真需要一个长期保持的事务上下文(极少见)。
-
START TRANSACTION是显式、短生命周期、语义清晰的事务边界;SET autocommit = 0是会话级开关,容易遗忘恢复,也容易被中间件/ORM 干扰 - Spring 的
@Transactional底层就是靠START TRANSACTION+COMMIT/ROLLBACK实现的,和 autocommit 设置无关 - 如果手写 JDBC,优先用
conn.setAutoCommit(false); ... conn.commit();,而不是依赖会话级SET autocommit = 0—— 前者作用域可控,后者一旦漏掉SET autocommit = 1,整个连接后续所有 SQL 都变手动事务
最危险的情况:你在调试时手动 SET autocommit = 0;,执行几条 UPDATE 后忘了 COMMIT 也忘了 SET autocommit = 1;,然后关闭终端 —— 连接断开,事务自动回滚,但锁可能残留数秒(尤其在 RR 隔离级别下),造成其他连接阻塞。


















