注册
DM性能分析常用方法与优化
技术分享/ 文章详情 /

DM性能分析常用方法与优化

DM_KSL 2026/08/07 154 0 0

DM性能分析常用方法与优化

一、 系统层面分析

(一) CPU性能分析

获取CPU性能指标,常用的命令是top,该命令可以实时查看CPU负载。
image.png

指标分类 关键指标 含义解读(结合数据库性能分析场景) 示例数据
系统基础 时间 当前系统时间,用于定位性能问题发生时刻 14:13:21
系统基础 负载平均值 过去 1/5/15 分钟系统平均负载,反映整体压力(如:CPU 核心数需警惕) load average: 0.15, 0.03, 0.01
CPU 核心 %CPU(核心占比) us(用户态,数据库进程常用)、%V(内核态,I/O 等消耗)、理想比例:us > %V > %V,持续高消耗系统调用过多 %CPU(5): 0.0, %V: 0.1, %V: 0.0
内存核心 总内存/可用内存 物理内存总量、已占用(含缓存)、实际可用/总可用内存(含缓存) Mem: 6690.0 total: 878.7 used: 5440.8 avail Mem: 0.0
交换空间 已用 Swap 虚拟内存使用量(过高可能导致数据库性能波动)、Swap 使用率 > 20% 会导致性能下降 Swap used: 0.0
进程关键 PID + 进程 ID 与名称(重点关注 dmesg 查看) PID 1561
进程关键 COMMAND 等数据库进程,单个进程 CPU 占用(如 dmesg 显示 0.3% 持续 > 80% 需排查 SQL 或索引问题) dmesg: 0.3% CPU: 0.0
进程关键 % CPU(进程) 进程内存占用(数据库进程内存不足会触发磁盘交换)持续增长可能预示内存泄漏 dmesg: 9.1% MEM: 0.0

说明:原表中的 %V 疑似笔误(可能为 %sy 或 %system),但按原文保留;PID + 和 COMMAND 均归类于“进程关键”分类,合并后顺序与图片一致。

sar -u 1 5 # CPU使用率统计
image.png
异常场景:
us > 60% → 用户进程消耗过高(需优化SQL/程序)
sy > 30% → 内核处理开销大(检查系统调用)
id < 30% + wa > 40% → CPU过载或I/O阻塞

(二) 内存性能分析

获取内存性能指标使用:vmstat 1 5 # 每秒采样,共5次
image.png
指标解读如下:
Procs
r:如果在 procs 中运行的序列 (processr) 是连续的大于在系统中的 CPU 的个数,表示 CPU 比较忙,系统现在运行比较慢,有多数的进程等待 CPU。如果 r 的输出数大于系统中可用CPU个数的4倍,则系统面临着CPU短缺的问题,或者是CPU的速率过低,系统中有多数的进程在等待 CPU,造成系统中进程运行过慢。
b:在procs中运行的序列(processb),即处于不可中断状态的进程数,如果连续为 CPU 的2~3倍,就表明 CPU 排队比较严重。
如果r连续大于CPU的个数甚至是CPU个数的几倍;b也持续有值,甚至是 CPU 的 2~3 倍,并且 id 也持续小于50%,wa 也比较小,这就表明 CPU 负荷很严重。
SYSTEM
in:每秒产生的中断次数。
cs:每秒产生的上下文切换次数。
in 和 cs 这两个值越大,由内核消耗的 CPU 时间会越大。
CPU
us:用户进程消耗的CPU时间百分比。us 的值比较高时,说明用户进程消耗的CPU时间多,在服务高峰期持续大于50~60是可以接受的范围,但是如果长期超过50%,就需要考虑优化程序算法。
sy:内核进程消耗的 CPU 时间百分比。sy 的值比较高时,说明系统内核消耗的 CPU 资源多,对于这种非良性表现需要检查原因。
wa:IO等待消耗的 CPU 时间百分比。wa的值比较高时说明 IO等待比较严重,可能是由于磁盘大量做随机访问造成的,也有可能是磁盘出现了瓶颈(块操作)。
id:CPU 处于空闲状态时间百分比,如果空闲时间(cpu id)持续为0并且系统时间 (cpu sy)是用户时间的两倍(cpu us)系统则面临着CPU资源的短缺,如果在服务高峰期持续小于50,是可以接受的范围。

