适用场景:压测期间定位长时间运行的 SQL、识别资源消耗和锁阻塞,并保存实际缓存计划用于优化前后对比。
不同 DM8 构建版本的视图字段、函数和权限可能不同,使用前先确认版本。以下以单实例排查为主;DSC、MPP 环境需按相应集群视图补充实例或节点信息。
监控连接中的会话级设置,不会自动作用于应用连接池中的其他连接。应在目标业务连接执行 SQL 前开启;已在执行的 SQL 不应假定能够补齐此前未采集的数据。
SELECT * FROM V$VERSION;
SELECT SESSID();
SELECT PARA_NAME, SESS_VALUE
FROM V$DM_INI
WHERE PARA_NAME IN ('ENABLE_MONITOR', 'MONITOR_TIME', 'MONITOR_SQL_EXEC');
将参数原值保存到本次压测记录。SESS_VALUE 表示当前连接看到的值;应用连接的会话级配置需在对应连接确认。
ALTER SESSION SET 'MONITOR_SQL_EXEC' = 1;
V$SQL_STAT 的资源统计依赖 ENABLE_MONITOR=1;操作符执行节点监控还需要开启 MONITOR_SQL_EXEC。如需 ET 时间分析,同时检查 MONITOR_TIME。
以下是参数未开启时的系统级设置示例,应由有权限的人员按本次压测方案执行,并记录原值:
-- 系统级设置:会影响实例监控配置
CALL SP_SET_PARA_VALUE(1, 'ENABLE_MONITOR', 1);
CALL SP_SET_PARA_VALUE(1, 'MONITOR_TIME', 1);
执行监控会增加额外开销,优先限定目标连接;优化前后对比应保持相同监控配置。参数及 ET 配置参考达梦官方:数据库优化。
SELECT SYSDATE AS SAMPLE_TIME, COUNT(*) AS ACTIVE_SESSION_COUNT
FROM V$SESSIONS
WHERE STATE = 'ACTIVE';
该值表示采样时处于 ACTIVE 状态的会话数量,不是总连接数,也不能直接作为正在消耗 CPU 的 SQL 数量。锁等待会话也可能处于活动状态,监控查询自身也可能计入。
建议在压测各并发阶段持续记录,并结合响应时间、吞吐量、错误率及服务器 CPU、磁盘指标判断。
SELECT S.SESS_ID,
S.SESS_SEQ,
S.TRX_ID,
S.STATE,
S.USER_NAME,
S.CLNT_IP,
S.SQL_TEXT,
SF_GET_SESSION_SQL(S.SESS_ID) AS FULL_SQL,
DATEDIFF(SS, S.LAST_RECV_TIME, SYSDATE) AS LAST_RECV_GAP_SEC,
'CALL SP_CLOSE_SESSION(' || S.SESS_ID || ');' AS CLOSE_SESSION_SQL
FROM V$SESSIONS S
WHERE S.STATE = 'ACTIVE'
AND DATEDIFF(SS, S.LAST_RECV_TIME, SYSDATE) >= 1
ORDER BY LAST_RECV_GAP_SEC DESC;
| 字段 | 用途与解释 |
|---|---|
SESS_ID / SESS_SEQ |
会话标识及序列号,后续关联资源统计时同时保留 |
TRX_ID |
关联事务、锁等待关系 |
USER_NAME / CLNT_IP |
定位业务账号与应用来源 |
SQL_TEXT |
快速识别 SQL,长文本可能截断 |
FULL_SQL |
会话最近处理的完整 SQL;不保证是最初持锁的语句 |
LAST_RECV_GAP_SEC |
最近接收消息到采样时间的秒数 |
CLOSE_SESSION_SQL |
仅生成关闭命令文本,此 SELECT 不会关闭连接 |
阈值为 大于等于 1 秒。原脚本的 Y_EXETIME 在此改为 LAST_RECV_GAP_SEC,避免误认为精确执行耗时。该间隔可能包含等待,不能解释为纯 CPU 时间。秒级 DATEDIFF 也不适合精确测量短 SQL。
动态视图会随执行变化,获取完整文本时会话可能已切换到下一条 SQL。定位后应尽快保存 SQL 和执行标识。
以下保留所有会话状态。示例目标为涉及 GTSHY_BH_JTJC 的计数 SQL,可替换为实际业务关键字。
SELECT SYSDATE AS SAMPLE_TIME,
S.SESS_ID,
S.SESS_SEQ,
S.STATE,
DATEDIFF(SS, S.LAST_RECV_TIME, SYSDATE) AS LAST_RECV_GAP_SEC,
Q.EXEC_ID,
Q.START_TIME,
Q.END_TIME,
Q.PLN_ADDR,
Q.LOGIC_READ_CNT,
Q.PHY_READ_CNT,
Q.RECYCLE_PHY_READ_CNT,
Q.IO_WAIT_TIME,
S.SQL_TEXT
FROM V$SESSIONS S
LEFT JOIN V$SQL_STAT Q
ON Q.SESSID = S.SESS_ID
AND Q.SESS_SEQ = S.SESS_SEQ
WHERE UPPER(S.SQL_TEXT) LIKE '%GTSHY!_BH!_JTJC%' ESCAPE '!'
AND UPPER(S.SQL_TEXT) NOT LIKE '%V$SESSIONS%';
关键词中的下划线已按字面值转义。匹配 SQL_TEXT 可能因文本截断漏查,应用存储过程内 SQL 也可能不直接显示在这里。定位会话后,建议将上述文本筛选替换为 WHERE S.SESS_ID = 实际会话ID AND S.SESS_SEQ = 实际序列号,继续采样。
保存两次完整结果及采样时间,必须对齐 SESS_ID + SESS_SEQ + EXEC_ID。不能将不同执行的累计值相减;若执行标识改变、计数回退或记录消失,应重新确认执行生命周期。
计数增量 = 第二次计数 - 第一次计数
每秒变化量 = 计数增量 / 两次实际采样间隔秒数
| 指标变化 | 排查方向 | 判断边界 |
|---|---|---|
LOGIC_READ_CNT 持续增加 |
大量扫描、重复关联、访问数据量大 | 不直接等于 CPU 使用量 |
PHY_READ_CNT 持续增加 |
数据物理读取 | 结合磁盘吞吐、延迟确认 I/O 瓶颈 |
IO_WAIT_TIME 持续增加 |
I/O 等待积累 | 单位为毫秒,不是 CPU 时间或锁等待时间 |
RECYCLE_PHY_READ_CNT 持续增加 |
临时表空间读取、中间结果落盘 | 结合排序、哈希连接及临时空间使用验证 |
| 所有指标基本不变 | 锁等待、其他等待、执行已结束或监控未更新 | 继续检查事务等待与执行标识 |
指标为 NULL 或无匹配行 |
未开启监控、关联无记录、记录生命周期或版本差异 | 不能据此认定没有资源消耗 |
逻辑读、物理读和临时空间读是访问次数类计数,不应直接当作字节量。字段含义及单位见达梦官方:动态性能视图。
对执行计划中“约 1313 万行扫描”等信息,应先确认其是否为优化器估算行数。仅凭估算不能断定它就是当前耗时来源,需结合资源增量和操作符实际统计。
例如 A 更新一行未提交,B 更新同一行,则 B 是等待方,A 是阻塞方。
SELECT W.*, T.SESS_ID AS WAIT_SESS_ID,
S.USER_NAME, S.STATE, S.CLNT_IP, S.SQL_TEXT
FROM V$TRXWAIT W
JOIN V$TRX T ON W.ID = T.ID
JOIN V$SESSIONS S ON T.SESS_ID = S.SESS_ID;
SELECT W.ID AS WAIT_TRX_ID,
W.WAIT_FOR_ID AS BLOCK_TRX_ID,
T.SESS_ID AS BLOCK_SESS_ID,
S.USER_NAME, S.STATE, S.CLNT_IP, S.SQL_TEXT
FROM V$TRXWAIT W
JOIN V$TRX T ON W.WAIT_FOR_ID = T.ID
JOIN V$SESSIONS S ON T.SESS_ID = S.SESS_ID;
SELECT SYSDATE AS SAMPLE_TIME,
W.ID AS WAIT_TRX_ID,
W.WAIT_FOR_ID AS BLOCK_TRX_ID,
WT.SESS_ID AS WAIT_SESS_ID,
WS.USER_NAME AS WAIT_USER,
WS.STATE AS WAIT_STATE,
WS.CLNT_IP AS WAIT_CLIENT_IP,
WS.SQL_TEXT AS WAIT_SQL,
BT.SESS_ID AS BLOCK_SESS_ID,
BS.USER_NAME AS BLOCK_USER,
BS.STATE AS BLOCK_STATE,
BS.CLNT_IP AS BLOCK_CLIENT_IP,
BS.SQL_TEXT AS BLOCK_LAST_SQL
FROM V$TRXWAIT W
LEFT JOIN V$TRX WT ON WT.ID = W.ID
LEFT JOIN V$SESSIONS WS ON WS.SESS_ID = WT.SESS_ID
LEFT JOIN V$TRX BT ON BT.ID = W.WAIT_FOR_ID
LEFT JOIN V$SESSIONS BS ON BS.SESS_ID = BT.SESS_ID;
此结果展示每条等待关系;一笔事务可能出现在多条关系中,不能直接用结果行数作为被阻塞会话数。动态取样期间事务可能结束,部分关联列为空时应重新采样确认。
原 V$LOCK 汇总脚本不作为通用主查询:同一事务可能对应多张表,标量子查询可能返回多行;ROW_IDX 的含义也需要结合锁类型确认。若需补查锁对象,可先保存相关事务的锁记录,再按版本文档解读:
-- 将 123456 替换为实际事务 ID
SELECT L.*, O.NAME AS OBJECT_NAME
FROM V$LOCK L
LEFT JOIN SYSOBJECTS O ON O.ID = L.TABLE_ID
WHERE L.TRX_ID = 123456;
对象名只作为锁对象线索,不直接说明该对象就是等待链源头。
SELECT SYSDATE AS SAMPLE_TIME,
S.SESS_ID, S.SESS_SEQ, S.TRX_ID,
S.USER_NAME, S.STATE, S.CLNT_IP,
DATEDIFF(SS, S.LAST_RECV_TIME, SYSDATE) AS LAST_RECV_GAP_SEC,
S.SQL_TEXT,
SF_GET_SESSION_SQL(S.SESS_ID) AS FULL_SQL,
'CALL SP_CLOSE_SESSION(' || S.SESS_ID || ');' AS CLOSE_SESSION_SQL
FROM V$SESSIONS S
WHERE EXISTS (
SELECT 1
FROM V$TRXWAIT W
WHERE W.WAIT_FOR_ID = S.TRX_ID
)
AND NOT EXISTS (
SELECT 1
FROM V$TRXWAIT W
WHERE W.ID = S.TRX_ID
)
ORDER BY LAST_RECV_GAP_SEC DESC;
含义:别人正在等它,但它没有出现在等待方列表中。对于 C 等 B,B 等 A,A 不再等其他事务,该查询定位 A。使用 NOT EXISTS 避免 NOT IN 遇到空值时的判断问题。
这里不筛选 STATE='ACTIVE',因此能发现空闲但未提交的阻塞源头。LAST_RECV_GAP_SEC 不是阻塞持续时间。循环等待可能没有这种源头,需结合完整等待关系继续排查;查询无结果不代表没有阻塞。
SELECT S.SESS_ID,
S.SESS_SEQ,
S.USER_NAME,
S.CLNT_IP,
S.SQL_TEXT,
S.CREATE_TIME AS SESSION_CREATE_TIME,
S.STATE,
S.TRX_ID,
T.STATUS AS TRX_STATUS,
T.INS_CNT,
T.UPD_CNT,
T.DEL_CNT,
T.UPD_INS_CNT
FROM V$SESSIONS S
JOIN V$TRX T ON S.TRX_ID = T.ID
WHERE S.STATE = 'IDLE'
AND T.STATUS = 'ACTIVE'
AND (T.INS_CNT > 0 OR T.UPD_CNT > 0
OR T.DEL_CNT > 0 OR T.UPD_INS_CNT > 0)
ORDER BY S.CREATE_TIME;
这是应用漏提交或漏回滚的重要线索:会话当前空闲,但事务尚未结束,且存在写操作计数。应与等待关系一起核对是否正在阻塞其他会话。
SESSION_CREATE_TIME 是连接创建时间,不能据此判断事务未提交多久。SQL_TEXT 是最近语句线索,不一定是最初获取锁的语句。UPD_INS_CNT 已删除。-- 替换为目标会话 ID
SELECT SF_GET_SESSION_SQL(139948995123864) AS FULL_SQL FROM DUAL;
-- disql 客户端显示设置;其他客户端需使用其对应的大字段显示配置
SET LONG 1000000
SELECT Q.SESSID,
Q.SESS_SEQ,
Q.EXEC_ID,
Q.START_TIME,
Q.END_TIME,
Q.PLN_ADDR
FROM V$SQL_STAT Q
WHERE Q.SESSID = 139948995123864;
先核对 SESS_SEQ、EXEC_ID 和 SQL 文本,避免取到该连接的其他执行。示例中的 ID 均须替换为现场值。
在支持 SF_TRACE_DUMP_PLN 的版本执行:
SELECT Q.SESSID,
Q.SESS_SEQ,
Q.EXEC_ID,
Q.PLN_ADDR,
SF_TRACE_DUMP_PLN(Q.PLN_ADDR) AS CACHED_PLAN
FROM V$SQL_STAT Q
WHERE Q.SESSID = 139948995123864;
若返回多条执行记录,按已记录的 SESS_SEQ、EXEC_ID 进一步限定。函数可用性需按实际构建版本验证;不存在时使用该版本支持的计划导出方式。地址可能因缓存淘汰或执行结束而失效,应在定位后尽快取证。
SET LONG 只调整当前客户端显示,不会开启监控或改变服务器执行计划。缓存计划展示计划结构,不能单独替代操作符实际耗时。
目标执行已开启相应监控且记录仍可用时:
-- 示例,替换为目标 SQL 的 EXEC_ID
CALL ET(123456);
重点查看耗时高的操作符及重复进入次数,结合扫描、关联、排序和临时空间读进行判断。未采集的执行不能仅靠事后开启监控还原节点统计。
以下操作会改变压测状态,应与常规采样分开记录。先保存证据,再确认目标及处置目的。
SELECT CACHE_ITEM,
SQLSTR,
'CALL SP_CLEAR_PLAN_CACHE(' || CACHE_ITEM || ');' AS CLEAR_SQL
FROM SYS.V$CACHEPLN
WHERE LOWER(SQLSTR) LIKE '%sscpfm!_task%' ESCAPE '!';
该 SELECT 只生成命令文本。!_ 配合 ESCAPE '!' 按字面匹配下划线;若直接写 %sscpfm_task%,其中 _ 会匹配任意单字符。仍需核对完整 SQL,避免仅因文本包含同名片段而误选。
确认目标后执行实际生成的命令:
-- 示例 ID,必须替换为现场确认的 CACHE_ITEM
CALL SP_CLEAR_PLAN_CACHE(123456789);
此操作清除指定缓存计划,后续需要重新获取计划时会重新解析或优化;不应把它作为终止当前运行 SQL 的方法。比较冷缓存与热缓存结果时,应分别标记;清缓存引起的额外解析可能影响压测结果。
-- 示例 ID,必须替换为现场确认的会话 ID
CALL SP_CLOSE_SESSION(139948995123864);
关闭前再次核对会话序列号、账号、客户端和事务,避免会话状态已变化。关闭会话会中断该连接,未提交事务需要回滚,锁释放可能需要等待回滚完成。优先由业务连接正常提交或回滚;长事务回滚期间继续观察资源与阻塞变化。
函数和过程语义参考达梦官方:系统函数与系统过程附录。11. 收尾检查
| 现场现象 | 优先执行 |
|---|---|
| 压测响应突然变慢 | 第 3、4 节:活动会话数量与长间隔会话 |
| 单条 COUNT 长时间不返回 | 第 5 节:同一次执行的两次资源采样 |
| 活动很多但资源增量小 | 第 6 节:事务等待与阻塞源头 |
| 源头会话显示 IDLE | 第 7 节:空闲未结束写事务 |
| 逻辑读、物理读或临时空间读持续增长 | 第 8 节:缓存计划与 ET,并结合系统指标 |
| 修改优化策略后仍复用旧计划 | 第 9.1 节:核对并按需清理指定计划缓存 |
| 必须终止异常连接 | 第 9.2 节:确认目标、保存证据并观察回滚 |
文章
阅读量
获赞
