1. 脏读(Dirty Read)
定义:
一个事务读取了另一个未提交事务修改的数据。如果未提交的事务回滚,那么读取到的数据就是无效的(即“脏数据”)。
示例:
事务 A 修改某一行数据,将值从
100
改为200
,但尚未提交。事务 B 读取该行数据,得到值
200
。事务 A 回滚,将值恢复为
100
。事务 B 读取到的
200
是无效的(脏数据)。
原因:
事务 B 读取了事务 A 未提交的数据。
事务 A 回滚后,事务 B 的数据不一致。
影响:
读取到无效数据,可能导致逻辑错误。
对应隔离级别:
读未提交(Read Uncommitted):允许脏读。
读已提交(Read Committed) 及以上:禁止脏读。
2. 不可重复读(Non-repeatable Read)
定义:
在一个事务中,多次读取同一行数据时,由于其他事务的修改,导致前后读取的结果不一致。
示例:
事务 A 读取某一行数据,值为
100
。事务 B 修改了这一行数据,将值改为
200
,并提交。事务 A 再次读取同一行数据时,发现值变为
200
。
原因:
其他事务对同一行数据进行了**更新(UPDATE)**操作。
事务 A 在两次读取之间,数据被修改。
影响:
事务 A 的结果不一致,可能导致逻辑错误。
对应隔离级别:
读未提交(Read Uncommitted) 和 读已提交(Read Committed):允许不可重复读。
可重复读(Repeatable Read) 及以上:禁止不可重复读。
3. 幻读(Phantom Read)
定义:
在一个事务中,多次执行相同的查询时,由于其他事务的插入或删除操作,导致前后查询的结果集不一致(新增或减少了行)。
示例:
事务 A 查询某个条件的数据,返回
10
条记录。事务 B 插入了一条符合条件的新记录,并提交。
事务 A 再次查询相同条件时,发现返回了
11
条记录。
原因:
其他事务对符合查询条件的范围进行了**插入(INSERT)或删除(DELETE)**操作。
事务 A 在两次查询之间,数据集中新增或减少了行。
影响:
事务 A 的结果集不一致,可能导致逻辑错误。
对应隔离级别:
读未提交(Read Uncommitted)、读已提交(Read Committed) 和 可重复读(Repeatable Read):允许幻读。
串行化(Serializable):禁止幻读。
4. 事务隔离级别与现象的关系
以下是不同隔离级别对脏读、不可重复读和幻读的解决能力:
隔离级别 | 脏读(Dirty Read) | 不可重复读(Non-repeatable Read) | 幻读(Phantom Read) |
---|---|---|---|
读未提交(Read Uncommitted) | 可能发生 | 可能发生 | 可能发生 |
读已提交(Read Committed) | 不会发生 | 可能发生 | 可能发生 |
可重复读(Repeatable Read) | 不会发生 | 不会发生 | 可能发生 |
串行化(Serializable) | 不会发生 | 不会发生 | 不会发生 |
5. 总结
现象 | 定义 | 原因 | 对应隔离级别 |
---|---|---|---|
脏读(Dirty Read) | 读取了未提交事务的数据,可能导致无效数据。 | 读取了未提交的数据。 | 读未提交(Read Uncommitted) |
不可重复读(Non-repeatable Read) | 同一行数据在事务中多次读取结果不一致。 | 其他事务更新了数据。 | 读已提交(Read Committed) |
幻读(Phantom Read) | 同一查询在事务中多次执行结果集不一致(新增或减少了行)。 | 其他事务插入或删除了数据。 | 可重复读(Repeatable Read) |
6. 如何选择合适的隔离级别
读未提交(Read Uncommitted):
优点:性能最高。
缺点:可能发生脏读、不可重复读和幻读。
适用场景:对数据一致性要求极低的场景(如统计数据分析)。
读已提交(Read Committed):
优点:避免了脏读。
缺点:可能发生不可重复读和幻读。
适用场景:大多数业务场景(如银行账户查询)。
可重复读(Repeatable Read):
优点:避免了脏读和不可重复读。
缺点:可能发生幻读。
适用场景:需要保证同一事务中多次读取结果一致的场景(如对账系统)。
串行化(Serializable):
优点:避免了脏读、不可重复读和幻读。
缺点:性能最低,并发性差。
适用场景:对数据一致性要求极高的场景(如金融交易系统)。
通过选择合适的隔离级别,可以在性能和数据一致性之间找到平衡。