DryRun模式不执行SQL,仅生成SQL字符串和参数,无法反映真实性能瓶颈;必须结合真实执行与数据库EXPLAIN分析,才能准确定位GORM逻辑或数据库索引问题。

DryRun 模式本身不参与性能分析,它只生成 SQL 而不执行——想测性能,必须真跑;但 DryRun 是你确认“SQL 长什么样”“参数是否正确”的第一道关卡,跳过它,后续所有性能数据都可能测在错误语句上。
为什么不能直接用 DryRun 测性能
DryRun 的本质是绕过数据库连接和执行环节,只走 GORM 的 SQL 构建逻辑。它返回的 stmt.SQL.String() 是字符串,stmt.Vars 是参数切片,全程不发包、不等待、不解析结果。所以:
– 它耗时通常在微秒级,反映不了网络延迟、锁竞争、索引缺失等真实瓶颈
– 它无法暴露 PreparedStmt 缓存是否生效、绑定变量是否被复用等关键路径
– 如果你用 db.Session(&gorm.Session{DryRun: true}) 做“伪压测”,得到的 benchmark 完全失真
用 DryRun + 真实执行组合做有效分析
真正有用的性能分析流程是:先 DryRun 确认 SQL 正确性,再用相同条件真实执行并采集指标。关键点包括:
- 确保
DryRun和真实执行使用完全一致的链式调用(包括Where、Joins、Order、Limit),否则生成的 SQL 可能不同 - 开启 GORM 的
PrepareStmt: true配置(尤其在微服务高频查询场景),否则每次都是文本协议执行,测不出预编译收益 - 用
context.WithTimeout包裹真实查询,避免慢 SQL 拖垮整个分析流程 - 记录
stmt.SQL.String()和stmt.Vars后,拿它们去数据库 CLI 手动EXPLAIN ANALYZE,比 ORM 内部日志更准
容易被忽略的 DryRun 参数陷阱
DryRun 下某些 GORM 行为会静默失效,导致你看到的 SQL 和真实执行时不一致:
-
db.Unscoped().Where(...)在 DryRun 中仍会忽略软删除字段,但若你没配DeletedAttag,真实执行时可能因 WHERE 条件缺失导致全表扫描 -
db.Select("id, name")在 DryRun 中生成预期字段,但如果结构体里有gorm:"-"字段且你用了Find(&users),真实执行时 GORM 可能自动补全 SELECT *,引发额外 IO - 使用命名参数(如
sql.Named("name", "xxx"))时,DryRun 输出的 SQL 里参数占位符仍是@name,但真实 PostgreSQL 执行时会转成$1,MySQL 则转为?——这影响EXPLAIN解析,务必按目标库语法校验
微服务中建议的最小分析闭环
在服务启动时注册一个临时 debug endpoint(比如 /debug/sql?query=user_list&age=25),内部逻辑应包含:
- 构造与线上请求完全一致的
db.Session(&gorm.Session{DryRun: true})获取 SQL 和参数 - 用同一组参数发起一次带
context.WithTimeout(ctx, 500*time.Millisecond)的真实查询,并记录time.Since()和rowsAffected - 把 SQL 发给数据库的
EXPLAIN (ANALYZE, BUFFERS)(PostgreSQL)或EXPLAIN FORMAT=JSON(MySQL),提取Execution Time、Seq Scan、Index Cond等关键字段 - 最终返回 JSON,含三块:DryRun SQL、真实执行耗时、数据库原生执行计划摘要
这个闭环不依赖外部 APM 工具,也不污染主业务逻辑,但能快速定位是 GORM 生成逻辑问题,还是数据库层索引/统计信息问题——后者才是性能优化真正的发力点。


















