达梦数据库性能优化实战指南
达梦数据库性能优化实战指南
一、核心优化框架
优化优先级从高到低,优先解决“投入少、见效快”的问题,避免盲目调优:
-
SQL 优化:最易落地,通常能解决80%的性能问题(如慢SQL、全表扫描);
-
统计信息优化:CBO(基于代价的优化器)生成最优执行计划的核心,统计信息过期会导致执行计划跑偏;
-
索引优化:合理建索引、避免失效,减少查询开销;
-
参数优化:适配硬件(CPU、内存)和业务场景(OLTP/OLAP),微调即可见效;
-
架构优化:底层支撑,针对高并发、大数据量场景(如分区表、读写分离)。
二、第一步:快速定位性能问题
核心目标:精准找到“拖慢系统”的慢SQL、无效索引或参数瓶颈,不做无用功。
2.1 3种方式定位慢SQL(直接复制可用)
方式1:查询正在执行的慢SQL(实时排查)
-- 执行时间>1秒的活跃SQL(可调整阈值,单位:毫秒)
SELECT
SESS_ID 会话ID,
USER_NAME 执行用户,
SF_GET_SESSION_SQL(SESS_ID) AS SQL_TEXT 完整SQL,
EXEC_TIME 执行时间(ms),
STATE 会话状态
FROM V$SESSIONS
WHERE STATE = 'ACTIVE' AND EXEC_TIME > 1000;
方式2:查询历史长执行SQL(事后排查)
-- 执行时间>5秒的历史SQL,按执行时间降序排列
SELECT
SQL_TEXT 完整SQL,
EXEC_TIME 执行时间(ms),
START_TIME 执行开始时间,
USER_NAME 执行用户,
CLIENT_IP 客户端IP
FROM V$LONG_EXEC_SQLS
WHERE EXEC_TIME > 5000
ORDER BY EXEC_TIME DESC;
方式3:开启SQL日志(持久化记录,适合长期监控)
-
修改
sqllog.ini配置(找到数据库安装目录下的 log 文件夹):-
MIN_EXEC_TIME = 2000 (仅记录执行时间>2秒的SQL,可调整);
-
LOG_TYPE = 1 (记录完整SQL文本);
-
FILE_NAME = dmsql_实例名 (日志文件名,自动按日期拆分)。
-
-
日志路径:
../log/dmsql_实例名_日期_时间.log,用记事本或DMLOG工具打开即可分析。
2.2 执行计划分析(核心:找瓶颈操作符)
拿到慢SQL后,第一步看执行计划,判断是“全表扫描”“无效索引”还是“排序开销大”。
实操代码(直接复制)
-- 1. 预估执行计划(快速查看,无实际执行)
EXPLAIN SELECT * FROM T_ORDER WHERE ORDER_DATE > '2026-01-01';
-- 2. 真实执行计划(含实际耗时、行数,精准分析)
SET AUTOTRACE TRACEONLY; -- 只显示执行计划,不返回结果集
SELECT * FROM T_ORDER WHERE ORDER_DATE > '2026-01-01';
关键操作符速查
| 操作符 | 含义 | 常见问题 | 快速优化建议 |
|---|---|---|---|
| CSCN | 全表扫描 | 查询无索引,或索引失效,扫描全表耗时久 | 1. 给过滤列建索引;2. 若返回数据>20%,索引无效,改SQL过滤条件 |
| SSEK/CSEK | 索引扫描(顺序/并发) | 正常,但可能存在“回表次数过多”(索引未覆盖查询列) | 创建覆盖索引(包含查询所需所有列),避免回表 |
| HASH JOIN | 哈希连接(多表关联常用) | 内存不足时,哈希表溢出到磁盘,速度骤降 | 调大 HJ_BUF_SIZE 参数,或拆分大表关联 |
| NEST LOOP | 嵌套循环连接 | 大表驱动小表时,效率极低 | 换 HASH JOIN,或调整表关联顺序(小表驱动大表) |
| SORT | 排序操作 | 不必要的 ORDER BY/GROUP BY,消耗CPU和内存 | 删除无用排序,或给排序列建索引(避免手动排序) |
三、高频优化手段
每个优化点都配套「业务案例+问题SQL+优化方案+优化效果」,方便直接参考落地。
3.1 SQL 优化(最易见效,重点)
核心:避免全表扫描、无用排序、低效关联,改写SQL逻辑,不改动底层架构。
案例1:WHERE 优先过滤,替代 HAVING 过滤
业务场景
某电商订单表 T_ORDER(100万行数据),查询2026年1月的订单平均金额,按订单类型分组,仅统计“实物订单”。
问题SQL(执行时间:8.6秒,执行计划含 CSCN 全表扫描)
-- 优化前:先分组,再过滤,全表扫描所有数据
SELECT
ORDER_TYPE 订单类型,
AVG(ORDER_AMOUNT) 平均金额
FROM T_ORDER
GROUP BY ORDER_TYPE
HAVING ORDER_DATE > '2026-01-01' AND ORDER_TYPE = '实物订单';
优化方案
先通过 WHERE 过滤时间和订单类型,减少分组的数据量,避免全表扫描。
优化后SQL(执行时间:0.3秒,执行计划含 SSEK 索引扫描)
SELECT
ORDER_TYPE 订单类型,
AVG(ORDER_AMOUNT) 平均金额
FROM T_ORDER
WHERE ORDER_DATE > '2026-01-01' AND ORDER_TYPE = '实物订单'
GROUP BY ORDER_TYPE;
优化关键点
HAVING 是“分组后过滤”,会扫描所有数据再分组;WHERE 是“分组前过滤”,提前筛选数据,减少分组开销。
案例2:EXISTS 替代 IN/DISTINCT,优化关联去重
业务场景
用户表 T_USER(50万行)、订单表 T_ORDER(100万行),查询“有订单记录的用户ID”,需去重。
问题SQL(执行时间:6.2秒,执行计划含 SORT 排序去重)
-- 优化前:DISTINCT 去重,多表关联后排序,开销大
SELECT DISTINCT U.USER_ID
FROM T_USER U, T_ORDER O
WHERE U.USER_ID = O.USER_ID;
优化方案
用 EXISTS 替代 IN/DISTINCT,无需排序去重,仅判断“存在关联记录”即可,效率更高。
优化后SQL(执行时间:0.5秒,无排序操作)
SELECT U.USER_ID
FROM T_USER U
WHERE EXISTS (
SELECT 1 FROM T_ORDER O
WHERE O.USER_ID = U.USER_ID
);
优化关键点
IN 会先查询子查询结果,再匹配主表,若子查询结果量大,效率极低;EXISTS 是“半连接”,只要子查询有匹配记录,就返回主表数据,无需全量匹配。
案例3:UNION ALL 替代 UNION,避免无用排序
业务场景
查询“2026年1月订单”和“2026年2月订单”的用户ID,无重复数据(业务保证)。
问题SQL(执行时间:4.8秒,执行计划含 SORT 排序去重)
-- 优化前:UNION 自动去重,会对结果集排序,耗时久
SELECT USER_ID FROM T_ORDER WHERE ORDER_DATE BETWEEN '2026-01-01' AND '2026-01-31'
UNION
SELECT USER_ID FROM T_ORDER WHERE ORDER_DATE BETWEEN '2026-02-01' AND '2026-02-28';
优化方案
业务已保证无重复数据,用 UNION ALL 替代 UNION,取消排序去重操作。
优化后SQL(执行时间:0.2秒,无排序操作)
SELECT USER_ID FROM T_ORDER WHERE ORDER_DATE BETWEEN '2026-01-01' AND '2026-01-31'
UNION ALL
SELECT USER_ID FROM T_ORDER WHERE ORDER_DATE BETWEEN '2026-02-01' AND '2026-02-28';
优化关键点
UNION 的核心是“去重+合并”,会触发排序操作;UNION ALL 仅“合并”,不排序,适合无重复数据的场景,性能提升80%以上。
案例4:避免函数操作,防止索引失效
业务场景
T_ORDER 表已给 ORDER_DATE 列建索引,查询“2026年1月的订单”,用函数截取日期。
问题SQL(执行时间:7.3秒,执行计划含 CSCN 全表扫描,索引失效)
-- 优化前:条件列用函数,索引失效,全表扫描
SELECT * FROM T_ORDER
WHERE SUBSTR(ORDER_DATE, 1, 7) = '2026-01';
优化方案
改写 SQL,避免在条件列上使用函数,让索引生效。
优化后SQL(执行时间:0.1秒,执行计划含 SSEK 索引扫描)
-- 优化后:条件列无函数,索引生效
SELECT * FROM T_ORDER
WHERE ORDER_DATE BETWEEN '2026-01-01' AND '2026-01-31 23:59:59';
优化关键点
达梦数据库中,条件列使用函数(如 SUBSTR、TO_DATE)会导致索引失效,优先改写条件,让索引正常触发。
3.2 索引优化(核心:建对、用对,不滥用)
索引是“查询加速器”,但建多了会影响 DML(插入/更新/删除)性能,核心是“精准建索引”。
核心建索引原则(必记)
-
✅ 高频查询、排序、分组的列优先建(如 WHERE 过滤列、ORDER BY 列);
-
✅ 组合索引遵循「最左匹配原则」(如建索引 (A,B,C),仅查 B+C 则索引失效);
-
✅ 唯一索引优先于普通索引(区分度高,查询效率更高);
-
❌ 避免过多索引(一张表索引不超过5个,DML操作会频繁维护索引,拖慢性能);
-
❌ 避免在函数/计算列上建索引(易失效);
-
❌ 避免给数据区分度低的列建索引(如性别、状态列,返回数据>20%,索引无效)。
案例:覆盖索引优化,避免回表开销
业务场景
T_ORDER 表(100万行),常用查询:根据 ORDER_DATE 过滤,查询 USER_ID、ORDER_AMOUNT、ORDER_STATUS 三个字段,已给 ORDER_DATE 建普通索引。
问题SQL(执行时间:1.8秒,执行计划含 SSEK+回表操作)
SELECT USER_ID, ORDER_AMOUNT, ORDER_STATUS
FROM T_ORDER
WHERE ORDER_DATE > '2026-01-01';
问题分析
普通索引仅包含 ORDER_DATE 列,查询时先通过索引找到行地址,再回表查询 USER_ID、ORDER_AMOUNT 等字段,回表次数多,耗时久。
优化方案
创建覆盖索引,将查询所需的所有列(过滤列+查询列)都包含在索引中,避免回表。
-- 创建覆盖索引(ORDER_DATE 为过滤列,其他为查询列)
CREATE INDEX IDX_ORDER_DATE_COVER ON T_ORDER (ORDER_DATE, USER_ID, ORDER_AMOUNT, ORDER_STATUS);
优化效果
执行时间从1.8秒降至0.15秒,执行计划无回表操作,直接通过索引获取所有所需数据。
索引失效快速排查清单(直接对照)
-
条件列使用函数/计算(如 SUBSTR(ORDER_DATE,1,7) = '2026-01');
-
组合索引未使用首列(如索引 (A,B),查询 WHERE B=10);
-
查询返回数据量>20%(索引失效,自动走全表扫描);
-
使用 !=、NOT IN、IS NULL/IS NOT NULL(易导致索引失效,优先用其他方式替代);
-
统计信息过期(索引被CBO忽略,需重新收集统计信息)。
3.3 统计信息优化
统计信息是CBO生成最优执行计划的基础,若统计信息过期(如数据大量插入/删除后未更新),CBO会选择错误的执行计划(如明明有索引,却走全表扫描)。
案例:统计信息过期导致的慢SQL优化
业务场景
T_ORDER 表新增50万行数据(总数据量150万),未更新统计信息,执行之前优化过的SQL,执行时间从0.3秒骤升至9.2秒。
问题排查
查看执行计划,发现原本的 SSEK 索引扫描,变成了 CSCN 全表扫描,原因是统计信息未更新,CBO认为“全表扫描比索引扫描更快”。
优化方案(重新收集统计信息)
-- 1. 手动收集单表+索引统计信息(业务低峰期执行,避免影响业务)
DBMS_STATS.GATHER_TABLE_STATS(
OWNNAME => 'SYSDBA', -- 用户名
TABNAME => 'T_ORDER', -- 表名
ESTIMATE_PERCENT => 100, -- 100%采样,精准度最高
CASCADE => TRUE -- 同时收集索引统计信息
);
-- 2. 配置自动收集(推荐,避免手动遗漏)
-- 开启表数据量监控
SP_SET_PARA_VALUE(1, 'AUTO_STAT_OBJ', 2);
-- 设置触发阈值:数据变化超15%,自动更新统计信息
DBMS_STATS.SET_TABLE_PREFS('SYSDBA', 'T_ORDER', 'STALE_PERCENT', 15);
优化效果
执行时间恢复至0.3秒,执行计划重新触发 SSEK 索引扫描,CBO选择正确的执行计划。
常用统计信息操作(直接复制)
-- 1. 查看表统计信息(判断是否过期)
DBMS_STATS.TABLE_STATS_SHOW('SYSDBA', 'T_ORDER');
-- 2. 查看索引统计信息
DBMS_STATS.INDEX_STATS_SHOW('SYSDBA', 'IDX_ORDER_DATE_COVER');
-- 3. 收集整个用户下的所有表统计信息
DBMS_STATS.GATHER_SCHEMA_STATS('SYSDBA', 100, TRUE);
-- 4. 删除过期统计信息(谨慎使用)
DBMS_STATS.DELETE_TABLE_STATS('SYSDBA', 'T_ORDER');
3.4 关键参数优化(适配硬件,微调见效)
参数优化无需盲目调大,核心是“适配硬件(CPU、内存)和业务场景(OLTP/OLAP)”,以下是最常用的关键参数,配套案例说明。
核心参数速查表(含推荐值)
| 参数名 | 核心作用 | OLTP(高并发交易)推荐值 | OLAP(海量数据分析)推荐值 | 修改方式 |
|---|---|---|---|---|
| BUFFER | 数据缓冲区,缓存数据块,减少磁盘IO | 物理内存的 2/3(数据量>内存时) | 物理内存的 3/4(分析场景需更多缓存) | 动态修改(立即生效) |
| WORKER_THREADS | 工作线程数,处理用户请求 | CPU 核数或 2 倍(高并发场景) | CPU 核数(分析场景无需过多线程) | 动态修改(立即生效) |
| HJ_BUF_SIZE | HASH 连接缓存,影响多表关联效率 | 512M ~ 1G | 1G ~ 4G(关联场景多,需更大缓存) | 会话级/动态修改 |
| ENABLE_MONITOR | 性能监控开关,影响监控数据采集 | 优化时设 3,运行时设 2 | 优化时设 3,运行时设 2 | 动态修改(立即生效) |
| RECYCLE | 临时表、排序缓冲区,减少磁盘排序 | 2G ~ 4G(高并发排序场景) | 4G ~ 8G(分析场景排序多) | 静态修改(需重启数据库) |
案例:调整 WORKER_THREADS 解决高并发卡顿
业务场景
某电商系统,数据库服务器 CPU 为 16 核,高峰时段并发请求达 500+,系统卡顿,查询响应时间超 3 秒,排查发现 WORKER_THREADS 默认为 8。
问题分析
WORKER_THREADS 为 8,无法处理 500+ 并发请求,导致请求排队,响应变慢。
优化方案(动态修改,立即生效)
-- 修改 WORKER_THREADS 为 32(16核CPU的2倍,适配高并发)
SP_SET_PARA_VALUE(1, 'WORKER_THREADS', 32);
-- 查看修改后的值
SELECT SF_GET_PARA_VALUE(1, 'WORKER_THREADS');
优化效果
高峰时段响应时间从 3+ 秒降至 0.5 秒内,并发请求无排队,系统流畅。
参数修改注意事项
-
动态参数(如 WORKER_THREADS、BUFFER):修改后立即生效,无需重启数据库;
-
静态参数(如 RECYCLE):修改后需重启数据库生效,建议在业务低峰期操作;
-
会话级参数(如 HJ_BUF_SIZE):仅对当前会话生效,适合临时调试,重启后失效。
3.5 架构优化(按需选择,底层支撑)
架构优化适合“高并发、大数据量”场景,无需盲目搭建,根据业务需求选择即可。
| 架构类型 | 核心优势 | 适用场景 | 实战案例 |
|---|---|---|---|
| 读写分离(DMRWC) | 读请求自动分流到备库,主库专注写操作,提升并发能力 | 读多写少(如OA系统、报表查询、电商商品查询) | 某OA系统,读请求占比85%,搭建读写分离后,主库CPU使用率从80%降至30%,查询响应时间缩短60% |
| 数据守护(DMDataWatch) | 实时数据同步,故障秒级切换,保障高可用,支持灾备 | 核心业务(如金融交易、电商订单),不允许停机 | 某银行核心系统,搭建双机守护,主库故障时,1秒切换至备库,无数据丢失,业务无感知 |
| 分区表 | 按时间/范围拆分大表,减少查询扫描范围,支持分区级操作(删除/ truncate) | 单表数据量>1000万(如订单表、日志表) | 某电商订单表(5000万行),按月份分区,查询2026年1月订单,扫描范围从5000万行降至400万行,执行时间从12秒降至0.8秒 |
| 列存储表(HUGE) | 按列存储,压缩率高(可达10:1),适合海量数据分析,查询效率提升明显 | OLAP场景(如数据仓库、报表分析、用户行为分析) | 某数据仓库,用户行为表(1亿行),改为列存储后,存储占用从500G降至50G,分析查询时间从30秒降至3秒 |
四、应急优化流程(5步搞定,快速止血)
适用于“系统卡顿、慢SQL爆发”的紧急场景,按步骤操作,快速恢复系统性能。
-
定位慢SQL(1分钟):用 V$LONG_EXEC_SQLS 或 SQL 日志,筛选出执行时间>5秒的SQL,按执行时间降序排列,优先处理Top10;
-
分析执行计划(2分钟):对慢SQL执行 EXPLAIN,查看是否有 CSCN(全表扫描)、SORT(无用排序)、HASH JOIN 溢出,定位瓶颈;
-
紧急优化(5分钟):
-
若全表扫描:临时建索引(优先覆盖索引);
-
若索引失效:改写SQL,避免函数操作,或重新收集统计信息;
-
若排序/关联开销大:改写SQL(如 UNION ALL 替代 UNION、EXISTS 替代 IN);
-
若内存不足:临时调大 HJ_BUF_SIZE、BUFFER 参数。
-
-
验证效果(2分钟):执行优化后的SQL,查看执行时间是否下降,执行计划是否优化;
-
后续优化(非紧急):清理无效索引、优化表结构(如分区表)、配置自动统计信息,避免问题复发。
五、日常运维 Checklist(避免问题复发)
5.1 DBA 日常运维(每日/每周/每月)
-
每日:查询 V$LONG_EXEC_SQLS,清理执行超5秒的慢SQL,记录优化情况;
-
每周:收集核心表(订单表、用户表)的统计信息,检查无效索引(无使用记录的索引),清理冗余索引;
-
每月:调整参数适配业务变化(如并发提升、数据量增长),检查分区表数据分布,清理过期分区数据。
5.2 开发人员规范(从源头避免性能问题)
-
SQL 编写规范:
-
避免 SELECT *,明确指定所需列(减少数据传输,便于建覆盖索引);
-
优先用 EXISTS 替代 IN/DISTINCT,UNION ALL 替代 UNION;
-
WHERE 过滤优先于 HAVING,减少分组数据量;
-
避免在条件列上使用函数,避免 !=、NOT IN 等易导致索引失效的语法;
-
批量操作(如批量插入/更新)使用 FORALL 或 BATCH 方式,减少事务提交次数。
-
-
索引设计规范:
-
高频查询列、过滤列优先建索引,组合索引遵循最左匹配;
-
新增索引前,先查看该列的查询频率,避免盲目建索引;
-
定期检查索引使用情况,清理无使用记录的无效索引。
-
-
表结构设计规范:
-
单表数据量超1000万,考虑分区表(按时间/业务维度分区);
-
复杂查询(多表关联、统计分析)使用临时表存储中间结果,减少重复计算;
-
OLTP 场景用行存储表,OLAP 场景用列存储表,适配业务需求。
-
六、总结
达梦数据库性能优化,核心是“先定位、后优化、再落地”:
-
优先解决慢SQL,通过执行计划找瓶颈,SQL改写和索引优化是最易见效的手段;
-
统计信息是基础,一定要定期收集,避免CBO选择错误的执行计划;
-
参数优化按需微调,适配硬件和业务,不盲目调大;
-
架构优化适合大数据量、高并发场景,按需选择,不过度设计;
-
日常运维是关键,定期排查、规范开发,才能从源头避免性能问题。
浙公网安备 33010602011771号