注册
达梦执行计划:新手搞定SQL慢查询
培训园地/ 文章详情 /

达梦执行计划:新手搞定SQL慢查询

ZE 2025/11/07 255 1 0

做业务开发或运维的同学,肯定遇到过这样的痛点:明明昨天还跑得很顺的SQL,今天突然变卡;或者同样的查询,小数据量时没问题,数据量一上来就超时。其实解决这些问题的核心钥匙,就是达梦数据库的「执行计划」。今天就用最通俗的话,带新手朋友吃透执行计划,搞定常见的生产慢查询。

一、先搞懂:执行计划到底是什么?

很多新手听到「执行计划」会觉得高深,其实它就是数据库的「操作说明书」。当你提交一条SQL时,数据库不会直接执行,而是先由「优化器」分析出多种执行方案,然后选一个成本最低的(比如CPU、IO消耗最少),这个最优方案就是执行计划。

执行计划里有两个核心信息必须看懂,新手记住这两点就够了:

  1. 操作符:表示数据库具体做了什么操作,比如全表扫描、索引扫描等,是判断SQL效率的关键;

  2. 代价三元组:格式是(代价,记录行数,字节数),比如(5,1000,8000),分别代表执行成本、处理的行数和数据量,数字越小效率越高。

二、3步看懂执行计划

首先要解决「怎么看」的问题。达梦数据库查看执行计划非常简单,用1个命令,3步就能看懂核心信息。

步骤1:用EXPLAIN命令生成执行计划

在SQL语句前加EXPLAIN关键字,执行后就会输出执行计划。比如查询订单表2024年的订单数据:

-- 查看订单查询的执行计划 EXPLAIN SELECT O.ORDER_ID,O.USER_ID FROM ORDER_INFO O WHERE O.ORDER_TIME BETWEEN '2024-01-01' AND '2024-12-31';

步骤2:看懂执行计划的输出格式

执行后会得到类似下面的结果,看起来像一串代码,其实按顺序解读就行(控制流从上到下,数据流从下到上):

1 #NSET2: (1, 500, 4000) 
2 #PRJT2: (1, 500, 4000); exp_num(2), is_atom(FALSE) 
3 #SLCT2: (1, 500, 4000); (O.ORDER_TIME >= '2024-01-01' AND O.ORDER_TIME <= '2024-12-31')
4 #CSCN2: (1, 500, 4000); INDEX33555476(ORDER_INFO as O); btr_scan(1)

步骤3:抓重点解读核心节点

新手不用逐字抠细节,重点看最下面的「表访问节点」(例子中是第4行),因为慢查询大多出在这里:

  • 第4行(CSCN2):CSCN2是「聚集索引扫描」,后面跟着索引名INDEX33555476,说明走了索引扫描,这是高效的操作;

  • 代价三元组(1, 500, 4000):成本1,处理500行数据,总字节4000,整体效率不错;

  • 第3行(SLCT2):表示按时间条件过滤数据,对应SQL里的WHERE子句;

  • 第2行(PRJT2):表示只取需要的ORDER_ID和USER_ID两列,对应SELECT子句;

  • 第1行(NSET2):最终返回的结果集,把上面处理的结果汇总给用户。

三、生产痛点实战:用执行计划解决4类常见慢查询

光看懂还不够,关键是用执行计划定位问题。下面结合生产中最常见的4个痛点,教你一步步优化。

痛点1:查询超时,执行计划显示「全表扫描」

场景:订单表ORDER_INFO有100万数据,查询某时间段订单时,耗时超10秒,执行计划如下:

4 #CSCN2: (100, 1000000, 8000000); SYSINDEXORDER_INFO(ORDER_INFO as O); btr_scan(1)

问题定位:虽然操作符是CSCN2,但代价100、处理100万行,说明实际走了「全表扫描」(达梦中无合适索引时,聚集索引会退化为全表扫描)。原因是ORDER_TIME列没建索引,数据库只能逐行遍历所有数据。

优化方案:给过滤条件列建索引:

-- 给订单时间列建索引 CREATE INDEX idx_order_time ON ORDER_INFO(ORDER_TIME); -- 重建执行计划后,再查询耗时降至300ms