(三) IO性能分析

获取IO性能指标使用:iostat -x 1 5 #查看I/O详情
image.png
结果解读:
rrqm/s:每秒进行 merge(多个 IO 的合并)读操作的数量。
wrqm/s:每秒进行 merge(多个 IO 的合并)写操作的数量。
rsec/s:每秒读取的扇区数。
wsec/s:每秒写入的扇区数。
rKB/s:每秒读多少k字节,在 kernel 2.4以上,rkB/s=2×rsec/s,因为一个扇区为512 bytes。
wKB/s:每秒写多少k字节,在 kernel 2.4以上,wkB/s=2×wsec/s,因为一个扇区为512 bytes。
avgrq-sz:平均请求扇区的大小。
avgqu-sz:是平均请求队列的长度,队列长度越短越好。
await:每一个 IO 请求的处理的平均时间(单位是微秒毫秒)。这里可以理解为 IO 的响应时间,一般地系统 IO 响应时间应该低于5ms,如果大于10ms 就比较大了。这个时间包括了队列时间和服务时间,一般情况下,await 大于svctm,它们的差值越小,则说明队列时间越短,反之差值越大,队列时间越长,说明系统出了问题。
svctm:表示平均每次设备 I/O 操作的服务时间(以毫秒为单位)。如果 svctm 的值与 await 很接近,表示几乎没有 I/O 等待,磁盘性能很好,如果 await 的值远高于 svctm的值,则表示 I/O 队列等待太长,系统上运行的应用程序将变慢。
%util:在统计时间内所有处理 IO 时间,除以总共统计时间,该参数表示设备的繁忙程度,如果该参数是100%表示设备已经接近满负荷运行了(如果是多磁盘,即使%util 是100%,因为磁盘的并发能力,所以磁盘使用不一定到了瓶颈)。
image.png

指标分类 关键指标 含义解读(结合数据库场景) 示例数据
CPU 关联 %iowait CPU 等待磁盘 I/O 完成的时间占比,高值说明磁盘慢 0.01%
磁盘设备 Device 磁盘设备名(如 sda 是系统盘/数据库盘需关注) scd0、nvme0n1
磁盘设备 tps 每秒向磁盘发起的 I/O 请求数(数据库读写压力) sda: 0.01
磁盘设备 kB_read/s 每秒从磁盘读取的数据量(读性能瓶颈参考) sda: 0.36KB/s

场景化分析:
•若%iowait 持续>10% + sda 的 tps 高,说明数据库所在磁盘 I/O 拥堵,可能导致 SQL 执行变慢(如查询需读磁盘、事务写落盘延迟);
•若kB_read/s 长期接近磁盘最大读速,需检查数据库是否有大量全表扫描(没走索引),或考虑升级存储(SSD 替换机械盘)。
核心用于快速判断“磁盘I/O是否拖慢数据库”,抓住磁盘读写请求、吞吐量、CPU 等待这几个核心点即可初步排查。
(四) 网络性能分析
网络性能对数据库也有很大的影响,数据库服务器和 Web 服务器之间会进行网络传输,网络延迟和带宽大小都是影响因素。
相关命令:ifconfig
ifconfig 是 linux 中用于显示或配置网络设备(网络接口卡)的命令。在某系统中输入此命令后显示结果如下所示:
ifconfig
image.png
ethtool ens160
image.png
结果解读:
Supported link modes 为网卡支持的连接模式。
speed 和 duplex 字段为当前网络速率和模式。
分析方法
使用 ping 命令测试网络的连通性和响应时间。
ping 发送 ICMP echo 数据包来探测网络的连通性,除了能直观地看出网络的连通状况外,还能获得本次连接的往返时间(RTT 时间),丢包情况,以及访问的域名所对应的 IP 地址(使用 DNS 域名解析)。
ping -c 4 baidu.com ##参数 -c 表示指定发包数。

二、 数据库层面分析

