什么是不可重复读?

参考回答

不可重复读是指在一个事务中多次读取同一条记录时,结果不一致,通常是因为在两次读取之间,另一事务修改了该记录的数据(包括更新或删除操作)。不可重复读是事务隔离性问题之一。


详细讲解与拓展

1. 不可重复读的定义

不可重复读的关键在于,事务中读取到的数据在操作期间被其他事务修改了,导致数据前后不一致。
– 主要表现:
– 第一次读取获得的结果和第二次读取的结果不同。
– 修改可以是更新现有数据或删除数据。

2. 不可重复读的例子

假设有一个用户余额表 Users

CREATE TABLE Users (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    balance DECIMAL(10, 2)
);

-- 初始数据
INSERT INTO Users VALUES (1, 'Alice', 1000.00);

事务 1

START TRANSACTION;

-- 第一次读取
SELECT balance FROM Users WHERE id = 1;  
-- 读取结果:1000.00

事务 2(并发执行):

START TRANSACTION;

-- 修改数据
UPDATE Users SET balance = 2000.00 WHERE id = 1;
COMMIT;

事务 1(继续执行):

-- 第二次读取
SELECT balance FROM Users WHERE id = 1;  
-- 读取结果:2000.00
  • 事务 1 第一次读取时余额是 1000.00,第二次读取时余额变成了 2000.00,发生了不可重复读。

3. 不可重复读的影响

  1. 数据一致性问题:事务依赖于读取的数据,如果读取的结果在事务执行期间发生变化,可能导致错误的决策。
  2. 业务逻辑错误:如在基于余额判断的场景中,数据前后不一致可能导致业务异常。

4. 如何解决不可重复读?

不可重复读可以通过提高事务隔离级别来避免。在 MySQL 中,REPEATABLE READ 及更高的隔离级别可以解决不可重复读问题。

1) 使用 REPEATABLE READ 隔离级别
MySQL 默认的 REPEATABLE READ 隔离级别通过 MVCC(多版本并发控制) 来解决不可重复读问题:
– 每个事务在开始时生成一个数据快照(Snapshot)。
– 后续查询都基于该快照进行读取,不会受其他事务修改影响。

示例

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;

-- 第一次读取
SELECT balance FROM Users WHERE id = 1;

-- 第二次读取,结果与第一次一致,即使其他事务修改了数据
SELECT balance FROM Users WHERE id = 1;

2) 使用 SERIALIZABLE 隔离级别
SERIALIZABLE 是 MySQL 的最高隔离级别,通过对读操作加共享锁,确保其他事务无法修改当前读取的数据,从而防止不可重复读。
示例

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
START TRANSACTION;

-- 读操作会加共享锁
SELECT balance FROM Users WHERE id = 1;

5. 不可重复读与幻读的区别

问题 描述 例子
不可重复读 同一事务中多次读取同一行数据,结果不同,通常是因为另一事务修改了数据(更新或删除)。 事务 1 第一次读取 balance = 1000,事务 2 更新为 balance = 2000,事务 1 第二次读取发现结果不同。
幻读 同一事务中多次范围查询结果的行数不同,通常是因为另一事务插入或删除了数据。 事务 1 查询 price < 300,事务 2 插入一条 price = 250 的记录,事务 1 再次查询多了一行。

6. 不可重复读的实际应用场景

1) 银行账户查询
– 在转账过程中,查询账户余额,若读取的结果前后不一致,可能导致错误转账或资金丢失。
解决方法:使用 REPEATABLE READ 隔离级别,确保读取的余额始终一致。

2) 库存管理
– 查询商品库存后扣减库存时,如果库存数量在事务期间被其他事务修改,可能导致超卖问题。
解决方法:使用 REPEATABLE READ 或加锁操作防止修改。


总结

不可重复读是指同一事务中多次读取同一行数据时,结果不同的问题,通常由其他事务修改了数据导致。在 MySQL 中,通过使用 REPEATABLE READ 隔离级别(默认值)即可有效避免不可重复读,同时更高的隔离级别(SERIALIZABLE)则提供了更强的一致性保证。开发者需要根据业务场景选择合适的隔离级别,避免此类问题对系统造成影响。

发表评论

后才能评论