直接查V$ACTIVE_SESSION_HISTORY需按USER_ID聚合并关联DBA_USERS获取用户名,因该视图无USERNAME字段;ASH仅保留内存中约1小时数据,无法跨天回溯。
直接查 v$active_session_history 按 user_id 聚合就能定位到具体用户,但必须注意:原始视图里没有 username,只有 user_id,得手动关联 dba_users;且默认只保留内存中最近约1小时(实际取决于ash缓冲区大小和负载),不能跨天回溯。
为什么不能直接用 USERNAME 字段聚合
V$ACTIVE_SESSION_HISTORY 视图里压根没有 USERNAME 列,只有 USER_ID。有人误写 SELECT USERNAME, COUNT(*) ... FROM V$ACTIVE_SESSION_HISTORY GROUP BY USERNAME,结果报错 ORA-00904: "USERNAME": invalid identifier。这是最常踩的第一个坑。
- 必须显式
JOIN DBA_USERS或用子查询查出用户名 -
DBA_USERS是静态字典,而USER_ID在 ASH 中是采样时刻的真实值,只要用户没被删,关联就是可靠的 - 如果只关心当前活跃用户,
DBA_USERS足够;若需历史用户(如已删账号),得用DBA_HIST_ACTIVE_SESS_HISTORY配合DBA_HIST_USER(但该视图不常用,且需 license)
按 USER_ID 统计 CPU 和等待资源的最小可行 SQL
下面这条语句能同时看出某用户在最近 30 分钟内占用了多少 CPU 时间、多少等待时间,以及主要等在什么事件上:
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;
-
SYSDATE - 30/1440表示 30 分钟前,比写- INTERVAL '30' MINUTE更兼容老版本 Oracle(如 11gR2) -
COUNT(*)是总采样数,1 样本 ≈ 10ms,所以cpu_samples * 10就是近似毫秒级 CPU 占用时长 -
MAX(...) KEEP (DENSE_RANK FIRST...)是取每个用户出现次数最多的等待事件,避免用GROUP BY event后结果太散
想看更细粒度:按会话+SQL+模块拆解用户行为
光知道“用户 A 消耗多”不够,得定位到是哪个会话、跑哪条 SQL、来自哪个应用模块。这时要加 SESSION_ID、SQL_ID、MODULE:
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;
- 加了
u.username IN (...)过滤,避免全表扫DBA_USERS(它可能很大) -
MODULE字段通常由应用设置(如 JDBC 的setModule()),比靠PROGRAM更可靠——因为多个 Java 应用可能都显示为jdbc thin client - 如果
sql_id是空(NULL),说明那段时间会话没在执行 SQL,可能在解析、PL/SQL 执行或空闲连接中
容易被忽略的权限与性能边界
执行这些查询需要两个关键权限:SELECT_CATALOG_ROLE(或显式授予 V_$ACTIVE_SESSION_HISTORY 和 DBA_USERS),否则报 ORA-00942: table or view does not exist。另外要注意:
- ASH 数据存在 SGA 内存中,高并发下可能被快速覆盖;别指望用它查“昨天下午 3 点”的明细——得切到
DBA_HIST_ACTIVE_SESS_HISTORY(AWR 归档版),但默认只保留 7 天,且采样间隔是 1 小时一次,精度下降 - 如果目标用户是
SYS或SYSTEM,它们的USER_ID很小(如 0、5),但 ASH 中这类会话样本极少,除非真在跑重负载的后台任务,否则别花时间盯它们 - 用
DBA_HIST_ACTIVE_SESS_HISTORY时,记得加dbid条件(尤其在 RAC 环境),否则可能混入其他实例的数据











