注册
DM 性能诊断思路
培训园地/ 文章详情 /

DM 性能诊断思路

DM_356848 2026/06/15 621 0 0

DM 性能诊断与根因分析
参考地址:https://eco.dameng.com/
核心概念对照表(MySQL vs DM)
优化领域 MySQL 8.0 达梦 DM8 差异说明
执行计划 EXPLAIN EXPLAIN(ALL) DM 支持更多选项
执行计划格式 JSON/TABLE JSON/TABLE/XML DM 支持 XML
索引 Hint USE INDEX /*+ INDEX / 语法不同
连接 Hint STRAIGHT_JOIN /
+ LEADING / DM 更灵活
并行 Hint - /
+ PARALLEL / DM 支持并行
物化 Hint - /
+ MATERIALIZE / DM 独有
统计信息 ANALYZE TABLE DBMS_STATS.GATHER DM 更精细
绑定变量 预处理语句 预处理语句 相同
SQL 缓存 query_cache 结果缓存 DM 更智能
执行计划缓存 plan_cache 计划缓存 DM 自动管理
实时 SQL 监控 performance_schema V$SQL DM 更直观
SQL 历史 - V$SQL_HISTORY DM 独有
执行统计 events_statements_
V$SQL_STAT DM 更详细
资源限制 resource_groups Profiles DM 更灵活
并行查询 有限支持 全面支持 DM 更强
查询重写 - 自动查询重写 DM 智能优化
增量统计 部分支持 全面支持 DM 更完善

一、SQL 执行流程剖析
1.1 DM SQL 执行流程
SQL 提交

  1. 语法检查(Parser)
  2. 语义检查(Resolver)
  3. 查询转换(Query Transformer)
  4. 执行计划生成(Optimizer)
  5. 执行计划选择(Plan Selector)
  6. 执行计划缓存(Plan Cache)
  7. 执行引擎(Row Source)
  8. 结果返回
    1.2 优化器决策过程
    基于成本的优化:
  9. 收集统计信息(表、索引、列)
  10. 生成候选执行计划
  11. 计算每个计划的成本
  12. 选择成本最低的计划
  13. 缓存执行计划
    成本计算公式:
    总成本 = I/O 成本 + CPU 成本 + 网络成本

I/O 成本 = 读取块数 × 块读取时间
CPU 成本 = CPU 周期数 × CPU 时间
网络成本 = 传输数据量 × 网络带宽
影响优化器决策的因素:
• 统计信息准确性
• 系统参数(OPTIMIZER_MODE 等)
• Hint 提示
• 绑定变量
• 数据分布

1.3 优化器如何最优选择
问题 说明
数据从哪里来? 全表扫描?索引扫描?
数据如何过滤? 下推?后过滤?
数据如何连接? Nested Loop?Hash Join?Merge Join?
数据如何排序? 内存排序?磁盘排序?
执行计划 = 优化器基于成本估算选择的"关键路径"
操作符 含义 性能评估
CSCN 全表扫描(Cluster Scan) 差
SSCN 二级索引扫描(Secondary Scan) 差
SSEK 二级索引定位(Secondary Seek) 好
BLKUP 回表查询(Block Lookup) 中

二、优化实验
实验 1:索引选择优化
实验目的: 理解优化器如何选择索引,学会引导优化器
MySQL 对应命令:
SQL
EXPLAIN SELECT * FROM T1 USE INDEX (IDX1) WHERE ...;
EXPLAIN SELECT * FROM T1 FORCE INDEX (IDX1) WHERE ...;
DM 索引 Hint 语法:
SQL
-- 建议使用索引
SELECT /*+ INDEX(T1 IDX_COL1) */ * FROM T1 WHERE COL1 = 100;

-- 强制使用索引
SELECT /*+ INDEX(T1 IDX_COL1) */ * FROM T1 WHERE COL1 = 100;

-- 不使用索引
SELECT /*+ NO_INDEX(T1 IDX_COL1) */ * FROM T1 WHERE COL1 = 100;

-- 使用多个索引
SELECT /*+ INDEX(T1 IDX_COL1, IDX_COL2) */ * FROM T1
WHERE COL1 = 100 AND COL2 = 200;
实验步骤:

  1. 创建多索引表
  2. 执行查询(让优化器选择)
  3. 使用 Hint 强制指定索引
  4. 对比性能
    实验过程:
    SQL
    -- 1. 创建表和多列索引
    CREATE TABLE T_INDEX_TEST (
    ID INT PRIMARY KEY,
    COL1 INT,
    COL2 INT,
    COL3 VARCHAR(100)
    );

CREATE INDEX IDX_COL1 ON T_INDEX_TEST(COL1);
CREATE INDEX IDX_COL2 ON T_INDEX_TEST(COL2);
CREATE INDEX IDX_COL1_COL2 ON T_INDEX_TEST(COL1, COL2);

INSERT INTO T_INDEX_TEST
SELECT LEVEL, MOD(LEVEL, 100), MOD(LEVEL, 50), RPAD('X', 100)
FROM DUAL CONNECT BY LEVEL <= 100000;

