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