注册
【#达梦数据库 #达梦同行者征文】《 DMLens_让 DM9 的 AWR 说真话:八处口径陷阱、BIC-QA 在线印证与一条命令跑完的锁争用闭环(下)》
专栏/技术分享/ 文章详情 /

【#达梦数据库 #达梦同行者征文】《 DMLens_让 DM9 的 AWR 说真话:八处口径陷阱、BIC-QA 在线印证与一条命令跑完的锁争用闭环(下)》

尚雷 2026/09/18 362 0 0
摘要

《 DMLens_让 DM9 的 AWR 说真话:八处口径陷阱、BIC-QA 在线印证与一条命令跑完的锁争用闭环(下)》

📚 系列连载(下篇):原稿因超官网单篇 40000 字词上限拆分为上 / 中 / 下三篇。上篇=写在前面+第 1–2 章+步骤 1–4;中篇=步骤 5–9+时序+第 4 章;本篇=第 5 章八处口径陷阱+第 6 章数据总表+第 7 章结论+第 8 章复测取证+附录 A–D。三篇同源于一台 DM9 实例的真实复测,截图与命令一致。

本篇目录

第 5 章 AWR 口径陷阱详解(八处 + 单位陷阱)

规则引擎若"直接算",会产出互相矛盾的结论。以下八处是实测归纳(汇总见图 2),每处都给出反例数据与正确写法。

fig2_awr_pitfalls.png

图 2 达梦 AWR 八处口径陷阱对照

5.1 缓冲池命中率报出「0.80%」

  • 现象:NORMAL 缓冲池命中率被算成 0.80,判为"近乎失效"。

  • 根因:V$BUFFERPOOL.RAT_HIT 是 0~1 的比例值,不是百分数;且该视图按子池展开(实测 NORMAL 池 17 个子池行、RECYCLE 池 11 行),必须按 NAME 去重。

  • 正确写法:内部按小数比较,展示时 ×100。可用原始计数验证:

    RAT_HIT = 1 − N_PHY_READS / (N_LOGIC_READS + N_PHY_READS)
    

    实测 FAST 池:1 − 77 / (580340 + 77) = 0.99986734,与该池报告值 0.9998673367 精确吻合。

  • 口径提醒:V$BUFFERPOOL 是实例级累计(cumulative),回答的是"自启动以来",不是"刚才那 8 秒"。

SELECT NAME, COUNT(*) AS SUBPOOLS, MAX(RAT_HIT) AS RAT_HIT, SUM(N_LOGIC_READS) AS LOGIC_READS, SUM(N_PHY_READS) AS PHY_READS FROM V$BUFFERPOOL GROUP BY NAME;

5.2 表空间使用率报出「99.2% 告急」

  • 根因:V$TABLESPACE 的 MAX_SIZE 实测全部为 0。若实现"退化"用 TOTAL_SIZE(当前文件大小)当上限,就会把"文件内已用 / 文件当前大小"当成容量使用率,随文件增长无限逼近 100%。
  • 正确写法:上限基数改取 DBA_DATA_FILES.MAXBYTES(唯一无歧义的字节口径)。实测 SYSAUX:当前文件 BYTES = 536870912(512 MB),上限 MAXBYTES = 17179869184(16 GB),真实用量约 462 MiB,容量使用率约 2.8%。
  • 报告应并列两个比例,避免混淆:
指标 算法 SYSAUX 实测
文件内占比 USED_SIZE / TOTAL_SIZE 90.1%(“文件当前有多满”)
容量使用率 已用字节 / MAXBYTES 2.8%(“离上限还有多远”)
SELECT TABLESPACE_NAME, ROUND(SUM(BYTES)/1024/1024, 1) AS FILE_MB_ALLOCATED, ROUND(SUM(MAXBYTES)/1024/1024, 1) AS LIMIT_MB FROM DBA_DATA_FILES GROUP BY TABLESPACE_NAME ORDER BY TABLESPACE_NAME;

5.3 硬解析占比报出「145.8%」

  • 根因:hard parse count 与 parse count 不构成包含关系。两条实测证据:

    观测区间 hard parse count 增量 parse count 增量 比值
    本实验区间(诊断报告登记) 978 686 1.43(hard 大于 parse)
    前置探针区间 982 9443 0.10(hard 小于 parse)

    同一实例、不同区间,比值时而 1.43、时而 0.10 —— 该比值没有稳定的物理含义。此外,达梦原生 AWR 报告中的 Soft Parse % 恒为 0.00,与"按计数相减约 49.6%"自相矛盾。

  • 正确写法:弃用"硬/软相除",改用与原生报告一致的**“解析耗时占 DB Time 比”**:

    解析耗时占 DB Time 比 = Δ(parse time(ms)) / Δ(DB time(ms))
    

    本实验实测:811 / 1460 = 55.5%(诊断报告 R05 的一致性输出)。

    第二次复测补强:独立区间 49 → 50 再现同样倒挂(997/774、898/816),且被 BIC-QA 在线分析独立指出——
    详见 5.10 节(含 18 行原始差分回显的证据图)。

SELECT SNAP_ID, NAME, STAT_VAL FROM SYS.WRH$_SYSSTAT WHERE NAME IN ('DB time(ms)','parse time(ms)','hard parse time(ms)','parse count','hard parse count') AND SNAP_ID IN (21, 22) ORDER BY NAME, SNAP_ID;

5.4 锁等待次数「很高」(其实是采集噪声)

  • 根因:transaction lock waits 的区间增量恒为 390,与区间跨时(29 / 195 / 214 / 2 / 8 秒)无关 → 属采集/结转噪声。而真正代表"等了多久"的 trx lock wait time(ms) 常为 0。
  • 注意区分:本实验中 R02 报出的 trxid lock wait(21.712 秒)才是真实的等待时间——它是通过等待事件视图(WRH$_SYSTEM_EVENT)在区间差分后得到的,与计数类的 transaction lock waits 不是一回事。
  • 正确写法:判定改用三个互不依赖的证据源:① trx lock wait time(ms) 区间增量;② 实时 V$TRXWAIT 行数;③ WRH$_LOCK.BLOCKED <> 0 行数。计数噪声降级为 info。
SELECT SNAP_ID, NAME, STAT_VAL FROM SYS.WRH$_SYSSTAT WHERE NAME IN ('transaction lock waits','trx lock wait time(ms)') AND SNAP_ID IN (21, 22) ORDER BY NAME, SNAP_ID; SELECT COUNT(*) AS WAITING_TRX FROM V$TRXWAIT;