(一) 动态视图定位问题

活动会话监控:

SELECT COUNT(*) FROM V$SESSIONS WHERE STATE='ACTIVE';

慢SQL检索(>1秒):

SELECT sess_id, sql_text, datediff(ss,last_recv_time,sysdate) 
FROM V$SESSIONS WHERE STATE='ACTIVE' AND datediff(ss,last_recv_time,sysdate)>=1;

查看锁

SELECT O.NAME,L.* FROM V$LOCK L,SYSOBJECTS O WHERE L.TABLE_ID=O.ID AND BLOCKED=1;

查询阻塞

WITH LOCKS
   AS (SELECT O.NAME,L.*,S.SESS_ID,S.SQL_TEXT,S.CLNT_IP,S.LAST_SEND_TIME
         FROM V$LOCK L, SYSOBJECTS O, V$SESSIONS S
        WHERE L.TABLE_ID = O.ID AND L.TRX_ID = S.TRX_ID),
   LOCK_TR
   AS (SELECT TRX_ID WT_TRXID, TID BLK_TRXID
         FROM LOCKS
        WHERE BLOCKED = 1),
   RES
   AS (SELECT SYSDATE STATTIME,T1.NAME,T1.SESS_ID WT_SESSID,S.WT_TRXID,
              T2.SESS_ID BLK_SESSID,S.BLK_TRXID,T2.CLNT_IP,
              SF_GET_SESSION_SQL (T1.SESS_ID) FULSQL,
              DATEDIFF (SS, T1.LAST_SEND_TIME, SYSDATE) SS,
              T1.SQL_TEXT WT_SQL
         FROM LOCK_TR S, LOCKS T1, LOCKS T2
        WHERE     T1.LTYPE = 'OBJECT'
              AND T1.TABLE_ID &lt;> 0
              AND T2.LTYPE = 'OBJECT'
              AND T2.TABLE_ID &lt;> 0
              AND S.WT_TRXID = T1.TRX_ID
              AND S.BLK_TRXID = T2.TRX_ID)
SELECT DISTINCT WT_SQL,CLNT_IP,SS,WT_TRXID,BLK_TRXID
FROM RES;

(二) Sqllog跟踪日志

跟踪日志文件是一个纯文本文件,以 ‘dmsql_实例名_日期_时间命名’,默认生成在 DM 安装目录的 log 子目录下。跟踪日志内容包含系统各会话执行的 SQL 语句、参数信息、错误信息、执行时间等。跟踪日志主要用于分析错误和分析性能问题,基于跟踪日志可以对系统运行状态进行分析。跟踪日志配置方式如下:
配置 dm.ini 文件,设置 SVR_LOG = 1 以启用 sqllog.ini 配置,该参数为动态参数,可通过调用数据库函数直接修改。

SP_SET_PARA_VALUE(1,'SVR_LOG',1);

配置数据文件目录下的 sqllog.ini 文件。
image.png
如果对 sqllog.ini 进行了修改,可通过调用以下函数即时生效,无需重启数据库。

SP_REFRESH_SVR_LOG_CONFIG();

sqllog.ini 文件配置成功后可在 dmsql 指定目录下生成 dmsql 开头的 log 日志文件。日志内容如下所示:
image.png

三、 性能优化

(一) 执行计划

执行计划就是一条 SQL 语句在数据库中的执行过程或访问路径的描述。SQL 语言是种功能强大且非过程性的编程语言,比如以下这条 SQL 语句:

explain select * from sysdba.table1;

image.png

执行计划的每行即为一个计划节点,主要包含三部分信息。
第一部分NEST2、PRJT2、CSCN2为操作符及数据库具体执行了什么操作。
第二部分的三元组为该计划节点的执行代价,具体含义为[代价,记录行数,字节数]。
第三部分为操作符的补充信息。
例如:第三个计划节点表示操作符是CSCN2(即全表扫描),代价估算是1ms,扫描的记录行数是3行,输出字节数是64个。
各计划节点的执行顺序为:缩进越多的越先执行,同样缩进的上面的先执行,下面的后执行,上下的优先级高于内外。缩进最深的,最先执行;缩进深度相同的,先上后下。口诀:最右最上先执行。

