MySQL数据库基本概念中什么是事务?事务的四大特性与隔离级别详解

1次阅读

事务是保证多条sql“全成或全挂”的逻辑单元,通过begin/commit/rollback控制,依托undolog、redolog和锁实现acid;mysql默认rr隔离级别存在幻读陷阱,需谨慎使用间隙锁与快照读。

MySQL数据库基本概念中什么是事务?事务的四大特性与隔离级别详解

事务就是一组必须“全成或全挂”的SQL操作

你在做转账、下单、库存扣减这类业务时,如果只执行一半就出错(比如张三扣了钱,李四没收到),数据库就会陷入不一致状态。事务就是为解决这个问题而生的:它把多条相关SQL打包成一个逻辑单元,要么全部成功写入,要么全部撤销,不留中间态。BEGIN 开启,COMMIT 提交,ROLLBACK 回滚——这是最底层的控制节奏。

ACID不是口号,是MySQL用undolog、redolog和锁硬扛出来的

原子性靠 undolog 实现:每步修改都记下“怎么 undo”,回滚时直接逆向重放;一致性不是事务自己保证的,而是靠你写的业务逻辑 + 外键/约束 + 隔离级别共同兜底;隔离性由锁和MVCC配合完成——比如 REPEATABLE READ 下,InnoDB 用快照读避免幻读,但当前读仍可能被 select ... for UPDATE 阻塞;持久性依赖 redolog 刷盘机制,哪怕断电也能恢复已提交事务。

MySQL默认的REPEATABLE READ其实有“陷阱”

很多人以为它能完全避免幻读,但真实情况是:普通 SELECT 看不到其他事务插入的新行(快照读),可一旦执行 SELECT ... FOR UPDATEINSERT,就可能触发间隙锁(gap lock),甚至升级为临键锁(next-key lock)。这意味着:

  • 并发插入相同范围数据时,可能意外阻塞,而不是报错
  • SELECT count(*)INSERT 做“存在性校验”,在RR下仍可能重复插入(因为两次SELECT看到的是同一快照,但INSERT会实际加锁)
  • 想真正串行化,得显式用 SELECT ... FOR UPDATE 或干脆设成 SERIALIZABLE,但性能代价极大

改隔离级别不能只靠SET session TRANSACTION ISOLATION LEVEL

这个命令只影响当前会话,且必须在事务开始前设置;如果用连接池(如Druid、HikariCP),连接复用后隔离级别可能继承上一次的值,导致行为不一致。更稳妥的做法是:

  • 在应用层连接初始化时统一设置(比如springTransactionDefinition.ISOLATION_REPEATABLE_READ
  • 避免在同一个事务里动态切换级别(MySQL不支持)
  • READ UNCOMMITTED 在InnoDB中基本无效——它忽略行锁,但InnoDB仍会加锁,实际效果接近READ COMMITTED

事务真正的复杂点不在概念,而在“哪条SQL触发什么锁”“快照何时生成”“undo日志什么时候清理”——这些细节不看INFORMATION_SCHEMA.INNODB_TRXSHOW ENGINE INNODB STATUS,光靠理论很难定位死锁或长事务问题。

text=ZqhQzanResources