5.5 CPU 占比报出「0%」

  • 根因:CPU time(us) 是惰性计数器——快照 5→6(跨时 2 秒)仅 +15 us;1→6(跨时 442 秒)才 +2832793 us。短区间可能完全不推进。

  • 正确写法:当区间跨时 < 30 秒或增量未推进时,不作占比判定,只登记负载量,并明确写"本项不计分(不制造误判)"。本实验跨时 7.57 秒,因此性能维度输出为:

    区间跨时 7.57s 过短(< 30s),惰性计数器 CPU time 不具可比性,本项不计分(Δ=18.061ms 仅供参考)

5.6 等待 TOP 是「network idle wait」

  • 根因:空闲等待(客户端不发请求时的空转)会计入累计等待时间,但不代表数据库瓶颈。实测该事件累计 1.851 秒 / 908 次,曾居榜首。
  • 正确写法:建立空闲等待识别(事件名含 idle、wait_class = Idle、客户端收发等待类),从 TOP 事件中先排除再排序。本实验报告明确标注"已排除 1 类空闲等待",修正后的 TOP 才是真实的 trxid lock wait。
SELECT E1.EVENT, E1.WAIT_CLASS, E1.TOTAL_WAITS - E0.TOTAL_WAITS AS WAITS, E1.TIME_WAITED_MICRO - E0.TIME_WAITED_MICRO AS US FROM SYS.WRH$_SYSTEM_EVENT E1 JOIN SYS.WRH$_SYSTEM_EVENT E0 ON E1.EVENT# = E0.EVENT# WHERE E1.SNAP_ID = 22 AND E0.SNAP_ID = 21 ORDER BY US DESC; -- 结果里再按 WAIT_CLASS / 事件名剔除 Idle 类,才得到真实 TOP

5.7 报告「检测到死锁」,而区间内并未发生

  • 根因:WRH$_DEADLOCK_HISTORY 会把历史死锁结转进后续每一个快照(实测同一条 TRX_ID=28295 在 SNAP_ID 9/10/11 各出现一次);且 transaction deadlock count 是高水位(自变为 1 后每个快照恒为 1)。

  • 正确写法:区间判据只能用增量 Δ(transaction deadlock count);历史表按 (TRX_ID, SEQNO) 去重,作为"历史痕迹"降级为 low。本实验输出:

    [低] DML-R04 历史存在死锁痕迹(非本区间,已消退):区间增量 0,去重后历史 1 条(结转副本已剔除)

第二次复测把这一坑"演活"了:区间 49 → 50 内 transaction deadlock count 3 → 4(Δ=1,落点 22:26:15),
而死锁历史表累计 4 条(22:26:15 / 22:21:17 / 22:05:53 / 14:59:56)——只有 1 条落在本区间。
若把"报告里有死锁"读成"本区间发生了死锁",前后结论相差 3 条。原始回显见证据图 15。

-- 死锁历史(注意:会结转,需按 (TRX_ID, SEQNO) 去重) SELECT SNAP_ID, SEQNO, TRX_ID, SESS_ID, HAPPEN_TIME FROM SYS.WRH$_DEADLOCK_HISTORY ORDER BY SNAP_ID, SEQNO; -- 计数指标:只有做差才有意义 SELECT SNAP_ID, STAT_VAL FROM SYS.WRH$_SYSSTAT WHERE NAME = 'transaction deadlock count' ORDER BY SNAP_ID;

📷 截图位 15|死锁历史旁证("结转"与"落点"同框)
复测命令:.\py.cmd run.py sessions(回显末段"死锁历史")或执行上方证据 SQL ⑥
截什么:死锁历史 N 条 与每条 SEQNO / SESS_ID / HAPPEN_TIME / SQL —— 能看出"多数不在本次区间"
存成:figures/shots/shot-15-deadlock-history.png
插入位置:本段下方

shot15deadlockhistory.png

复测证据图 15 第二次复测的 run.py sessions 回显:阻塞链 0 条 + 死锁历史 4 条(SEQNO-3 22:26:15 / SEQNO-2 22:21:17 / SEQNO-1 22:05:53 / SEQNO-0 14:59:56)——与区间 49 → 50(22:24:51 → 22:28:42)对照,仅 SEQNO-3 一条落在本区间;若把"报告里有死锁"读成"本区间发生了死锁",就踩了 5.7 节的坑。同时可见锁汇总里 lock_types: ["OBJECT","TID"] 与 waiting_trx: 0——现场确已干净

5.8 区间跨时算错:把「快照采集窗口」一起算进去

  • 现象:同一区间(SNAP_ID 21 → 22)的跨时被算成 9.0 秒,而达梦原生 AWR 报告里 Elapsed 写的是 0.12 分钟 = 7.57 秒 —— 两者相差 1.43 秒(相对误差 19%)。对 7 秒级的短区间而言,这已足以让"每秒指标"整体失真。

  • 根因(两处叠加,缺一不可):

    1. 取错端点。WRM$_SNAPSHOT 每条记录都有 BEGIN_INTERVAL_TIME 与 END_INTERVAL_TIME,两者相差约 0.9 秒(快照自身的采集窗口)。原生 AWR 报告的 Snap Time 展示的是 END_INTERVAL_TIME,其 Elapsed 亦按 END(end) − END(begin) 计算。若写成 END(end) − BEGIN(begin),就把起始快照的采集窗口一并算了进来。
    2. 精度被 CAST 抹掉。CAST(x AS DATE) 在达梦会把 DATETIME 降精度到秒并四舍五入 —— 实测把真实的 8.46 秒变成 9.0 秒。
  • 实测三种写法对照(同一区间 21→22):

写法 实测结果 与原生报告 Elapsed 是否一致
CAST(END₂₂ AS DATE) − CAST(BEGIN₂₁ AS DATE) × 86400 9.0 s × 虚增
END₂₂ − BEGIN₂₁ × 86400 8.459 s × 虚增
END₂₂ − END₂₁ × 86400 7.57 s √ 与 Elapsed = 0.12 min 吻合
  • 正确写法:直接对原生 DATETIME 相减(得到"天"的小数)再 × 86400 转秒;不要先 CAST 成 DATE:
SELECT ROUND(((SELECT END_INTERVAL_TIME FROM SYS.WRM$_SNAPSHOT WHERE SNAP_ID = 22) - (SELECT END_INTERVAL_TIME FROM SYS.WRM$_SNAPSHOT WHERE SNAP_ID = 21)) * 86400, 2) AS SPAN_SEC FROM DUAL; -- 等价的毫秒写法(任选其一,语义相同): -- SELECT DATEDIFF(MILLISECOND, -- (SELECT END_INTERVAL_TIME FROM SYS.WRM$_SNAPSHOT WHERE SNAP_ID = 21), -- (SELECT END_INTERVAL_TIME FROM SYS.WRM$_SNAPSHOT WHERE SNAP_ID = 22)) / 1000.0 AS SPAN_SEC -- FROM DUAL;
  • 列类型旁证:WRM$_SNAPSHOT.BEGIN_INTERVAL_TIME / END_INTERVAL_TIME 的类型是 DATETIME(不是 DATE)——CAST(... AS DATE) 属于主动降精度,是这处误差的直接来源。
  • 口径提醒:报告的 Elapsed 只覆盖"两个快照点之间"的墙钟时间,不含任一快照自身的采集耗时。用它去除"区间增量"得到的每秒指标,才能与 Load Profile 对齐;这也是本节把它作为基准的原因。
  • 留在本次留痕里的"物证":本文留痕目录中的诊断报告(output/e2e_20260914_164409/DMLens_diagnosis60914_164420.md)生成于修正之前,因此其中仍写作"区间跨时约 9.0 s";修正后对同一区间复算即为 7.57 s。读者按第 8 章复测,取得的将是 7.57 s——这处差异本身就是本节结论的现场证据,而非文档疏漏。

5.9 单位陷阱:把"页"当"字节"

来源 列 单位 实测样例
WRH$_TABLESPACE / V$TABLESPACE TOTAL_SIZE / USED_SIZE / MAX_SIZE 页 SYSAUX TOTAL_SIZE = 32768 页 → 512 MB
V$DATAFILE TOTAL_SIZE / FREE_SIZE 页 ——
V$DATAFILE MAX_SIZE MB ——
DBA_DATA_FILES BYTES / MAXBYTES 字节 SYSAUX BYTES = 536870912(512 MB)

同一张视图里 TOTAL_SIZE 是页、MAX_SIZE 却是 MB —— 这是最容易踩的一处。正确写法:

  1. 统一到字节口径,再按 MB / GB / TB 人类可读化;
  2. 页 → 字节:字节数 = 页数 × PAGE_SIZE,页大小取自 SELECT DISTINCT PAGE FROM V$PARAMETER;;
  3. 上限只用 DBA_DATA_FILES.MAXBYTES;
  4. 外推设阈值保护:days_to_full 超过 36500 天(100 年) 一律输出"不适用"——因为部分表空间的默认上限极大(实测 MAIN / ROLL / SYSTEM / TEMP 的 MAXBYTES 均为 35184371040256 字节 ≈ 32 TB),照此线性外推会得到"还能用几万年"的荒谬结论。

5.10 复测新发现:解析计数器会"打架"(两次独立区间交叉确认)

本节是第二次复测(区间 49 → 50)新增的一手发现。它不是新陷阱,而是 5.3 节的加强证据——
同一组计数器,在两个完全独立的区间里以同样的方式"倒挂",且被 BIC-QA 在线分析独立指出,与规则口径层交叉验证。

现象(三个"不可能"的数字,全部来自快照侧原始差分,见证据图 13):

观测项 第一次复测(21 → 22) 第二次复测(49 → 50)
Δ hard parse count vs Δ parse count 978 > 686(硬解析增量大于总解析增量) 997 > 774(同样的倒挂再次出现)
Δ hard parse time(ms) vs Δ parse time(ms) 887 > 811 898 > 816
原生报告 Soft Parse % 恒为 0.00 恒为 0.00

“部分(硬解析)的增量大于整体(解析)的增量”,在数学上只有两种解释:两个计数器不是同源包含关系,或计数推进本身不同步(惰性 / 分桶 / 结转)。无论哪一种,结论都一样:

  • Δ hard parse count / Δ parse count 不可相除——第一次比值 1.43、前置探针 0.10、第二次 1.29,没有任何稳定物理含义;
  • 两计数器不可互推——知道硬解析增量,推不出总解析增量,反之亦然;
  • Soft Parse % = 0.00 是口径产物,绝不等于"软解析为零"(BIC-QA 在第二次分析正文里原话:“hard parse time (898 ms) 大于 parse time (816 ms) → Soft Parse 0.00% 是口径产物,不等于软解析为零”)。

在线分析还独立指出了另外两处"数字打架"(与本文规则口径层结论一致,见证据图 7):

在线分析指出的矛盾 本文的口径结论
Top 5 表中 trxid lock wait 平均耗时 169,438 ms 与明细 16,409.9 ms 不一致 以明细差分为准(Top 表按排序截取后均值的呈现口径不同)
Load Profile 的 DB CPU 列与 DB Time 列对不上 报告内不同分区采样窗口不同,跨分区相除前先对齐口径(5.5 节 / 5.8 节)

正确写法(不变,且两次复测后更加确定):

解析开销占比 = Δ(parse time(ms)) / Δ(DB time(ms))     ← 分母同源、分子同源

值得玩味的一组对照:第一次区间(7.57 s,DB Time 仅 1,460 ms)里解析占比高达 55.5%;
第二次区间(231.13 s,DB Time 165,555 ms,几乎全被锁等待占据)里解析占比只有 0.49%。
同一个数据库、同样的解析计数器,"解析开销是否严重"完全取决于分母——
这就是为什么本文坚持"每个数字都标注口径与分母",也是为什么只看报告里的百分比而不看原始差分会误判根因。

证据 SQL(SNAP_ID 换成你自己的两个快照即可,第二次复测即 (49, 50),18 行原始回显见证据图 13):

SELECT SNAP_ID, NAME, STAT_VAL FROM SYS.WRH$_SYSSTAT WHERE NAME IN ('DB time(ms)','parse time(ms)','hard parse time(ms)','parse count','hard parse count', 'CPU time(us)','transaction lock waits','trx lock wait time(ms)','transaction deadlock count') AND SNAP_ID IN (49, 50) ORDER BY NAME, SNAP_ID;

第 6 章 实验数据总表

本章给出四次独立运行的对照:6.0 为第一次一键 e2e 受控执行;6.1 为当晚按第 8 章逐命令执行的第二次复测;6.2 为 2026-09-15 晨的第三 / 四次受控执行(在线 / 离线两种知识通道,补拍证据图取证)。
三次的 SNAP_ID、事务号、会话 ID、条数全部不同——判据是结构一致,这正是"可复现"的实证书。

6.0 第一次运行(e2e_20260914_164409,一键受控执行)

一次完整运行(e2e_20260914_164409)的全部关键数据:

类别 指标 实测值
实例 版本 / 构建 DM Database Server x64 V9 / 03151060506-…
实例 状态 / 模式 / 页大小 OPEN / NORMAL / 16384
区间 AWR 区间 / 跨时 21 → 22 / 7.57 s
争用 根事务 / 根会话 / 根用户 56100 / 140359434824088 / SYSDBA
争用 链上阻塞会话数 8
锁汇总 锁行 / 被阻塞锁 / 等待事务 / 等待中 110 / 4 / 6 / 4
诊断 健康评分 88.98 / 100(良好)
诊断 发现 7 条(严重 1 / 高 2 / 中 2 / 低 1 / 提示 1)
诊断 最重发现 DML-R13 阻塞链,最长链阻塞 8 个会话
诊断 等待 TOP trxid lock wait 21.712 s(类 = Transaction)
诊断 解析开销 解析耗时 811 ms / DB Time 1460 ms = 55.5%
知识 规则诊断通道 / 在线·降级条目 offline(Key 失效)/ 0 · 6
知识 AWR → BIC-QA 官方在线分析 附件 dm_awr_21_22.html(294,225 B)→ online,正文 4,897 字,2,701,275 tokens / 174.3 s(另有 1 个 file 事件,即 BIC-QA 生成的 HTML 报告)
知识 回流条目 5 条
处置 动作 / 目标 SP_CLOSE_SESSION / SESS_ID=140359434824088
处置 结果 ok(放行 1 / 拦截 0)
复测 阻塞链 / 等待会话 1 → 0 / 8 → 0
复测 判定 / 归因 healed / 是
复测 独立旁证 4/4 等待事务成功获锁
复测 旁路复核 V$TRXWAIT 行数 = 0
完整性 审计哈希链 ok = true(已校验 56 条)
收尾 残余阻塞链 0
效率 全流程耗时 约 14 秒

6.1 第二次复测(区间 49 → 50,2026-09-14 晚,按第 8 章逐命令执行)

类别 指标 实测值
区间 AWR 区间 / 跨时 49 → 50 / 231.13 s(3.85 分钟)(Snap 49 = 22:24:51.562900 → Snap 50 = 22:28:42.586140)
争用 实时现场 V$TRXWAIT 7 行等待关系(最长链跨 5 个事务);WRH$_LOCK 被阻塞锁 91 行(快照侧留存口径,见证据图 3 判读)
争用 四场景锁实验 两轮 locklab --scenario all 全部 ok=True / 收尾清零=True;热点行轮根事务阻塞 8 个会话;死锁轮实测 [CODE:-6403] 与 [CODE:-608] 并现
争用 死锁落点 transaction deadlock count 3 → 4(Δ=1),落点 22:26:15;WRH$_DEADLOCK_HISTORY 累计 4 条,仅 1 条在本区间(5.7 节 结转语义的现场证据,见证据图 15)
等待 TOP(区间差分) trxid lock wait 10 次 / 164.099 s(单次平均 ≈16.41 s,占 DB Time ≈99.1%)
等待 空闲类(须剔除) network idle wait 261 次 / 572.040 s——累计值大于 DB Time 本身即为"空闲类"信号(5.6 节)
差分 DB time Δ +165,555 ms,AAS ≈ 0.72
差分 CPU time Δ +19,782,172 μs ≈ 19.8 s(主机空闲率 99%+,CPU 非瓶颈)
差分 解析 parse count **+774 / hard parse count +997(倒挂再现);parse time +816 ms / hard parse time +898 ms;解析占 DB Time 仅 0.49%(与第一次的 55.5% 对照,见 5.10 节)
差分 锁计数 transaction lock waits +400(1.73/秒);trx lock wait time Δ = 0(计数噪声 vs 真实等待,5.4 节)
知识 在线分析 两轮 online:不带提问 tokens = 99,053 / 124.8 s;--question "重点看锁与解析开销" tokens = 1,609,954 / 150.9 s(均 √ 在线分析成功)
处置 受控执行 auto --execute(双闸门临时放开)→ 阻塞链 0 → no_action_nothing_to_do,执行动作 0 个,不可归因(归因红线现场证据,见证据图 9b)
完整性 审计哈希链 ok = true(已校验 94 条,head 0d4a34a9…82dd6)
收尾 清理复核 repro_cleanup.py:扫描出残留实验会话并逐一 SP_CLOSE_SESSION,复核 阻塞链 0,结论"现场已干净"(见证据图 16)
效率 证据图 本次复测落位 15 张(其余 6 张由 2026-09-15 第三 / 四次运行补齐,见 6.2 与 8.4 表 A——全文 21 张现已全部为真实执行截图)

6.2 第三次 / 第四次受控执行(2026-09-15 晨,补拍取证 + 一次真实的时间竞态)

第三次运行暴露了一个计划外的真实现象:知识通道为 online 时,诊断的逐条在线印证耗时约 7 分钟,
超过了 60 秒的争用保持窗口——流水线进入处置阶段前,等待方已全部 -608 超时退出、阻塞链自行消失,
工具如实输出 no_action_nothing_to_do(执行动作 0 个,不可归因)。这正好是 5.4 节"计数噪声"与
步骤 8"归因红线"在真实时序下的联合现场:在线印证有价值但也有延迟成本,处置时效应与知识深度需要权衡。
第四次以离线通道(DMLENS_BICQA_API_KEY=none 如实降级)复跑,约 13 秒完成全流程,完整复现 healed 闭环。

指标 第三次(e2e_20260915_053212) 第四次(e2e_20260915_054616)
知识通道 online(逐条在线印证,诊断耗时 ≈7 分钟) offline(Key=none 如实降级,诊断 ≈1 秒)
AWR 区间 68 → 69(05:32:08 → 05:32:16) 70 → 71(05:46:20 → 05:46:25)
争用现场 根 trx=71651,阻塞 8 个会话 根 trx=72604,阻塞 8 个会话
规则诊断 健康 94.81/100(优秀),发现 8 条 健康 88.79/100(良好),发现 8 条
新增发现 —— DML-R07 缓冲池命中率 94.69%(NORMAL,低于 95% 阈值——5.1 节小数口径的直接应用)
受控处置 链已自行消失 → no_action_nothing_to_do,执行动作 0 个,不可归因 SP_CLOSE_SESSION(140359790075568) → ok,放行 1 / 拦截 0
复测判定 阻塞链 0 → 0;no_action_nothing_to_do(不冒领) 阻塞链 1 → 0;healed(归因本工具 = 是)
独立旁证 0/4(链早已消失,等待方均已超时退出) 4/4 等待事务成功获锁
审计哈希链 ok = true(已校验 104 条) ok = true(已校验 110 条)
收尾 残余阻塞链 0 条 残余阻塞链 0 条
证据图产出 原生 AWR 报告(1857 行)、诊断首屏(图 5)、WRH$_SYSSTAT 复核 图 9a / 11a / 11b / 12(healed 判定与一键复测全流程)

四次运行对照一句话:同一套实验设计,在 offline / online 两种知识通道、快与慢两种时序下各跑两遍,
结论层全部一致(口径方法、归因红线、闭环结构),变值层全部如实(健康分、SNAP_ID、通道、判定随现场变化)——
这正是 8.5 节"看结构、不抠数字"判读方法的最佳注脚。

