Oracle 重做日志(Redo Log)机制详解
Oracle 重做日志(Redo Log)机制详解
适用版本:Oracle Database 19c / 23ai 阅读基础:了解 Oracle 后台进程 LGWR、DBWn 文档版本:v1.0 / 2026-07
目录
- 1. 概述:Redo Log 是 ACID 持久性的核心保障
- 2. Redo Log 的核心概念
- 3. Redo Log 写入机制
- 4. 日志切换与检查点
- 5. Redo Log 状态详解
- 6. Redo Log 配置最佳实践
- 7. Redo Log 相关视图
- 8. Redo Log 管理操作
- 9. Redo Log 性能优化
- 10. 常见坑与排错
- 11. 最佳实践
- 12. 参考资料
1. 概述:Redo Log 是 ACID 持久性的核心保障
重做日志(Redo Log)是 Oracle 数据库实现事务 ACID 特性中**持久性(Durability)**的核心机制[1][2]。
核心作用:
- 实例恢复:异常关机后,重做日志用于恢复未持久化的事务
- 介质恢复:数据文件损坏后,结合备份和归档日志恢复数据
- 变更追踪:用于 Data Guard、GoldenGate 等数据同步
底层原理:Oracle 采用 WAL(Write-Ahead Logging,预写式日志) 策略[1][2]——所有数据修改必须先记录到日志并落盘,然后才允许数据文件更新。
核心特点:
- 二进制文件:记录变更向量,不是 SQL 文本
- 顺序写入:LGWR 进程顺序追加写入,I/O 效率极高
- 循环使用:至少需要 2 个日志组
- 多路复用:每个组可以有多个成员(镜像)
2. Redo Log 的核心概念
2.1 重做记录(Redo Record)
重做记录(Redo Entry)是 LGWR 写入的最小单位[2]。
记录内容:
- 变更的数据块地址
- 变更前的前映像(Before Image)
- 变更后的后映像(After Image)
- 事务 ID(XID)
- SCN(System Change Number)
示例:
UPDATE emp SET sal = 5000 WHERE empno = 7900;
对应 Redo Record:
- 数据块地址:file=4, block=1234
- 偏移量:56
- 前映像:800(旧 sal 值)
- 后映像:5000(新 sal 值)
- 事务 ID:8.12.1234
- SCN:1234567
2.2 变化向量(Change Vector)
变化向量是重做记录的组成单元[2],描述对单个块的修改。
一条 UPDATE 可能产生多个变化向量:
- 数据段块变化(表中数据)
- Undo 段块变化(记录前映像)
- 事务表变化(事务槽更新)
- 索引块变化(如修改了索引列)
2.3 日志组(Group)和成员(Member)
日志组(Group)
├── 成员 1(Member 1) -- /u01/oradata/orcl/redo01a.log
├── 成员 2(Member 2) -- /u02/oradata/orcl/redo01b.log
└── 成员 3(Member 3) -- /u03/oradata/orcl/redo01c.log
Group:
- 逻辑概念
- 包含一个或多个完全相同的成员(镜像)
- LGWR 同时写入组内所有成员
Member:
- 物理文件
- 同一组内的成员内容完全相同
- 用于冗余,提高可用性
最小配置:至少 2 个组,每组至少 1 个成员。
生产配置:3+ 个组,每组 2-3 个成员(分布在不同磁盘)。
2.4 日志循环使用
┌── Group 1 ──┐
│ │
↓ │
写满 → 切换 │
│ │
↓ │
Group 2 ──→ Group 3
│
└→ Group 1(循环回来,覆盖旧数据)
关键特性[1][2]:
- 当前活动组(CURRENT)接收所有新产生的重做记录
- 写满后触发日志切换(Log Switch)
- 切换后变为 ACTIVE 状态,等待检查点完成
- 检查点完成后变为INACTIVE,可被覆盖
- 最终回到 UNUSED 队列等待重用
3. Redo Log 写入机制
3.1 LGWR 触发条件
LGWR(Log Writer)进程在以下情况触发写操作[1][3]:
| 触发条件 | 说明 |
|---|---|
| 用户提交事务(COMMIT) | 确保已提交数据持久化 |
| Redo Log Buffer 达 1/3 满 | 防止缓冲区溢出 |
| Redo Log Buffer 有 1MB 脏数据 | 即使不满 1/3 |
| 每 3 秒超时 | 定时写入 |
| DBWn 写脏块前 | WAL 原则:先记日志,再写数据 |
| 检查点发生时 | 同步日志和数据 |
坑 1:LGWR 写入性能直接影响数据库吞吐量,是 OLTP 系统最关键的性能瓶颈之一。
3.2 WAL(Write-Ahead Logging)原则
核心原则[2]:
数据修改流程:
1. 用户进程修改数据块(Buffer Cache)
2. 生成 Redo Record 写入 Redo Log Buffer
3. COMMIT 时 LGWR 写入 Redo Log 文件
4. DBWn 异步写入数据文件(可能滞后很久)
关键点:
- DBWn 写数据文件前,必须先 LGWR 写完 Redo Log
- 这确保了:即使数据文件未更新,但日志已持久化,可通过日志恢复
- 提交返回成功标志前,必须等 LGWR 完成写入(产生
log file sync等待)
3.3 写入流程
用户会话 LGWR 磁盘
│ │ │
├─→ 修改 Buffer Cache │ │
├─→ 生成 Redo Record │ │
├─→ 写入 Redo Log Buffer │ │
│ │ │
├─→ COMMIT │ │
│ │ │
│ ├─→ 读取 Redo Log Buffer │
│ ├─→ 写入 Redo Log File │
│ │ ├─→ fsync
│ │ │
│ ├─→ 写入完成通知 │
│ │ │
├─→ 收到 COMMIT 成功 │ │
│ │ │
log file sync 等待事件[3]:用户会话等待 LGWR 完成写入,是 OLTP 系统常见性能瓶颈。
4. 日志切换与检查点
4.1 日志切换(Log Switch)
日志切换指 LGWR 停止写当前日志组,开始写下一个日志组[2]。
触发方式:
- 自动切换:当前日志组写满
- 手动切换:
ALTER SYSTEM SWITCH LOGFILE;
切换过程:
- LGWR 停止写当前组
- 选择下一个可用组
- 当前组状态变为 ACTIVE
- 触发检查点(Checkpoint)
- 启动 ARCn 进程归档(如 ARCHIVELOG 模式)
- LGWR 开始写新组
4.2 检查点(Checkpoint)
检查点是 Oracle 同步内存和数据文件的关键操作[2]。
检查点类型:
| 类型 | 触发时机 | 说明 |
|---|---|---|
| 完全检查点 | SHUTDOWN IMMEDIATE、ALTER SYSTEM CHECKPOINT | 所有脏块写入数据文件 |
| 日志切换检查点 | 日志切换时 | 写入切换组的脏块 |
| 增量检查点 | 持续运行 | 按 RBA 顺序渐进写入 |
| 局部检查点 | 表空间离线、热备 | 特定数据文件的脏块 |
检查点作用:
- 减少实例恢复时间
- 同步数据文件头与控制文件
- 释放 redo log 空间
4.3 Checkpoint not complete 故障
现象[3]:
Alert Log 显示:
Thread 1 cannot allocate new log, sequence 1234
Checkpoint not complete
Current log# 1 seq# 1233 mem# 0: /u01/oradata/orcl/redo01.log
原因[3]:
- LGWR(生产者)跑得太快,绕了一圈回来
- DBWR(消费者)还没把对应脏块写入数据文件
- 为了数据一致性,Oracle 强制挂起所有写入操作
- 业务表现为:数据库突然卡顿,DML 全部等待
解决:
-
临时解决:手动切换 + 检查点
ALTER SYSTEM SWITCH LOGFILE; ALTER SYSTEM CHECKPOINT; -
根本解决:增加日志组数和大小
ALTER DATABASE ADD LOGFILE GROUP 4 '/u01/oradata/orcl/redo04.log' SIZE 1G; ALTER DATABASE ADD LOGFILE GROUP 5 '/u01/oradata/orcl/redo05.log' SIZE 1G; -
优化 DBWn 性能:增加 DBWR 进程数
ALTER SYSTEM SET db_writer_processes = 4 SCOPE=SPFILE;
5. Redo Log 状态详解
状态流转:
UNUSED → CURRENT → ACTIVE → INACTIVE → (循环回 CURRENT)
| 状态 | 含义 | 可否覆盖 |
|---|---|---|
UNUSED | 新建的、从未使用 | 是 |
CURRENT | LGWR 正在写入的组 | 否 |
ACTIVE | 还没完成检查点,崩溃恢复需要 | 否 |
INACTIVE | 检查点完成,可被覆盖 | 是 |
CLEARING | 正在被清空(CLEAR 命令) | 否 |
CLEARING_CURRENT | 当前正在被清空 | 否 |
查询:
SELECT group#, status, sequence#, bytes/1024/1024 AS size_mb, members,
first_change#, first_time
FROM v$log
ORDER BY group#;
6. Redo Log 配置最佳实践
6.1 日志组数量
| 场景 | 推荐组数 |
|---|---|
| 小型 OLTP | 3 |
| 中型 OLTP | 4-5 |
| 大型 OLTP | 5-8 |
| DSS/OLAP | 3-4 |
6.2 日志文件大小
目标:每 10-30 分钟切换一次日志。
| 场景 | 推荐大小 |
|---|---|
| 小型 | 50-100 MB |
| 中型 | 200-500 MB |
| 大型 | 1-4 GB |
| Exadata | 4-8 GB |
估算公式:
日志大小 = (每秒 Redo 生成量 × 1800 秒)
≈ 30 分钟 Redo 量
查询 Redo 生成速率:
SELECT
TO_CHAR(first_time, 'YYYY-MM-DD HH24') AS hour,
SUM(blocks * block_size) / 1024 / 1024 / 1024 AS redo_gb
FROM v$archived_log
WHERE first_time > SYSDATE - 7
GROUP BY TO_CHAR(first_time, 'YYYY-MM-DD HH24')
ORDER BY hour;
6.3 多路复用
-- 推荐:每组 2-3 个成员,分布在不同磁盘
ALTER DATABASE ADD LOGFILE GROUP 1
('/u01/oradata/orcl/redo01a.log',
'/u02/oradata/orcl/redo01b.log') SIZE 500M;
6.4 日志文件位置
- 与数据文件、控制文件分开存放
- 放在高性能磁盘(SSD)
- 与归档日志放不同磁盘
7. Redo Log 相关视图
7.1 v$log
SELECT group#, thread#, sequence#, bytes, members,
archived, status, first_change#, first_time
FROM v$log;
字段说明:
| 字段 | 含义 |
|---|---|
GROUP# | 日志组号 |
THREAD# | 线程号(RAC) |
SEQUENCE# | 日志序列号 |
BYTES | 大小(字节) |
MEMBERS | 成员数 |
ARCHIVED | 是否已归档(YES/NO) |
STATUS | 状态(CURRENT/ACTIVE/INACTIVE) |
FIRST_CHANGE# | 第一条记录的 SCN |
FIRST_TIME | 第一条记录的时间 |
7.2 v$logfile
SELECT group#, status, type, member, is_recovery_dest_file
FROM v$logfile
ORDER BY group#, member;
status 字段:
| 状态 | 含义 |
|---|---|
INVALID | 文件不可访问 |
STALE | 文件内容不完整 |
DELETED | 文件已删除 |
| 空白 | 文件正常 |
7.3 v$log_history
SELECT recid, thread#, sequence#, first_change#, next_change#,
first_time, next_time
FROM v$log_history
ORDER BY sequence# DESC
FETCH FIRST 20 ROWS ONLY;
7.4 v$archived_log
SELECT name, sequence#, first_change#, next_change#,
completion_time, blocks, block_size
FROM v$archived_log
WHERE completion_time > SYSDATE - 1
ORDER BY sequence# DESC;
8. Redo Log 管理操作
8.1 添加日志组
-- 单成员
ALTER DATABASE ADD LOGFILE GROUP 4
'/u01/oradata/orcl/redo04.log' SIZE 500M;
-- 多成员
ALTER DATABASE ADD LOGFILE GROUP 4
('/u01/oradata/orcl/redo04a.log',
'/u02/oradata/orcl/redo04b.log') SIZE 500M;
8.2 添加日志成员
ALTER DATABASE ADD LOGFILE MEMBER
'/u02/oradata/orcl/redo01b.log' TO GROUP 1;
8.3 删除日志组
-- 删除前确保不是 CURRENT 或 ACTIVE
ALTER DATABASE DROP LOGFILE GROUP 4;
-- 手工删除 OS 文件
-- rm /u01/oradata/orcl/redo04.log
坑 2:不能删除 CURRENT 组,需先切换:
ALTER SYSTEM SWITCH LOGFILE;
ALTER DATABASE DROP LOGFILE GROUP 4;
8.4 删除日志成员
ALTER DATABASE DROP LOGFILE MEMBER '/u02/oradata/orcl/redo01b.log';
8.5 重做日志文件重定位
-- 1. 关闭数据库
SHUTDOWN IMMEDIATE;
-- 2. OS 层移动文件
-- mv /u01/oradata/orcl/redo01.log /u04/oradata/orcl/redo01.log
-- 3. 启动 mount
STARTUP MOUNT;
-- 4. 重命名
ALTER DATABASE RENAME FILE '/u01/oradata/orcl/redo01.log'
TO '/u04/oradata/orcl/redo01.log';
-- 5. 打开数据库
ALTER DATABASE OPEN;
8.6 清空损坏的日志文件
-- 清空日志组(不归档)
ALTER DATABASE CLEAR LOGFILE GROUP 3;
-- 清空未归档的日志组(需用 UNARCHIVED)
ALTER DATABASE CLEAR UNARCHIVED LOGFILE GROUP 3;
坑 3:清空未归档日志会导致数据丢失(如果该日志包含已提交事务),需立即做全备。
9. Redo Log 性能优化
9.1 监控关键等待事件
SELECT event, total_waits, time_waited, average_wait
FROM v$system_event
WHERE event IN (
'log file sync',
'log file parallel write',
'log file switch (checkpoint incomplete)',
'log file switch (archiving needed)',
'redo log space wait',
'redo buffer allocation retries'
)
ORDER BY time_waited DESC;
9.2 优化 log file sync
原因:用户会话等待 LGWR 完成写入。
优化方向:
- 提升 Redo 日志 I/O 性能:换 SSD
- 减少提交频率:批量提交
- 优化 LGWR:避免多个 LGWR 争用
- 使用 NOLOGGING 操作:批量加载
9.3 Redo Log Buffer 调优
-- 查看当前值
SHOW PARAMETER log_buffer
-- 调优
ALTER SYSTEM SET log_buffer = 256M SCOPE=SPFILE;
-- 重启生效
监控:
-- Redo Buffer 分配重试(应接近 0)
SELECT name, value
FROM v$sysstat
WHERE name = 'redo buffer allocation retries';
9.4 NOLOGGING 操作
-- 批量加载用 NOLOGGING 减少日志
ALTER TABLE big_table NOLOGGING;
-- INSERT 加 hint
INSERT /*+ APPEND */ INTO big_table SELECT * FROM source_table;
-- 索引创建
CREATE INDEX idx_name ON big_table(col) NOLOGGING;
坑 4:NOLOGGING 操作后必须立即备份,否则介质恢复时该数据会丢失。
10. 常见坑与排错
坑 1:Checkpoint not complete
现象:业务卡顿,alert log 报错。
解决:见 4.3 节,增加日志组数和大小。
坑 2:ORA-00312 无法访问联机日志
现象:
ORA-00312: online log 1 thread 1: '/u01/oradata/orcl/redo01.log'
ORA-27041: unable to open file
解决:
# 检查文件权限
ls -l /u01/oradata/orcl/redo01.log
# 如有副本,复制
cp /u02/oradata/orcl/redo01b.log /u01/oradata/orcl/redo01.log
# 如无副本,且非 CURRENT,清空
sqlplus / as sysdba
SQL> ALTER DATABASE CLEAR LOGFILE GROUP 1;
坑 3:CURRENT 日志组损坏
现象:数据库无法启动。
解决:
-- 不完全恢复(会丢数据)
SQL> STARTUP MOUNT;
SQL> RECOVER DATABASE UNTIL CANCEL;
SQL> ALTER DATABASE OPEN RESETLOGS;
-- 立即做全备
坑 4:日志组过小
现象:日志切换频繁(每分钟切换多次)。
解决:
-- 查看切换频率
SELECT
TO_CHAR(first_time, 'YYYY-MM-DD HH24:MI') AS time,
COUNT(*) AS switches
FROM v$log_history
WHERE first_time > SYSDATE - 1
GROUP BY TO_CHAR(first_time, 'YYYY-MM-DD HH24:MI')
ORDER BY time;
-- 加大日志组
ALTER DATABASE ADD LOGFILE GROUP 4 '/u01/oradata/orcl/redo04.log' SIZE 2G;
坑 5:日志文件 I/O 瓶颈
现象:log file parallel write 等待高。
解决:
-- 1. 查看日志文件 I/O 性能
SELECT file#, phyrds, phywrts, phyblkrd, phyblkwrt
FROM v$filestat
WHERE file# IN (SELECT file# FROM v$logfile);
-- 2. 换 SSD 或独立磁盘
坑 6:成员丢失导致写失败
现象:LGWR 报错,数据库挂起。
解决:
-- 1. 查看状态
SELECT group#, status, member FROM v$logfile;
-- 2. 删除失效成员
ALTER DATABASE DROP LOGFILE MEMBER '/u02/oradata/orcl/redo01b.log';
-- 3. 添加新成员
ALTER DATABASE ADD LOGFILE MEMBER '/u04/oradata/orcl/redo01b.log' TO GROUP 1;
坑 7:归档日志目标空间不足
现象:log file switch (archiving needed) 等待。
解决:
# 1. 检查归档目标空间
df -h /u01/arch
# 2. 清理旧归档日志
rman target /
RMAN> CROSSCHECK ARCHIVELOG ALL;
RMAN> DELETE EXPIRED ARCHIVELOG ALL;
RMAN> DELETE ARCHIVELOG ALL COMPLETED BEFORE 'SYSDATE-7';
11. 最佳实践
- 至少 3 个日志组:避免 Checkpoint not complete[3]
- 每组 2-3 个成员:分布在不同磁盘
- 日志大小让切换间隔 10-30 分钟:避免频繁切换
- 日志放独立高性能磁盘:SSD 优先
- 监控日志切换频率:每小时 5-10 次为宜
- 监控关键等待事件:log file sync、log file parallel write
- 批量操作用 NOLOGGING:减少 Redo 生成
- 不要把所有日志放同磁盘:单点故障
- 日志文件和归档日志分开:避免 I/O 争用
- RAC 每个实例至少 2 个组:线程独立
12. 参考资料
[1] 墨天轮,暮雨,《Oracle 体系结构-日志文件汇总》: https://www.modb.pro/db/1970883931515924480
[2] Oracle Database 19c Administrator’s Guide,Managing the Redo Log: https://docs.oracle.com/en/database/oracle/oracle-database/19/admin/managing-the-redo-log.html
[3] 墨天轮,安呀智数据坊,《Oracle 数据库突然卡顿?警惕 Checkpoint not complete 导致的性能雪崩》: https://www.modb.pro/db/2031315603437477888
[4] CSDN,凤舞飘伶,《Oracle 日志》: https://blog.csdn.net/woshaguayi/article/details/102697676
[5] CSDN,《深入浅出 Oracle:DBA 入门、进阶与诊断案例》读书笔记: https://blog.csdn.net/weixin_33910460/article/details/94703916
[6] Oracle Database 19c Performance Tuning Guide: https://docs.oracle.com/en/database/oracle/oracle-database/19/tgdba/
[7] Oracle Database 19c Backup and Recovery User’s Guide: https://docs.oracle.com/en/database/oracle/oracle-database/19/bradv/
相关文章