📚 系列连载(下篇):原稿因超官网单篇 40000 字词上限拆分为上 / 中 / 下三篇。上篇=写在前面+第 1–2 章+步骤 1–4;中篇=步骤 5–9+时序+第 4 章;本篇=第 5 章八处口径陷阱+第 6 章数据总表+第 7 章结论+第 8 章复测取证+附录 A–D。三篇同源于一台 DM9 实例的真实复测,截图与命令一致。
规则引擎若"直接算",会产出互相矛盾的结论。以下八处是实测归纳(汇总见图 2),每处都给出反例数据与正确写法。
图 2 达梦 AWR 八处口径陷阱对照
现象: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;
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;
根因: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;
transaction lock waits 的区间增量恒为 390,与区间跨时(29 / 195 / 214 / 2 / 8 秒)无关 → 属采集/结转噪声。而真正代表"等了多久"的 trx lock wait time(ms) 常为 0。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;
根因:CPU time(us) 是惰性计数器——快照 5→6(跨时 2 秒)仅 +15 us;1→6(跨时 442 秒)才 +2832793 us。短区间可能完全不推进。
正确写法:当区间跨时 < 30 秒或增量未推进时,不作占比判定,只登记负载量,并明确写"本项不计分(不制造误判)"。本实验跨时 7.57 秒,因此性能维度输出为:
区间跨时 7.57s 过短(< 30s),惰性计数器 CPU time 不具可比性,本项不计分(Δ=18.061ms 仅供参考)
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
根因: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 count3 → 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
插入位置:本段下方
复测证据图 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——现场确已干净
现象:同一区间(SNAP_ID 21 → 22)的跨时被算成 9.0 秒,而达梦原生 AWR 报告里 Elapsed 写的是 0.12 分钟 = 7.57 秒 —— 两者相差 1.43 秒(相对误差 19%)。对 7 秒级的短区间而言,这已足以让"每秒指标"整体失真。
根因(两处叠加,缺一不可):
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),就把起始快照的采集窗口一并算了进来。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——这处差异本身就是本节结论的现场证据,而非文档疏漏。| 来源 | 列 | 单位 | 实测样例 |
|---|---|---|---|
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 —— 这是最容易踩的一处。正确写法:
字节数 = 页数 × PAGE_SIZE,页大小取自 SELECT DISTINCT PAGE FROM V$PARAMETER;;DBA_DATA_FILES.MAXBYTES;days_to_full 超过 36500 天(100 年) 一律输出"不适用"——因为部分表空间的默认上限极大(实测 MAIN / ROLL / SYSTEM / TEMP 的 MAXBYTES 均为 35184371040256 字节 ≈ 32 TB),照此线性外推会得到"还能用几万年"的荒谬结论。本节是第二次复测(区间
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.0 为第一次一键 e2e 受控执行;6.1 为当晚按第 8 章逐命令执行的第二次复测;6.2 为 2026-09-15 晨的第三 / 四次受控执行(在线 / 离线两种知识通道,补拍证据图取证)。
三次的 SNAP_ID、事务号、会话 ID、条数全部不同——判据是结构一致,这正是"可复现"的实证书。
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 秒 |
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 张现已全部为真实执行截图) |
第三次运行暴露了一个计划外的真实现象:知识通道为
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 (<B>, <E>))
截什么:9 个指标 × 2 个快照的原始值——本文所有"增量"结论的原始依据
存成:figures/shots/shot-13-sysstat-diff.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换为<B>/<E>)
截什么:trxid lock wait与network idle wait两行(对比"真实等待"与"空闲等待"的量级)
存成:figures/shots/shot-14-event-diff.png
插入位置:紧随证据图 13
复测证据图 14 第二次复测的等待事件差分(16 个事件全量):trxid lock wait(Transaction 类)10 次 / 164.099 s 是唯一真实瓶颈;network idle wait(Network 类)261 次 / 572.040 s 累计值大于 DB Time,属空闲等待、先剔除再排序(5.6 节)
V$TRXWAIT 还原链路"的经验。缺了知识,规则只报现象;缺了证据,知识只是泛泛之谈。| 面向 | 建议 |
|---|---|
| DBA | 引用任何 AWR 指标前先问三句:区间还是累计?页还是字节?会不会结转? |
| DBA | 建监控时"先排除空闲等待再排序",否则排行榜永远被 network idle wait 占据 |
| DBA | 判断锁问题优先看等待时间与阻塞链,不要被计数的"高数字"误导 |
| 工具作者 | 任何"自动处置"都要有双闸门 + 白名单 + 身份复核 + 哈希链审计,并设归因约束 |
| 所有人 | 集成第三方 API 时不要只看 HTTP 状态码,一定要校验载荷内容(本次即命中"伪成功") |
degraded(配额耗尽)→ offline(Key 失效)→ online(换用有效 Key 后在线分析成功,见步骤 5.3) 三种状态(详见 1.4 节 与步骤 5.5 的三态实测);生产部署建议同时配置在线与内置图谱两条腿,并把降级写进报告而不是静默切换;以上结论落到产品层面,笔者作为一线使用者,想提出五条具体、可落地的建议。
它们都来自本次实验中真实踩到的坑,而非泛泛而谈:
| # | 建议 | 触发场景(本文实测) | 期望 |
|---|---|---|---|
| 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.4 节的表 A 插图即可。
配套文档《04_DMLens复测取证单》是本章的操作版(含三套 shell 的命令块、逐条预期回显、排障表与收尾),两文配合使用:本文看"为什么",取证单看"怎么做"。
① 工作目录——以下所有命令的当前目录都是工程根(含 run.py 的那个目录):
cd "C:/Users/达梦数据库征文/DMLens"
② Python 解释器——必须用已安装 dmPython 的那一个。工程根目录已内置包装脚本
py.cmd / py.sh,内部把解释器锁死了,所以你不需要设任何变量:
| 终端 | 固定用法 |
|---|---|
| PowerShell / CMD(Windows) | .\py.cmd <参数>(例如 .\py.cmd run.py check) |
| Git Bash / Linux | ./py.sh <参数>(例如 ./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;这样正文中的图片占位可被直接覆盖生效,无需再改文档。
.\py.cmd tools\repro_preflight.py
判定:四组全 √ 才能走满全流程。若"BIC-QA 在线通道"为 ×,其余步骤仍可复测,但步骤 5 拿不到在线分析正文(工具会如实降级为内置图谱,并写进报告,不会伪装成在线)。
该输出本身即"证据图 1"的素材(第二次复测已按此落图,见 2.2 节末)。
# ① 干跑:完整走九步,但不动实例
.\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取在线分析正文(该命令不受此影响)。
状态列说明:✅=真实执行截图已落位(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 <B> --end <E> → 浏览器打开 |
shot-04-awr-report.png |
✅ |
| 5 | 证据图 5 | 诊断报告首屏(健康分 + 分级发现 + 知识通道) | .\py.cmd run.py diagnose --begin <B> --end <E> |
shot-05-diagnosis.png |
✅ |
| 6 | 证据图 6 | AWR → BIC-QA 调用链成功 | .\py.cmd run.py awr-analyze --begin <B> --end <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_<时间戳>/ |
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登录的截图已裁掉登录行避免暴露口令。
本表说明每个脚本/命令的职责与关键点;核心代码片段已就近嵌入正文对应步骤(第 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 压测延迟分位(mixed · 8 并发 · 15 s · 5 万行基线)
-- ① 实例画像与页大小
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;
本清单是"结论层"的自检;"操作层"的完整流程(命令、截图位、插入位置、排障、收尾)见第 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; |
本附录不与正文重复收录全量源码。 正文第 3 章的九个步骤已就近给出每个脚本的
关键核心代码片段(保留原工程中文注释,未做改写),并逐一说明该脚本"在链路里承担什么职责"。
本附录的作用是:①给出工程结构,让读者知道"这些片段各自住在哪里";②给出核心代码索引,便于快速定位;
③给出配置示例,便于搭建同等环境。完整工程源码不随文公开;如需整包复现(含规则引擎
analyzer.py全部 R01~R15 实现),可与作者联系。
引擎层(dmlens/)承载全部口径算法:AWR 数据面 awr.py、规则引擎 analyzer.py(R01~R15)、八维评分 health.py、阻塞链还原 sessions.py、BIC-QA 接入 bicqa.py、自治闭环 autoheal.py、哈希链审计 audit.py。编排层(scripts/)只负责"按什么顺序调、把什么留下",不含任何数字算法。二者边界刻意分离——换命令行外壳不影响任一数字,这是本文结论可独立复算的前提。完整目录树见配套《附录_DMLens完整工程源码.md》。
正文第 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 即可独立复算同一组数字。
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
文章
阅读量
获赞