📷 截图位 13|区间差分旁证:WRH$_SYSSTAT 双快照原始值(18 行)
复测命令:disql 执行 5.10 节 末尾的差分 SQL(SNAP_ID IN (&lt;B>, &lt;E>))
截什么:9 个指标 × 2 个快照的原始值——本文所有"增量"结论的原始依据
存成:figures/shots/shot-13-sysstat-diff.png
插入位置:本表下方

shot13sysstatdiff.png

复测证据图 13 第二次复测的事后差分旁证:CPU time(us) 119,843,377 → 139,625,549、DB time(ms) 2,024,871 → 2,190,426、hard parse count 48,068 → 49,065、parse count 52,441 → 53,215、transaction deadlock count 3 → 4——5.10 节"倒挂"与死锁 Δ=1 的原始数字都出自这 18 行

📷 截图位 14|区间等待事件差分旁证(TOP 与空闲类同框)
复测命令:disql 执行附录 B 的证据 SQL ⑤(SNAP_ID 换为 &lt;B>/&lt;E>)
截什么:trxid lock wait 与 network idle wait 两行(对比"真实等待"与"空闲等待"的量级)
存成:figures/shots/shot-14-event-diff.png
插入位置:紧随证据图 13

shot14eventdiff.png

复测证据图 14 第二次复测的等待事件差分(16 个事件全量):trxid lock wait(Transaction 类)10 次 / 164.099 s 是唯一真实瓶颈;network idle wait(Network 类)261 次 / 572.040 s 累计值大于 DB Time,属空闲等待、先剔除再排序(5.6 节)


第 7 章 结论与建议

7.1 三个结论

  1. 口径立住,AWR 才可用。同一份 AWR 数据,口径不对会产出"缓冲池命中率 0.80%"“表空间告急 99.2%”“硬解析占比 145.8%”"锁等待很高"四条互相矛盾的误报;口径立住后,本次实验只剩下真实发现(1 条阻塞链 + 相关等待)。
  2. 知识与证据必须互相锚定。规则给出"锁等待 21.712 秒、阻塞链 1 条"的证据,知识库给出"未提交事务持锁是最常见根因、用 V$TRXWAIT 还原链路"的经验。缺了知识,规则只报现象;缺了证据,知识只是泛泛之谈。
  3. 自动化必须可归因。同一条阻塞链,干跑时"1 → 1"、链自行消失时"1 → 0 但不归因"、受控执行时"1 → 0 且归因"。敢把"不是我修的"说出来,才是能上生产的自动化。

7.2 五条建议

面向 建议
DBA 引用任何 AWR 指标前先问三句:区间还是累计?页还是字节?会不会结转?
DBA 建监控时"先排除空闲等待再排序",否则排行榜永远被 network idle wait 占据
DBA 判断锁问题优先看等待时间与阻塞链,不要被计数的"高数字"误导
工具作者 任何"自动处置"都要有双闸门 + 白名单 + 身份复核 + 哈希链审计,并设归因约束
所有人 集成第三方 API 时不要只看 HTTP 状态码,一定要校验载荷内容(本次即命中"伪成功")

7.3 局限与后续

  • 本实验在单机集中式实例上完成;集群形态下的全局等待与缓存一致性等待需要另行建模;
  • 知识印证通道的在线可用性受 Key 状态与配额影响:写本文期间同一实例先后经历了 degraded(配额耗尽)→ offline(Key 失效)→ online(换用有效 Key 后在线分析成功,见步骤 5.3) 三种状态(详见 1.4 节 与步骤 5.5 的三态实测);生产部署建议同时配置在线与内置图谱两条腿,并把降级写进报告而不是静默切换;
  • 达梦本构建无语句级取消,闭环目前只能到会话级终止。若后续版本提供更细粒度手段,白名单可相应扩展。

7.4 给 DM9 的五条产品建议(用户视角)

以上结论落到产品层面,笔者作为一线使用者,想提出五条具体、可落地的建议。
它们都来自本次实验中真实踩到的坑,而非泛泛而谈:

# 建议 触发场景(本文实测) 期望
1 原生 AWR 报告首屏直接给出“区间跨时”及其计算依据 5.8 节:报告首屏只有起止时间戳,需自行差分;笔者初版实现取了 BEGIN₁→END₂,与真实跨时差出数十秒 头部直接打印 Elapsed 的取字段口径(哪个减哪个),避免二次计算歧义
2 缓冲池命中率区分“子池明细”与“实例总体” 5.1 节:V$BUFFERPOOL 按子池返回十余行,直接对 RAT_HIT 求和会得到 0.80% 这类荒谬值 报告同时给出实例总体命中率与子池明细,并标注聚合方法(按逻辑读加权,而非简单求和)
3 为“惰性计数器”与“高水位”指标增加语义标记 5.6 / 5.7 节:CPU time(us) 在短区间完全不推进、WRH$_DEADLOCK_HISTORY 记录跨快照结转,都极易被误当实时量或区间增量 在数据字典中提供计数器语义列(累计 / 惰性 / 高水位 / 瞬时),让工具与 DBA 不必靠经验猜
4 提供更细粒度的会话处置手段 步骤 7:当前只能会话级终止(SP_CLOSE_SESSION),无法只取消单条语句 提供语句级取消能力,自治闭环才能从“杀会话”进化到“影响面最小”
5 Open API 的错误载荷规范化 1.4 / 步骤 5:配额耗尽时接口仍返回 HTTP 200 与正常 SSE 帧序,错误信息只出现在正文里 以业务错误码 + 结构化字段表达异常,降低集成方的“伪成功”风险

这五条的共性只有一句:让“正确的用法”成为最容易的用法。
口径信息、计数器语义、错误状态,如果只能靠经验或试错获得,那么每一位新上手的 DBA 都要把同样的坑重新踩一遍——
这正是本文想替后来者省下的成本。


第 8 章 复测取证:按单操作,取齐全部证据图

本章的作用:把前面九个步骤落成一次可执行、可截图的复验。你不需要再做任何判断——按命令跑、按 8.4 节的表 A 插图即可。
配套文档《04_DMLens复测取证单》是本章的操作版(含三套 shell 的命令块、逐条预期回显、排障表与收尾),两文配合使用:本文看"为什么",取证单看"怎么做"。

8.1 三条前置约定

① 工作目录——以下所有命令的当前目录都是工程根(含 run.py 的那个目录):

cd "C:/Users/达梦数据库征文/DMLens"

② Python 解释器——必须用已安装 dmPython 的那一个。工程根目录已内置包装脚本
py.cmd / py.sh,内部把解释器锁死了,所以你不需要设任何变量:

终端 固定用法
PowerShell / CMD(Windows) .\py.cmd &lt;参数>(例如 .\py.cmd run.py check)
Git Bash / Linux ./py.sh &lt;参数>(例如 ./py.sh run.py check)

