
Oracle 数据库 latch: redo writing 等待事件深度解析
1. 等待事件本质
latch: redo writing:
当LGWR(日志写入进程)或其他进程尝试访问重做日志缓冲区进行写入操作时发生的闩锁争用。该闩锁保护重做日志缓冲区在写入磁盘期间的完整性。- 关键特性:
- 直接关联LGWR进程活动
- 保护重做日志缓冲区的写入操作
- 高频率出现表明重做日志子系统存在瓶颈
- 与相关等待的区别:
等待事件 触发进程 保护资源 典型原因 latch: redo writing LGWR为主 日志缓冲区写入过程 I/O慢/日志配置不当 latch: redo copy 用户进程 日志缓冲区数据插入 高并发DML log file parallel write LGWR 日志文件物理写入 磁盘性能不足
2. 产生过程详解
- LGWR准备写入:
日志缓冲区满或超时触发写入 - 请求redo writing闩锁:
LGWR进程申请闩锁以独占访问缓冲区 - 冲突检测:
- 如果闩锁可用 → 立即获取
- 如果闩锁被其他进程持有 → 进入等待
- 执行写入操作:
获取闩锁后,将缓冲区内容写入磁盘日志文件 - 清理缓冲区:
写入完成后标记缓冲区为可重用状态 - 释放闩锁:
完成所有操作后释放闩锁
3. 高频场景
高负载事务系统
-- 每秒数千次提交
UPDATE accounts SET balance=balance-100 WHERE id=123;
COMMIT; -- 高频提交触发LGWR
大批量数据加载
-- 大批量INSERT产生海量重做
INSERT /*+ APPEND */ INTO sales
SELECT * FROM sales_staging; -- 未使用NOLOGGING
日志切换频繁
-- 日志文件过小导致频繁切换
ALTER DATABASE ADD LOGFILE GROUP 4 ('+DATA') SIZE 100M; -- 建议1G+
归档模式下的延迟
-- 归档速度跟不上日志生成
ALTER SYSTEM SWITCH LOGFILE; -- 触发归档
RAC全局缓存操作
-- RAC中的全局事务
UPDATE gs_table SET status='P' WHERE node=2; -- 跨节点事务
4. 根本原因分类
I/O系统问题
| 问题类型 | 典型案例 |
|---|---|
| 日志磁盘慢 | 重做日志位于7200转HDD,而非SSD |
| I/O带宽不足 | 存储网络仅1GbE,无法处理高峰日志量 |
| 日志文件争用 | 所有日志组位于同一物理磁盘 |
数据库配置问题
- 重做日志文件过小:
SELECT group#, bytes/1024/1024 size_mb FROM v$log; -- <500MB - 日志缓冲区过大:
SHOW PARAMETER log_buffer; -- >128MB导致写入延迟 - 归档目标响应慢:
SELECT dest_name, status, error FROM v$archive_dest; -- 状态ERROR
应用设计问题
- 过度提交:
BEGIN FOR i IN 1..100000 LOOP INSERT ...; COMMIT; -- 每次插入都提交 END LOOP; END; - 未用批量操作:
UPDATE huge_table SET col=val; -- 未分批更新
Oracle内部机制
- LGWR写入延迟:
I/O子系统无法及时处理写入请求 - Bug导致争用:
- Bug 13542050:11g中高负载下的redo writing争用
- Bug 21382587:12c RAC中闩锁分配不均
- Bug 29936857:19c中并行DML引发问题
- 检查点风暴:
增量检查点过于频繁导致LGWR过载
5. 深度排查流程
步骤1:系统级诊断
-- 确认等待强度
SELECT event, total_waits, time_waited_micro
FROM v$system_event
WHERE event = 'latch: redo writing';
-- 关联LGWR活动
SELECT
(SELECT value FROM v$sysstat WHERE name='redo writes') redo_writes,
(SELECT value FROM v$sysstat WHERE name='redo write time') write_time
FROM dual;
- 严重性阈值:
time_waited_micro> 60秒/分钟 +redo_writes> 1000/秒
步骤2:闩锁争用分析
-- 检查redo writing闩锁
SELECT
name,
gets,
misses,
ROUND((misses/DECODE(gets,0,1,gets))*100,2) miss_ratio,
sleeps
FROM v$latch
WHERE name = 'redo writing';
-- 实时争用监控
SELECT sid, event, p1raw, state
FROM v$session_wait
WHERE event LIKE '%latch%' AND state = 'WAITING';
步骤3:LGWR性能分析
-- LGWR进程状态
SELECT process, status, seq#, blocks, delay
FROM v$log_writer;
-- 日志文件I/O统计
SELECT
l.group#,
l.thread#,
f.member,
i.bytes/1024/1024 size_mb,
i.blocks,
i.block_size
FROM v$log l
JOIN v$logfile f ON l.group# = f.group#
JOIN dba_data_files i ON f.member = i.file_name;
步骤4:日志配置检查
-- 日志组配置
SELECT group#, bytes/1024/1024 size_mb, members, status, archived
FROM v$log;
-- 日志切换频率
SELECT
to_char(first_time, 'YYYY-MM-DD HH24:MI') time,
sequence#,
round(blocks*block_size/1024/1024) size_mb
FROM v$log_history
ORDER BY first_time DESC;
步骤5:I/O性能诊断
-- 文件级I/O延迟
SELECT
file_name,
phyrds,
phywrts,
ROUND((readtim*1000)/DECODE(phyrds,0,1,phyrds)) avg_read_ms,
ROUND((writetim*1000)/DECODE(phywrts,0,1,phywrts)) avg_write_ms
FROM v$filestat
JOIN dba_data_files USING (file_id)
WHERE file_name LIKE '%redo%';
-- 操作系统I/O统计 (需OS访问)
$ iostat -dx 2 5 # 查看await和%util
步骤6:高负载事务分析
-- 高提交率会话
SELECT
sid,
username,
status,
logon_time,
last_call_et
FROM v$session
WHERE status = 'ACTIVE'
AND EXISTS (
SELECT 1 FROM v$sesstat
WHERE statistic# = (SELECT statistic# FROM v$statname WHERE name = 'user commits')
AND value > 100
AND sid = v$session.sid
);
-- 重做生成排名
SELECT
sql_id,
sql_text,
executions,
ROUND(io_interconnect_bytes/executions/1024) redo_kb_per_exec
FROM v$sql
WHERE io_interconnect_bytes > 0
ORDER BY io_interconnect_bytes DESC
FETCH FIRST 10 ROWS ONLY;
步骤7:高级诊断
-- LGWR进程追踪
ALTER SYSTEM SET events 'immediate trace name processstate level 10' sid = 'LGWR';
-- 闩锁转储
ALTER SESSION SET events 'immediate trace name latch level 10';
-- 检查Bug
SELECT patch_id, description
FROM dba_registry_sqlpatch
WHERE patch_id IN (13542050, 21382587, 29936857);
6. 根治方案
紧急处置
-- 临时减少提交频率
-- 推迟非关键批处理作业
-- 手动切换日志释放压力
ALTER SYSTEM SWITCH LOGFILE;
-- 增加日志组大小 (需重建)
ALTER DATABASE ADD LOGFILE GROUP 4 '+FAST_DATA' SIZE 1G;
ALTER SYSTEM SWITCH LOGFILE; -- 切换到新组
ALTER DATABASE DROP LOGFILE GROUP 1;
I/O系统优化
- 升级存储设备:
将重做日志迁移至NVMe SSD - 分离日志存储:
ALTER DISKGROUP FAST_LOGS ADD DISK '/dev/nvme0n1'; ALTER DATABASE ADD LOGFILE GROUP 4 '+FAST_LOGS' SIZE 1G; - 优化RAID配置:
使用RAID 10替代RAID 5
数据库配置优化
-- 优化日志配置
ALTER SYSTEM SET log_buffer=64M SCOPE=SPFILE; -- 合理大小
ALTER DATABASE ADD LOGFILE GROUP 4 '+DATA' SIZE 1G;
ALTER DATABASE ADD LOGFILE GROUP 5 '+DATA' SIZE 1G;
-- 调整LGWR参数
ALTER SYSTEM SET "_use_adaptive_log_file_sync"=FALSE; -- 11gR2+ 关闭自适应
ALTER SYSTEM SET "_log_writer_parallelism"=4; -- 18c+ 增加并行度
-- 优化检查点
ALTER SYSTEM SET "_disable_adaptive_log_file_sync"=TRUE; -- 禁用自适应
应用层优化
- 批量提交:
-- 每1000行提交一次 DECLARE CURSOR c_upd IS SELECT ... FOR UPDATE; BEGIN FOR r IN c_upd LOOP UPDATE ... WHERE CURRENT OF c_upd; IF MOD(c_upd%ROWCOUNT,1000)=0 THEN COMMIT; END IF; END LOOP; COMMIT; END; - NOLOGGING操作:
ALTER TABLE sales NOLOGGING; INSERT /*+ APPEND NOLOGGING */ INTO sales ...; - 最小化日志操作:
CREATE TABLE new_table NOLOGGING AS SELECT * FROM old_table; -- 使用NOLOGGING
补丁与升级
- 关键补丁:
- Bug 13542050:应用11.2.0.4.210119+ PSU
- Bug 21382587:安装12.1.0.2.220719补丁
- Bug 29936857:19c中应用Jan 2023 RU
- 升级建议:
- 19c引入日志写入增强
ALTER SYSTEM SET "_redo_transport_compress_all"=TRUE; -- 压缩传输
- 19c引入日志写入增强
架构级优化
- RAC日志分离:
-- 节点1日志 ALTER DATABASE ADD LOGFILE THREAD 1 GROUP 4 '+DATA_RAC1' SIZE 1G; -- 节点2日志 ALTER DATABASE ADD LOGFILE THREAD 2 GROUP 5 '+DATA_RAC2' SIZE 1G; - 使用Standby重定向:
ALTER SYSTEM SET log_archive_dest_2='SERVICE=standby LGWR ASYNC'; - In-Memory日志优化:
ALTER SYSTEM SET inmemory_size=10G; -- 18c+特性
根治原则:
解决latch: redo writing= 加速日志写入(I/O优化) + 优化日志配置(大小/组数) + 减少日志生成(批量提交/NOLOGGING)。
核心在于确保LGWR能够高效地将重做日志写入磁盘,避免进程在闩锁上长时间等待。
通过此方案可显著降低redo writing闩锁争用,提升数据库事务吞吐量。
欢迎关注我的公众号《IT小Chen》
351

被折叠的 条评论
为什么被折叠?



