注册
从 DMLOG 到执行计划,再到 Hint 实验
专栏/技术分享/ 文章详情 /

从 DMLOG 到执行计划,再到 Hint 实验

chinuppp 2026/09/30 244 0 0
摘要

一、背景

在达梦数据库运维和优化中,慢 SQL 是绕不开的话题。这一周我完整走了一遍慢 SQL 分析流程:从 DMLOG 收集慢 SQL,到看懂执行计划,再到更新统计信息、使用 hint 干预执行路径。本文把这套流程整理成可复用的经验,分享给同样在做达梦优化的朋友。

二、用 DMLOG 收集慢 SQL

实例级 SQL 日志分析工具,DMLOG 工具是一款简单易用的达梦数据库 SQL 日志分析工具,通过分析数据库的 SQL 日志文件(达梦数据库可通过开启 SVR_LOG 捕捉数据库中运行的所有 SQL,并生成 SQL 日志文件),来统计日志中最长执行时间和执行最高频次的 SQL 语句,直观地反映 SQL 执行情况,对于 SQL 的优化工作提供了极大的便利。

DMLOG 工具可实现如下功能:

  • 达梦数据库 SQL 日志分析功能,统计分析日志并根据执行时间和执行频次进行排序,生成 excel 文档。
  • 支持生成 echart 散点图,每个点上的 sql 可以显示和拷贝。
  • 支持生成 sql 统计图,根据执行次数和执行时间统计。
  1. 使用前需检查 Java 安装情况。
java -version

image.png

  1. 进入工具包并配置工具参数
    image.png
    一共需要配置五个参数,数据库的IP、端口号、用户名和密码,还有SQL日志的存放位置。
  2. 执行 jar 包命令
[root@localhost DMLOG8]# java -jar Dmlog_DM_8.18.jar

image.png

会生成两个两个excel表格:

image.png
more_than_0_ms_log_result.xls:统计的是执行时间大于 0 毫秒的 SQL 日志分析结果。

more_than_0_times_log_result.xls:统计的是执行次数大于 0 次的 SQL 日志分析结果。

image.png

在达梦数据库的日志分析中,发现一条执行次数为 59 次、平均执行时间约 18.5 秒(最大 46.7 秒)的慢 SQL。该 SQL 用于从 bpmqregbillnoderecord 表中查询待处理记录,并按 srcdocid 分组取最新一条,最后进行分页返回。

三、看懂执行计划

执行计划是数据库在执行 SQL 前规划的一条“执行路线图”,描述了数据从底层表被读取、过滤、排序、连接,最终返回结果的完整过程。你写 SQL 只是告诉数据库“要什么”,执行计划决定了它“怎么拿”。

同一条 SQL 可能对应多种执行方式,数据库会基于代价选出它认为最优的方案,并以操作符形式展示每一步动作。

以一条三层嵌套的分页查询为例,用 EXPLAIN 查看执行计划:

SELECT id, srcdocid, sortorder, processedtimes, errmsg, content, timestamp_createdon, timestamp_createdby, timestamp_lastchangedon, timestamp_lastchangedby, version FROM ( SELECT rownum AS rn_outer, t.* FROM ( SELECT id, srcdocid, sortorder, processedtimes, errmsg, content, timestamp_createdon, timestamp_createdby, timestamp_lastchangedon, timestamp_lastchangedby, version FROM ( SELECT id, srcdocid, sortorder, processedtimes, errmsg, content, timestamp_createdon, timestamp_createdby, timestamp_lastchangedon, timestamp_lastchangedby, version, ROW_NUMBER() OVER (PARTITION BY srcdocid ORDER BY sortorder) AS rn FROM test_biz_record WHERE (processedtimes < 3 AND timestamp_lastchangedon <= SYSDATE - 30) OR (processedtimes = 0 AND timestamp_lastchangedon > SYSDATE - 30) ) temp1 WHERE rn = 1 ORDER BY sortorder ) t ) temp3 WHERE rn_outer BETWEEN 1 AND 5000;

加入explain,分析执行计划:

image.png
为什么操作符又有2又有3的,难道好多种全表扫描吗