为什么不用变量:本文早期版本用过「先 $env:PY="…"、再 & $env:PY run.py …」的写法,
实测证明它只在"同一个窗口里先赋过值"时才成立——新开窗口、或只复制了第二行,就会各自报出:
InvalidOperation:管道元素中的"&"后面的表达式生成无效的对象、
ParserError:表达式或语句中包含意外的标记"tools/…"。
变量只活在当前窗口,脚本活在工程里——所以统一用 .\py.cmd / ./py.sh,命令中不出现任何变量。
完整对照与原始报错见《04 复测取证单》2.2 节。

不要换成裸 python:本机系统 python 未装 dmPython,直接执行报
ModuleNotFoundError: No module named 'dmPython';先 python -c "import dmPython; print('ok')" 验证,有输出才可替代。
py.cmd 的三级兜底逻辑(受管环境 → PATH 上第一个带驱动的解释器 → 中文报错并退出码 2)见 2.2 节,可自行打开查看与修改。

必须注意:不要在脚本里直接 import dmPython——须先 import dmlens.dmdb(它在导入时会注入驱动自带的 dmssl 搜索路径),否则连接时会报 [CODE:-70089] 加密模块加载失败(报错发生在连接时而非导入时,极易误判为数据库故障)。

③ 截图统一存放——全部存入 docs/figures/shots/,文件名严格照 8.4 节表 A;这样正文中的图片占位可被直接覆盖生效,无需再改文档。

8.2 第一步:复测前自检(只读,约 5 秒)

.\py.cmd tools\repro_preflight.py

判定:四组全 √ 才能走满全流程。若"BIC-QA 在线通道"为 ×,其余步骤仍可复测,但步骤 5 拿不到在线分析正文(工具会如实降级为内置图谱,并写进报告,不会伪装成在线)。
该输出本身即"证据图 1"的素材(第二次复测已按此落图,见 2.2 节末)。

8.3 第二步:一键复测(推荐路径)

# ① 干跑:完整走九步,但不动实例 .\py.cmd run.py e2e --waiters 4 --hold 30 # ② 受控执行:在双闸门放行时真实处置根锁会话 # 保护名单必须写成 NONE(非空占位),不能留空——原因见下方说明 $env:DMLENS_SAFETY_AUTO_EXECUTE = "1" $env:DMLENS_SAFETY_DENY_USERS = "NONE" .\py.cmd run.py e2e --execute --waiters 4 --hold 60 # ③ 把本次区间的 AWR 报告交给 BIC-QA 在线分析(<B>/<E> 取 ② 中的两个 SNAP_ID) .\py.cmd run.py awr-analyze --begin <B> --end <E>
# Git Bash 等价写法(注意:环境变量与命令写在同一行才只对该次执行生效) ./py.sh run.py e2e --waiters 4 --hold 30 DMLENS_SAFETY_AUTO_EXECUTE=1 DMLENS_SAFETY_DENY_USERS=NONE \ ./py.sh run.py e2e --execute --waiters 4 --hold 60 ./py.sh run.py awr-analyze --begin <B> --end <E>

为什么受控执行要设这两个环境变量:目标根会话的用户是 SYSDBA,而工程配置默认保护 SYSDBA(deny_users = SYSDBA)。
所以需要①打开执行闸门 DMLENS_SAFETY_AUTO_EXECUTE=1、②把目标用户从保护名单中放开 DMLENS_SAFETY_DENY_USERS=NONE。
演示完毕请立即恢复默认(见 8.7);仓库提交的配置本身始终保持安全默认,这一点已在步骤 7 如实交代。

⚠️ 为什么是 NONE 而不是"留空"(这是一处实测踩出来的坑):保护名单的判定是"变量只要存在就覆盖",
而空值在 PowerShell 与 CMD 下都等于"删除该变量"——覆盖因此失效,保护名单仍是 SYSDBA,
结果是"放行 0,拦截 1",容易被误读为工具故障。写成 NONE 表示"名单里放一个不存在的用户",
三种终端行为一致,回显中会打印 deny_users=['NONE'],可据此确认已生效。

耗时提示(影响体感,务必先看):知识通道为 online 时,e2e / diagnose 会逐条调用在线大模型,实测可超过 17 分钟;
同一流程走离线通道只需 14 秒。推荐策略:先用 --no-knowledge(或把在线 Key 临时置为无效值 none,
注意置空无效——Key 的读取是"非空才覆盖",空值会被忽略而回落真 Key)把时序类截图取齐,
再单独跑 awr-analyze 取在线分析正文(该命令不受此影响)。

8.4 表 A 截图位 ↔ 命令对照(照此插图,零判断)

状态列说明:✅=真实执行截图已落位(15 张来自 2026-09-14 晚第二次复测,6 张来自 2026-09-15 晨第三/四次补拍)。

截图位 证据图 内容 复测命令 文件名(存入 figures/shots/) 状态
1 证据图 1 环境就绪自检(四组全 √) .\py.cmd tools\repro_preflight.py shot-01-check.png ✅
2 证据图 2 争用现场(根 trx / 根会话 / 阻塞 N 个) .\py.cmd run.py locklab --scenario all --hold 15 shot-02-contention.png ✅
3 证据图 3 动态视图旁证(可选) disql 中执行附录 B 的 V$TRXWAIT / WRH$_LOCK SQL shot-03-trxwait.png ✅
4a 证据图 4a 两次手工快照回显(SNAP_ID=49/50) .\py.cmd run.py snapshot shot-04-snapshots.png ✅
4b 证据图 4b 原生 AWR 报告首屏 .\py.cmd run.py report --begin &lt;B> --end &lt;E> → 浏览器打开 shot-04-awr-report.png ✅
5 证据图 5 诊断报告首屏(健康分 + 分级发现 + 知识通道) .\py.cmd run.py diagnose --begin &lt;B> --end &lt;E> shot-05-diagnosis.png ✅
6 证据图 6 AWR → BIC-QA 调用链成功 .\py.cmd run.py awr-analyze --begin &lt;B> --end &lt;E> shot-06-awr-bicqa.png ✅
6b 证据图 6b 在线通道成功回执(SSE + 载荷校验) 同上(回显 [3] 段) shot-06b-bicqa.png ✅
7 证据图 7 在线分析正文首屏 打开 output/awranalyze_*/awr_bicqa_analysis.md shot-07-bicqa-online.png ✅
7b 证据图 7b 在线分析正文收尾 + 留痕路径 同上(末段) shot-07b-bicqa-report.png ✅
8 证据图 8 阻塞链定位(根 → 下游) .\py.cmd run.py locklab(链段)/ .\py.cmd run.py sessions shot-08-blocking-chain.png ✅
9a 证据图 9a 复测判定 healed + 可归因 .\py.cmd run.py e2e --execute / demo --execute shot-09-verify.png ✅
9b 证据图 9b 对照:无链可修时不归因(no_action_nothing_to_do) 争用结束后 .\py.cmd run.py auto --execute shot-09-race.png ✅
10 证据图 10 审计 ok=true .\py.cmd run.py audit --verify shot-10-audit.png ✅
11a 证据图 11a 一键复测前半段(体检 → 造争用 → 采集 → 诊断) .\py.cmd run.py e2e --execute --waiters 4 --hold 60 shot-11-e2e-a.png ✅
11b 证据图 11b 一键复测后半段(受控修复 → healed → 审计收尾) 同上 shot-11-e2e-b.png ✅
12 证据图 12 留痕目录 ls -la output/e2e_&lt;时间戳>/ shot-12-artifacts.png ✅
13 证据图 13 WRH$_SYSSTAT 双快照差分(18 行) disql 执行 5.10 节 末尾 SQL shot-13-sysstat-diff.png ✅
14 证据图 14 WRH$_SYSTEM_EVENT 等待差分 disql 执行附录 B 证据 SQL ⑤ shot-14-event-diff.png ✅
15 证据图 15 死锁历史(结转 vs 落点) .\py.cmd run.py sessions / 证据 SQL ⑥ shot-15-deadlock-history.png ✅
16 证据图 16 清理与收尾复核 python tools/repro_cleanup.py --yes shot-16-cleanup-verify.png ✅

