一、报告概述
1.1 项目背景
本报告针对 HIS 系统在业务高峰期出现的数据库性能下降问题进行专项分析。故障期间,前端业务系统出现页面响应变慢、查询延迟增加、部分统计类页面长时间无返回等现象,影响了业务人员的正常使用体验。
本次分析重点基于 Oracle AWR 报告、ADDM 报告、等待事件、Top SQL、实例负载、I/O 消耗、SGA 使用情况以及 SQL 访问路径进行综合判断。
1.2 分析对象
| 项目 | 内容 |
|---|---|
| 数据库系统 | Oracle Database 11.2.0.4 |
| 数据库名称 | ORCL |
| 部署形态 | RAC 双实例 |
| 实例 | orcl1、orcl2 |
| 节点 | node1、node2 |
| 分析窗口 | 2026-04-28 约 09:00 - 10:00 |
| 主要业务 | HIS 主业务库 |
| 问题类型 | 高峰期数据库响应变慢、I/O 等待升高 |
说明:从 AWR 报告看,ORCL 显示为 RAC=YES,并且存在 orcl1、orcl2 两个实例,分别运行在 node1、node2。因此本次 ORCL 分析应按 RAC 双实例数据库处理,而不是简单理解为单机双实例。
二、故障现象
2.1 用户侧表现
业务人员反馈主要集中在以下几个方面:
| 现象 | 表现 |
|---|---|
| 页面卡顿 | HIS 页面打开慢,操作响应延迟 |
| 查询慢 | 门诊、住院、费用、医嘱相关查询等待时间明显增加 |
| 报表慢 | 统计类、汇总类页面长时间无返回 |
| 高峰明显 | 问题集中发生在上午业务高峰时段 |
| 非持续性故障 | 并非数据库完全不可用,而是整体响应能力下降 |
2.2 数据库侧表现
AWR 显示,两个 RAC 实例均存在较高 DB Time 和明显 I/O 等待。
| 指标 | orcl1 | orcl2 | 说明 |
|---|---|---|---|
| Elapsed Time | 约 60 分钟 | 约 60 分钟 | AWR 采样窗口一致 |
| DB Time | 508.12 分钟 | 565.92 分钟 | 数据库累计活动时间较高 |
| DB Time/s | 8.5 秒/秒 | 9.4 秒/秒 | AAS 约 8.5 / 9.4 |
| Read IO | 401.5 MB/s | 445.0 MB/s | 读 I/O 吞吐较高 |
| Physical Read | 51,385 blocks/s | 56,962 blocks/s | 物理读压力明显 |
| User I/O DB Time | 63.5% | 64.4% | I/O 是主要瓶颈 |
该数据说明:故障期间数据库不是 CPU 打满,而是大量时间消耗在用户 I/O 等待上。
三、总体结论
本次 HIS 数据库性能问题的本质不是单纯的“数据库性能不够”,而是典型的:
报表/统计类 SQL 与 OLTP 主业务库混跑,在高峰期触发大量物理读和直接路径读,叠加 SGA 偏小、热点大表扫描、SQL 访问路径低效,最终导致数据库进入 I/O 瓶颈状态。
综合判断,根因链路如下:
统计/报表类 SQL 在业务高峰执行
↓
SQL 中大量使用 to_char(date)、listagg、group by、复杂视图、多表 join
↓
谓词不可 SARG 化,索引无法有效使用
↓
大表 / 分区表被反复扫描
↓
产生大量 direct path read / physical read
↓
SGA 容量不足,缓存命中无法覆盖热点访问
↓
大量数据重复从共享存储读取
↓
User I/O 等待占比升高
↓
前端业务响应变慢
四、关键证据分析
4.1 CPU 不是主要瓶颈
从 AWR 看,两个实例 CPU 均未达到瓶颈状态。
| 指标 | orcl1 | orcl2 |
|---|---|---|
| DB CPU 占 DB Time | 29.3% | 27.7% |
| User I/O 占 DB Time | 63.5% | 64.4% |
如果是 CPU 瓶颈,通常会看到 DB CPU 占比持续居高、CPU 使用率接近满载、Run Queue 明显升高。但本次 AWR 中,主要等待集中在 User I/O,说明数据库大部分时间不是在计算,而是在等待存储读。
结论:CPU 不是本次故障主因。
4.2 I/O 是直接瓶颈
两个实例的 Top Foreground Events 均显示 direct path read 排名第一。
| 等待事件 | orcl1 | orcl2 | 说明 |
|---|---|---|---|
| direct path read | 42.8% DB Time | 38.9% DB Time | 大量直接路径读 |
| db file sequential read | 13.5% DB Time | 16.1% DB Time | 索引访问/单块读 |
| log file sync | 5.1% DB Time | 6.2% DB Time | 提交等待,有影响但不是主因 |
| db file parallel read | 4.3% DB Time | 4.9% DB Time | 多块并行读/批量读 |
direct path read 占比高,通常说明 SQL 正在绕过 Buffer Cache 进行大量读操作,常见于大表扫描、分区扫描、并行查询、排序/哈希操作等场景。它不能简单粗暴地等同于“所有都是全表扫描”,但结合本次 SQL 文本、ADDM 热点对象和 Top SQL 特征,可以明确判断:大范围扫描是本次 I/O 压力的核心来源。
结论:数据库主要瓶颈为读 I/O,尤其是大数据量扫描导致的直接路径读。
4.3 ADDM 明确指出 SGA 偏小
ADDM 报告中 Finding 1 为 Undersized SGA,影响 56.59% 的数据库活动,并指出当前 sga_target=32768M,建议调整至 45056M。
| 项目 | 当前值 | ADDM 建议值 | 影响 |
|---|---|---|---|
| sga_target | 32768M | 45056M | 影响 56.59% 活动 |
| 主要症状 | User I/O 高 | 减少额外 I/O | 缓解重复物理读 |
这说明数据库缓冲区无法有效承载当前访问模式下的热点数据,导致数据被频繁淘汰,进而增加重复物理读。
但是需要注意:
SGA 不足是重要放大因素,不是唯一根因。
如果 SQL 访问路径不合理,即使增加 SGA,也只是把问题延后,并不能从根本上解决大量扫描的问题。
4.4 Top SQL 是核心突破口
ADDM 报告中 Finding 2 为 Top SQL Statements,影响 23.25% 的数据库活动。
其中最典型的 SQL_ID 为:
cvxb7bjwbdjwg
该 SQL 特征非常明显:
select listagg(...)
from (
select to_char(f.jssj, 'yyyy-MM') rq,
sum(...)
from mjzjz_fyjsjl f
join mjzjz_fymxjl m
on f.jsjlh = m.jsjlh
where to_char(f.jssj, 'yyyy-MM') >= '2025-11'
group by to_char(f.jssj, 'yyyy-MM')
)
...
ADDM 显示该 SQL:
| 指标 | 数值 |
|---|---|
| 执行次数 | 4 次 |
| 平均耗时 | 484 秒 |
| SQL 类型 | 报表/统计类 SQL |
| 特征 | listagg、to_char、group by、多层 left join |
| 执行计划 | 至少使用 2 个不同执行计划 |
AWR 中该 SQL 在 orcl1 上的表现也非常突出:
| 指标 | 数值 |
|---|---|
| Elapsed Time | 1937.12 秒 |
| Executions | 4 次 |
| Elapsed / Exec | 484.28 秒 |
| %IO | 90.50% |
这说明该 SQL 单次执行成本极高,虽然执行次数不多,但每次执行都会产生大量 I/O,足以拖慢整个数据库。
4.5 SQL 写法导致索引无法有效使用
本次问题 SQL 中最典型的写法是:
where to_char(f.jssj, 'yyyy-MM') >= '2025-11'
这种写法的问题在于:对列 f.jssj 使用函数后,普通 B-tree 索引很难直接用于范围扫描,优化器更容易选择全表扫描或大范围扫描。
应改写为:
where f.jssj >= date '2025-11-01' and f.jssj < date '2026-05-01'
如果是按月统计,应将月份边界在应用层或 SQL 中提前计算好,避免在过滤条件中对字段本身做函数转换。
对比说明
| 写法 | 是否推荐 | 原因 |
|---|---|---|
to_char(date_col,'yyyy-MM') >= '2025-11' |
不推荐 | 函数作用于列,影响索引使用 |
date_col >= :begin_date and date_col < :end_date |
推荐 | 可走范围索引扫描 |
trunc(date_col) = :day |
谨慎 | 普通索引可能失效 |
date_col >= :day and date_col < :day + 1 |
推荐 | OLTP 场景更稳 |
4.6 热点对象集中在大表分区
ADDM 显示 Top Segments by User I/O and Cluster 影响 22.74% 活动,热点对象集中在 GL_PLATFORM.MJZJZ_FYMXJL 的多个分区,例如 P6、P7、P8。
其中报告显示:
| 对象 | 典型问题 |
|---|---|
| GL_PLATFORM.MJZJZ_FYMXJL.P7 | 110 次 full object scans,约 2352 万 physical reads,约 2348 万 direct reads |
| GL_PLATFORM.MJZJZ_FYMXJL.P8 | 113 次 full object scans,约 2541 万 physical reads,约 2533 万 direct reads |
| GL_PLATFORM.MJZJZ_FYMXJL.P6 | 110 次 full object scans,约 2092 万 physical reads,约 2092 万 direct reads |
这说明问题不是某一个小 SQL 偶发慢,而是核心业务大表分区被反复、大量扫描。
结论:热点大表分区的访问路径必须优化,否则单纯加内存效果有限。
五、根因分层分析
5.1 第一层根因:SQL 设计不合理
主要表现
| 问题类型 | 示例 | 影响 |
|---|---|---|
| 日期字段函数化 | to_char(jssj,'yyyy-MM') |
索引失效,扫描扩大 |
| 聚合复杂 | listagg、group by |
排序、聚合成本高 |
| 多层子查询 | 多个 tab1/tab3/tab4/tab5 | 中间结果集大 |
| 重复扫描同表 | 多个子查询分别扫费用表/处方表 | I/O 重复消耗 |
| 复杂视图/派生表 | 多层 left join | 优化器估算偏差 |
| 统计类 SQL 跑主库 | 高峰期执行 | 与 OLTP 争用资源 |
根因判断
这类 SQL 的问题不在于“写法复杂”本身,而在于:
- 过滤条件无法有效利用索引;
- 同一批大表被多次重复扫描;
- 统计维度跨月、跨机构,数据范围大;
- SQL 运行在 HIS 主业务库高峰时段;
- 聚合、排序、拼接导致 PGA/Temp/I/O 负担增加。
5.2 第二层根因:OLTP 与报表负载混跑
HIS 主业务库的核心目标应是保障挂号、收费、医嘱、处方、入出院、护士站等在线事务处理。
但本次 Top SQL 明显属于统计报表类 SQL,其特点是:
| OLTP SQL | 报表 SQL |
|---|---|
| 执行频繁 | 执行次数较少 |
| 单次耗时短 | 单次耗时长 |
| 访问少量数据 | 扫描大量数据 |
| 依赖索引定位 | 依赖聚合、排序、扫描 |
| 要求毫秒/秒级响应 | 可接受异步/预计算 |
当两类负载混跑时,报表 SQL 会消耗大量 I/O、Buffer Cache、PGA、Temp、RAC 全局缓存资源,最终影响 HIS 在线交易。
结论:报表类负载与交易型负载未隔离,是本次问题的重要架构原因。
5.3 第三层根因:SGA 偏小放大了物理读
ADDM 已明确指出 SGA 不足,并建议从 32GB 提升至 45GB。
但需要准确理解:
| 判断 | 说明 |
|---|---|
| SGA 偏小 | 是事实,ADDM 已给出明确建议 |
| SGA 偏小是否根因 | 不是唯一根因 |
| SGA 偏小的作用 | 放大 I/O 问题 |
| 优化优先级 | 应与 SQL 优化并行,而不是替代 SQL 优化 |
如果不改 SQL,仅增加 SGA,可能出现:
短期响应改善
↓
报表继续扩大扫描范围
↓
缓存继续被污染
↓
高峰期再次出现 I/O 等待
5.4 第四层根因:热点分区缺少有效访问路径
热点对象 MJZJZ_FYMXJL 多个分区存在大量 full object scans 和 direct reads。
这说明至少存在以下一种或多种情况:
- 分区键与查询条件不匹配;
- 查询条件被函数包裹,无法触发分区裁剪;
- 本地索引或组合索引不合理;
- 统计信息不准确;
- SQL 中多次重复扫描同一张明细大表;
- 报表没有做汇总表或中间结果预计算。
六、完整故障链路复盘
6.1 技术链路
上午业务高峰
↓
HIS OLTP 正常交易负载上升
↓
统计/报表类 SQL 同时执行
↓
SQL 使用 to_char(date)、listagg、group by、多层 join
↓
普通索引无法有效使用,分区裁剪不充分
↓
MJZJZ_FYMXJL、MJZJZ_FYJSJL、MJZJZ_CFXX、MJZJZ_CFMX 等大表被扫描
↓
direct path read 和 physical read 快速升高
↓
SGA 32GB 无法承载扫描产生的数据压力
↓
热点数据被频繁挤出 Buffer Cache
↓
共享存储读 I/O 成为瓶颈
↓
OLTP SQL 等待 I/O
↓
前端 HIS 页面卡顿、查询变慢
6.2 责任边界判断
本次问题不能简单归咎于数据库、中间件或存储单一组件。
| 层面 | 是否存在问题 | 说明 |
|---|---|---|
| CPU | 否 | CPU 不是主要瓶颈 |
| 存储 | 有压力 | 表现为 I/O 等待高,但压力来源主要是 SQL 扫描 |
| 数据库参数 | 有问题 | SGA 偏小,ADDM 明确建议调整 |
| SQL | 严重问题 | Top SQL 单次耗时高,I/O 占比高 |
| 架构 | 有问题 | 报表与交易混跑 |
| 数据模型 | 有问题 | 大表分区/索引/汇总策略需优化 |
| 应用使用方式 | 有问题 | 高峰期触发重型统计 SQL |
七、优化方案
7.1 P0:立即止血措施
目标:先降低高峰期 I/O 冲击,恢复 HIS 在线业务响应。
7.1.1 限制报表 SQL 高峰期执行
将以下类型 SQL 从上午高峰期剥离:
| SQL 类型 | 处理方式 |
|---|---|
| 跨月统计 | 禁止高峰期实时执行 |
| listagg 汇总 | 改为异步生成 |
| 大表 group by | 转移到报表库或低峰期 |
| 多机构统计 | 预汇总后查询 |
| 全院级指标分析 | 通过调度任务生成结果表 |
建议策略:
07:30 - 11:30 禁止执行重型统计报表
11:30 - 13:30 允许部分低优先级报表
18:00 以后执行全量统计任务
7.1.2 对 Top SQL 做临时限流
针对以下 SQL_ID 建立重点监控和限流:
| SQL_ID | 问题 |
|---|---|
| cvxb7bjwbdjwg | 单次约 484 秒,I/O 占比高 |
| b3ggzqszuvnwp | 平均约 256 秒,多个执行计划 |
| 1jpa6xgg1kw19 | 报表型 listagg / group by SQL |
| fv7gpwsuxgbyj | 高频业务 SQL,需检查索引与执行计划稳定性 |
建议在应用侧增加:
- 查询时间范围限制;
- 默认只查近 1 个月;
- 超过 3 个月必须走异步任务;
- 全院级统计禁止实时查询;
- 同一用户/同一报表增加并发控制。
7.2 P1:SQL 改写优化
7.2.1 日期条件改写
原写法
where to_char(f.jssj, 'yyyy-MM') >= '2025-11'
推荐写法
where f.jssj >= date '2025-11-01' and f.jssj < date '2026-05-01'
按月分组可保留函数,但不能用于过滤
select trunc(f.jssj, 'MM') as rq,
sum(m.xmzje) as zje
from mjzjz_fyjsjl f
join mjzjz_fymxjl m
on f.jsjlh = m.jsjlh
where f.jssj >= :begin_date
and f.jssj < :end_date
group by trunc(f.jssj, 'MM');
过滤条件使用原始日期列,分组展示时再使用 trunc 或 to_char。
7.2.2 减少重复扫描
当前 SQL 中多个子查询重复扫描相同大表,例如费用结算表、费用明细表、处方表、处方明细表。应改为一次扫描,多指标条件聚合。
原模式
子查询 A 扫一次费用表
子查询 B 再扫一次费用表
子查询 C 再扫一次处方表
子查询 D 再扫一次处方表
优化模式
select trunc(f.jssj, 'MM') rq,
sum(m.xmzje) total_fee,
sum(case when m.fyfldm in ('01','02','03') then m.xmzje else 0 end) drug_fee,
sum(case when exists (...) then m.xmzje else 0 end) special_fee
from mjzjz_fyjsjl f
join mjzjz_fymxjl m
on f.jsjlh = m.jsjlh
where f.jssj >= :begin_date
and f.jssj < :end_date
group by trunc(f.jssj, 'MM');
这样可以将多次全表/分区扫描压缩为一次扫描。
7.2.3 listagg 延后处理
listagg 不应参与大表明细阶段计算,应先生成月度结果,再对小结果集做拼接。
推荐拆分
第一步:从明细表生成月度聚合结果
第二步:从月度结果集 listagg 拼接展示
示例:
with month_stat as (
select trunc(f.jssj, 'MM') rq,
sum(m.xmzje) zje
from mjzjz_fyjsjl f
join mjzjz_fymxjl m
on f.jsjlh = m.jsjlh
where f.jssj >= :begin_date
and f.jssj < :end_date
group by trunc(f.jssj, 'MM')
)
select listagg(to_char(rq, 'yyyy-MM'), ',') within group(order by rq) rq_list,
listagg(zje, ',') within group(order by rq) zje_list
from month_stat;
7.3 P1:索引优化
需要结合实际执行计划确认,但从 SQL 特征看,建议重点检查以下索引。
7.3.1 费用结算表
-- 示例,需结合实际字段选择性验证
create index IDX_FYJSJL_JSSJ_JSJLH
on MJZJZ_FYJSJL (JSSJ, JSJLH);
适用场景:
where f.jssj >= :begin_date and f.jssj < :end_date join m on f.jsjlh = m.jsjlh
7.3.2 费用明细表
create index IDX_FYMXJL_JSJLH_FYFLDM
on MJZJZ_FYMXJL (JSJLH, FYFLDM);
或根据实际 SQL:
create index IDX_FYMXJL_JSJLH_XMDM
on MJZJZ_FYMXJL (JSJLH, XMDM);
7.3.3 处方表
create index IDX_CFXX_KLRQ_CFBH
on MJZJZ_CFXX (KLRQ, CFBH);
如果有机构维度:
create index IDX_CFXX_YLJGDM_KLRQ
on MJZJZ_CFXX (YLJGDM, KLRQ);
7.3.4 处方明细表
create index IDX_CFMX_CJSJ_SFKSS_CFBH
on MJZJZ_CFMX (CJSJ, SFKSS, CFBH);
如果以业务代码关联:
create index IDX_CFMX_YWDM_YYTJDM
on MJZJZ_CFMX (YWDM, YYTJDM);
7.3.5 索引建设注意事项
不能盲目加索引,必须按以下流程验证:
获取 SQL 执行计划
↓
确认过滤字段、关联字段、排序字段
↓
评估字段选择性
↓
创建测试索引
↓
收集统计信息
↓
对比 buffer gets / physical reads / elapsed time
↓
确认无明显 DML 副作用
↓
生产变更
7.4 P1:分区与分区裁剪优化
MJZJZ_FYMXJL 已经表现出明显分区热点,例如 P6、P7、P8 被反复扫描。
需要重点确认:
| 检查项 | 目标 |
|---|---|
| 分区键是否为日期 | 确认是否与查询时间条件匹配 |
| SQL 是否能触发分区裁剪 | 避免 to_char(partition_key) |
| 本地索引是否可用 | 大表分区优先考虑 local index |
| 历史分区是否过大 | 必要时归档冷数据 |
| 统计信息是否准确 | 分区级统计信息必须及时收集 |
推荐查询方式:
where partition_date_col >= :begin_date and partition_date_col < :end_date
避免:
where to_char(partition_date_col, 'yyyy-MM') >= '2025-11'
7.5 P2:SGA 与内存调整
ADDM 建议将 sga_target 从 32768M 调整到 45056M。
7.5.1 调整原则
SGA 调整不能只看数据库本身,还必须考虑操作系统、RAC 节点内存、另一个业务库 EMRDB、PGA、文件系统缓存等因素。
你原始环境中提到:
| 实例 | SGA | PGA |
|---|---|---|
| ORCL | 32GB | 6.4GB |
| EMRDB | 12.7GB | 4.2GB |
| 合计 | 44.7GB | 10.6GB |
数据库内存合计约 55GB+,单节点物理内存约 63GB。如果这些实例确实部署在同一物理节点上,则 OS 可用内存会非常紧张,文件系统缓存空间不足,容易进一步放大 I/O 问题。
7.5.2 推荐调整方案
方案 A:资源不扩容情况下
| 项目 | 建议 |
|---|---|
| ORCL SGA | 不建议直接拉满到 45GB |
| EMRDB | 评估是否可下调 SGA |
| PGA | 设置合理上限,避免高峰失控 |
| OS 可用内存 | 至少保留 8GB~12GB |
| 大页 HugePages | 建议启用并锁定 Oracle SGA |
| swappiness | 降低,避免数据库内存换出 |
方案 B:资源允许情况下
| 项目 | 建议 |
|---|---|
| ORCL SGA | 逐步提升至 40GB,再观察 |
| 最终目标 | 接近 ADDM 建议 45GB |
| 调整方式 | 分阶段,避免一次性变更 |
| 验证指标 | physical reads、direct path read、DB Time、AAS |
7.6 P2:报表架构改造
这是长期根治方案。
7.6.1 主库与报表库拆分
推荐架构:
HIS 主库
↓ Data Guard / ETL / CDC / 定时同步
报表库 / 查询库
↓
统计报表、BI、大屏、监管指标
主库只承担在线交易:
挂号、收费、医嘱、处方、护士站、入出院、药房发药
报表库承担:
月度统计、费用分析、处方占比、抗菌药物比例、机构指标、监管报表
7.6.2 建立汇总表
对于月度统计类 SQL,不应每次从明细表实时聚合。
建议建立:
REPORT_MONTH_FEE_STAT REPORT_MONTH_DRUG_STAT REPORT_ORG_MONTH_INDICATOR REPORT_PRESCRIPTION_STAT
示例表结构:
create table REPORT_ORG_MONTH_INDICATOR (
stat_month date,
yljgdm varchar2(50),
total_fee number,
drug_fee number,
prescription_cnt number,
antibiotic_cnt number,
update_time date
);
查询时直接访问汇总表:
select stat_month,
yljgdm,
drug_fee,
prescription_cnt
from report_org_month_indicator
where stat_month >= :begin_month
and stat_month < :end_month;
这样可以把秒级/分钟级扫描变为毫秒级查询。
八、整改优先级
| 优先级 | 整改项 | 目标 | 预期收益 |
|---|---|---|---|
| P0 | 限制高峰期重型报表 | 快速止血 | 立即降低 I/O |
| P0 | Top SQL 临时限流 | 避免单 SQL 拖垮实例 | 降低峰值冲击 |
| P1 | 改写 to_char(date) 条件 |
恢复索引/分区裁剪 | 明显降低扫描量 |
| P1 | 重构 listagg 报表 SQL | 减少重复扫描 | 降低物理读 |
| P1 | 建立关键组合索引 | 优化访问路径 | 降低响应时间 |
| P1 | 收集统计信息 | 稳定执行计划 | 减少计划漂移 |
| P2 | 调整 SGA | 缓解重复物理读 | 降低 User I/O |
| P2 | 报表库拆分 | 架构级隔离 | 根治混跑问题 |
| P3 | 历史数据归档 | 缩小热数据集 | 长期降本 |
| P3 | 建立性能基线 | 持续治理 | 可观测、可预警 |
九、验证方案
优化后不能只看“用户说不卡了”,必须通过指标闭环验证。
9.1 AWR 对比指标
| 指标 | 优化前 | 优化目标 |
|---|---|---|
| User I/O % DB Time | 63%~64% | 降至 40% 以下 |
| direct path read | Top 1 | 占比明显下降 |
| Read IO MB/s | 400MB/s+ | 降低 30% 以上 |
| Top SQL elapsed/exec | 484 秒 | 降至 10~30 秒以内,最好秒级 |
| Physical reads | 高 | 下降 30%~60% |
| AAS | 8~9 | 高峰稳定下降 |
| log file sync | 5%~6% | 持续观察,不作为第一主因 |
9.2 SQL 级验证
每条 Top SQL 优化前后对比:
select sql_id,
executions,
elapsed_time / 1000000 elapsed_s,
buffer_gets,
disk_reads,
rows_processed
from v$sql
where sql_id in (
'cvxb7bjwbdjwg',
'b3ggzqszuvnwp',
'1jpa6xgg1kw19',
'fv7gpwsuxgbyj'
);
重点比较:
| 指标 | 目标 |
|---|---|
| elapsed_time/executions | 单次耗时下降 |
| disk_reads | 物理读下降 |
| buffer_gets | 逻辑读下降 |
| rows_processed | 判断是否误查全量 |
| plan_hash_value | 执行计划稳定 |
| physical read bytes | I/O 下降 |
9.3 执行计划验证
select *
from table(dbms_xplan.display_cursor('cvxb7bjwbdjwg', null, 'ALLSTATS LAST'));
重点看:
| 执行计划项 | 判断 |
|---|---|
| TABLE ACCESS FULL | 是否减少 |
| PARTITION RANGE ALL | 是否变为 PARTITION RANGE ITERATOR |
| INDEX RANGE SCAN | 是否出现 |
| HASH JOIN | 是否合理 |
| SORT GROUP BY | 是否处理小结果集 |
| TEMP | 是否减少 |
| A-Rows / E-Rows | 估算是否准确 |
十、最终结论
本次 HIS 数据库性能问题是一次典型的高峰期 OLTP 与报表型负载混跑导致的 I/O 型性能故障。
从 AWR 和 ADDM 证据看,数据库主要瓶颈不是 CPU,而是 User I/O。两个 RAC 实例中,direct path read 均为首要等待事件,User I/O 占 DB Time 约 63%~64%。ADDM 同时指出 SGA 偏小,当前 sga_target=32768M,建议提升至 45056M,并认为 SGA 不足影响 56.59% 的数据库活动。
但从根因上看,SGA 不足只是放大器,核心问题仍然是 SQL 访问路径低效。典型 SQL 使用 to_char(date)、listagg、多层子查询、多表 join 和大范围 group by,导致索引和分区裁剪无法充分发挥作用,进而引发大表分区反复扫描和大量 direct reads。
最终判断:
核心根因:报表/统计类 SQL 设计不合理,访问路径低效
关键放大器:SGA 偏小、OS 缓存空间不足、共享存储 I/O 压力高
架构问题:报表负载与 HIS 主交易库混跑
直接表现:direct path read、physical read、User I/O 等待显著升高
建议按照以下顺序治理:
先止血:限制高峰期重型报表
再优化:改写 Top SQL、恢复索引和分区裁剪
再加固:调整 SGA、优化内存分配
最后根治:建设报表库/汇总表,实现 OLTP 与统计负载隔离
一句话总结:
这不是单纯的数据库参数问题,而是 SQL、内存、数据模型和架构混合导致的系统性性能问题。真正的优化重点,应从“让数据库扛住扫描”转向“避免数据库反复扫描”。




