在使用达梦数据库时,经常会遇到一种情况:SQL 可以正常执行,但速度很慢。此时,仅看 SQL 语句本身往往无法找到原因,因为 SQL 只说明"需要查询什么数据",没有说明数据库内部采用什么方式获取这些数据。
例如:
sql
SELECT *
FROM EMPLOYEE
WHERE EMP_ID = 1001;
如果 EMP_ID 上存在索引,数据库可能直接定位目标数据;如果没有合适的索引,则可能需要扫描整张表。当数据量较小时,两种方式差别不明显,但当表中存在几十万甚至上百万行数据时,执行方式会直接影响 SQL 性能。
因此,分析慢 SQL 的第一步,是查看数据库为 SQL 生成的执行计划。
一、什么是执行计划
一条 SQL 提交到数据库后,大致会经历以下过程:
text
SQL解析
→ 查询优化
→ 生成执行计划
→ 按计划执行
→ 返回结果
其中,查询优化器负责选择 SQL 的执行方式,例如:
- 使用全表扫描还是索引扫描;
- 多张表按照什么顺序连接;
- 使用嵌套循环还是哈希连接;
- 是否需要排序、聚合或回表;
- 过滤条件在哪个阶段执行。
执行计划就是优化器最终选择的执行路线。
可以简单理解为:
text
SQL:告诉数据库要什么数据
执行计划:告诉数据库怎样得到这些数据
二、怎样阅读执行计划
达梦执行计划通常以树形结构显示,例如:
text
NSET2
PRJT2
SLCT2
CSCN2
虽然计划从上到下显示,但数据通常由底层节点产生,再逐层向上传递。因此阅读时应从缩进最深的节点开始。
上面的计划可以理解为:
text
CSCN2:扫描表中的数据
SLCT2:按照条件过滤
PRJT2:计算并返回需要的列
NSET2:形成最终结果集
阅读计划时,可以按照以下顺序分析。
1. 看访问了哪些表
先找到计划最底层的表扫描或索引扫描节点,确认 SQL 访问了哪些表。
常见操作符包括:
CSCN2:扫描表中的聚集索引数据,常表现为整表扫描;CSEK2:按条件扫描聚集索引;SSEK2:扫描二级索引;BLKUP2:根据二级索引结果回表读取其他列。
如果计划中出现:
text
BLKUP2
SSEK2
说明数据库先通过二级索引找到目标记录,再回到原表读取索引中没有保存的列。
2. 看是否使用索引
看到 SSEK2、CSEK2 或具体索引名称,通常说明计划使用了索引。
看到 CSCN2 时,需要判断是否扫描了大量不需要的数据。
但不能简单认为全表扫描一定不好、索引扫描一定更快。
如果 SQL 本身需要读取大部分数据,或者索引条件区分度很低,使用索引后进行大量回表,可能比直接扫描整张表更慢。
因此,判断计划是否合理,需要结合:
- 表的数据量;
- 查询返回的数据比例;
- 索引区分度;
- 是否需要大量回表;
- 实际执行时间和读取量。
3. 看过滤条件
执行计划中可能出现:
text
access(...)
filter(...)
可以简单理解为:
access:用于数据定位或表连接;filter:数据读取后再进行过滤。
如果 SQL 先读取大量数据,之后才进行过滤,通常会产生更多资源消耗。
4. 看表连接方式
多表查询中常见的连接方式包括嵌套循环和哈希连接。
嵌套循环可以理解为:
text
从左侧取一行数据
→ 到右侧查找匹配记录
→ 再取下一行
→ 再次到右侧查找
它适合左侧结果较少、右侧连接列有索引的情况。如果左侧返回几十万行,右侧节点也可能被执行几十万次。
哈希连接通常先读取一侧数据建立哈希结构,再扫描另一侧进行匹配,更适合较大结果集之间的等值连接。
连接方式本身没有绝对好坏,需要结合数据量和实际耗时判断。
三、怎样理解计划中的数字
达梦执行计划中经常出现类似内容:
text
CSCN2: [10, 1000, 48]
三个数字通常表示:
text
[估算代价, 预计行数, 每行数据长度]
1. 估算代价
代价是优化器用于比较不同执行方案的相对指标。
它不是实际执行时间,不能把代价 10 理解为 SQL 执行需要 10 毫秒。
2. 预计行数
预计行数表示优化器估计该节点会处理或返回多少条记录。
如果优化器预计返回 10 行,实际却返回 10 万行,后续连接方式和扫描方式就可能不再合适。
估算不准确可能与以下原因有关:
- 统计信息过期;
- 数据分布不均;
- 多列之间存在关联;
- 条件中使用函数或隐式类型转换。
3. 每行数据长度
该值表示节点每处理一行数据的大致长度。
返回列越多,每行数据越宽,排序、连接和网络传输的成本通常也越高。因此,在不需要全部字段时,应避免长期使用 SELECT *。
四、EXPLAIN:查看优化器生成的预估计划
EXPLAIN 是分析 SQL 执行计划时最基础的工具。它的作用不是执行查询,而是让优化器根据当前的表结构、索引、统计信息和数据库参数,为 SQL 生成一份预估执行计划。
基本形式如下:
sql
EXPLAIN
SELECT *
FROM EMPLOYEE
WHERE DEPT_ID = 10;
执行后,数据库不会真正读取并返回查询结果,只会显示优化器准备采用的执行路线。因此,EXPLAIN 适合用于性能分析的第一步。
1. EXPLAIN 实际上在看什么
一条 SQL 通常存在多种可选执行方式。
例如:
sql
SELECT *
FROM EMPLOYEE
WHERE DEPT_ID = 10;
优化器可能考虑:
- 扫描整张
EMPLOYEE表; - 使用
DEPT_ID上的二级索引; - 使用某个包含
DEPT_ID的联合索引; - 先扫描索引,再回表获取其他字段;
- 在多表查询中改变表的连接顺序;
- 在嵌套循环、哈希连接等方式之间进行选择。
EXPLAIN 展示的是优化器比较不同候选方案后,最终选择的那一份计划。
因此,EXPLAIN 主要回答的是:
根据优化器目前掌握的信息,这条 SQL 准备怎样执行?
这里的"目前掌握的信息"非常重要。优化器并不知道业务上的真实含义,它主要根据统计信息估算条件会返回多少数据,再决定扫描方式和连接方式。如果统计信息不准确,优化器对数据量的估计就可能出现偏差,最终计划也可能不合理。
2. 查看 EXPLAIN 时应重点关注什么
初学阶段不需要立即分析所有字段,可以先关注以下内容。
数据访问路径
先看最底层节点使用了哪种方式获取数据,例如:
text
CSCN2
SSEK2
CSEK2
BLKUP2
重点判断:
- 是整表数据扫描还是索引扫描;
- 使用了哪个索引;
- 是否发生回表;
- 扫描范围是否与查询条件相符。
例如出现:
text
BLKUP2
SSEK2
说明数据库先通过二级索引找到符合条件的索引记录,再根据索引结果回到原表读取其他列。
这并不一定有问题,但如果索引返回几十万条记录,就可能产生大量回表,实际性能未必比全表扫描更好。
表连接顺序
对于多表查询,要看哪张表先产生数据,哪张表后参与连接。
在嵌套循环中,左侧节点通常是驱动侧。左侧返回多少行,可能直接决定右侧节点被调用多少次。
如果优化器原本估计左侧只返回 10 行,实际却返回 10 万行,那么右侧索引扫描也可能被重复执行 10 万次。
连接方式
计划中需要关注:
- 嵌套循环连接;
- 哈希连接;
- 归并连接。
不同连接方式适合不同的数据规模。EXPLAIN 可以帮助判断优化器是否因为预计结果集较小而选择嵌套循环,或者因为预计数据量较大而选择哈希连接。
预计行数
执行计划三元组中的第二个值通常表示预计处理或输出的行数。
例如:
text
CSCN2: [10, 1000, 48]
其中 1000 表示优化器预计该节点会处理大约 1000 行。
预计行数会影响:
- 是否使用索引;
- 选择哪张表作为驱动表;
- 使用哪种连接方式;
- 是否进行排序;
- 为哈希、排序等操作分配多少资源。
因此,看计划时不能只看有没有索引,还要看优化器对数据量的估计是否符合实际业务情况。
过滤条件的位置
此外,还需要检查 WHERE 条件是在底层扫描阶段生效,还是读取大量数据之后才进行过滤。
如果条件能够限制索引扫描范围,数据库可能只访问少量数据;如果条件只能作为上层 filter 使用,则可能先读取大量记录,再排除其中大部分。
3. 为什么 EXPLAIN 只是"预估计划"
EXPLAIN 不真正执行 SQL,因此计划中的代价和行数都是估算值。
它无法直接告诉我们:
- SQL 实际执行了多长时间;
- 每个节点实际返回多少行;
- 实际发生多少逻辑读和物理读;
- 是否发生磁盘排序;
- 某个节点实际进入了多少次;
- 时间主要消耗在哪一个操作符。
所以,即使 EXPLAIN 看起来正常,也不能证明 SQL 的实际执行一定正常。
例如:
text
优化器预计返回10行
实际执行返回20万行
如果计划选择依赖"只返回 10 行"这一前提,实际执行就可能与预估结果产生巨大差异。
这也是为什么性能分析不能停留在 EXPLAIN,而需要继续使用 AUTOTRACE 和 ET 查看真实执行情况。
4. EXPLAIN FOR
达梦还提供更加详细的:
sql
EXPLAIN FOR
SELECT *
FROM EMPLOYEE
WHERE DEPT_ID = 10;
普通 EXPLAIN 通常以树形文本显示计划,而 EXPLAIN FOR 会以结构化结果集的形式返回信息。
除了操作符、表名、索引名、预计行数和代价外,还可以包含:
- 扫描类型;
- 扫描范围;
- CPU 代价;
- I/O 代价;
- 过滤条件;
- 连接条件;
- 分区扫描范围;
- 索引建议;
- 其他扩展计划信息。
EXPLAIN FOR 还可用于查看相关 DML 语句的计划,适合需要保存、比较或进一步分析计划字段的场景。
可以简单区分为:
text
EXPLAIN:
适合快速阅读执行计划树。
EXPLAIN FOR:
适合查看更完整、更结构化的计划信息。
5. EXPLAIN 最适合解决的问题
EXPLAIN 最适合用于快速判断:
- 是否使用了预期索引;
- 是否出现不合理的大表扫描;
- 是否存在大量回表的可能;
- 表连接顺序是否符合数据规模;
- 为什么选择嵌套循环或哈希连接;
- SQL 改写后计划是否发生变化;
- 创建或删除索引后访问路径是否变化;
- 统计信息变化是否影响优化器估算。
但 EXPLAIN 主要用于发现"可能存在的问题",不能单独作为最终性能结论。
五、AUTOTRACE:查看实际执行计划与整体资源消耗
EXPLAIN 反映的是优化器的预估。如果需要知道 SQL 真正执行时采用了什么计划、读取了多少数据、发生了多少 I/O,就需要使用 AUTOTRACE。
AUTOTRACE 是 disql 中用于控制执行计划和统计信息跟踪方式的环境变量,语法如下:
sql
SET AUTOTRACE
<OFF | NL | INDEX | ON | TRACE | TRACEONLY>;
不同模式并不是简单的显示格式差异,其中最重要的区别是:有些模式不会真正执行 SQL,有些模式会真正执行 SQL。
1. OFF:关闭跟踪
sql
SET AUTOTRACE OFF;
关闭 AUTOTRACE,后续 SQL 按普通方式执行。
这是缺省状态。
2. NL:只观察嵌套循环信息
sql
SET AUTOTRACE NL;
该模式不会真正执行目标 SQL。
如果计划中存在嵌套循环,则主要显示与 NEST LOOP 有关的内容,适合快速观察:
- 是否使用嵌套循环;
- 嵌套循环大致位于计划的什么位置;
- 哪些节点参与嵌套循环。
但它不会提供完整执行统计,也不能反映真实耗时。
3. INDEX 或 ON:快速查看访问路径
sql
SET AUTOTRACE INDEX;
或者:
sql
SET AUTOTRACE ON;
这两种模式也不会真正执行 SQL,主要显示:
- 表扫描方式;
- 表名;
- 使用的索引。
它们适合快速回答:
这条 SQL 查哪些表,使用了哪些索引?
需要注意,达梦中的 AUTOTRACE ON 主要用于查看扫描方式和索引信息,不能直接等同于其他数据库中"执行 SQL 并输出完整统计"的 AUTOTRACE ON。
4. TRACE:真正执行并显示结果
sql
SET AUTOTRACE TRACE;
设置完成后执行 SQL,数据库会:
- 真正执行目标 SQL;
- 返回查询结果;
- 显示服务器实际采用的执行计划;
- 显示本次执行的部分资源统计信息。
AUTOTRACE TRACE 和 EXPLAIN 的核心区别是:
text
EXPLAIN:
不执行SQL,看到的是优化器预估计划。
AUTOTRACE TRACE:
真正执行SQL,看到的是服务器实际采用的计划。
实际计划可能是本次新生成的,也可能来自数据库已有的计划缓存。因此,当管理工具中手工 EXPLAIN 的结果与应用程序实际执行表现不一致时,实际计划通常更具有排查价值。
5. TRACEONLY:真正执行但不输出查询结果
sql
SET AUTOTRACE TRACEONLY;
TRACEONLY 与 TRACE 的执行过程基本一致,也会:
- 真正执行 SQL;
- 显示实际执行计划;
- 收集资源统计信息。
区别是,对于查询语句,TRACEONLY 不会把结果集打印到客户端。
这对于返回大量数据的 SQL 很重要。假设一条查询返回 50 万行,如果使用 TRACE,大量时间可能消耗在:
- 服务器向客户端传输数据;
- 客户端接收数据;
- 终端格式化和显示结果。
此时 SQL 的业务执行耗时和结果输出耗时会混在一起,不便于分析。使用 TRACEONLY 可以减少终端输出带来的干扰。
但必须明确:
TRACEONLY 只是"不显示查询结果",并不是"不执行 SQL"。
如果目标语句是:
sql
UPDATE ...
DELETE ...
INSERT ...
数据仍然可能被真正修改。因此,分析 DML 时应在测试环境操作,或明确控制事务。
6. AUTOTRACE 输出中的实际行数
预估计划的节点通常显示类似:
text
[代价, 预计行数, 行长度]
实际计划中则可能出现预计值与实际值的对比,例如带有箭头的行数信息:
text
[代价, 预计行数 -> 实际行数, 行长度]
如果出现:
text
100 -> 100
说明优化器预计与实际较接近。
如果出现:
text
10 -> 200000
说明优化器严重低估了该节点的数据量。
这种差异非常值得关注,因为它可能导致:
- 错误选择嵌套循环;
- 选择不合适的驱动表;
- 错误判断索引访问成本;
- 哈希表或排序空间估算不足;
- 中间结果集明显膨胀。
因此,AUTOTRACE 不只是为了看 SQL 执行了多少毫秒,还可以帮助验证优化器估算是否准确。
7. AUTOTRACE 统计信息怎样理解
TRACE 或 TRACEONLY 可以显示部分执行统计,常见指标包括以下内容。
logical reads
逻辑读表示数据库访问缓冲区中数据页的次数。
逻辑读较高,通常说明 SQL 处理的数据量较大,可能存在:
- 大范围表扫描;
- 大范围索引扫描;
- 大量回表;
- 嵌套循环反复读取;
- 连接产生大量中间结果;
- 过滤条件生效过晚。
逻辑读发生在内存中,不等于磁盘读取,但逻辑读过多仍会消耗 CPU 和内存访问资源。
在比较两种 SQL 或两份计划时,逻辑读往往比单次执行时间更稳定、更具有参考价值。
physical reads
物理读表示需要从磁盘或存储设备加载数据页。
物理读较高可能说明:
- 查询数据尚未进入缓存;
- 扫描范围很大;
- 缓冲区缓存不足;
- 数据页被其他负载挤出;
- 当前测试属于冷缓存执行。
同一条 SQL 第一次执行和第二次执行的物理读可能明显不同,因此不能只根据一次执行结果判断优化效果。
sorts (memory)
表示排序在内存中完成。
出现内存排序并不一定有问题,因为 ORDER BY、GROUP BY、DISTINCT 和部分连接操作本来就需要排序。
需要结合排序的数据量和执行耗时判断。
sorts (disk)
表示排序过程中使用了磁盘临时空间。
磁盘排序通常比内存排序开销更大。如果该指标出现,需要进一步检查:
- 排序前的数据是否过多;
- 能否让过滤条件更早执行;
- 是否存在不必要的排序或去重;
- 是否可以利用索引有序性;
- 内存配置是否适合当前数据规模。
rows processed
表示 DML 语句处理或影响的记录数。
如果 UPDATE 预计只修改几十行,实际却处理几十万行,需要检查 WHERE 条件和访问路径。
redo size、data pages changed 和 undo pages changed
这些指标主要用于观察 DML 的修改量。
一次大批量更新可能产生:
- 大量数据页修改;
- 大量 UNDO;
- 大量 REDO 日志。
此时 SQL 慢的原因可能不仅是查询路径,还包括日志写入、事务规模和存储 I/O。
roundtrips to/from client
表示客户端与服务器的交互次数。
如果应用程序循环执行大量单条 SQL,或者每次只读取很少的数据,网络往返次数可能成为额外负担。
io wait time 与 exec time
io wait time 表示 I/O 等待时间,exec time 表示执行耗时。
如果执行时间高且 I/O 等待占比也高,问题可能集中在:
- 数据读取量过大;
- 物理存储性能;
- 缓存命中不足;
- 大量临时空间读写;
- 日志写入。
如果执行时间很高但 I/O 等待较低,则还要关注:
- 大量逻辑读;
- CPU 计算;
- 排序和哈希;
- 操作符重复执行;
- 复杂表达式或函数。
8. 使用 AUTOTRACE 时的监控条件
要获得较完整的实际执行统计,需要开启相关监控参数,例如:
text
ENABLE_MONITOR
MONITOR_SQL_EXEC
ENABLE_MONITOR_DMSQL
其中,MONITOR_SQL_EXEC 可以按会话开启,用于记录操作符和执行计划节点统计;ENABLE_MONITOR_DMSQL 也可用于动态 SQL 执行时间监控。详细监控会增加一定开销,因此更适合在指定分析会话中临时开启,而不是在生产系统长期全局启用。
9. AUTOTRACE 使用场景
AUTOTRACE 适合进一步确认:
- 实际计划是否与 EXPLAIN 计划一致;
- 优化器预计行数与实际行数差异多大;
- SQL 的逻辑读和物理读是否过高;
- 是否发生磁盘排序;
- 查询结果传输是否影响总耗时;
- DML 是否产生大量日志和数据页修改;
- 优化前后实际资源消耗是否下降。
AUTOTRACE 提供的是整条 SQL 和整份计划的总体情况。如果要进一步精确到"究竟是哪一个操作符最慢",还需要使用 ET。
六、ET:定位执行计划中的真实耗时节点
AUTOTRACE 可以告诉我们整条 SQL 执行了多长时间、读取了多少数据页,但一份复杂执行计划可能包含十几个甚至几十个节点。
例如 SQL 总执行时间为 8 秒,仅凭这个结果仍然不能确定:
- 是表扫描用了 8 秒;
- 是排序用了 8 秒;
- 是某个哈希连接耗时;
- 还是嵌套循环中的右侧节点被重复调用了几十万次。
ET 的作用,就是把一次 SQL 执行拆解到各个操作符,查看每个节点实际消耗的时间和资源。
1. ET 分析的是"某一次执行"
ET 不是直接接收 SQL 文本,而是接收 SQL 的执行号:
sql
ET(执行号);
例如:
sql
ET(843);
这里的执行号代表 SQL 的某一次具体执行。
即使 SQL 文本完全相同,两次执行也可能有不同的执行号。这样 ET 可以区分:
- 第一次执行;
- 第二次执行;
- 不同参数值下的执行;
- 优化前和优化后的执行。
因此,ET 分析的是:
某一条 SQL 在某一次真实执行中的操作符耗时。
而不是一份静态的预估执行计划。
2. ET 与 EXPLAIN、AUTOTRACE 的区别
三者可以这样理解:
text
EXPLAIN:
显示优化器预估的计划结构。
AUTOTRACE:
显示实际计划和整条SQL的总体资源消耗。
ET:
显示这次执行中每个操作符分别消耗了多少时间。
假设 AUTOTRACE 显示:
text
exec time = 5000ms
ET 可能进一步显示:
text
CSCN2 3600000us
HASH JOIN 900000us
SORT 400000us
其他节点 100000us
这样就能确认主要耗时集中在扫描节点,而不是排序或连接节点。
3. 使用 ET 需要的监控信息
ET 依赖 SQL 操作符监控,通常需要开启:
text
ENABLE_MONITOR
MONITOR_TIME
MONITOR_SQL_EXEC
其中 MONITOR_SQL_EXEC 可以设置为会话级,只采集当前会话的操作符统计。
如果监控参数未开启,SQL 虽然可以正常执行,但 ET 可能无法取得对应的节点耗时信息。
4. ET 输出中的核心字段
ET 输出的字段较多,初学阶段应重点关注以下几项。
OP
表示操作符名称,例如:
text
CSCN2
SSEK2
BLKUP2
HASH JOIN
SORT
PRJT2
同一份计划中可能出现多个相同操作符,因此不能只根据 OP 判断具体是哪个节点。
TIME(US)
表示该操作符的实际执行耗时,单位为微秒。
这是定位瓶颈最直接的字段。
一般先按耗时从高到低查看,优先分析 TIME 最大的几个节点。
但需要注意,某些上层节点的时间可能包含等待下层节点返回数据的时间。因此,不能只看单个数字,还要结合计划树的父子关系判断。
PERCENT
表示操作符耗时在整个计划中的占比。
例如:
text
PERCENT = 82%
说明该节点消耗了大部分执行时间,通常是最优先分析的对象。
如果一个节点耗时比例很高,可以继续判断它属于:
- 表扫描;
- 索引扫描;
- 回表;
- 连接;
- 排序;
- 聚合;
- 数据输出。
RANK
表示节点的耗时排名。
ET 通常会将节点按耗时从高到低排列,RANK 可以帮助快速找到最耗时的节点。
SEQ
表示该操作符在执行计划中的节点序号。
这是 ET 与执行计划建立对应关系的关键字段。
例如执行计划中存在:
text
4 #CSCN2: DEPARTMENT
7 #CSCN2: EMPLOYEE
ET 显示:
text
OP=CSCN2
SEQ=7
PERCENT=80%
就可以确定真正耗时的是 EMPLOYEE 的扫描,而不是 DEPARTMENT。
因此,分析 ET 时不能只说"CSCN2 很慢",应准确到:
执行计划中编号为 7、访问 EMPLOYEE 表的 CSCN2 节点耗时最高。
N_ENTER
表示操作符被进入或调用的次数。
这是 ET 中非常重要、但容易被忽略的字段。
例如某个索引扫描:
text
SSEK2
TIME(US)=3000000
N_ENTER=500000
说明该索引扫描节点被调用了 50 万次。
这种情况经常出现在嵌套循环中:
text
NEST LOOP
左侧节点返回50万行
右侧SSEK2被调用50万次
每次索引定位可能只需要很短时间,但累计执行 50 万次后,整体耗时仍然很高。
此时,问题的根源可能不是索引本身慢,而是:
- 左侧结果集过大;
- 驱动表选择不合理;
- 过滤条件没有提前生效;
- 连接顺序不合理;
- 优化器预计行数严重偏小;
- 当前数据规模不适合嵌套循环。
MEM_USED(KB)
表示操作符使用的内存空间。
对于哈希连接、排序和聚合等操作,可以通过该字段观察节点的内存使用情况。
DISK_USED(KB)
表示操作符使用的磁盘空间。
如果排序、哈希或聚合节点使用了较多磁盘空间,通常说明:
- 中间数据量较大;
- 内存空间不足;
- 操作发生了磁盘溢写;
- 过滤和数据缩减发生得太晚。
ET 还可以显示哈希表槽位、哈希冲突等更深入的信息,适合进一步分析哈希连接和哈希聚合,但入门阶段不必立即展开。
5. 怎样根据 ET 判断常见问题
扫描节点耗时最高
例如:
text
CSCN2
PERCENT=85%
说明时间主要花在表数据扫描上。
需要继续检查:
- 扫描的表有多大;
- WHERE 条件能否提前过滤;
- 是否缺少合适索引;
- 查询是否本来就需要返回大量数据;
- 预计行数与实际行数是否一致。
不能看到 CSCN2 就直接认定必须创建索引。如果 SQL 返回大部分数据,全表扫描可能仍然合理。
回表节点耗时较高
例如:
text
BLKUP2
PERCENT=70%
说明数据库虽然使用了二级索引,但大量时间消耗在回表获取其他列。
可能原因包括:
- 索引返回记录过多;
- 查询字段较多;
- 索引选择性较差;
- 数据页访问较分散;
- 索引虽然被使用,但并不适合当前查询。
索引节点 N_ENTER 很高
例如:
text
SSEK2
N_ENTER=300000
通常需要检查该节点是否处于嵌套循环右侧。
优化方向可能包括:
- 减少左侧结果集;
- 调整连接顺序;
- 更新统计信息;
- 判断哈希连接是否更合适;
- 优化连接条件和过滤条件。
SORT 节点磁盘使用较高
如果排序节点的 DISK_USED(KB) 较大,说明排序过程中可能使用了磁盘临时空间。
需要检查:
- 是否存在不必要的
ORDER BY; - 是否存在重复的
DISTINCT; - 排序前是否可以减少数据量;
- 是否可以利用索引顺序;
- 内存配置是否合理。
哈希节点耗时或内存使用较高
如果哈希连接或哈希聚合耗时明显,需要检查:
- 建立哈希表的一侧是否过大;
- 优化器是否错误选择了构建侧;
- 实际行数是否远大于预计行数;
- 哈希表是否发生大量冲突;
- 是否使用了磁盘空间。
6. ET 为什么可能出现 EXPLAIN 中没有的操作符
ET 反映的是实际执行过程,可能出现预估 EXPLAIN 中没有直接显示的节点。
官方文档指出,某些实际执行中的内部操作符,例如用于字典对象加锁的 DLCK,可能出现在 ET 中,但不会出现在普通 EXPLAIN 计划里。
此外,如果目标 SQL 触发了:
- 触发器;
- 内部函数;
- 约束检查;
- 级联操作;
- 数据字典访问;
ET 中也可能出现与 SQL 主计划之外相关的操作符。
因此,不能看到额外节点就立即判断 ET 结果错误。需要先确认 SQL 是否触发了其他内部执行过程。
7. 为什么有些 EXPLAIN 节点没有出现在 ET 中
官方文档也说明,如果某些操作符:
- 实际没有被执行;
- 执行时间非常短;
- 没有形成可记录的统计;
它们可能不会出现在 ET 结果中。
因此,ET 不是简单复制 EXPLAIN 后再附加时间,而是展示实际执行中被监控并产生有效统计的节点。
8. ET 没有结果时怎样排查
如果输入执行号后 ET 没有返回有效信息,可以检查:
ENABLE_MONITOR是否开启;MONITOR_TIME是否开启;MONITOR_SQL_EXEC是否在 SQL 执行前开启;- 使用的是否为正确执行号;
- 查询结果是否已经被完整处理;
- 当前用户是否有执行 ET 的权限;
- 监控信息是否已经被后续大量执行覆盖。
普通用户没有权限时,可以由管理员按最小权限原则授权:
sql
GRANT EXECUTE ON SYS.ET TO 用户名;
ET 分析应尽量在目标 SQL 执行完成后及时进行,避免监控数据发生变化。
9. ET 适合的场景
ET 最适合用来查看:
- 一份复杂计划中哪个节点最耗时;
- 是否存在某个节点被重复调用大量次数;
- 索引扫描慢,还是回表慢;
- 扫描、排序、连接和聚合各自消耗多少;
- 哈希或排序是否使用较多内存、磁盘;
- 实际瓶颈是否与 EXPLAIN 中的高代价节点一致;
- 优化前后高耗时节点是否真正下降。
因此,ET 的价值不只是"显示操作符时间",而是把性能问题从"这条 SQL 很慢"进一步缩小到:
执行计划中的哪一个节点慢,它为什么慢,以及下一步应分析什么。
七、三个工具怎样配合
EXPLAIN、AUTOTRACE 和 ET 的关系可以概括为:
text
EXPLAIN
查看预估计划
AUTOTRACE
查看实际计划和整体资源消耗
ET
查看每个操作符的实际耗时
分析一条慢 SQL 时,可以按照以下思路进行:
text
发现SQL较慢
→ 使用EXPLAIN查看扫描、索引和连接方式
→ 使用AUTOTRACE查看实际计划和资源消耗
→ 使用ET定位耗时最高的节点
→ 再决定是否调整SQL、索引或统计信息
这里最重要的是,不要在没有分析计划的情况下直接创建索引,也不要只凭一次执行时间判断优化是否有效。
八、总结
达梦数据库 SQL 性能分析的核心,不是记住多少操作符,而是建立正确的分析顺序。
首先看执行计划,确认数据库选择了什么扫描方式、索引和连接方式;然后通过实际执行统计,观察 SQL 读取了多少数据、进行了多少排序、消耗了多少时间;最后再定位具体的高耗时操作符。
三个工具的分工非常明确:
text
EXPLAIN:数据库准备怎么执行
AUTOTRACE:数据库实际上怎么执行
ET:执行时间主要花在哪里