因为v$active_session_history视图不含username字段,仅有user_id,直接select username会触发ora-00904错误;正确做法是join dba_users通过user_id关联,并用sample_time>sysdate-30/1440过滤近30分钟数据,再按username聚合统计cpu与等待样本。
为什么直接查 v$active_session_history 会报 ora-00904: "username"?
因为该视图压根没有 username 字段,只有 user_id。有人写 select username, count(*) from v$active_session_history group by username,必然失败。这是最常踩的第一个坑——误把字典视图的字段当成 ash 的原生列。
如何正确关联用户名并按用户聚合资源消耗?
必须显式 JOIN DBA_USERS,用 USER_ID 匹配。注意三点:
-
DBA_USERS是静态字典,只要用户没被删,关联就可靠;若需查已删除的历史用户(如 job 执行时的临时账号),得换用DBA_HIST_ACTIVE_SESS_HISTORY+DBA_HIST_USER,但需额外 license - 时间范围别写死,用
SAMPLE_TIME > SYSDATE - 30/1440(30 分钟前)比INTERVAL更兼容 11gR2 等老版本 - 采样单位是“约每秒 1 次”,
COUNT(*)× 10 ≈ 毫秒级 CPU 占用时长,不是精确值,但足够定位热点
最小可行 SQL:
SELECT u.username,
COUNT(*) total_samples,
SUM(CASE WHEN ash.session_state = 'ON CPU' THEN 1 ELSE 0 END) cpu_samples,
SUM(CASE WHEN ash.session_state = 'WAITING' THEN 1 ELSE 0 END) wait_samples,
MAX(ash.event) KEEP (DENSE_RANK FIRST ORDER BY COUNT(*) DESC) OVER (PARTITION BY u.username) top_wait_event
FROM v$active_session_history ash
JOIN dba_users u ON ash.user_id = u.user_id
WHERE ash.sample_time > SYSDATE - 30/1440
GROUP BY u.username
ORDER BY cpu_samples + wait_samples DESC;
怎样进一步定位到具体会话、SQL 和模块?
光知道“用户 A 消耗高”不够,得下钻到执行上下文。加 SESSION_ID、SQL_ID、MODULE 即可:
-
MODULE字段在应用规范接入时通常填入服务名(如ORDERS_SERVICE),比PROGRAM更稳定 -
SQL_ID对应的文本可能已老化出共享池,此时要查V$SQLAREA(当前)或DBA_HIST_SQLTEXT(历史) - 若看到大量
SQL_ID = '0000000000000000',说明是硬解析或未解析状态,重点看SQL_OPNAME和EVENT
示例扩展:
SELECT u.username,
ash.session_id,
ash.sql_id,
ash.module,
COUNT(*) samples
FROM v$active_session_history ash
JOIN dba_users u ON ash.user_id = u.user_id
WHERE ash.sample_time > SYSDATE - 15/1440
AND u.username IN ('APP_USER', 'BATCH_USER')
GROUP BY u.username, ash.session_id, ash.sql_id, ash.module
ORDER BY samples DESC
FETCH FIRST 10 ROWS ONLY;
为什么有时查不到目标用户的 ASH 记录?
ASH 默认只保内存中最近约 1 小时数据(实际取决于 _ash_size 和负载),且仅记录非空闲会话。查不到常见原因:
- 用户会话已退出,对应采样被循环覆盖——尤其当
_ash_size过小(如默认 4MB)且实例并发高时 - 时间窗口太窄:卡顿发生在 14:22:17,你却查
SAMPLE_TIME BETWEEN '14:22:20' AND '14:22:30',大概率漏掉 - 用户是后台作业(如 Scheduler Job),其
SESSION_TYPE = 'BACKGROUND',默认查询容易忽略,需显式放开条件
务必先确认缓冲区是否真有数据:SELECT * FROM v$sgastat WHERE name LIKE '%ASH%'; 若 bytes 极小或为 0,说明内存不足,_ash_size 可能需要调大(但改后需重启实例)。











