在数据库系统中,RR(Repeatable Read)和RC(Read Committed)是两种常见的隔离级别。它们定义了事务如何与其他并发事务进行交互,特别是如何处理读操作之间的可见性问题。
RR级别保证在同一个事务内多次读取同一数据时,结果始终一致。这种一致性是通过"快照"机制实现的——事务第一次读取时会建立一个数据快照,后续读取都基于这个快照。而RC级别则允许事务看到其他已提交事务的最新修改,每次读取都可能获取到不同的结果。
关键区别:RR级别的快照在事务开始时创建,而RC级别的快照在每条SELECT语句执行时创建
ReadView是MVCC(多版本并发控制)实现中的核心数据结构,主要包含四个关键信息:
这些信息共同决定了当前事务能看到哪些版本的数据记录。InnoDB中每条记录都会保存两个隐藏字段:trx_id(最后修改该记录的事务ID)和roll_pointer(指向历史版本的指针)。
当一条记录被访问时,系统会按以下规则判断其是否对当前事务可见:
这个算法确保了事务只能看到在其开始前已提交的数据,或者本事务自己修改的数据。
快照读(Snapshot Read)是指普通的SELECT语句,它读取的是记录在某个时间点的快照版本,不阻塞其他事务的读写操作。在RR级别下,快照是在事务第一次SELECT时建立的;在RC级别下,快照是在每条SELECT语句执行时建立的。
快照读的特点:
当前读(Current Read)是指需要获取记录最新版本的读操作,包括:
当前读的特点:
在RR级别中:
典型场景:
sql复制-- 事务1
START TRANSACTION;
SELECT * FROM users WHERE id=1; -- 快照读,建立ReadView
-- 此时事务2更新了id=1的记录并提交
SELECT * FROM users WHERE id=1; -- 仍然看到旧数据
SELECT * FROM users WHERE id=1 FOR UPDATE; -- 当前读,看到最新数据
COMMIT;
在RC级别中:
典型场景:
sql复制-- 事务1
START TRANSACTION;
SELECT * FROM users WHERE id=1; -- 快照读1
-- 此时事务2更新了id=1的记录并提交
SELECT * FROM users WHERE id=1; -- 快照读2,看到新数据
COMMIT;
在RR级别下,虽然快照读可以避免不可重复读,但仍可能出现幻读现象——即一个事务内两次查询看到不同的行数。这是因为快照读只对已存在的记录有效,而新插入的记录不受快照控制。
解决方案:
长时间运行的RR事务会保留旧的ReadView,导致:
优化建议:
当执行快照读时,InnoDB会沿着记录的roll_pointer遍历版本链,对每个版本应用可见性判断规则,直到找到第一个对当前ReadView可见的版本。这个过程的关键优化点包括:
为控制undo日志大小,系统会定期purge不再需要的旧版本。Purge线程会:
配置参数:
虽然RR和RC是SQL标准定义的隔离级别,但不同数据库的实现存在差异:
MySQL InnoDB:
PostgreSQL:
Oracle:
理解这些差异对于跨数据库应用开发非常重要,特别是在需要保证一致性的场景下。