共 17 个截图位 / 21 张证据图全部落位(无占位卡);涉及 disql 登录的截图已裁掉登录行避免暴露口令。

附录 A 脚本清单与内容说明

本表说明每个脚本/命令的职责与关键点;核心代码片段已就近嵌入正文对应步骤(第 4 列给出位置),
完整工程源码不随文公开,工程结构与源码索取方式见 附录 D。
要把这些脚本跑成一套证据,请看 第 8 章(复测取证) 与配套《04_DMLens复测取证单》。

脚本 / 命令 作用 关键点 核心代码位置
run.py check 环境体检 实例画像 + AWR 可用性 + 页大小 步骤 1
run.py init / enable 初始化 AWR / 开启自动快照 SP_INIT_AWR_SYS(1);间隔可调 步骤 1(见前注)+ 附录 D.2
run.py snapshot / snapshots 手工快照 / 列表 故障现场用它把区间"夹"出来 步骤 3
run.py diagnose 完整诊断 区间口径 + 规则 + 知识印证 + 回流 + 报告 步骤 4
run.py awr-analyze AWR → BIC-QA 在线分析 上传附件 + 携 batchId 问答;含伪成功识别 步骤 5
run.py bicqa 三通道探测 区分 online / degraded / offline 步骤 5.5
run.py sessions 会话 / 锁 / 阻塞链 定位根持锁会话 步骤 6
run.py auto 自治闭环(默认干跑) 加 --execute 才真实处置 步骤 7
run.py audit --verify 审计哈希链校验 校验每个动作的完整性 步骤 9
scripts/lock_lab.py 锁模拟实验室 四场景构造 + 重试(处理 -608)+ 收尾清零 步骤 2
scripts/bench_run.py 压测 mixed 等五种模式;多线程独立连接 本附录(压测段)
scripts/closed_loop_demo.py 闭环受控执行演示 干跑 / --race / --execute 三模式 步骤 8(图 5)
scripts/e2e_story.py 到端实验编排* 制造问题→AWR→诊断→定位→处置→闭环,一键复现 第 4 章
scripts/make_figures.py 配图生成 matplotlib + 微软雅黑,输出 200dpi PNG 附录 D.2

