ANSI SQL中的可重复读(Repeatable Read)隔离级别确保在同一个事务内,多次读取同一数据的结果始终一致,即使其他事务修改了该数据并提交。它通过防止不可重复读(Non-repeatable Read)现象实现这一点,但可能允许幻读(Phantom Read)。以下是详细解释和一个实际例子:
不可重复读 vs. 可重复读
- 不可重复读:事务A多次读取同一数据,事务B在期间修改并提交了该数据,导致事务A两次读取结果不一致。
- 可重复读:事务A在多次读取同一数据时,结果始终一致(基于事务开始时的数据快照),不受其他已提交事务的影响。
实际例子:银行账户余额查询
-
表结构:
CREATE TABLE accounts ( account_id INT PRIMARY KEY, balance DECIMAL(10, 2) ); INSERT INTO accounts VALUES (1001, 500.00);
-
事务A(查询余额):
BEGIN TRANSACTION; -- 隔离级别设置为可重复读 SELECT balance FROM accounts WHERE account_id = 1001; -- 第一次读取,结果为500.00 -- 事务B在此处修改余额并提交 SELECT balance FROM accounts WHERE account_id = 1001; -- 第二次读取,结果仍为500.00 COMMIT;
-
事务B(更新余额):
BEGIN TRANSACTION; UPDATE accounts SET balance = 600.00 WHERE account_id = 1001; COMMIT; -- 提交更新
结果对比
- 读已提交(Read Committed):事务A的第二次读取会看到事务B提交后的结果(600.00)。
- 可重复读:事务A的两次读取均返回500.00,保证了一致性。
如何实现?
- 快照隔离(Snapshot Isolation):事务开始时创建数据快照,后续读操作均基于此快照,不受其他事务提交的影响。
- 锁机制:某些数据库通过行级锁阻止其他事务修改已读取的数据。
幻读问题
虽然可重复读解决了不可重复读,但幻读仍可能发生。例如:
- 事务A查询余额>500的账户,返回空结果。
- 事务B插入一个余额为700的新账户并提交。
- 事务A再次查询余额>500的账户,可能看到新插入的记录(取决于数据库实现,如MySQL的RR级别通过MVCC避免幻读)。
总结
- 可重复读的核心是保证同一事务内多次读取的一致性。
- 实际数据库(如MySQL、PostgreSQL)的实现可能与ANSI标准略有差异,需结合具体数据库文档理解细节。