(二) 查看执行计划

达梦数据库可通过两种方式查看执行计划。
方式一:通过 DM 数据库配套管理工具查看。
方式二:使用 explain 命令查看。
以下对两种查看方式进行介绍。

(1)管理工具查看执行计划

在 DM 配套管理工具中,选中待查看执行计划的 SQL 语句,点击工具栏中的按钮,或使用快捷键 F9,即可查看执行计划。
image.png

(2)使用 explain 命令查看执行计划

在待查看执行计划的 SQL 语句前加 explain 执行 SQL 语句即可查看预估的执行计划:
explain select * from sysdba.table1;-- 文本模式查看执行计划
image.png

(3)使用 disql 命令行查真实执行计划

set autotrace traceonly
select * from sysdba.table1;

image.png

重点关注 logical reads(逻辑读)和 physical reads(物理读)相应的指标值,并结合 rows processed 返回处理行数多少来分析。如果返回行数少(并且 bytes sent to client 总量不大),应尽可能减少 IO 开销,让执行计划选择正确的索引路径。Sort(disk)一般因排序( hash join 发生归并、order by、group by 场景)区内存不足,如果数据库服务器物理内存充足,可以适当上调排序区内存,尽量避免操作刷盘,否则会影响执行性能。

(三) 执行计划操作符

操作符 含义 常用场景
NSET 结果集收集 NSET 是用于结果集收集的操作符,一般是查询计划的顶层节点,优化工作中无需对该操作符过多关注,一般没有优化空间。
PRJT 投影 PRJT 是关系的【投影】(project)运算,用于选择表达式项的计算。广泛用于查询、排序、函数索引创建等。优化工作中无需对该操作符过多关注,一般没有优化空间。
SLCT 选择 SLCT 是关系的【选择】运算,用于查询条件的过滤。可比较返回结果集与代价估算中是否接近,如相差较大可考虑收集统计信息。若该过滤条件过滤性较好,可考虑在条件列增加索引。
AAGR 简单聚集 AAGR 用于没有 GROUP BY 的 COUNT、SUM、AGE、MAX、MIN 等聚集函数的计算。
FAGR 快速聚集 FAGR 用于没有过滤条件时,从表或索引快速获取 MAX、MIN、COUNT 值。
HASH HASH 分组聚集 HAGR 用于分组列没有索引只能走全表扫描的分组聚集,该示例中 C2 列没有创建索引。
SAGR 流分组聚集 SAGR 用于分组列是有序的情况下,可以使用流分组聚集,C1 列上已经创建了索引,SAGR2 性能优于 HAGR2。
BLKUP 二次扫描(回表) BLKUP 先使用二级索引索引定位 rowid,再根据表的主键、聚集索引、rowid 等信息获取数据行中其它列。
CSCN 全表扫描 CSCN2 是 CLUSTER INDEX SCAN 的缩写即通过聚集索引扫描全表,全表扫描是最简单的查询,如果没有选择谓词,或者没有索引可以利用,则系统一般只能做全表扫描。全表扫描 I/O 开销较大,在一个高并发的系统中应尽量避免全表扫描。
SSEK, CSEK, SSCN 索引扫描 SSEK2 是二级索引扫描即先扫描索引,再通过主键、聚集索引、rowid 等信息去扫描表。CSEK2 是聚集索引扫描只需要扫描索引,不需要扫描表,即无需 BLKUP 操作,如果 BLKUP 开销较大时,可考虑创建聚集索引。SSCN 是索引全扫描,不需要扫描表。
NEST LOOP 嵌套循环连接 嵌套循环连接是最基础的连接方式,将一张表(驱动表)的每一个值与另一张表(被驱动表)的所有值拼接,形成一个大结果集,再从大结果集中过滤出满足条件的行。驱动表的行为就是循环的次数,将在很大程度上影响执行效率。适用场景:驱动表有很好的过滤条件、表连接条件能使用索引、结果集比较小。
HASH JOIN 哈希连接 哈希连接是在没有索引或索引无法使用情况下大多数连接的处理方式。哈希连接使用关联列去重后结果集较小的表做成 HASH 表,另一张表的连接列在 HASH 后向 HASH 表进行匹配,这种情况下匹配速度极快,主要开销在于对连接表的全表扫描以及 HASH 运算。
MERGE JOIN 归并排序连接 归并排序连接需要两张表的连接列都有索引,对两张表扫描索引后按照索引顺序进行归并。

