mysql默认autocommit=1,每条dml语句独立提交且立即生效,因此连续多条语句无法保证原子性;必须使用innodb引擎、显式开启事务(start transaction)、手动commit/rollback三者缺一不可。

必须用 InnoDB 引擎 + 显式开启事务 + 手动控制提交或回滚,否则多条语句之间毫无事务保障。
为什么单条SQL自动提交不能保证多条语句原子性
MySQL 默认开启 autocommit=1,每条 INSERT/UPDATE/DELETE 都会立刻写入磁盘并生效。这意味着:
- 即使你连续执行两条
UPDATE,第一条成功后立刻对其他连接可见,第二条失败也不会触发回退 -
START TRANSACTION本身不会改变表结构或引擎,如果目标表是MyISAM,事务指令会被静默忽略 - 客户端断开、脚本异常退出、甚至 Ctrl+C 都可能导致部分语句已提交、部分未执行,数据处于中间态
正确开启事务的三步硬要求
缺一不可,顺序不能乱:
- 确认表使用
InnoDB:运行SHOW CREATE TABLE your_table;,检查输出中是否含ENGINE=InnoDB;若不是,先执行ALTER TABLE your_table ENGINE=InnoDB; - 关闭自动提交:在事务开始前显式执行
SET autocommit = 0;(或直接用START TRANSACTION;,它隐式设置autocommit=0) - 手动收尾:所有语句执行完后,必须明确调用
COMMIT;或ROLLBACK;;不执行任一者,连接保持在事务中,锁不释放,可能阻塞其他操作
批量执行 SQL 文件时事务容易漏掉的点
用 source 或管道导入 .sql 文件时,默认每条语句独立提交。要整体事务化,需额外处理:
- 文件开头加
START TRANSACTION;,结尾加COMMIT;或ROLLBACK;—— 但注意:一旦某条语句报错(如主键冲突),后续语句仍会继续执行,COMMIT不会自动跳过 - 更可靠的做法是用客户端脚本封装:Python 中用
mysql-connector-python设置autocommit=False,捕获Exception后调用rollback();PHP 的mysqli需在query()前调用begin_transaction() - 命令行导入大文件时,
mysql -u user -p db 不支持事务包裹,除非整个 <code>script.sql是单个事务块且无语法错误——出错即中断,但已执行部分无法回滚
存储过程中嵌套事务的实际限制
MySQL 不支持真正的“保存点嵌套事务”,START TRANSACTION 在存储过程里遇到外层事务时会报错 ERROR 1305 (42000): SAVEPOINT does not exist。可行路径只有:
- 用
SAVEPOINT sp1;+ROLLBACK TO sp1;模拟局部回滚,但仅限同一事务内 - 在过程开头判断
SELECT @@in_transaction;,为 0 时才START TRANSACTION,避免嵌套 - 异常处理必须配
DECLARE EXIT HANDLER FOR SQLEXCEPTION,并在 handler 内明确写ROLLBACK;,否则过程结束但事务仍挂起
事务不是开关,而是状态机;从 START TRANSACTION 到 COMMIT 或 ROLLBACK 之间的每一毫秒,都存在锁等待、死锁、超时、连接中断等真实风险——别依赖“应该没问题”。











