导读:一条 SQL 执行 18 秒,可真正「跑」数据只花了约 3 秒,剩下 15 秒全耗在解析(含优化)阶段。很多人的第一反应是「加索引」「调执行计划」,方向全错了。本文还原这类「解析慢」场景的定位过程:如何用
V$SQL_STAT发现解析占比、如何识别「视图互引」这个真正的元凶、以及「去冗余 → 改写法 → 参数 hint」三层递进的优化手段。
生产上一条 SQL 执行耗时 18 秒,分析后发现大部分时间花在了 SQL 解析阶段,而不是执行阶段。
怎么发现的?查 V$SQL_STAT:
-- 查出 exec_time(执行时长)与 parse_time(解析时长)的占比
SELECT * FROM sys.v$sql_stat WHERE sql_txt LIKE '%UPDATE%';
结果清晰:exec_time 约 18 秒,其中 parse_time 就占了 15 秒多。瓶颈在解析,不在执行。
回到 SQL 文本,看到 FROM 关键字后有三张主表,还关联了多个 _v1 视图。进一步查看这些视图的 DDL 定义,发现视图的定义里还关联了其他视图。
这就是问题所在:视图层层嵌套、互相引用,导致优化器要展开的查询树成倍膨胀,解析/优化阶段的代价随之飙升。
达梦的解析优化能力其实很强——纯物理表多(几十张)、长 UNION ALL、CTE、ROWNUM 视图等,解析都在几十毫秒内。真正让解析爆炸的,是「同一批视图被反复 join(自互引)+ 每层再叠聚合/子查询」。
V1 JOIN V1 JOIN V2 JOIN V3... 的互引,改写为单次引用 + 提前物化中间结果(临时表 / CTE 引用一次)。这个 SQL 里有一个视图里写了 11 个 UNION ALL 分支,按业务逻辑可以改写为 DECODE:
usage_detail_code 的 11 个等值条件合并成一个 IN;page_type 通过 DECODE 处理。这既是「去掉冗余」的一部分,也是「减少视图复杂度」的手段(详见《视图内 11 个 UNION ALL 分支改 IN + DECODE》一文)。
对这条 SQL 加了 MAX_OPT_N_TABLES 的 hint 后,执行时长从 18 秒下降到 2 秒左右:
SELECT /*+MAX_OPT_N_TABLES(4)*/ ...
这是通过优化参数加一个 hint 的临时应急解决办法。
MAX_OPT_N_TABLES用于指定在处理连接时,一次能优化的最大表连接个数。如果待连接的表个数超过了MAX_OPT_N_TABLES,则先将前MAX_OPT_N_TABLES个表作为一组进行探测优化,得到一个固定的连接顺序,将其视为一个整体 Ta,再将 Ta 与后续(MAX_OPT_N_TABLES - 1)个表进行探测优化,如此反复进行探测优化。
简单说:达梦默认 MAX_OPT_N_TABLES = 6,≤6 张表走穷举搜索最优连接顺序,>6 张转启发式。hint 调大它能让更多表参与穷举,有时能选到更优计划;但也会放大解析时间,需要权衡。
在干净实例上构造同型场景实测,验证「解析时间」这一判断:
| 场景 | EXPLAIN / 执行耗时 |
|---|---|
| 3 表 join | 22 ms |
| 6 表 join | 73 ms |
| 20 表 join | 183 ms(转启发式,不再爆炸) |
| 3 层聚合视图链(不可合并) | 153 ms |
| 6 个业务视图互相引用 + EXISTS + 大 IN | EXPLAIN 270 ms / 执行 1.995 s |
| 多视图互引 + 聚合链 + 大 IN(复现峰值) | 7.064 s / 1.2 s |
结论:纯表多不爆炸,视图互引才爆炸。把同一批视图反复 join(自互引)+ 每层带聚合/子查询,解析时间能稳定到秒级;生产 16 秒场景 = 视图互引数量 × 规模远超本模拟。
V$SQL_STAT 的 parse_time 占比,方向错了全白费。文章
阅读量
获赞