(四) 统计信息更新

统计信息主要是描述数据库中表和索引的大小数以及数据分布状况等的一类信息。比如:表的行数、块数、平均每行的大小、索引的高度、叶子节点数以及索引字段的行数等。
统计信息对于 CBO(基于代价的优化器)生成执行计划具有直接影响。例如在嵌套循环连接(链接)中需要选择小表作为驱动表,两个关联表哪个是小表完全取决于统计信息中记录的数据量信息。此外,访问一个表是否要走索引,关联查询能否采用其它关联方式等都是 CBO 基于统计信息确定的。因此,统计信息的准确是生成最优执行计划的必要前提。

1. 手动收集统计信息

–收集指定用户下所有表所有列的统计信息:

DBMS_STATS.GATHER_SCHEMA_STATS('username',100,TRUE,'FOR ALL COLUMNS SIZE AUTO');

–收集指定用户下所有索引的统计信息:

DBMS_STATS.GATHER_SCHEMA_STATS('usename',1.0,TRUE,'FOR ALL INDEXED SIZE AUTO');

–或 收集单个索引统计信息:

DBMS_STATS.GATHER_INDEX_STATS('username','IDX_T2_X');

–收集指定用户下某表统计信息:

DBMS_STATS.GATHER_TABLE_STATS('username','table_name',null,100,TRUE,'FOR ALL COLUMNS SIZE AUTO');

–收集某表某列的统计信息:

STAT 100 ON table_name(column_name);

统计信息收集过程中将对数据库性能造成一定影响,避免在业务高峰期收集统计信息。

2. 自动收集统计信息

–打开表数据量监控开关,参数值为 1 时监控所有表,2 时仅监控配置表

SP_SET_PARA_VALUE(1,'AUTO_STAT_OBJ',2);

–设置 SYSDBA.T 表数据变化率超过 15% 时触发自动更新统计信息

DBMS_STATS.SET_TABLE_PREFS('SYSDBA','T','STALE_PERCENT',15);

–配置自动收集统计信息触发时机

SP_CREATE_AUTO_STAT_TRIGGER(1, 1, 1, 1,'14:36', '2026/8/3',60,1);
/*
函数各参数介绍
SP_CREATE_AUTO_STAT_TRIGGER(
    TYPE                    INT,    --间隔类型,默认为天
    FREQ_INTERVAL         INT,    --间隔频率,默认 1
    FREQ_SUB_INTERVAL    INT,    --间隔频率,与 FREQ_INTERVAL 配合使用
    FREQ_MINUTE_INTERVAL INT,    --间隔分钟,默认为 1440
    STARTTIME              VARCHAR(128), --开始时间,默认为 22:00
    DURING_START_DATE    VARCHAR(128), --重复执行的起始时间,默认 1900/1/1
    MAX_RUN_DURATION    INT,    --允许的最长执行时间(秒),默认不限制
    ENABLE                  INT     --0 关闭,1 启用  --默认为 1
);
*/

查看统计信息
–用于经过 GATHER_TABLE_STATS、GATHER_INDEX_STATS 或 GATHER_SCHEMA_STATS 收集之后展示。

dbms_stats.table_stats_show(‘模式名’,’表名’);

–用于经过 GATHER_TABLE_STATS、GATHER_INDEX_STATS 或 GATHER_SCHEMA_STATS 收集之后展示。 返回两个结果集:一个是索引的统计信息;另一个是直方图的统计信息。

dbms_stats.index_stats_show(‘模式名’,’索引名’);

–用于经过 GATHER_TABLE_STATS、GATHER_INDEX_STATS 或GATHER_SCHEMA_STATS 收集之后展示。

