数据库事务基础
事务是什么?
数据库事务(Transaction)是将一系列数据库操作打包成一个不可分割的逻辑整体。事务执行时,其内部的数据库操作要么全部成功,要么全部失败!它是保证数据一致性的核心机制。
例子:转账
考虑 A 账户给 B 账户转100元的业务场景,一般需要如下步骤:
步骤1:从A账户扣减100元。 步骤2:给B账户增加100元。
假如没有事务的干预,我们独立执行这两个操作。
假设在步骤1执行成功后,正准备执行步骤2时因为某些原因导致步骤2执行失败,此时 A 的钱就凭空消失了,这是个不可容忍的重大事故!
事务的基础操作
事务的基础操作主要包含三个核心命令:
- 开启事务(
BEGIN或START TRANSACTION) - 提交事务(
COMMIT) - 回滚事务(
ROLLBACK)
MySQL 默认开启了**自动提交(autocommit)**模式。这意味着,每一条单独的 SQL 语句都会被当作一个独立的事务,执行完毕后自动提交,无需手动干预。
当我们需要将多条 SQL 语句放到一个事务中执行时,就必须显式开启事务。
操作流程如下:
- 开启事务:执行
BEGIN;或START TRANSACTION; - 执行业务 SQL:依次执行需要捆绑的多条SQL语句
- 判断结果并结束事务:
- SQL 全部执行成功 → 执行
COMMIT;,将修改持久化到数据库 - 任意一条 SQL 执行失败 → 执行
ROLLBACK;,撤销当前事务中所有已执行的操作
- SQL 全部执行成功 → 执行
事务的四大特性(ACID)
- 原子性(Atomicity):事务中的所有操作像原子一样不可分割,它们要么全部成功,要么全部失败。
- 一致性(Consistency):事务执行前后,数据从一个合法性状态变换到另外一个合法性状态 。这里的合法性状态是根据具体的业务决定的。
- 隔离性(Isolation):多个事务并发执行时,它们之间互不干扰。
- 持久性(Durability):一旦事务提交成功,那么它对数据库的改变就是永久性的。
并发事务存在的问题
在 ACID 四大特性中,最需要进行权衡取舍的,正是隔离性。
我们不妨设想一种最完美的隔离性保证——不允许事务并发执行,让它们全部串行执行。
然而,串行执行意味着并发性能极差,这在如今需要高并发的互联网应用场景下是不可容忍的!
为了提高并发性能,DBMS 提供了不同等级的隔离级别。而隔离级别越低,数据库对并发操作的“监控”就越松,由此可能引发以下三类典型的并发问题:
- 脏读
- 不可重复读
- 幻读
脏读
脏读是指一个事务读取到另一个事务未提交的数据。
请看如下的场景图:

- 事务B将该行的数据改为“张老三”
- 事务A读到了“张老三”
- 事务B回滚,数据恢复为“张三”
此时事务A原先读到的 “张老三” 就成了脏数据!这就是脏读。
不可重复读
不可重复读是指一个事务对同一条数据进行两次查询,得到的结果却不一致。
请看如下的场景图:

- 事务A先查询该行的数据,此时的结果为“张三”
- 事务A去处理其他数据
- 事务B更新这条数据的值为 “张老三” 并提交
- 事务A再次查询该行的数据,此时的结果为“张老三”
看到这里,很多人其实会产生这样的疑问:“这有什么问题?事务B改完并提交了,事务A查到最新的,不是很正常吗?”
这个疑问非常合理,但是我们从事务隔离性的角度去重新审视,这个疑问便不攻自破。
在事务A看来,它的内部从未对这行数据有过修改操作,因此按照事务之间理应互不干扰的理想状态,它有充分的理由相信它前后两次读取的同一行数据必须完全一致,这样才合理!
然而结果却不一致,这说明事务B的临门一脚对事务A造成了影响,因此隔离性遭到了破坏。
幻读
幻读是指一个事务使用相同的条件对数据集合进行两次查询,后一次查询返回的记录条数与之前不一致。
请看如下的场景图:

- 事务A查询整张 stu 表,此时只有一条记录 “张三”
- 事务B往 stu 表中插入一条数据 “李四” 并提交
- 事务A再次查询整张 stu 表,此时发现多出来一条 “李四”
看到这里,同样的疑问可能又会浮现:“这有什么问题?事务B插入了新数据并提交了,事务A查到新增的,不是很正常吗?”
这个疑问和上一节的“不可重复读”如出一辙。如果我们再次从事务 A 的视角来看
在事务A看来,它的内部从未对这张表做过任何插入或删除操作,因此按照事务理应互不干扰的理想状态,它前后两次查询到的记录条数应该完全一致,这样才合理!
然而第二次却凭空多出了一条“幽灵”记录(仿佛出现了幻觉),这说明事务B的插入操作对事务A造成了影响,事务的隔离性遭到破坏。
讲到这,其实很多人可能会认为不可重复读和幻读很像啊,其实不然。
二者的本质区别在于:
- 不可重复读:同一条记录的字段值变了(由其他事务的 UPDATE 操作引发);
- 幻读:满足条件的记录条数变了(由其他事务的 INSERT 或 DELETE 操作引发)。
事务隔离级别
SQL标准定义了4种事务的隔离级别,它们的级别从低到高(隔离性由弱到强,并发性能由高到低)依次为:
- 读未提交(Read Uncommitted)
- 读已提交(Read Committed)
- 可重复读(Repeatable Read)
- 串行化(Serializable)
读未提交
读未提交,顾名思义,就是一个事务能够读到另一个事务未提交的数据。
显然,这种隔离级别的隔离性几乎为 0,但并发性能是最好的。代价是它无法解决任何一类并发事务问题
读已提交
读已提交,顾名思义,就是一个事务只能读到另一个事务已提交的数据。
这种隔离级别能够避免脏读,但不可重复读和幻读仍然可能发生。
可重复读
可重复读,顾名思义,就是保证在同一个事务内,多次读取同一条记录的结果始终一致
这种隔离级别能够避免脏读和不可重复读,但仍然存在幻读的问题。
值得一提的是,MySQL 的 InnoDB 存储引擎默认采用此隔离级别,并且通过 MVCC机制,解决了大部分场景下的幻读问题。
串行化
串行化是最高的隔离级别。它通过强制事务串行执行,彻底杜绝了脏读、不可重复读和幻读。
这是隔离性最强、数据最安全的级别,但代价是并发性能最差,在实际生产环境中很少使用。
总结:
| 脏读 | 不可重复读 | 幻读 | |
|---|---|---|---|
| 读未提交 | 可能 | 可能 | 可能 |
| 读已提交 | 不可能 | 可能 | 可能 |
| 可重复读 | 不可能 | 不可能 | 可能(MySQL InnoDB 解决了一部分) |
| 串行化 | 不可能 | 不可能 | 不可能 |
