MySQL 通过事务(
START TRANSACTION/
BEGIN)配合存储引擎(主要是 InnoDB)的底层机制来保证 ACID,不是 SQL 本身具备该能力,也不是所有引擎都支持——MyISAM 就完全不支持事务。
为什么只有 InnoDB 能真正保障 ACID
InnoDB 是 MySQL 默认且唯一在生产中被广泛用于事务场景的引擎。它通过以下机制协同工作:
redo log(重做日志)确保 Durability:事务提交前,变更先写入磁盘上的
ib_logfile*,崩溃后可重放恢复
undo log(回滚日志)支撑 Atomicity 和 Isolation:记录行修改前的镜像,用于回滚或 MVCC 快照读 行级锁 +
next-key lock实现可重复读(
REPEATABLE READ)隔离级别,避免幻读 所有 DML 操作(
INSERT/
UPDATE/
DELETE)在事务内自动受锁和日志保护;DDL(如
ALTER TABLE)默认隐式提交,无法回滚
显式事务中哪些操作会意外中断 ACID 保证
即使写了
BEGIN,以下行为会让事务提前结束或失效: 执行
CREATE、
DROP、
ALTER等 DDL 语句 → 自动触发
COMMIT,之前未提交的变更立即落库且不可回滚 执行
SELECT以外的非事务语句(如
SET autocommit = 1)→ 切换会话模式,后续语句不再受当前事务控制 客户端连接异常断开(未发
COMMIT或
ROLLBACK)→ InnoDB 会检测并自动回滚该事务,但依赖
innodb_lock_wait_timeout和连接池配置 在
READ COMMITTED隔离级别下执行多次
SELECT,结果可能不一致 → 这是隔离级别允许的行为,不违反 ACID,但容易误判为“不一致”
如何验证一条 UPDATE 是否真的在事务中生效
不能只看客户端返回“Query OK”,要确认是否处于有效事务上下文:
检查SELECT @@autocommit:值为
0才表示手动事务模式启用 执行
SELECT trx_id, trx_state, trx_started FROM information_schema.INNODB_TRX:能看到当前活跃事务 ID 和状态(
RUNNING/
LOCK WAIT) 在另一会话中查同一行数据:若未
COMMIT,应看到旧值(
REPEATABLE READ下)或新值(
READ COMMITTED下),而非报错或阻塞 —— 阻塞说明锁已生效,是事务起作用的信号 故意让事务超时:
SET innodb_lock_wait_timeout = 1,然后用
UPDATE锁住某行,再在另一会话尝试更新同一行 → 应收到错误
ERROR 1205 (40001): Deadlock found when trying to get lock或超时提示
ACID 不是开关,而是 InnoDB 在日志、锁、内存结构之间精密协作的结果。最容易被忽略的是:事务边界由引擎控制,不是 SQL 语句决定;DDL 会偷偷提交;隔离级别差异直接影响“一致性”的表现形式——这些细节一旦出错,表面看 SQL 执行成功,实际已破坏业务语义。