dbms_stats.COLUMN_STATS_SHOW(‘模式名’,’表名’,’列名’);

更新统计信息
–更新已有统计信息

DBMS_STATS.UPDATE_ALL_STATS();

删除统计信息
–表

DBMS_STATS.DELETE_TABLE_STATS(‘模式名’,’表名’,’分区名’,...);

–模式

DBMS_STATS.DELETE_SCHMA_STATS('模式名','',',...);

–索引

DBMS_STATS.DELETE_INDEX_STATS(‘模式名’,’索引名’,’分区表名’,...);

–字段

DBMS_STATS.DELETE_COLUMN_STATS(‘模式名’,’表名’,’列名’,’分区表名’,...);

(五) hint优化管理

在不改动原SQL时,可使用ENABLE_RQ_TO_NONREF_SPL(3)参数优化,
相关说明如下:
•该参数用于将相关查询表达式转化为非相关查询表达式,使相关查询表达式的执行处理由之前的平坦化方式转化为一行处理。
•取值含义:
•0表示不启用该优化;
•1表示对查询项中出现的相关子查询表达式进行优化处理;
•2表示对查询项和WHERE表达式中出现的相关子查询表达式进行优化处理;
•4表示相关查询采用SPL方式去相关性后,可作为单表过滤条件。支持组合值,
•如3表示同时进行1和2的优化。

1. 使用方式

开启参数

SELECT SF_GET_PARA_VALUE(1, 'ENABLE_INJECT_HINT');
SP_SET_PARA_VALUE(1, ‘ENABLE_INJECT_HINT’, 1);

在优化参数的sql执行

SELECT /*+ ENABLE_RQ_TO_NONREF_SPL(3) */ 
    s.student_id,
    s.name,
    s.class_id,
    (SELECT AVG(grade) FROM student_courses sc WHERE sc.student_id = s.student_id) AS avg_grade,
    (SELECT COUNT(*) FROM student_courses sc WHERE sc.student_id = s.student_id AND grade >= 90) AS excellent_count,
    (SELECT c.course_name FROM courses c WHERE c.course_id = 
        (SELECT sc.course_id FROM student_courses sc 
         WHERE sc.student_id = s.student_id 
         ORDER BY sc.grade DESC LIMIT 1)
    ) AS best_course
FROM students s
WHERE s.department = '计算机'
AND s.class_id IN ('CLASS2', 'CLASS3', 'CLASS4');

2. hint注入方式

对指定SQL增加hint注入命令:
–启用hint注入功能

SP_SET_PARA_VALUE(1, 'ENABLE_INJECT_HINT', 1);

–注入优化hint

SF_INJECT_HINT('SELECT s.student_id, s.name, s.class_id, (SELECT AVG(grade) FROM student_courses sc', 
               'ENABLE_RQ_TO_NONREF_SPL(3)', 
               '学生成绩查询优化', 
               null, TRUE, TRUE);

–查询已注入的hint

SELECT * FROM SYSINJECTHINT WHERE NAME = ‘学生成绩查询优化’;

–通过SF_INJECT_HINT函数注入的优化规则已生效

--执行带注入HINT的SQL(无需修改原SQL)
SELECT 
    s.student_id,
    s.name,
    s.class_id,
    (SELECT AVG(grade) FROM student_courses sc WHERE sc.student_id = s.student_id) AS avg_grade,
    (SELECT COUNT(*) FROM student_courses sc WHERE sc.student_id = s.student_id AND grade >= 90) AS excellent_count,
    (SELECT c.course_name FROM courses c WHERE c.course_id = 
        (SELECT sc.course_id FROM student_courses sc 
         WHERE sc.student_id = s.student_id 
         ORDER BY sc.grade DESC LIMIT 1)
    ) AS best_course
FROM students s
WHERE s.department = ‘计算机’
AND s.class_id IN ('CLASS2', 'CLASS3', 'CLASS4');

3. 删除hint注入

--删除注入的HINT
SF_DEINJECT_HINT('学生成绩查询优化');
--确认HINT已被删除
SELECT * FROM SYSINJECTHINT WHERE NAME =’学生成绩查询优化’;

评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服