痛点2:建了索引却没用,还是慢

场景:给ORDER_TIME建了索引,但查询还是慢,执行计划显示依然处理100万行。

问题定位:查看执行计划发现操作符是「SSCN2(二级索引全表扫描)」,而非「SSEK2(二级索引范围扫描)」。原因可能是统计信息过时,数据库不知道表数据量已经变大,优化器选错了执行路径;也可能是查询条件用了函数,比如WHERE DATE(ORDER_TIME) = '2024-01-01',索引失效。

优化方案

  1. 更新统计信息,让优化器准确判断数据分布;

  2. 修改查询条件,避免函数操作索引列:WHERE ORDER_TIME BETWEEN '2024-01-01 00:00:00' AND '2024-01-01 23:59:59'

痛点3:多表关联查询慢,出现「回表查询」

场景:关联订单表和用户表查询时耗时超5秒,执行计划中有「BLKUP(回表查询)」操作符。

问题定位:BLKUP表示先通过二级索引找到ROWID,再回表取数据,频繁回表会增加IO消耗。比如用用户ID关联时,二级索引只存了用户ID和ROWID,需要多次回表取订单信息。

优化方案:建「覆盖索引」,把查询需要的列都包含在索引里,避免回表:

-- 覆盖索引:包含关联列和查询列 CREATE INDEX idx_user_order ON ORDER_INFO(USER_ID, ORDER_ID, ORDER_TIME); -- 优化后执行计划中BLKUP消失,耗时降至500ms

痛点4:GROUP BY分组慢,执行计划显示「HASH分组」

场景:统计各用户订单数时,执行计划出现「HAGR2(HASH分组)」,耗时超3秒。

问题定位:HASH分组需要缓存所有数据再分组,大数据量下IO消耗大。原因是GROUP BY的列没有建索引,优化器无法使用更高效的「排序分组」。

优化方案:给GROUP BY列建索引,让优化器切换为排序分组:

-- 给用户ID列建索引(GROUP BY列) CREATE INDEX idx_order_uid ON ORDER_INFO(USER_ID); -- 优化后执行计划变为SAGR2(排序分组),耗时降至800ms

四、新手必备:执行计划核心操作符速查表

最后整理了生产中最常见的操作符,记不住可以收藏,遇到时对照看:

操作符 含义 效率 优化建议
CSCN2 聚集索引扫描 正常使用,确保索引合适
SSEK2/CSEK2 索引范围扫描 范围查询时优先出现,最优解
SSCN2 二级索引全表扫描 检查查询条件,更新统计信息
BLKUP 回表查询 建覆盖索引,减少回表次数
HAGR2 HASH分组 中低 给GROUP BY列建索引,切换排序分组
SAGR2 排序分组 中高 优先选择,需索引支持
HASH JOIN 哈希连接 中高 等值连接时高效,大数据量适用
NEST LOOP 嵌套连接 小表驱动大表时更高效

五、进阶技巧:3个让执行计划更优的小细节

  1. 避免SELECT *:只查询需要的列,不仅减少数据传输,还能让覆盖索引发挥作用;

  2. 用COUNT(*)代替COUNT(列名):COUNT()直接读取索引行数,无需扫描数据,效率更高(注意:COUNT()包含NULL,COUNT(列名)不包含,需结合场景使用);

  3. 合理使用HINT提示:如果优化器选了差的执行计划,可手动指定索引或连接方式,比如SELECT /*+ INDEX(O, idx_order_time) */ ...

六、总结:优化SQL的3个核心步骤

  1. 遇到慢查询,先加EXPLAIN看执行计划;

  2. 找最下面的表访问节点,看操作符是不是高效的SSEK2/CSEK2/CSCN2;

  3. 若出现全表扫描、回表或HASH分组,优先建合适的索引(普通索引/覆盖索引/GROUP BY列索引),再更新统计信息。

执行计划看似复杂,但只要抓住「操作符」和「代价」两个核心,再结合实际场景练几次,就能快速搞定大部分生产慢查询问题。

评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服