90%以上go应用数据库慢查询源于sql未走索引、连接池耗尽或过滤逻辑未下推至数据库;应先查mysql慢日志(需配置slow_query_log=on及long_query_time)和执行计划(explain),再优化索引、连接池参数与sql写法。

Go 应用数据库慢查询,90% 以上不是 Go 写得差,而是 SQL 没走索引、连接池卡死、或 Go 层把过滤逻辑搬到了内存里。先别改代码,直接看数据库执行计划和连接状态。
查不到慢查询日志?开关在 MySQL,不在 Go
Go 应用本身不生成慢查询日志,slow_query_log 和 long_query_time 是 MySQL 的配置项。常见错误是只在 Go 里加 db.SetLogger 或开 GORM 日志,结果日志里全是“SQL 执行了”,却看不到“为什么慢”。
- 登录 MySQL 执行
SHOW VARIABLES LIKE 'slow_query_log';,确认值为ON - 开发环境建议设
SET GLOBAL long_query_time = 0.1;(单位秒),否则默认 10 秒的阈值基本没用 - 日志路径由
slow_query_log_file控制,典型位置是/var/lib/mysql/xxx-slow.log,不是 Go 项目目录下 - MySQL 8.0+ 默认关闭慢日志,
SET GLOBAL重启即失效,必须写进/etc/mysql/my.cnf的[mysqld]段落并重启服务
EXPLAIN 显示 type=ALL?大概率缺索引
拿到慢 SQL 后,第一件事是 EXPLAIN SELECT ...。如果 type 列是 ALL,且 rows 值远大于实际返回行数(比如查 5 行,rows=248391),就是典型的全表扫描——不是 SQL 写得烂,是没索引可走。
- WHERE 条件字段必须建索引:如
WHERE tradeofferid = ? AND accepted = ?,应建复合索引idx_trade_accepted (tradeofferid, accepted) - ORDER BY 字段也要索引:如
ORDER BY created_at DESC,单独建idx_created_at (created_at)或纳入复合索引最右位 - 避免冗余索引:
tradeofferid单列索引在已有(tradeofferid, accepted)复合索引时完全多余,会拖慢 INSERT/UPDATE 速度 - 字符串字段注意长度:若
tradeofferid是VARCHAR(64),建索引时要显式指定长度,如INDEX idx_tradeid (tradeofferid(32)),否则可能因隐式截断失效
QueryRow 和 Query 选错?连接池会悄悄耗尽
QueryRow 和 Query 性能差异几乎可以忽略,但用错场景会导致连接泄漏,进而让整个 DB 操作排队阻塞。
- 明确只取一行 → 用
QueryRow,它自动调rows.Close(),语义清晰也防漏 - 需要遍历多行 → 必须用
Query+defer rows.Close(),漏掉Close()就等于“借连接不还”,连接池很快打满 - 循环里反复调
QueryRow查不同 ID?这是 N+1 查询,应改用WHERE id IN (?, ?, ?)或 JOIN 合并 -
QueryRow.Scan()遇到空结果会报sql.ErrNoRows;Query不会,但你不rows.Next()就直接Scan会 panic
连接池参数设多少?不是越大越好
连接池配置不当比 SQL 慢更致命。设太大浪费 DB 资源,设太小请求排队;更隐蔽的是没设 ConnMaxLifetime,导致 DB 主动断连后 Go 还拿着失效连接重试失败。
-
SetMaxOpenConns:建议按「峰值 QPS × 平均查询耗时(秒)」估算,比如峰值 120 QPS、平均查一次 150ms,就设20(120 × 0.15 ≈ 18 → 向上取整) -
SetMaxIdleConns:通常设为SetMaxOpenConns的 1/2~2/3,但不能小于 1;设为 0 表示不缓存空闲连接,每次都要新建 -
SetConnMaxLifetime必须设,如30 * time.Minute,防止 DB 端 kill 掉长连接后 Go 还在用 - 事务里漏掉
tx.Commit()或tx.Rollback()是最常踩的坑:该连接永远不归还,后续所有db.Query都在等它
真正难的不是加索引或调参数,而是区分“SQL 慢”和“Go 让 SQL 变慢”——比如从 DB 拉 10 万行再用 for 循环筛 5 条,这时 EXPLAIN 再好看也没用,问题在 Go 没把 WHERE 下推。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











