1. Oracle 12c SQL执行历史与实时监控全解析
作为DBA日常运维中最频繁的操作之一,SQL语句监控直接关系到数据库性能分析和问题排查效率。Oracle 12c在SQL监控方面提供了比以往版本更强大的工具链,今天我们就来深度剖析如何系统化地追踪历史SQL和实时SQL。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心监控场景与技术原理
2.1 共享池工作机制解析
Oracle的共享池(Shared Pool)是SQL监控的数据源头,它本质上是一个内存结构,主要包含:
- 库缓存(Library Cache):存储解析后的SQL语句和执行计划
- 数据字典缓存(Dictionary Cache):保存数据对象元数据
当SQL首次执行时,Oracle会进行硬解析(Hard Parse),生成执行计划并存入共享池。后续相同SQL执行时,直接复用已有计划(软解析),这种机制使得我们可以通过查询共享池获取SQL执行信息。
注意:共享池大小直接影响SQL监控数据的保存时长,过小的共享池会导致历史SQL被快速淘汰。建议通过
ALTER SYSTEM SET shared_pool_size=2G SCOPE=BOTH;调整(具体值需根据系统负载确定)
2.2 关键数据视图说明
Oracle 12c提供了一系列动态性能视图用于SQL监控:
| 视图名称 | 数据内容 | 保留时间 |
|---|---|---|
| V$SQL | 共享池中所有SQL的详细执行统计 | 直到被挤出共享池 |
| V$SQLAREA | SQL语句的汇总统计信息 | 同上 |
| V$SQLSTATS | SQL执行的详细性能指标 | 同上 |
| V$SESSION | 当前会话信息(含正在执行的SQL) | 实时数据 |
| V$SQL_PLAN | SQL执行计划详情 | 同上 |
| DBA_HIST_SQLTEXT | AWR快照中的历史SQL文本 | 取决于AWR保留策略 |
3. 实时SQL监控实战
3.1 查看正在执行的SQL
sql复制SELECT
s.sid,
s.serial#,
s.username,
s.status,
s.machine,
q.sql_id,
q.sql_text,
q.executions,
q.elapsed_time/1000000 as elapsed_sec,
q.cpu_time/1000000 as cpu_sec,
q.disk_reads,
q.buffer_gets
FROM
v$session s
JOIN
v$sql q ON s.sql_id = q.sql_id
WHERE
s.type = 'USER'
AND s.status = 'ACTIVE'
ORDER BY
q.elapsed_time DESC;
关键字段说明:
elapsed_time:SQL已执行时间(微秒)cpu_time:消耗的CPU时间disk_reads:物理读次数(I/O密集型SQL指标)buffer_gets:逻辑读次数(内存压力指标)
3.2 实时SQL执行计划查看
获取到sql_id后,可通过以下命令查看实时执行计划:
sql复制SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('&sql_id', null, 'ALLSTATS LAST'));
典型执行计划分析要点:
Rows列显示预估和实际行数差异Buffers列显示内存读取量Reads列显示物理I/O次数- 重点关注全表扫描(TABLE ACCESS FULL)操作
4. 历史SQL查询技术
4.1 共享池中的历史SQL
sql复制SELECT
sql_id,
sql_text,
executions,
elapsed_time/executions/1000 as avg_ms,
disk_reads/executions as avg_reads,
buffer_gets/executions as avg_gets,
last_active_time
FROM
v$sqlarea
WHERE
executions > 0
AND UPPER(sql_text) LIKE '%关键表名%'
ORDER BY
elapsed_time DESC
FETCH FIRST 50 ROWS ONLY;
4.2 AWR报告中的历史SQL
对于更早的历史SQL,需要通过AWR报告获取:
- 首先确定要分析的快照区间:
sql复制SELECT snap_id, begin_interval_time, end_interval_time
FROM dba_hist_snapshot
ORDER BY snap_id DESC;
- 生成指定快照区间的AWR报告:
sql复制SELECT output FROM TABLE(
DBMS_WORKLOAD_REPOSITORY.awr_report_text(
l_dbid => (SELECT dbid FROM v$database),
l_inst_num => (SELECT instance_number FROM v$instance),
l_bid => &begin_snap_id,
l_eid => &end_snap_id
)
);
在生成的AWR报告中重点关注:
- SQL ordered by Elapsed Time
- SQL ordered by CPU Time
- SQL ordered by Gets
5. 高级监控技巧
5.1 SQL执行历史追踪
创建SQL监控基线(适用于关键业务SQL):
sql复制-- 创建SQL集
DECLARE
l_sqlset_name VARCHAR2(30) := 'CRITICAL_SQLSET';
BEGIN
DBMS_SQLTUNE.CREATE_SQLSET(
sqlset_name => l_sqlset_name,
description => 'Critical business SQL monitoring'
);
-- 将指定SQL加入监控
DBMS_SQLTUNE.CAPTURE_CURSOR_CACHE_SQLSET(
sqlset_name => l_sqlset_name,
time_limit => 3600,
repeat_interval => 300
);
END;
/
-- 查看监控结果
SELECT sql_text, executions, elapsed_time/executions/1000 avg_ms
FROM dba_sqlset_statements
WHERE sqlset_name = 'CRITICAL_SQLSET';
5.2 自动化监控脚本
创建定期运行的监控脚本(保存到monitor_sql.sql):
sql复制SET LINESIZE 200
SET PAGESIZE 1000
COL sql_text FOR a60
COL username FOR a15
COL machine FOR a20
SPOOL /tmp/active_sql_monitor.log
SELECT
TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS') as capture_time,
s.sid,
s.serial#,
s.username,
s.machine,
q.sql_id,
SUBSTR(q.sql_text, 1, 60) as sql_text,
q.executions,
q.elapsed_time/1000000 as elapsed_sec
FROM
v$session s
JOIN
v$sql q ON s.sql_id = q.sql_id
WHERE
s.type = 'USER'
AND s.status = 'ACTIVE'
ORDER BY
q.elapsed_time DESC;
SPOOL OFF
通过crontab设置每小时执行一次:
bash复制0 * * * * /u01/app/oracle/product/12.2.0/dbhome_1/bin/sqlplus / as sysdba @/scripts/monitor_sql.sql
6. 常见问题排查指南
6.1 SQL查询无结果的可能原因
-
共享池被清空:
- 检查是否执行了
ALTER SYSTEM FLUSH SHARED_POOL - 解决方案:配置定期AWR快照保留历史SQL
- 检查是否执行了
-
SQL已老化淘汰:
- 检查
v$sqlarea的last_active_time - 解决方案:增大shared_pool_size
- 检查
-
权限不足:
- 确认用户有
SELECT ANY DICTIONARY权限 - 解决方案:
GRANT SELECT_CATALOG_ROLE TO 用户名
- 确认用户有
6.2 性能分析要点
当发现性能问题时,按以下步骤分析:
- 确认SQL执行频率(executions)
- 检查单次执行耗时(elapsed_time/executions)
- 分析资源消耗:
- 高CPU:检查cpu_time
- 高I/O:检查disk_reads
- 内存压力:检查buffer_gets
- 对比执行计划变化(通过AWR基线比较)
7. 生产环境最佳实践
-
监控策略:
- 关键业务SQL:实时监控(每5分钟采集)
- 普通SQL:每小时采集快照
- 全量SQL:每日AWR报告分析
-
保留策略:
- 调整AWR保留时间:
EXEC DBMS_WORKLOAD_REPOSITORY.MODIFY_SNAPSHOT_SETTINGS(retention => 43200);(30天) - 设置基线:
DBMS_WORKLOAD_REPOSITORY.CREATE_BASELINE(start_snap_id, end_snap_id, 'baseline_name')
- 调整AWR保留时间:
-
安全审计:
sql复制-- 启用SQL跟踪 ALTER SYSTEM SET sql_trace=TRUE SCOPE=MEMORY; -- 创建审计策略 DBMS_FGA.ADD_POLICY( object_schema => 'SCOTT', object_name => 'EMP', policy_name => 'EMP_SELECT_AUDIT', audit_condition => '1=1', audit_column => 'SAL,COMM', handler_schema => NULL, handler_module => NULL, enable => TRUE );
在实际运维中,我发现最有效的监控方式是将实时监控与AWR分析结合使用。对于突发的性能问题,实时v$session和v$sql查询能快速定位问题SQL;而对于趋势性分析,AWR报告提供的长期数据更为可靠。另外,建议为关键业务SQL建立专属监控基线,这样可以更精确地捕捉执行计划变化。
