事务处理与事务隔离
1. 事务处理
定义:事务是数据库执行过程中的一个逻辑单位,要么全部执行成功,要么全部失败回滚。
1.1. 事务的特性ACID
特性 | 英文 | 说明 |
原子性 A | Atomicity | 操作不可分割,要么全部成功要么全部失败。 |
一致性 C | Consistency | 事务前后数据库都处于一致状态,遵循约束(如主键唯一)。 |
隔离性 I | Isolation | 并发事务之间互不干扰,一个事务未提交前对其他事务不可见。 |
持久性 D | Durability | 一旦事务提交,对数据库的更改是永久性的,重启也不会丢失。 |
- 一致性注意点:是业务定义的一致状态,和约束规则(如唯一性、外键)密切相关。
- 持久性保障机制:通过 重做日志(redo log) 实现,确保系统崩溃后仍能恢复已提交事务。
MySQL 中事务的支持依赖存储引擎:
- ✅
InnoDB
:支持事务 - ❌
MyISAM
:不支持事务 - 使用
SHOW ENGINES
可查看各引擎是否支持事务。
1.2. 事务控制
事务控制语句
命令 | 功能 |
| 显式开启一个事务 |
| 提交事务,使修改永久保存 |
| 回滚事务,撤销所有未提交的操作 |
| 创建保存点 |
| 回滚到某个保存点 |
| 删除保存点 |
| 设置事务隔离级别 |
隐式 vs 显式事务
隐式事务(默认自动提交):
autocommit=1
:每条 SQL 默认自动提交。
不过这时,如果采用 START TRANSACTION 或者 BEGIN 的方式来显式地开启事务,那么这个事务只有在 COMMIT 时才会生效,在 ROLLBACK 时才会回滚。
显式事务:
autocommit=0
或使用START TRANSACTION
开启,需要显式COMMIT
或ROLLBACK
。- 可通过命令设置自动提交:
SET autocommit = 0; -- 关闭自动提交
SET autocommit = 1; -- 开启自动提交
注意:
- 事务是保持数据一致性的核心机制,尤其在并发和出错场景下至关重要。
- InnoDB 引擎支持事务,配合日志机制保障数据完整性。
- 熟练掌握事务控制语句和 MySQL 中事务默认行为,有助于规避数据异常。
- 注意配置参数(如 autocommit, completion_type)对事务行为的影响。
2. 事务隔离
2.1. 事务并发导致的三种异常现象
异常类型 | 描述 |
脏读 | 读取到其他事务尚未提交的数据。 |
不可重复读 | 同一查询在同一事务中读取结果不一致。 |
幻读 | 两次相同条件查询结果集条数不同。 |
三种异常的区别:
异常类型 | 定义 | 涉及的数据变化 | 何时发生 |
脏读 | 读取了其他事务尚未提交的数据 | 同一条数据,未提交 | 另一个事务还在进行时 |
不可重复读 | 同一事务中两次读取同一记录,内容不同 | 同一条数据,被提交修改 | 另一个事务提交后 |
幻读 | 同一事务中两次读取满足条件的多条记录,结果条数不同 | 新增或删除了记录(而非修改) | 另一个事务提交后 |
2.2. 四种事务隔离级别(SQL-92标准)
隔离级别 | 是否允许脏读 | 是否允许不可重复读 | 是否允许幻读 |
读未提交 | ✅ 是 | ✅ 是 | ✅ 是 |
读已提交 | ❌ 否 | ✅ 是 | ✅ 是 |
可重复读 (MySQL 默认) | ❌ 否 | ❌ 否 | ✅ 是 |
可串行化(最高) | ❌ 否 | ❌ 否 | ❌ 否 |
- 读未提交(Read Uncommitted):最低隔离级别,效率高,但问题最多。
- 读已提交(Read Committed):可避免脏读,仍存在不可重复读和幻读。
- 可重复读(Repeatable Read):避免脏读和不可重复读,但仍可能幻读。
- 可串行化(Serializable):完全隔离,避免所有异常,但性能最差。
- 读未提交,也就是允许读到未提交的数据,这种情况下查询是不会使用锁的,可能会产生脏读、不可重复读、幻读等情况。
- 读已提交就是只能读到已经提交的内容,可以避免脏读的产生,属于 RDBMS 中常见的默认隔离级别(比如说 Oracle 和 SQL Server),但如果想要避免不可重复读或者幻读,就需要我们在 SQL 查询的时候编写带加锁的 SQL 语句。
- 可重复读,保证一个事务在相同查询条件下两次查询得到的数据结果是一致的,可以避免不可重复读和脏读,但无法避免幻读。MySQL 默认的隔离级别就是可重复读。
- 可串行化,将事务进行串行化,也就是在一个队列中按照顺序执行,可串行化是最高级别的隔离等级,可以解决事务读取中所有可能出现的异常情况,但是它牺牲了系统的并发性。
隔离级别越低,意味着系统吞吐量(并发程度)越大,但同时也意味着出现异常问题的可能性会更大。
数据库事务的隔离级别决定了事务之间可以看到对方中间状态的程度。隔离级别越高,系统需要加更多锁或其他机制来避免数据被其他事务“干扰”,因此限制了并发访问的自由度。
在实际使用过程中我们往往需要在性能和正确性上进行权衡和取舍,没有完美的解决方案,只有适合与否。
一个隔离级别的实现满足下面两个条件:
- 正确性:只要能满足某一个隔离级别,一定能解决这个隔离级别对应的异常问题。
- 与实现无关:实际上 RDBMS 种类很多,这就意味着有多少种 RDBMS,就有多少种锁的实现方式,因此它们实现隔离级别的原理可能不同,然而一个好的标准不应该限制其实现的方式。