直接结论:PL/SQL性能下降主因是SQL执行计划突变,根源在于统计信息不准(尤以直方图缺失、采样失真为甚);应优先检查业务核心表、含状态码/类型码的表(如ORDER_HEADER)、频繁JOIN的维度表的last_analyzed时间及直方图状态,并确保DBMS_STATS.GATHER_TABLE_STATS中cascade=TRUE与degree≥4协同使用。
直接结论:不是存储过程写错了,是它调用的sql在19c里选错了执行计划——根源几乎总是统计信息不准,尤其直方图缺失或采样失真。
查哪几张表的统计信息最该优先盯
别全库扫,先锁定高风险对象。业务核心表、WHERE条件含状态码/类型码的表(如ORDER_HEADER)、被频繁JOIN的维度表,这三类最易因统计失真导致执行计划跳变。
- 检查统计最后更新时间:
SELECT owner, table_name, last_analyzed FROM dba_tables WHERE owner IN ('PROD', 'PROD_CB') AND last_analyzed - 确认关键列是否缺直方图:
SELECT column_name, histogram FROM dba_tab_col_statistics WHERE table_name = 'ORDER_HEADER' AND owner = 'PROD' AND histogram = 'NONE' - 比对执行计划突变:在19c中跑
EXPLAIN PLAN FOR SELECT ...,再用SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY),重点看是否从INDEX RANGE SCAN变成TABLE ACCESS FULL
重收集统计信息时必须显式控制的三个参数
DBMS_STATS.GATHER_SCHEMA_STATS不能裸跑,19c默认行为会放大问题。
-
estimate_percent:超大表禁用DBMS_STATS.AUTO_SAMPLE_SIZE,改用固定值如10或20,避免采样逻辑激进导致高频值密度被低估 -
method_opt:禁用'FOR ALL COLUMNS SIZE AUTO',对取值少但倾斜严重的字段(如order_status只有'P','S','C'),必须写成'FOR COLUMNS SIZE 254 order_status' -
cascade和degree要配对:cascade => TRUE必须搭配degree => 4或更高,否则索引统计滞后于表统计,优化器看到“表小但索引大”,直接弃用索引
为什么PL/SQL里跑得慢,但单独执行SQL却快
这是典型客户端环境差异导致的隐性干扰,不是代码本身问题。
- 检查是否残留11g的
dynamic_samplingHint,19c中这类Hint可能被忽略或误解析 - 确认
_optimizer_ignore_hints参数值:11g默认FALSE,19c中该参数行为已变,未显式设为TRUE可能导致hint半生效 - JDBC驱动版本不匹配:PL/SQL Developer用旧驱动可能走老路径,而应用连19c JDBC驱动触发新优化器逻辑,建议统一用Oracle 19c官方驱动
清理缓存和验证顺序不能乱
统计信息重收集后,必须按顺序操作,否则旧执行计划仍驻留共享池。
- 先执行
DBMS_STATS.GATHER_TABLE_STATS(带正确参数) - 再查
V$SQLAREA定位对应SQL的SQL_ID,例如:SELECT sql_id, address, hash_value FROM v$sqlarea WHERE sql_text LIKE '%your_key_word%' - 用
DBMS_SHARED_POOL.PURGE清理,格式为exec dbms_shared_pool.purge('address,hash_value', 'c');注意需DBA权限 - 清理后立刻执行一次该SQL,强制生成新执行计划,再观察性能
真正容易被忽略的是:分区表上SIZE AUTO几乎必然漏掉关键直方图,而ESTIMATE_PERCENT => AUTO_SAMPLE_SIZE在百亿级表上会让19c采样率跌破1%,导致优化器把99%的行当成“稀疏数据”处理——这种偏差不会报错,但会让所有依赖该表的存储过程集体变慢。



