CALL DBMS_STATS.GATHER_TABLE_STATS('SYSDBA', 'T_INDEX_TEST');

-- 2. 查询 1:单列条件
EXPLAIN(ALL) SELECT * FROM T_INDEX_TEST WHERE COL1 = 50;
-- 优化器选择:IDX_COL1(正确)

-- 3. 查询 2:双列条件
EXPLAIN(ALL) SELECT * FROM T_INDEX_TEST WHERE COL1 = 50 AND COL2 = 25;
-- 优化器选择:IDX_COL1_COL2(复合索引,最优)

-- 4. 查询 3:范围查询
EXPLAIN(ALL) SELECT * FROM T_INDEX_TEST WHERE COL1 > 50 AND COL1 < 100;
-- 优化器选择:IDX_COL1(范围扫描)

-- 5. 查询 4:函数查询(索引失效)
EXPLAIN(ALL) SELECT * FROM T_INDEX_TEST WHERE YEAR(COL3) = 2026;
-- 优化器选择:FULL TABLE SCAN(索引失效!)

-- 6. 优化:改写查询
EXPLAIN(ALL) SELECT * FROM T_INDEX_TEST
WHERE COL3 >= '2026-01-01' AND COL3 < '2027-01-01';
-- 优化器选择:IDX_COL3(如果有的话)
索引选择原则:

  1. 最左前缀原则:复合索引 (A,B,C),查询条件必须从 A 开始
  2. 等值优先:等值条件优于范围条件
  3. 选择性高的索引优先:区分度高的列优先
  4. 覆盖索引最优:查询列都在索引中,无需回表
    索引失效场景:
    SQL
    -- ❌ 索引失效:函数操作
    SELECT * FROM T WHERE YEAR(DATE_COL) = 2026;
    -- ✅ 优化后
    SELECT * FROM T WHERE DATE_COL >= '2026-01-01' AND DATE_COL < '2027-01-01';

-- ❌ 索引失效:隐式转换
SELECT * FROM T WHERE VARCHAR_COL = 123; -- 数字
-- ✅ 优化后
SELECT * FROM T WHERE VARCHAR_COL = '123'; -- 字符串

-- ❌ 索引失效:LIKE 前缀通配符
SELECT * FROM T WHERE COL LIKE '%ABC';
-- ✅ 优化后(如果可能)
SELECT * FROM T WHERE COL LIKE 'ABC%';

-- ❌ 索引失效:OR 条件(部分情况)
SELECT * FROM T WHERE COL1 = 1 OR COL2 = 2;
-- ✅ 优化后
SELECT * FROM T WHERE COL1 = 1
UNION ALL
SELECT * FROM T WHERE COL2 = 2;
性能对比测试:
SQL
-- 测试 1:无 Hint(优化器选择)
SELECT * FROM T_INDEX_TEST WHERE COL1 = 50 AND COL2 = 25;
-- 耗时:0.05 秒(使用 IDX_COL1_COL2)

-- 测试 2:强制使用单列索引
SELECT /*+ INDEX(T_INDEX_TEST IDX_COL1) */ *
FROM T_INDEX_TEST WHERE COL1 = 50 AND COL2 = 25;
-- 耗时:0.15 秒(使用 IDX_COL1,然后过滤 COL2)

-- 测试 3:强制全表扫描
SELECT /*+ FULL(T_INDEX_TEST) */ *
FROM T_INDEX_TEST WHERE COL1 = 50 AND COL2 = 25;
-- 耗时:1.2 秒(全表扫描)

-- 结论:优化器选择通常是最优的,Hint 用于特殊情况

实验 2:连接优化(JOIN 类型选择)
实验目的: 理解不同 JOIN 类型的适用场景,优化多表查询
MySQL 对应命令:
SQL
EXPLAIN SELECT * FROM T1 JOIN T2 ON T1.ID = T2.T1_ID;
-- JOIN_TYPE: NESTED LOOP / HASH JOIN / MERGE JOIN
DM 连接类型:
SQL
-- NESTED LOOP JOIN(适合小表驱动大表)
SELECT /*+ USE_NL(T1 T2) */ * FROM T1 JOIN T2 ON T1.ID = T2.T1_ID;

-- HASH JOIN(适合大表连接)
SELECT /*+ USE_HASH(T1 T2) */ * FROM T1 JOIN T2 ON T1.ID = T2.T1_ID;

-- MERGE JOIN(适合已排序数据)
SELECT /*+ USE_MERGE(T1 T2) */ * FROM T1 JOIN T2 ON T1.ID = T2.T1_ID;

-- 指定驱动表
SELECT /*+ LEADING(T1 T2) */ * FROM T1 JOIN T2 ON T1.ID = T2.T1_ID;
实验步骤:

  1. 创建两个表(大小不同)
  2. 执行 JOIN 查询
  3. 对比不同 JOIN 类型的性能
  4. 选择最优方案
    实验过程:
    SQL
    -- 1. 创建测试表
    CREATE TABLE T_SMALL (
    ID INT PRIMARY KEY,
    COL1 VARCHAR(100)
    );