脚本与引擎的分工:run.py 与 scripts/*.py 是编排层(只做参数解析、流程编排、打印留痕);
真正的口径算法、规则引擎、知识印证与安全闸门位于 dmlens/ 包(引擎层)。两者边界清晰,
因此"换个命令行外壳"不会影响任何一个数字——这也是本文所有结论可独立复算的原因。

关于压测(可选的"问题注入"方式):除锁争用外,也可用压测制造负载型问题——snapshot → bench --mode mixed --clients 8 --duration 15 --rows 50000 → snapshot → diagnose。实测(mixed · 8 并发 · 15 s · 5 万行):21130 ops = 1408.1 ops/s,p50 3.789 ms、p95 14.36 ms、p99 22.63 ms、max 44.311 ms,0 错误(图 7)。

图 7 压测延迟分位

图 7 压测延迟分位(mixed · 8 并发 · 15 s · 5 万行基线)

附录 B 证据 SQL 速查

-- ① 实例画像与页大小 SELECT INSTANCE_NAME, SVR_VERSION, BUILD_VERSION, STATUS$, MODE$ FROM V$INSTANCE; SELECT DISTINCT PAGE FROM V$PARAMETER; -- ② 缓冲池命中率(小数口径,按 NAME 去重) SELECT NAME, COUNT(*) AS SUBPOOLS, MAX(RAT_HIT) AS RAT_HIT, SUM(N_LOGIC_READS) AS LOGIC_READS, SUM(N_PHY_READS) AS PHY_READS FROM V$BUFFERPOOL GROUP BY NAME; -- ③ 容量(唯一无歧义字节口径) SELECT TABLESPACE_NAME, SUM(BYTES) AS USED_BYTES, SUM(MAXBYTES) AS LIMIT_BYTES FROM DBA_DATA_FILES GROUP BY TABLESPACE_NAME; -- ④ 区间指标对照(解析 / CPU / 锁 / 死锁) -- (第二次复测实测:SNAP_ID IN (49, 50),18 行原始回显见证据图 13) SELECT SNAP_ID, NAME, STAT_VAL FROM SYS.WRH$_SYSSTAT WHERE NAME IN ('DB time(ms)','parse time(ms)','hard parse time(ms)','parse count','hard parse count', 'CPU time(us)','transaction lock waits','trx lock wait time(ms)','transaction deadlock count') AND SNAP_ID IN (21, 22) ORDER BY NAME, SNAP_ID; -- ⑤ 区间等待事件 TOP(先排除 Idle 类) -- (第二次复测实测:trxid lock wait 10 次 / 164,099,080 μs,见证据图 14) SELECT E1.EVENT, E1.WAIT_CLASS, E1.TOTAL_WAITS - E0.TOTAL_WAITS AS WAITS, E1.TIME_WAITED_MICRO - E0.TIME_WAITED_MICRO AS US FROM SYS.WRH$_SYSTEM_EVENT E1 JOIN SYS.WRH$_SYSTEM_EVENT E0 ON E1.EVENT# = E0.EVENT# WHERE E1.SNAP_ID = 22 AND E0.SNAP_ID = 21 ORDER BY US DESC; -- ⑥ 死锁历史(会结转,需按 (TRX_ID, SEQNO) 去重) SELECT SNAP_ID, SEQNO, TRX_ID, SESS_ID, HAPPEN_TIME FROM SYS.WRH$_DEADLOCK_HISTORY ORDER BY SNAP_ID, SEQNO; -- ⑦ 实时锁与等待(定位根因) SELECT ID, WAIT_FOR_ID, WAIT_TIME, LOCK, WAIT_FOR_LOCK FROM V$TRXWAIT; SELECT COUNT(*) AS BLOCKED_LOCKS FROM SYS.WRH$_LOCK WHERE BLOCKED <> 0;

附录 C 复测检查清单

本清单是"结论层"的自检;"操作层"的完整流程(命令、截图位、插入位置、排障、收尾)见第 8 章与《04_DMLens复测取证单》。

# 检查项 预期 若不符
1 run.py check 的 usable true 先 run.py init 并确认 SYSAUX 就绪
2 页大小读取方式 用 SELECT DISTINCT PAGE FROM V$PARAMETER 不要用 V$PARAMETER.NAME='PAGE_SIZE'
3 制造问题后能否观测到阻塞链 ≥ 1 条 检查等待方是否带重试(-608)
4 区间跨时 建议 ≥ 30 秒 过短则 CPU 类惰性计数器不计分(属预期行为)
5 诊断是否出现"历史死锁"误报 应标 low 且注明"非本区间" 检查是否误用了历史表而非增量
6 知识通道标注 online 或 degraded(不得把错误载荷当知识) 检查载荷内容校验是否生效
7 受控执行是否真实生效 阻塞链 → 0 且 归因=是 确认双闸门与保护名单配置
8 干跑时是否误报 healed 不得出现;应为 no_action_chain_persists 检查归因约束是否生效
9 审计哈希链 ok = true 检查审计文件是否被外部改写
10 收尾 阻塞链 0 / 等待事务 0 手工 rollback + DROP TABLE DMLENS.LOCK_T;

附录 D 工程结构、核心代码索引与配置示例

本附录不与正文重复收录全量源码。 正文第 3 章的九个步骤已就近给出每个脚本的
关键核心代码片段(保留原工程中文注释,未做改写),并逐一说明该脚本"在链路里承担什么职责"。
本附录的作用是:①给出工程结构,让读者知道"这些片段各自住在哪里";②给出核心代码索引,便于快速定位;
③给出配置示例,便于搭建同等环境。

完整工程源码不随文公开;如需整包复现(含规则引擎 analyzer.py 全部 R01~R15 实现),可与作者联系。

D.0 工程结构与引擎层分工

引擎层(dmlens/)承载全部口径算法:AWR 数据面 awr.py、规则引擎 analyzer.py(R01~R15)、八维评分 health.py、阻塞链还原 sessions.py、BIC-QA 接入 bicqa.py、自治闭环 autoheal.py、哈希链审计 audit.py。编排层(scripts/)只负责"按什么顺序调、把什么留下",不含任何数字算法。二者边界刻意分离——换命令行外壳不影响任一数字,这是本文结论可独立复算的前提。完整目录树见配套《附录_DMLens完整工程源码.md》。

D.1 核心代码索引(正文就近收录,12 段)

正文第 3 章九个步骤已就近给出每个脚本的关键片段并说明其职责,映射如下:run.py(步骤1)→引擎层唯一入口;dmlens/dmdb.py(步骤1)→固定"达梦怎么问";scripts/lock_lab.py(步骤2)→四类锁争用注入;dmlens/awr.py(步骤3/4)→快照/报告/区间差分/口径登记;dmlens/bicqa.py(步骤5)→伪成功识别+附件通道;dmlens/sessions.py(步骤6)→阻塞链还原;dmlens/autoheal.py(步骤7/8)→六道防线+归因约束;dmlens/audit.py(步骤9)→哈希链审计。

analyzer.py(R01~R15)、health.py(八维权重)、capacity.py(增长外推)的实现细节篇幅较大,其行为已由第 5–6 章完整交代,据正文正确写法与附录 B 证据 SQL 即可独立复算同一组数字。

D.2 配置示例 dmlens.ini(口令脱敏)

口令一律以 ${secret:NAME} 引用,明文存于 var/secrets.local.json 或环境变量,不入版本库;
[safety] 各项对应第 1.5 节的六道防线。

; 口令一律走 ${secret:NAME} 引用,禁止写明文;优先级:环境变量 > 本文件 > 内置默认 [db] host = 192.168.3.22 ; 远程 DM9 实例(CentOS 7.9 · DMSERVER · 5236) port = 5236 user = SYSDBA password = ${secret:dm_password} database = DMDB [bicqa] url = https://api.bic-qa.com api_key = ${secret:bicqa_api_key} dataset_code = 2103 ; 达梦知识库:BIC-QA 目录 code=2103(support_awr=1) prefer_online = true topn = 2 timeout = 25 export_entries = true [awr] interval_minutes = 30 ; 合法范围 [10, 525600],0 关闭自动快照 retention_days = 8 topnsql = 30 server_report_dir = /home/dmdba/dmlens_reports ; 须对 dmdba 可写,否则 [-20125] [safety] auto_execute = false ; 双闸门之一:+ CLI --execute 才允许真实修复,生产务必 false allow_terminate = true deny_users = SYSDBA ; 保护名单,演示时可置空 max_terminate_per_round = 3 [platform] out_dir = output data_dir = var store_path = var/dmlens.db

配套资料

文件 用途
docs/04_DMLens复测取证单.md 配套复测手册:三套终端的命令块、逐条预期回显、排障表与收尾清理,与本文第 8 章一一对应
docs/附录_DMLens完整工程源码.md 工程源码(内部参考,不随征文公开)
scripts/render_terminal_shot.py 终端回显渲染为证据图的脚本(本文 6 张终端风格证据图由它生成,不改动任何字符)

(全文完) 上 / 中 / 下三篇共同构成一次“可完整复测”的 DM9 锁争用诊断实验。所有结论均附真实回显与证据图,欢迎在你的环境里按第 4 章一键命令原样重跑。《 DMLens_让 DM9 的 AWR 说真话:八处口径陷阱、BIC-QA 在线印证与一条命令跑完的锁争用闭环(上)》.pdf《 DMLens_让 DM9 的 AWR 说真话:八处口径陷阱、BIC-QA 在线印证与一条命令跑完的锁争用闭环(下)》.pdf《 DMLens_让 DM9 的 AWR 说真话:八处口径陷阱、BIC-QA 在线印证与一条命令跑完的锁争用闭环(中)》.pdf

评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服