脏读是什么意思?
脏读是指一个事务读取了另一个事务尚未提交的数据。也就是说,当事务 A 修改了数据但尚未提交,而事务 B 读取了这部分数据时,如果事务 A 后续发生回滚,那么事务 B 所读取的数据就成了“脏数据”,因为这些数据实际上并未被最终写入数据库。这样可能会导致数据的不一致和错误的业务逻辑。
举例
假设有一个银行账户的余额记录,账户 ID 为 1。现在有两个事务,分别为事务 A 和事务 B。
- 事务 A:
BEGIN;
UPDATE account SET balance = balance - 100 WHERE id = 1;
- 开始事务后,将账户余额减少 100 元,但此时还未提交。
- 事务 B:
BEGIN;
SELECT balance FROM account WHERE id = 1;
- 在事务 A 修改后、提交前,事务 B 读取该账户余额。
- 事务 B 读到了事务 A 尚未提交的减少 100 元后的余额,这就是“脏读”。
- 后续情况:
- 如果事务 A 最终回滚,则事务 A 的修改不会被保存,而事务 B 已经读取到一个错误的、未最终确定的数据,导致数据不一致的问题。
通过这个例子,可以看出脏读的问题在于一个事务读取了另一个事务未提交的数据,这种数据在最终可能会被撤销,从而造成数据不一致。
不可重复读是什么意思?
不可重复读是指在同一个事务中,重复读取相同的数据记录时,由于其他事务对该数据进行了更新或删除,导致两次读取的结果不一致的现象。例如,事务 A 在读取某条记录后,事务 B 修改或删除了该记录并提交了更改,当事务 A 再次读取同一记录时,得到的结果与第一次不同。这种情况在 READ COMMITTED 隔离级别下可能会发生,但在 REPEATABLE READ 及更高隔离级别下则可以避免这种现象。
举例
假设有一个银行账户的余额记录,账户 ID 为 1,初始余额为 1000 元。现在有两个事务,分别为事务 A 和事务 B,说明不可重复读现象:
- 事务 A 开始并首次读取余额
BEGIN;
SELECT balance FROM account WHERE id = 1;
-- 返回结果为:1000
此时事务 A 读取了账户余额为 1000 元。
- 事务 B 修改余额
在事务 A 仍未结束时,事务 B 开始并修改该账户余额,例如存入 500 元:
BEGIN;
UPDATE account SET balance = balance + 500 WHERE id = 1;
COMMIT;
事务 B 修改并提交后,账户余额变为 1500 元。
- 事务 A 再次读取余额
事务 A 在之前读取后仍未提交,再次读取账户余额:
SELECT balance FROM account WHERE id = 1;
-- 返回结果为:1500
此时事务 A 得到了与第一次不同的余额,从 1000 元变为 1500 元,这就是不可重复读的现象。
这个例子展示了在同一个事务内多次读取同一数据时,由于其他事务的修改而导致读取结果不一致,从而产生了不可重复读的问题。
幻读是什么意思?
幻读是指在同一事务中,执行相同查询条件的多次查询时,结果集中的记录数量发生变化的现象。换句话说,当事务 A 第一次执行某个查询时返回了一组数据,而在事务 A 进行过程中,另一个事务 B 插入了满足相同查询条件的新数据(或删除了部分数据),当事务 A 再次执行相同查询时,就会发现之前没有的“幻影”记录出现了。这种现象就是所谓的幻读。
在 MySQL 的 InnoDB 存储引擎中,为了防止幻读,在 REPEATABLE READ 隔离级别下会使用间隙锁(Gap Lock)来锁定查询范围,从而确保在同一事务内多次读取结果保持一致。
举例
假设有一个员工表 Employees,其中有个字段 department 表示员工所在的部门。两个事务 T1 和 T2 同时运行:
- 事务 T1 开始并执行查询:
SELECT * FROM Employees WHERE department = 'Sales';
此时返回满足条件的所有销售部门员工。
- 在 T1 还未提交之前,事务 T2 执行插入操作:
INSERT INTO Employees (name, department) VALUES ('张三', 'Sales');
T2 插入了一条新记录,其部门为 ‘Sales’,并提交了事务。
- 事务 T1 再次执行同样的查询:
SELECT * FROM Employees WHERE department = 'Sales';
这次返回的结果中,除了之前的记录外,还多了一条刚刚插入的新记录,即“张三”。
在这种情况下,由于 T2 在 T1 的事务过程中插入了新数据,导致 T1 第二次查询时看到的数据集(幻影数据)与第一次不一致,这就是幻读现象。
注意
- 幻读问题通常出现在 可重复读(Repeatable Read)隔离级别下,而在更高的隔离级别(如 串行化)中可以避免幻读。
- 不同数据库的隔离级别实现和默认行为可能有所不同。
通过上述示例可以清楚地看到,幻读现象是由于并发事务对数据集的动态修改所引起的。
评论留言
欢迎您,!您可以在这里畅言您的的观点与见解!