​ 数字越大,通常是越新的实现版本,但不代表功能不同,核心作用是一样的。比如CSCN->全表扫描(基础版),CSCN2->全表扫描(优化版)。

序号 名称 描述 含义
13 CSCN2 CLUSTER SCAN 全表扫描,读取 test_biz_record 全部 20 万行
12 SLCT2 SELECT 过滤,执行 WHERE 中的 processedtimes 和 timestamp_lastchangedon 条件
11 SORT3 SORT 排序,为窗口函数 ROW_NUMBER() 做准备
10 AFUN ANALYTIC FUNCTION 分析函数,执行 ROW_NUMBER() OVER (PARTITION BY srcdocid ORDER BY sortorder)
9 PRJT2 PROJECT OPERATION 投影,输出窗口函数计算后的中间结果
8 SLCT2 SELECT 过滤,执行 WHERE rn = 1,每组只保留一条
7 SORT3 SORT 排序,执行 ORDER BY sortorder
6 PRJT2 PROJECT OPERATION 投影,准备输出给外层
5 RNSK ROWNUM STOP KEY ROWNUM 停止键,执行分页限制,凑够 5000 行即停
4 PRJT2 PROJECT OPERATION 投影,输出 rn_outer 等列
3 SLCT2 SELECT 过滤,执行 rn_outer BETWEEN 1 AND 5000
2 PRJT2 PROJECT OPERATION 投影,最终输出字段
1 NSET2 RESULT SET 结果集收集,返回给客户端

CSCN2 全表扫描,SLCT2 过滤行,SORT3 排序,AFUN 分析函数(窗口函数),PRJT2 投影(决定输出哪些列、计算表达式),NSET2 结果收集。

达梦常见执行计划操作符

类别 操作符 描述(英文) 含义
数据访问 CSCN2 CLUSTER SCAN 全表扫描
SSEK SECONDARY INDEX SEEK 二级索引范围扫描
CSEK CLUSTER INDEX SEEK 聚集索引范围扫描
SSCN SECONDARY INDEX SCAN 二级索引全扫描
BLKUP BACK LOOP UP 回表(通过 rowid 回原表取数据)
过滤 SLCT2 SELECT 按条件过滤行
投影 PRJT2 PROJECT OPERATION 选择输出列、计算表达式
排序 SORT3 SORT 对数据排序
分析函数 AFUN ANALYTIC FUNCTION 执行窗口函数(如 ROW_NUMBER)
连接 NEST LOOP NESTED LOOP JOIN 嵌套循环连接
HASH JOIN HASH JOIN 哈希连接
MERGE JOIN MERGE JOIN 归并排序连接
聚集 AAGR SIMPLE AGGREGATE 简单聚集(无 GROUP BY)
HAGR HASH GROUP AGGREGATE 哈希分组聚集
SAGR STREAM GROUP AGGREGATE 流分组聚集
分页优化 RNSK ROWNUM STOP KEY ROWNUM 停止键,提前终止扫描
结果输出 NSET2 RESULT SET 结果集收集,返回客户端

四、收集统计信息

什么是收集统计信息?

统计信息是数据库对表中数据分布情况的“描述”,比如:

  • 这张表大概有多少行?
  • 某个列有多少个不同的值?
  • 某个值出现的频率高不高?
  • 数据在磁盘上大概占多少块?

这些信息数据库不会实时去数,而是定期收集一次,存下来,供优化器估算代价时用。

为什么需要统计信息?

数据库执行 SQL 前,优化器要决定“走哪条路最快”,而它的判断依据就是统计信息。

举个例子:

  • 如果统计信息说 test_biz_record 有 20 万行,优化器可能觉得全表扫描也行。
  • 如果统计信息说这张表有 2000 万行,优化器就会更倾向于走索引。

统计信息越准,优化器选的路就越靠谱。

为什么要“更新”统计信息?

因为数据是会变的:

  • 今天插了 10 万行,明天删了 5 万行
  • 某个列之前只有几个值,现在变得很分散

如果统计信息一直不更新,优化器就会拿着过时的数据做判断,可能选错执行计划,导致 SQL 变慢。

所以需要定期重新收集,让统计信息跟上数据的真实情况。

