答案是:必须在连接池初始化时配置set autocommit = 1,否则驱动可能覆盖服务端设置导致锁残留、数据不可见;应优先查select @@autocommit和select @@in_transaction确认真实状态,而非依赖默认值。

别设全局 autocommit=0,也别依赖服务端默认值——应用层必须在连接池初始化时强制设 SET autocommit = 1,否则锁会卡住、数据查不到、事务行为不可控。
查当前连接的 @@autocommit 和 @@in_transaction 状态
光看文档说“默认是 1”没用,不同驱动、不同连接池、甚至同个客户端不同版本都可能覆盖它。真实状态只能现场查:
-
SELECT @@autocommit;返回1表示自动提交开,0表示关(整数比SHOW VARIABLES LIKE 'autocommit'的字符串更易判断) -
SELECT @@in_transaction;更关键:返回1才说明你真正在一个活跃事务里;哪怕@@autocommit=0,执行了ALTER TABLE后它也会变0,意味着前面的修改已隐式提交 - 生产环境排查 SELECT 查不到刚 UPDATE 的数据、UPDATE 后连接挂起、锁长时间不释放,第一步永远是这两个查询,而不是翻配置文件
连接池里配 connection-init-sql 才算真正生效
JDBC、PyMySQL、PDO 这些驱动建连后大概率会悄悄执行 setAutoCommit(false),哪怕你 MySQL 服务端设了 SET GLOBAL autocommit = 1,新连上来还是 0。靠 SET autocommit = 1 在应用代码里临时执行?晚了——事务可能已经启了。
- HikariCP:设
spring.datasource.hikari.connection-init-sql=SET autocommit = 1 - Druid:用
connectionInitSqls=["SET autocommit = 1"] - MyBatis + Spring Boot:同样走 Hikari 配置项,别信
application.yml里写的default-auto-commit: true,它不生效 - 不配 init-sql 的后果:单条
UPDATE执行完,别的会话查不到,锁还占着,SELECT @@in_transaction是1,但你根本没写START TRANSACTION——就是驱动干的
START TRANSACTION 比 SET autocommit = 0 更安全
两者都能关自动提交,但语义和边界不同:
-
SET autocommit = 0是会话级开关,之后所有 DML 都进同一个事务,直到你显式COMMIT或ROLLBACK;万一忘了,连接归还池子时事务还挂着,拖慢整个池 -
START TRANSACTION(或BEGIN)只是临时把autocommit设为0,并初始化事务上下文;COMMIT或ROLLBACK后,autocommit自动恢复原值(通常是1),不容易漏关 - DDL(如
ALTER TABLE)、TRUNCATE TABLE、LOCK TABLES在autocommit=0下仍会触发隐式COMMIT,导致前面的修改提前落库;而用START TRANSACTION包裹业务逻辑,至少能明确事务起点和终点
别让 DDL 或系统变量操作偷偷提交你的事务
哪怕你确认 @@autocommit=0 且 @@in_transaction=1,下面这些操作仍会立刻结束当前事务:
-
CREATE/ALTER/DROP表、库、索引等 DDL 语句 -
TRUNCATE TABLE(InnoDB 里它是 DDL,不是 DML) -
LOCK TABLES和UNLOCK TABLES -
SET autocommit = 1或SET SESSION tx_isolation = ...这类影响事务行为的系统变量修改 - 验证方法很简单:
START TRANSACTION; UPDATE t SET x=1; ALTER TABLE t ADD y INT;之后再查SELECT @@in_transaction;,结果是0—— 事务早没了,ROLLBACK也救不回那条UPDATE
最常被忽略的点是:事务不是靠“BEGIN”开启的,而是靠第一条写操作(INSERT/UPDATE/DELETE)真正启动;SELECT 不触发事务,也不加锁。所以别以为写了 BEGIN 就万事大吉,得盯住 @@in_transaction 和实际 SQL 类型。











