📋 想照着敲?这篇有 交互操作版——步骤卡片导航、命令一键复制,现场对着敲更顺手:打开交互操作版 →
本文讲的工具是 dmsqlq,自研,单文件,开源之前先放二进制。下面每条命令、每个输出都是本机实跑取的,没改写、没补全。性能数字标了样本来源,你可以自己复核。
日志里那句 where fbill_id = ?,到底在执行什么
客户说"早上那条单子查不出来",你去翻 SQL 日志,看到的是这个:
[SEL] SELECT * FROM t_rim_invoice WHERE fbill_id = ? AND status = ? EXECTIME: 18.4(ms)
两个问号。fbill_id 是什么、status 是几,这行本身都没写。
DM 把这两个值记在另一行:紧跟着的 PARAMS(SEQNO, TYPE, DATA),靠时间戳和会话/语句句柄跟语句配对。
2026-09-10 09:00:00.000 (EP[0]{sess:0x7f100a,user:SYSDBA,stmt:0x2010,...}) PARAMS(SEQNO, TYPE, DATA)={(1, VARCHAR, 'dac000000'),(2, INT, 0)}
2026-09-10 09:00:00.000 (EP[0]{sess:0x7f100a,user:SYSDBA,stmt:0x2010,...}) [SEL] SELECT * FROM t_rim_invoice WHERE fbill_id = ? AND status = ? EXECTIME: 18.4(ms)
所以能不能还原,只看两件事:采集侧有没有把 PARAMS 行记下来;工具在参数值本身就带 ? 的时候,还敢不敢继续往下填。第二件是很多工具翻车的地方,后面讲 DMLOG 那一节会说到。
先把它拿到手
单文件,免安装,不需要运行时。不连库、不建表、不联网,只读日志、只写结果文件。install 做的事就是把二进制放进你的用户目录并修 PATH,不动 /usr;--remove 撤得干净。
| 平台 | 包 | 大小 |
|---|---|---|
| Windows x64 | dmsqlq-V2.37-windows-x64.zip | 2.2 MB |
| Linux x86_64 | dmsqlq-V2.37-linux-x64.zip | 2.1 MB |
| Linux arm64(鲲鹏/飞腾) | dmsqlq-V2.37-linux-arm64.zip | 1.9 MB |
下完先对一遍校验值,十秒的事,能挡住中间人替换安装包——企业网里真发生过。
# Linux / macOS:进入解压后的 V2.37 目录
sha256sum -c CHECKSUMS.txt
# Windows PowerShell:和 CHECKSUMS.txt 里对应那一行的值比
Get-FileHash .\dmsqlq-V2.37-windows-x64.zip -Algorithm SHA256
公司网络拦 gitee,或者下载慢得离谱,去 issue 说一声,我换个地方给你。
下载页:gitee.com/jiangchuang/dm-tools(只放编译好的包,不放源码;dmkit 也在这)
四条命令,从「上不了手」到「能交付」
第四条只是同一个命令换了个参数。命令里的目录改成你自己的,其余照抄。
第 1 步:先把日志目录找出来,别背路径
网上教程大多写 /opt/dmdbms/data/DAMENG/log。这串是某次安装的默认值,不是固定值——装的时候数据目录可以指到任何地方,实例名也可以不叫 DAMENG。我这台的数据目录在 /dmdata/DAMENG,照着教程敲那行,得到的是 No such file。
更要紧的一点:日志压根不在数据目录下。我这台的日志在 /home/dmdba/dmdbms/log,是安装目录下的 log。数据目录和日志目录是两回事,别拿一个推另一个。
所以别猜,先问实例自己要。最省事是看进程:
ps -ef | grep dmserver | grep -v grep
输出里 path= 后面那串就是 dm.ini 的完整路径,它的父目录是实例目录:
dmdba 105734 1 0 10:03 ? 00:00:03 /home/dmdba/dmdbms/bin/dmserver path=/dmdata/DAMENG/dm.ini -noconsole
这条路不用连库。客户端连不上、口令也忘了的时候,它照样能动。
能连库的话直接查更快:
select para_value from v$dm_ini where para_name = 'SYSTEM_PATH';
返回 /dmdata/DAMENG,这就是实例目录。参数名是 SYSTEM_PATH,不是 PATH——v$dm_ini 里没有叫 PATH 的参数,有些教程写 para_name = 'PATH',跑出来 0 行。
日志到底落哪儿:查 sqllog.ini,别猜
实例目录下有个 sqllog.ini,SQL 日志写到哪里由里面的 FILE_PATH 决定。最快是连库直接查,不用登服务器翻文件:
select mode_name, para_name, para_value, is_default
from v$dm_sqllog_ini where para_name like '%PATH%';
我这台的返回值:
MODE_NAME PARA_NAME PARA_VALUE IS_DEFAULT
<DEFAULT> FILE_PATH ../log Y
SLOG_ALL FILE_PATH ../log Y
SLOG_ERROR FILE_PATH ../log Y
SLOG_DDL FILE_PATH ../log Y
SLOG_LONG_SQL FILE_PATH ../log Y
五行对应五个配置段:一个全局加四个模式。最后那列 IS_DEFAULT = Y 说明全是默认值,没人改过。
../log 是相对路径,按 bin 目录解析,落到 /home/dmdba/dmdbms/log,也就是安装目录/log。它不是相对 sqllog.ini 所在的实例目录:按「实例目录/log」去翻是空的,我这台连那个目录都没建。
这个目录确实存在,里面放着 dm_DMSERVER_*.log 这类事件日志。但 dmsql_*.log 我没见它生成过,因为这台实例的 SQL 日志从来没真正打开过(原因见下),所以落点这一句是按 FILE_PATH 推算的,不是我亲眼看到文件在那儿。
要挪位置就把 FILE_PATH 改成绝对路径,比如 /data/dmlog。改完在库里执行一句,让内存重新读文件,不用重启:
call sp_refresh_svr_log_config();
实测返回 DMSQL executed successfully。这一句管的是「日志已经开着、你改了配置」,刷一下就换新路径;本来就没开的,刷了也不会凭空长出日志来。
两个视图名字像,查的东西不是一个:
V$DM_SQLLOG_INI读磁盘上那份sqllog.ini,不挑SVR_LOG状态,任何时候都查得到。
V$DM_SQLLOG_CONFIG读内存里已经生效的那份。能查到它的时候,拿它跟 INI 对一遍,就知道你改完 sqllog.ini 到底刷进内存没有,这是它比 INI 多出来的用处。但这台实例上它查不出东西,我试了三种状态:
SVR_LOG = 0时 0 行;动态改成1(按 sqllog.ini 记)再sp_refresh_svr_log_config(),还是 0 行;改成2(详细模式)再刷,仍然 0 行。我猜是动态改参数不会让日志子系统重新初始化,得重启实例才真正加载。这一点我没重启验证,所以只是猜测。还有更要紧的一层:两个视图给的
FILE_PATH都是../log这种相对值。就算 CONFIG 里有内容,也拿不到绝对路径,还是得知道 bin 目录在哪。想过这一关,别指望查视图,用前面那条ps命令。
这个目录里躺着好几种日志,名字像,内容完全不是一回事:
| 文件名 | 是什么 |
|---|---|
dmsql_*.log | 要的就是这个——SQL 日志,带语句、耗时、参数 |
dm_*.log | 实例事件日志(启停、切换、报错),没有 SQL |
dmtrc_*.log | 跟踪日志,另一套开关 |
一个 dmsql_*.log 都没有?多半是 SQL 日志压根没开。这个开关分两层,连上库查一句就见分晓:
select para_name, para_value from v$dm_ini
where para_name in ('SVR_LOG', 'SVR_LOG_NAME');
我这台的返回值:
SVR_LOG 0
SVR_LOG_NAME SLOG_ALL
SVR_LOG = 0 就是采集没开。这时候 dm_*.log 里只有启停和报错,一条 SQL 都不会有——不是工具读不出来,是压根没写进去。SVR_LOG_NAME 指向 sqllog.ini 里的 [SLOG_ALL] 那一段,具体采哪些位号、路径写哪儿,都在那一段里。
我这台的 sqllog.ini 在实例目录下(/dmdata/DAMENG/sqllog.ini),不在 bin 下。找不到就 find / -name sqllog.ini 2>/dev/null,或者干脆问库:
select para_value from v$dm_ini where para_name = 'CONFIG_PATH';
返回的目录就是实例目录,sqllog.ini 在那儿等着。
第 2 步:先扫一眼,不出文件
dmsqlq /home/dmdba/dmdbms/log -S
-S 大写是「只打到屏幕」,小写 -s 会额外生成一份 HTML 报告并用浏览器打开。实际输出(样例日志,600 条语句):
dmsqlq 2.37 统计报告 2026-10-31 00:45:14
────────────────────────────── 概览 ──────────────────────────────
★ 头号问题:累计耗时最高的是一类语句(600 次 / 累计 18.03s,
占语句总耗时的 89.2%)—— 从它开始优化收益最大。
日志文件 1 个(0.2 MB)
SQL 事件 686 条
参数还原 600 成功 / 86 待还原 (87.46%)
扫描耗时 7ms → 36 MB/s
日志时段 2026-09-10 09:00:00 → 09:09:59(跨度 10.0 分钟)
────────────── 优先优化推荐 Top2(按影响占比排序) ───────────────
1 [优先] 影响占比 89.2% 次数 600 平均 30ms 累计 18.03s
问题信号:SELECT * 取全字段(直接查基表)
建议动作:只取需要的列 —— 覆盖索引时才可能免回表
语句样例:SELECT * FROM t_rim_invoice WHERE fbill_id = 'dac000000' AND status = 0
注意它排的是累计耗时占比,不是单次最慢。一天只跑一次的 8 秒慢查询,不该排在每分钟 60 次、每次 200ms 的语句前面——后者一天耗掉的小时数才是真的。
第 3 步:把 ? 换成真实值,导出能直接执行的 SQL
dmsqlq /home/dmdba/dmdbms/log -o out.sql
out.sql 里长这样:
-- 2026-09-10 09:00:00 · [SEL] · 0.1ms · SYSDBA, · trxid:3816438, · SAMPLE
SELECT * FROM t_rim_invoice WHERE fbill_id = 'dac000000' AND status = 0;
-- 2026-09-10 09:00:00 · [UPD] · 0.5ms · SYSDBA, · trxid:3816438, · SAMPLE · /*未找到匹配参数: 占位符2个*/
update ACCOUNTS set BALANCE = BALANCE + ? where ACCT_ID = ?;
两条对照着看:第一条参数填好了,可以直接贴进 disql;第二条工具自己承认没找到参数,没有编个值糊上去。
「没还原」和「还原错了」是两种后果。前者你知道要手工补;后者你会拿着一条假 SQL 去复现一个根本不存在的现场,结论错得还很自信。所以还原不了的语句原样留 ?,注释头里写清占位符个数。
第 4 步:只要慢的,出交付清单
dmsqlq /home/dmdba/dmdbms/log -m 1000 -B 20 -o slow.sql
-m 1000 是耗时下限(毫秒),-B 20 是最多取 20 条。实际输出(样例取最慢 3 条):
-- 2026-09-10 09:09:59 · [SEL] · 60ms · SYSDBA, · trxid:3816637, · SAMPLE
SELECT * FROM t_rim_invoice WHERE fbill_id = 'dac000599' AND status = 5;
-- 2026-09-10 09:09:58 · [SEL] · 59.9ms · SYSDBA, · trxid:3816637, · SAMPLE
SELECT * FROM t_rim_invoice WHERE fbill_id = 'dac000198' AND status = 0;
要发给开发商或客户当优化清单,加 -e 交付_202609.xlsx 直接出 Excel,已经按累计耗时降序排好优先级,省掉你在 Excel 里手工排序那一小时。
它到底能扛多大
数字来自工具仓库的现网实测记录,样本、版本、结果都列出来:
| 样本 | 实测结果 | 说明 |
|---|---|---|
| 单文件 1.03 GB 现网日志 334 万条记录 / 92 万 SQL 事件 |
2.26 s ≈ 455 MB/s(单线程) |
输出 46.8 万条可执行 SQL;参数还原成功率 97.8%。热数据单文件实测 549 MB/s |
| 7 个日志 7.3 GB(冷盘) | -j 1 → 99 MB/s-j 4 → 157 MB/s-j 8 → 146 MB/s |
并发甜点在 4;到 8 已经撞上磁盘读带宽,再加没用 |
| 13.9 GB 全量(14 个来源) | 锁等待 1,446 次 最长 1.71 s |
顺带给出被争抢最狠的对象(表 10175:1,156 次,占全部等待 80%),拿 table id 反查字典即可定位 |
这活吃 I/O,不吃 CPU,否则单线程跑不到 549 MB/s。在外面再套一层 rg 之类的东西也不会更快:花钱的是磁盘读和 gzip 解压,不是文本匹配。
在库服务器上跑,建议 -j 2~4,别开满。省下的那两秒,不够抵你抢数据库实例 CPU 惹出来的麻烦。
报告能不能信,看这三个地方
一、先看采集缺口,「查不出来」的第一嫌疑
日志里显示「锁等待 0 次」,是真没锁竞争吗?大概率是没采集到。工具会把这件事挑明,不让你误判:
采集缺口 缺位号 29:本批日志无事务/锁事件行(TRX: / DLCK / LOCK_TID 全为 0)
→ 「锁等待」「长事务」「TPS」是没采集到,不是没发生
判据很硬:29 位号一旦开启,事务事件行必然大量出现,不可能恰好是 0。要这个维度,就在 sqllog.ini 的 SQL_TRACE_MASK 里打开 29 重采;短期替代是在库上查 V$TRXWAIT / V$LOCK。
它还会顺手做一份采集配置体检:缺 28 就没有参数行(还原率直接归零)、缺 27 没有原始语句、缺 23 没有报错语句。位号记不住没关系,报告会直接告诉你缺哪个。
二、还原不了的,它明说,不装
/*未找到匹配参数: 占位符2个*/ 这行注释就是自检点。看到它,说明这批日志的 PARAMS 行缺失或被截断,不是工具坏了。回去查采集配置(位号 28),或者确认日志有没有被跨文件截断。
三、排序口径是「累计」,不是「单次最慢」
要回答「哪条 SQL 最拖垮系统」,累计耗时才是答案;要回答「哪条 SQL 最慢」,用 -m 卡耗时下限。两个问题不一样,别拿一个答案去回另一个。
和官方 DMLOG 怎么选
DMLOG 是这条路上的先行者,从零到一把「SQL 日志能分析」这件事做成了能用的东西。后来者包括 dmsqlq,都是在它提出的思路和口径上往前走的。下面只讲形态差异,不是谁替代谁。
| 维度 | 官方 DMLOG 8.183 | dmsqlq |
|---|---|---|
| 运行方式 | 要一个 DM 分析库(32K 页)+ dmlog.properties + JDBC 连接 | 单个二进制,dmsqlq . 就完事 |
| 结果落地 | 在被分析的库里 DROP / CREATE 6 张分析表 | 只写 SQL 文本文件,不落库 |
参数值本身含 ? | 整条放弃(同一批现网日志实测还原率 10.9%) | 继续还原(同批 97.8%) |
| 参数值里带逗号 | 会静默切掉前缀,仍标记为成功 | 实测 14.9 万条含逗号/含引号的值全部完整 |
怎么选:要长期建库做趋势分析、本来就有 analys 库,DMLOG 那套仍然合适;要临时拿一份现网日志、几分钟出结论,而且绝不能在生产库上建表(尤其不能出现 LOG_COMMIT 同名被 DROP 的风险),用不落库这一支。
这次没有对 DMLOG 做计时实测,所以不引用任何耗时对比,只比有实测支撑的维度。
三种用法
现场救火,只有一台能连的机器
把日志目录整个拷出来,或者直接在库机上跑 dmsqlq 目录 -S,三十秒看结论。这时候最怕的是「为了分析先建个库」——现网不允许,也没时间。
给客户交优化清单
-m 200 -B 50 -e 交付.xlsx,按累计耗时排好序,附上每条的真实参数。客户拿着这份清单能自己去改索引、改 SQL,不用你再口头解释「那个时间段那条语句」。
长期观察,日志堆了几十 GB
直接喂目录或压缩包,-j 4。它按文件并行,压缩包内的条目也并行。这一点手工脚本很难做对,因为 gzip 本身是串行流,能重叠的只有「解压」和「解析」。
常见问题
单个大日志,开 -j 30 会快很多吗?
不会。并发只作用于多个文件或压缩包内的多个条目;单个大文件开满核也没用(实测 -j 1 与 -j 30 同速)。
它会不会改我的库?要不要装 Java / .NET?
都不会,都不用。单文件、零依赖,不连数据库、不建表、不联网。你可以把它拷到一台完全隔离的机器上跑,只要有日志文件。
记不住参数怎么办?
直接敲 dmsqlq 不带参数,就是交互向导:五问(日志在哪 / 要什么结果 / 时间范围 / 耗时下限 / 操作类型),每一步回车就是推荐值,最后把等效命令行打印出来,下次照着敲。TAB 补全首次交互运行时自动装好,选项和路径都能补。
为什么报告里有些语句没有 EXECTIME?
说明这条语句在日志里没有执行完成的行(采集期间实例重启、或语句还在跑)。这类不计入耗时统计,但会出现在事件计数里,看着「条数对不上」是正常的。
Windows 上用,路径要怎么写?
反斜杠和正斜杠都认:dmsqlq D:\dmsql_log -s 和 dmsqlq D:/dmsql_log -s 等价。路径里有空格就加引号。
能不能只处理某一段时间?
能,--from / --to 给时间范围(YYYY-MM-DD HH24:MI:SS)。只关心某类操作就加 --ops SEL(或多选 SEL,UPD)。
最后
它只解决一个问题:日志里那句话到底在执行什么,三分钟内给你一个能执行的答案,而不是让你在几百万行里手工捞参数。
做不到的也说清楚:它不替你判断该加什么索引,也不判断这条 SQL 业务上该不该存在。报告给的是证据和优先级,判断还是人做。
用着有问题、或者你手上的日志它读不动,提个 issue,把
--version的输出和报错原文贴上就够定位。这类问题基本都能修,修完别人也能用。