V$ACTIVE_SESSION_HISTORY无法直接查Redo生成量,因其仅采样活跃状态、无redo_size等字段;需结合v$session、v$sql、v$sess_io中block_changes等指标闭环验证,并警惕SELECT FOR UPDATE及全表扫描混发DML等伪只读场景。

V$ACTIVE_SESSION_HISTORY 本身不记录 Redo 生成量,直接靠它查“哪条 SQL 写了多少 Redo”是行不通的。你看到的 ASH 中的 sql_id + event 组合,只能告诉你“这个会话在等什么”,而不是“它改了多少块、写了多少 Redo”。真要定位高 Redo SQL,得绕过这个认知陷阱,用间接但可靠的归因链。
为什么不能直接从 ASH 查 Redo 量
V$ACTIVE_SESSION_HISTORY 的设计目标是采样“活跃状态”,不是计量变更。它没有 redo_size、block_changes 或任何 Redo 相关统计字段;它的 p1/p2 参数只存等待事件上下文(比如 block#、file#),不存修改行为本身。试图用 WHERE event LIKE '%db file%' 筛选再按 sql_id 聚合 COUNT(*),结果只是“IO 等待次数”,和 Redo 生成量完全无关——一条 UPDATE 可能只等 1 次 IO 却写 50MB Redo,而一条全表扫描可能等 1000 次却零 Redo。
用 ASH 定位可疑 SQL 的合理路径
虽然 ASH 不记 Redo,但它能高效暴露“正在大量修改数据”的会话特征。关键不是看等待事件,而是看会话生命周期内是否高频出现以下组合:
- 会话状态为
ACTIVE,且session_state = 'ON CPU'或session_state = 'WAITING'但wait_class = 'User I/O'或'Commit' -
sql_id对应的语句在V$SQL中disk_reads > 0且buffer_gets > 100000(说明访问广) - 该
sql_id在最近 15 分钟内累计出现频次 > 20(排除毛刺,用COUNT(*) OVER (PARTITION BY sql_id)算) - 关联
V$SESSION发现program是批量作业类(如sqlplus@host、python、ETL_JOB)而非应用连接池
执行示例(需 DBA 权限):
SELECT sql_id, COUNT(*) cnt FROM v$active_session_history WHERE sample_time > SYSDATE - 15/1440 AND session_state = 'ON CPU' AND sql_id IS NOT NULL GROUP BY sql_id HAVING COUNT(*) > 20 ORDER BY cnt DESC FETCH FIRST 5 ROWS ONLY;
必须补查 V$SQL 和 V$SESS_IO 才算闭环
拿到候选 sql_id 后,立刻查 V$SQL 和 V$SESS_IO 验证 Redo 关联性。这两张视图才是 Redo 归因的锚点:
-
V$SQL.disk_reads高 +V$SQL.executions低 → 可能是大事务单次执行(如 CTAS、大批量 UPDATE) -
V$SQL.buffer_gets / V$SQL.executions > 50000→ 表明每次执行逻辑读巨大,大概率触发大量块清理(cleanout),隐式产生 Redo -
V$SESS_IO.block_changes是最直接指标:值越大,Redo 越多。注意它来自V$SESSION关联,不是实时刷新,需查当前活跃会话(status = 'ACTIVE') - 避免查
V$TRANSACTION.used_ublk:它反映的是 undo 块,不是 Redo;undo 多 ≠ Redo 多(例如INSERT /*+ APPEND */undo 少但 Redo 可能不少)
典型验证 SQL:
SELECT s.sid, s.sql_id, q.sql_text, i.block_changes FROM v$session s JOIN v$sess_io i ON s.sid = i.sid JOIN v$sql q ON s.sql_id = q.sql_id WHERE s.status = 'ACTIVE' AND i.block_changes > 5000 ORDER BY i.block_changes DESC;
容易被忽略的两个 Redo 伪“只读”场景
很多 DBA 排查时默认跳过 SELECT,但以下两类 SELECT 会真实写 Redo,且常被 ASH 漏掉根本原因:
-
SELECT FOR UPDATE:只要锁住的行有未提交变更,就会触发延迟块清除(delayed block cleanout),更新 ITL 槽并写 Redo —— 这种等待在ASH中显示为enq: TX - row lock contention或cursor: pin S wait on X,不是 IO 类事件,极易被过滤掉 - 全表扫描 + 高并发 DML 混合:当一个
SELECT /*+ FULL(t) */扫描过程中,目标表正被其他会话频繁UPDATE,Oracle 会在扫描到脏块时做快速块清理(fast cleanout),更新块头 ITL 并写 Redo —— 此时ASH只显示ON CPU,无等待事件,但V$SESS_IO.block_changes会飙升
这类问题无法靠 ASH 单独发现,必须结合 V$SESS_IO 的 block_changes 和 V$SQL 的 sql_text 人工识别关键词:FOR UPDATE、FULL、ROWNUM <= N(分页扫描)、DBMS_STATS(统计信息收集也写 Redo)。


