image.png

  • 'SYSDBA':收集 SYSDBA 模式下的所有对象。
  • 100:采样率 100%,全量扫描,结果最准。
  • FALSE:不使用数据页采样,按行采样。
  • 'FOR ALL COLUMNS SIZE AUTO':自动决定为哪些列收集分布信息(直方图)。
  • 8:并行度 8,同时开 8 个线程收集,速度更快。
-- 看表的行数、块数 SELECT TABLE_NAME, NUM_ROWS, BLOCKS, LAST_ANALYZED FROM USER_TABLES WHERE TABLE_NAME = 'TEST_BIZ_RECORD'; -- 看列的唯一值数、是否收集直方图 SELECT COLUMN_NAME, NUM_DISTINCT, NUM_NULLS, HISTOGRAM, LAST_ANALYZED FROM USER_TAB_COLUMNS WHERE TABLE_NAME = 'TEST_BIZ_RECORD';

1. 唯一值数量(NUM_DISTINCT)

  • ID 列有 200000 个唯一值 → 正好等于表的总行数,说明 ID 是唯一的(主键)。
  • SRCDOCID 列有 5000 个唯一值 → 说明 20 万行数据被分到了 5000 个不同的 srcdocid 组里。
  • PROCESSEDTIMES 列只有 5 个唯一值 → 说明这个字段取值很集中(比如只有 0、1、2、3、4 这几种状态)。

2. 空值数量(NUM_NULLS)

  • 所有列都是 0 → 说明表里没有 NULL 值,数据很规整。

3. 直方图类型(HISTOGRAM)—— 这是最关键的

  • FREQUENCY(频率直方图):出现在 SRCDOCID、SORTORDER、PROCESSEDTIMES、TIMESTAMP_CREATEDON 等列上。说明这些列唯一值数量较少(如 PROCESSEDTIMES 只有 5 个值),优化器为每个值都记录了精确的出现频率。
  • HEIGHT BALANCED(高度平衡直方图):出现在 ID、ERRMSG、CONTENT 等列上。说明这些列唯一值数量太多(接近行数),优化器无法为每个值单独记录,只能把数据粗略描述分布。

4. 最后收集时间(LAST_ANALYZED)

  • 全部显示为 2026-09-23 → 说明你刚刚执行的收集命令成功了,所有列的统计信息都刷新到了最新。

五、如何使用 hint

hint 是写在 SQL 注释里的手动干预指令,用来强制优化器选择指定的索引、连接方式或并行度,比如执行一条sql,优化器走了A路,我们想让他走B路。

对于第三节中的sql,加入hint,尝试改变执行计划:

EXPLAIN SELECT /*+ INDEX(test_biz_record idx_test_biz_1) */ id, srcdocid, sortorder, processedtimes, errmsg, content, timestamp_createdon, timestamp_createdby, timestamp_lastchangedon, timestamp_lastchangedby, version FROM ( SELECT rownum AS rn_outer, t.* FROM ( SELECT id, srcdocid, sortorder, processedtimes, errmsg, content, timestamp_createdon, timestamp_createdby, timestamp_lastchangedon, timestamp_lastchangedby, version FROM ( SELECT id, srcdocid, sortorder, processedtimes, errmsg, content, timestamp_createdon, timestamp_createdby, timestamp_lastchangedon, timestamp_lastchangedby, version, ROW_NUMBER() OVER (PARTITION BY srcdocid ORDER BY sortorder) AS rn FROM test_biz_record WHERE (processedtimes < 3 AND timestamp_lastchangedon <= SYSDATE - 30) OR (processedtimes = 0 AND timestamp_lastchangedon > SYSDATE - 30) ) temp1 WHERE rn = 1 ORDER BY sortorder ) t ) temp3 WHERE rn_outer BETWEEN 1 AND 5000;

image.png

​ 对一条三层嵌套的分页查询,尝试用 /*+ INDEX(…) */ hint 强制走索引。执行计划从全表扫描(CSCN2,代价 36)变为二级索引全扫描 + 回表(SSCN + BLKUP2,代价 863)。虽然 hint 成功干预了执行路径,但由于需要大量回表,代价反而大幅上升,说明优化器选择全表扫描是合理的。这也验证了 hint 是“强制手段”而非“优化手段”,使用前需对比实际效果。

评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服