应使用 START TRANSACTION 而非 BEGIN 开启事务,因 START TRANSACTION 是 SQL 标准语法,语义明确、兼容性好、支持扩展子句且不受 autocommit 干扰;BEGIN 仅在存储程序中表示语句块,在普通会话中易引发误解和错误。

MySQL 中 BEGIN 和 START TRANSACTION 都能开启事务,但语义和行为不完全等价
BEGIN 在 MySQL 里不是标准的事务启动语句,而是被当作“开始一个语句块”的别名处理(尤其在存储过程或函数中),仅当它不在存储程序上下文里且后面没跟其他语句时,MySQL 才会隐式将其解释为 START TRANSACTION。这种隐式转换容易引发误解。
-
START TRANSACTION是 SQL 标准语法,明确表示事务起点,无歧义 -
BEGIN在存储过程中是合法语句(用于 BEGIN...END 块),此时它和事务无关 - 如果你在普通会话里写
BEGIN; SELECT ...;,MySQL 会报错:ERROR 1064 (42000): You have an error in your SQL syntax,因为BEGIN后不能直接跟查询语句(除非在存储程序里)
为什么用 START TRANSACTION 更安全
MySQL 官方文档明确建议:始终使用 START TRANSACTION 显式开启事务。原因包括:
- 兼容性更好:
START TRANSACTION在所有 MySQL 版本(5.5+)及多数兼容模式(如 Percona、MariaDB)中行为一致 - 不受
autocommit状态干扰:即使autocommit=1,START TRANSACTION也能可靠关闭自动提交;而BEGIN在某些旧版本或特殊配置下可能被忽略 - 支持可选子句:
START TRANSACTION WITH CONSISTENT SNAPSHOT或READ ONLY等扩展,BEGIN不支持这些 - 工具链识别更稳定:ORM(如 Django、SQLAlchemy)、监控工具、慢日志解析器都优先匹配
START TRANSACTION
BEGIN 被误用的典型错误现象
常见于复制粘贴脚本或从其他数据库迁移过来的用户:
- 写了
BEGIN后执行UPDATE,发现没进事务——其实是autocommit=1下每条语句独立提交,BEGIN没生效 - 在存储过程中混用:
BEGIN ... START TRANSACTION ... COMMIT; END,结果外层BEGIN是块标记,内层才是事务控制,逻辑嵌套混乱 - 使用
mysqldump --single-transaction导出时,若手动加了BEGIN,可能破坏一致性快照机制(因未触发WITH CONSISTENT SNAPSHOT)
示例对比:
-- ✅ 推荐:明确、可预测 START TRANSACTION; UPDATE accounts SET balance = balance - 100 WHERE id = 1; UPDATE accounts SET balance = balance + 100 WHERE id = 2; COMMIT; <p>-- ❌ 风险:依赖隐式行为,且无法加选项 BEGIN; UPDATE accounts SET balance = balance - 100 WHERE id = 1; -- 如果这里崩溃或连接断开,事务可能未按预期开启</p>
事务真正生效的关键其实不在开头用哪个词
真正决定事务是否启动的是:autocommit 状态 + 是否有显式事务控制语句。哪怕写了 START TRANSACTION,如果 session 的 autocommit 是 0,那后续语句仍会自动进入事务;反之,如果 autocommit=1 且没任何 START TRANSACTION / BEGIN,每条语句都是独立事务。
- 查当前状态:
SELECT @@autocommit; - 临时关闭:
SET autocommit = 0;(之后第一条 DML 就自动开启事务) - 但这种方式容易遗漏
COMMIT或ROLLBACK,导致长事务锁表
最稳妥的做法始终是:显式 START TRANSACTION → 执行操作 → 显式 COMMIT 或 ROLLBACK。别指望 BEGIN 替你兜底。
注意:MySQL 8.0.13+ 引入了 TRANSACTION 作为 START TRANSACTION 的同义词,但它仍是标准语法的一部分;而 BEGIN 始终不是事务专用关键字。


















