Explain必须在真实数据库中执行,不能依赖GORM的DryRun;DryRun仅生成SQL字符串和参数,不触发查询优化器,无法反映真实执行计划。

Explain 必须在真实数据库里跑,不能靠 GORM 的 DryRun
DryRun 模式只生成 stmt.SQL.String() 和 stmt.Vars,它不发包、不执行、不触发查询优化器——你看到的 SQL 字符串,和 PostgreSQL/MySQL 真正收到并解析的可能有差异(比如命名参数 @name 在 PostgreSQL 里实际转成 $1,MySQL 是 ?)。所以直接拿 DryRun 输出去 EXPLAIN,结果不可信。
正确做法是:先用 DryRun 拿到语句和参数,再手动拼进数据库 CLI 或用 db.Raw() 执行 EXPLAIN (ANALYZE, BUFFERS):
- 确保链式调用完全一致(
Where、Joins、Order、Limit一个都不能少,否则 SQL 可能不同) - PostgreSQL 推荐加
BUFFERS查缓存命中率;MySQL 用EXPLAIN FORMAT=JSON看更细的执行路径 - 如果用了
Preload,注意它生成的 LEFT JOIN 很容易让优化器放弃主表索引——type=ALL就是信号
用 db.Raw() + EXPLAIN 手动验证 GORM 查询
别依赖 GORM 日志里的 SQL,日志可能被截断、参数没展开、甚至漏掉隐式 JOIN 条件。最稳的方式是把 DryRun 的 SQL 拿出来,套一层 EXPLAIN 后用 db.Raw() 执行:
// 先 DryRun 拿 SQL
var users []User
stmt := db.Session(&gorm.Session{DryRun: true}).Joins("JOIN orders ON orders.user_id = users.id").Where("users.status = ?", "active").Select("users.id, users.name, orders.amount").Find(&users)
sql := stmt.Statement.SQL.String()
vars := stmt.Statement.Vars
// 再用 Raw 执行 EXPLAIN(PostgreSQL 示例)
var explainResult []map[string]interface{}
db.Raw("EXPLAIN (ANALYZE, BUFFERS) "+sql, vars...).Scan(&explainResult)
注意点:
-
db.Raw()不做参数类型转换,vars必须和目标库语法对齐(如 PostgreSQL 的$1占位符,不能传@name) - 如果原 SQL 有子查询或 CTE,
EXPLAIN结果会嵌套,建议复制到 psql/pgAdmin 里看格式化输出 - 避免在事务里跑
EXPLAIN ANALYZE,某些数据库(如 MySQL 8.0+)会锁表或影响并发
Preload 和 Joins 的 EXPLAIN 差异特别明显
Preload("Orders") 默认生成两条独立查询(N+1)或一条 LEFT JOIN,但字段控制粒度差;而显式 Joins("JOIN orders...").Select("users.id, orders.amount") 能精准约束返回列,这对索引使用很关键。
常见陷阱:
-
Preload("Orders.Items.Tags")会生成两个 LEFT JOIN,若orders.user_id没索引,EXPLAIN显示type=ALL是常态 -
Joins不自动建索引,必须手动确认外键字段(如orders.user_id)上有INDEX,否则 JOIN 性能断崖下跌 - 结构体里有
gorm:"-"字段时,Find(&users)可能触发 GORM 自动补全SELECT *,导致 IO 暴增——EXPLAIN的rows值会远高于预期
EXPLAIN 关键字段怎么看
拿到 EXPLAIN 输出后,盯住这几个字段:
-
type(MySQL)或Node Type(PostgreSQL):ALL/Seq Scan表示全表扫描,大概率缺索引 -
rows(MySQL)或Rows Removed by Filter(PostgreSQL):如果远大于实际返回行数(比如查 5 行却扫 24 万),说明条件没走索引或索引失效 -
key(MySQL)或Index Name(PostgreSQL):为空表示没用上索引;非空但rows仍很大,可能是索引选择性差或统计信息过期 -
Buffers(PostgreSQL):Shared Hit高说明缓存友好;Shared Read高且慢,就要查磁盘 IO 或缓存配置
复杂查询别只信 GORM 生成的 SQL,EXPLAIN 是唯一能告诉你“数据库到底怎么执行”的工具——它不撒谎,但需要你亲手喂对语句、喂对参数。


