CREATE TABLE T_LARGE (
ID INT PRIMARY KEY,
T_SMALL_ID INT,
COL2 VARCHAR(100)
);

-- 2. 插入数据
INSERT INTO T_SMALL
SELECT LEVEL, RPAD('S', 100) FROM DUAL CONNECT BY LEVEL <= 1000;

INSERT INTO T_LARGE
SELECT LEVEL, MOD(LEVEL, 1000) + 1, RPAD('L', 100)
FROM DUAL CONNECT BY LEVEL <= 100000;

CREATE INDEX IDX_T_LARGE_T_SMALL_ID ON T_LARGE(T_SMALL_ID);

CALL DBMS_STATS.GATHER_TABLE_STATS('SYSDBA', 'T_SMALL');
CALL DBMS_STATS.GATHER_TABLE_STATS('SYSDBA', 'T_LARGE');

-- 3. 测试 1:NESTED LOOP(小表驱动大表)
EXPLAIN(ALL) SELECT /*+ USE_NL(S L) */ *
FROM T_SMALL S JOIN T_LARGE L ON S.ID = L.T_SMALL_ID;

-- 执行计划:
-- 1) TABLE SCAN (T_SMALL) -- 驱动表(小表)
-- 2) INDEX RANGE SCAN (IDX_T_LARGE_T_SMALL_ID) -- 被驱动表
-- COST: 500.00

SET TIMING ON;
SELECT /*+ USE_NL(S L) */ *
FROM T_SMALL S JOIN T_LARGE L ON S.ID = L.T_SMALL_ID;
-- 耗时:0.8 秒

-- 4. 测试 2:HASH JOIN(大表连接)
EXPLAIN(ALL) SELECT /*+ USE_HASH(S L) */ *
FROM T_SMALL S JOIN T_LARGE L ON S.ID = L.T_SMALL_ID;

-- 执行计划:
-- 1) HASH JOIN
-- 2) TABLE SCAN (T_SMALL)
-- 3) TABLE SCAN (T_LARGE)
-- COST: 800.00

SELECT /*+ USE_HASH(S L) */ *
FROM T_SMALL S JOIN T_LARGE L ON S.ID = L.T_SMALL_ID;
-- 耗时:1.5 秒

-- 5. 测试 3:MERGE JOIN(已排序数据)
EXPLAIN(ALL) SELECT /*+ USE_MERGE(S L) */ *
FROM T_SMALL S JOIN T_LARGE L ON S.ID = L.T_SMALL_ID;

-- 执行计划:
-- 1) MERGE JOIN
-- 2) INDEX FULL SCAN (T_SMALL)
-- 3) INDEX FULL SCAN (IDX_T_LARGE_T_SMALL_ID)
-- COST: 600.00

SELECT /*+ USE_MERGE(S L) */ *
FROM T_SMALL S JOIN T_LARGE L ON S.ID = L.T_SMALL_ID;
-- 耗时:1.0 秒
JOIN 类型选择原则:
JOIN 类型 适用场景 数据量要求 内存要求
NESTED LOOP 小表驱动大表,有索引 驱动表<1000 行 低
HASH JOIN 大表连接,无索引 两表都大 高(需要 hash 表)
MERGE JOIN 已排序数据,等值连接 任意 中
性能对比总结:
NESTED LOOP: 0.8 秒 ✅ 最优(小表驱动)
MERGE JOIN: 1.0 秒
HASH JOIN: 1.5 秒
优化建议:

  1. 小表驱动大表 → 使用 NESTED LOOP
  2. 大表连接且无索引 → 使用 HASH JOIN
  3. 数据已排序 → 使用 MERGE JOIN
  4. 确保连接列有索引
  5. 使用 LEADING Hint 指定驱动表顺序

三、性能对比总结
3.1 MySQL vs DM 优化能力对比
优化功能 MySQL 8.0 DM8 优势方
执行计划选项 基础 丰富(ALL/JSON/XML) DM
Hint 支持 有限 全面 DM
并行查询 有限 全面支持 DM
物化视图 不支持 全面支持 DM
查询重写 不支持 自动重写 DM
SQL 轮廓 不支持 支持 DM
增量统计 部分 全面 DM

四、核心收获与建议
4.1 核心收获

  1. 执行计划是优化基础
    ○ 学会解读 COST、CARD、ACCESS_TYPE
    ○ 识别全表扫描、文件排序等瓶颈
    ○ EXPLAIN(ALL) 提供最多信息
  2. 索引是性能关键
    ○ 正确设计索引(选择性、复合索引顺序)
    ○ 避免索引失效场景(函数、隐式转换)
    ○ 利用覆盖索引减少回表
  3. 绑定变量至关重要
    ○ 减少硬解析,降低 CPU 消耗
    ○ 执行计划可复用
    ○ 防止 SQL 注入
  4. SQL 改写威力大
    ○ OR 改 UNION ALL
    ○ NOT IN 改 NOT EXISTS
    ○ 子查询改 JOIN
  5. 高级优化技术
    ○ 并行查询加速大数据量操作
    ○ 物化视图预计算复杂查询
    ○ 查询重写自动优化
评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服