MySQL 默认 autocommit 是开启的,即5.7+和8.0新连接默认 autocommit=1,但受客户端、驱动和配置三重影响,JDBC驱动常显式设为false,需在连接池initSQL中执行SET autocommit = 1确保生效。

MySQL 默认 autocommit 是开还是关?
MySQL 5.7+ 和 8.0 默认是开启的,也就是每个 INSERT/UPDATE/DELETE 语句都自动提交,不依赖 COMMIT。但注意:这个“默认”只对新连接生效,且受客户端、驱动、配置三重影响。
常见误判场景:mysql 命令行客户端连上后执行 SELECT @@autocommit; 返回 1,就以为“稳了”,结果 Java 应用里却没生效——因为 JDBC 驱动默认会显式设 autocommit=false(尤其在使用连接池时)。
-
SET autocommit = 1;只影响当前会话,断开即失效 - 全局设置
SET GLOBAL autocommit = 1;需要SUPER权限,且新连接才继承,已存在的连接不变 - 真正可靠的初始化方式,是在连接建立后立刻执行
SET autocommit = 1;(比如在应用连接池的initSQL配置项里)
my.cnf 里怎么配 autocommit 才有效?
在 my.cnf 的 [mysqld] 段落加 autocommit=1 是有效的,但仅控制 MySQL 服务启动后新建连接的默认值。它不会改变运行中已存在的连接,也不会覆盖客户端或驱动的显式设置。
容易踩的坑:autocommit=ON 或 autocommit=true 是无效写法,必须是数字 1 或 0;写成 autocommit=on(小写)会被忽略,MySQL 启动时也不报错,静默失效。
- 正确写法:
autocommit=1 - 修改后需重启 MySQL 服务才生效(
systemctl restart mysqld) - 验证是否加载成功:连上后执行
SELECT @@global.autocommit;,不是@@autocommit
Java 应用里 autocommit 总是 false,为什么改 my.cnf 没用?
因为 JDBC 驱动在创建连接时,默认调用 Connection.setAutoCommit(false),这会直接覆盖 MySQL 服务端的默认值。my.cnf 和 SET GLOBAL 对它完全无效。
典型表现:Spring Boot + HikariCP 连接池里,即使数据库设了 autocommit=1,事务注解 @Transactional 仍能生效,但单条 update 不加事务时也卡住不提交——其实是驱动把 autocommit 关了,又没手动 commit,导致锁一直挂着。
- HikariCP 配置加
connection-init-sql=SET autocommit = 1 - Druid 配置用
connectionInitSqls=["SET autocommit = 1"] - 更稳妥的做法:不在连接池层面硬控,而是业务代码里明确控制,比如非事务操作用
try-with-resources + setAutoCommit(true)
autocommit 关闭时,哪些语句会隐式提交?
这是最容易引发数据不一致的地方:哪怕你设了 autocommit=0,执行某些语句也会触发隐式 COMMIT,导致你以为还在事务里,其实已经提交了。
典型例子:CREATE TABLE、DROP TABLE、ALTER TABLE、TRUNCATE TABLE、RENAME TABLE,还有 LOCK TABLES、UNLOCK TABLES —— 它们都会强制提交当前事务(如果有的话),然后自己再单独走一套隐式事务。
- 执行
START TRANSACTION;后,紧跟着一个ALTER TABLE,前面的 DML 就已经提交了 -
SELECT不会触发提交,但SELECT ... FOR UPDATE会(因为它属于事务内锁操作) - DDL 语句无法回滚,哪怕你在事务块里写
BEGIN; ALTER TABLE ...; ROLLBACK;,表结构变更依然生效
这事没法绕开,只能提前预判:DDL 和 DML 别混在同一个逻辑单元里,尤其是迁移脚本或管理后台的“一键升级”功能。










