Oracle 多版本并发控制(MVCC)详解
Oracle 多版本并发控制(MVCC)详解
适用版本:Oracle Database 11g / 12c / 19c / 23ai 文档版本:v1.0 / 2026-07
1. 概述
Oracle 通过多版本并发控制(MVCC,Multi-Version Concurrency Control) 实现高并发下的数据一致性[1]。
核心原则:
- 读不阻塞写:查询不阻塞 DML
- 写不阻塞读:DML 不阻塞查询
- 写写串行:同一行的并发修改排队执行
实现基础:
- Undo 数据:保存行的旧版本
- SCN:标识版本时间
- CR 块(Consistent Read):从 Undo 构造的旧版本块
2. 工作原理
2.1 旧版本构造流程
事务 T1(SCN=1000)开始查询:
SELECT sal FROM emp WHERE id=1;
事务 T2(SCN=1005)已经修改了 emp.id=1:
UPDATE emp SET sal=5000 WHERE id=1;
COMMIT;
T1 的查询执行流程:
1. 找到 id=1 所在数据块
2. 检查块头 SCN:当前块 SCN=1005
3. 1005 > 1000(T1 查询 SCN),需要构造 CR 块
4. 从 Undo 段读取 ITL 中记录的 Undo 地址
5. 应用 Undo 数据,回滚到 SCN ≤ 1000 的状态
6. 得到 CR 块,返回旧值
2.2 Undo 链
如果一行被多次修改,旧版本通过 Undo 链连接:
当前块(SCN=1005): sal=5000
│
▼ Undo 记录1
CR 块(SCN=1003): sal=4500
│
▼ Undo 记录2
CR 块(SCN=1001): sal=4000
│
▼ Undo 记录3
CR 块(SCN=0998): sal=3500(足够旧,T1 可读)
3. CR 块构造示例
-- 准备测试数据
DROP TABLE mvcc_test PURGE;
CREATE TABLE mvcc_test (id NUMBER, sal NUMBER);
INSERT INTO mvcc_test VALUES (1, 1000);
COMMIT;
-- 会话 1:开始事务但不提交
UPDATE mvcc_test SET sal=2000 WHERE id=1;
-- 不要 COMMIT
-- 会话 2:查询(应该看到旧值 1000)
SELECT * FROM mvcc_test WHERE id=1;
-- 返回:1, 1000(CR 块)
-- 会话 2:使用 AS OF 查询
SELECT * FROM mvcc_test AS OF TIMESTAMP (SYSTIMESTAMP - INTERVAL '1' MINUTE);
4. 读一致性的三个层次
| 层次 | 范围 | 设置方式 |
|---|---|---|
| 语句级 | 单条 SQL 执行期间 | 默认(READ COMMITTED) |
| 事务级 | 整个事务期间 | SET TRANSACTION ISOLATION LEVEL SERIALIZABLE |
| 只读 | 整个事务,不允许 DML | SET TRANSACTION READ ONLY |
-- 语句级(默认)
SELECT * FROM emp; -- 此语句看到的是开始执行时的 SCN 快照
-- 事务级
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SELECT * FROM emp; -- 看到事务开始时的快照
-- 其他会话修改并提交后
SELECT * FROM emp; -- 仍看到事务开始时的快照
COMMIT;
-- 只读事务
SET TRANSACTION READ ONLY;
SELECT * FROM emp;
UPDATE emp SET sal=sal+1; -- 报错:ORA-01456
5. 相关视图
-- 查看当前事务
SELECT addr, xidusn, xidslot, xidsqn, status,
start_time, used_ublk
FROM v$transaction;
-- 查看 Undo 段
SELECT segment_name, tablespace_name, status
FROM dba_rollback_segs;
-- 查看 Undo 表空间使用
SELECT
tablespace_name,
SUM(bytes)/1024/1024 AS used_mb
FROM dba_undo_extents
WHERE status='ACTIVE'
GROUP BY tablespace_name;
-- CR 块统计
SELECT name, value
FROM v$sysstat
WHERE name IN (
'consistent gets', -- CR 读次数
'consistent gets - examination',
'data blocks consistent reads - undo records applied',
'no work - consistent read gets',
'cleanouts only - consistent read gets'
);
6. 常见坑与排错
6.1 ORA-01555: snapshot too old
现象:长查询报 ORA-01555。
原因:CR 块构造需要的 Undo 数据已被覆盖。
修复:
-- 1. 增大 Undo 表空间
ALTER TABLESPACE undotbs1 ADD DATAFILE '/u02/undo02.dbf' SIZE 1G;
-- 2. 增加 undo_retention
ALTER SYSTEM SET undo_retention=3600 SCOPE=BOTH;
-- 3. 启用 Undo 保留保证
ALTER TABLESPACE undotbs1 RETENTION GUARANTEE;
-- 4. 优化长查询,分批处理
6.2 长事务导致 Undo 膨胀
现象:Undo 表空间快速增长。
修复:
-- 找出长事务
SELECT
s.sid, s.serial#, s.username,
t.start_time, t.used_ublk
FROM v$session s
JOIN v$transaction t ON s.taddr = t.addr
ORDER BY t.used_ublk DESC;
-- 终止长事务
ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE;
6.3 CR 块构造消耗 Undo
现象:查询慢,统计显示大量 data blocks consistent reads - undo records applied。
修复:缩短事务持续时间,减少长事务与长查询并行。
7. 最佳实践
- 合理设置 undo_retention:业务最长查询时间的 2-3 倍
- 避免长事务:定期 COMMIT,减少 Undo 占用
- 批量操作分批提交:避免一次性事务过大
- 监控 Undo 使用率:
dba_undo_extents视图 - 关键业务启用 Guarantee:保证 Undo 不被覆盖
- 长查询使用 AS OF:明确指定时间点,避免依赖默认 Undo 保留
8. 参考资料
[1] Oracle Database Concepts 19c, “Multiversion Read Consistency” https://docs.oracle.com/en/database/oracle/oracle-database/19/cncpt/data-concurrency-and-consistency.html
[2] AskTOM, “MVCC and Undo Internals” https://asktom.oracle.com/pls/apex/f?p=100:1:0
[3] 墨天轮,“Oracle MVCC 与读一致性深度解析” https://www.modb.pro/db/1759656813559025664