高并发下数据库慢查询主因是连接池配置错误、索引失效(常因go参数类型不匹配)和go层过滤逻辑上移;应优先检查这三处。需开启mysql慢日志、合理设置连接池参数、确保go传参类型与字段一致、显式指定select字段、使用request-scoped context。

高并发下数据库慢查询,八成不是 SQL 写得差,而是连接池卡死、索引没建对、或者 Go 层把过滤逻辑搬到了内存里——先查这三处,比改 SQL 更快见效。
查不到慢查询日志?MySQL 没开开关
Go 应用本身不生成慢查询日志,日志开关在 MySQL 侧。很多人在 Go 代码里加日志、调 GORM 的 LogMode,结果一无所获。
- 登录 MySQL 执行
SHOW VARIABLES LIKE 'slow_query_log';,确认值为ON - 设低阈值便于定位:
SET GLOBAL long_query_time = 0.1;(开发环境) - 日志路径由
slow_query_log_file控制,常见为/var/lib/mysql/xxx-slow.log,不是你的 Go 项目目录 - MySQL 8.0+ 默认关闭慢日志,
SET GLOBAL重启即失效,必须写入my.cnf持久化
连接池参数设错,比 SQL 慢更致命
SetMaxOpenConns 和 SetMaxIdleConns 配错,会导致连接排队、阻塞、甚至 driver: bad connection 错误,而你还在优化 WHERE 条件。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
-
SetMaxOpenConns建议设为「数据库max_connections× 0.7」,比如 MySQL 返回max_connections=151,就设db.SetMaxOpenConns(100) -
SetMaxIdleConns设为SetMaxOpenConns的 1/3~1/2,如30~50;设太大,空闲连接占着不放,新请求等不到连接 - 必须加
db.SetConnMaxLifetime(30 * time.Minute):防防火墙静默断连后,Go 还拿着失效连接重试失败 - 上线后定期跑
db.Stats(),关注WaitCount是否突增——飙升说明连接池已吃紧
EXPLAIN 显示 type=ALL?先看 Go 传参类型是否匹配
加了索引还慢,常因 Go 层参数类型和数据库字段不一致,触发隐式转换,导致索引失效。MySQL 的 EXPLAIN 看到 type=ALL,别急着改 SQL,先核对 Go 传参。
- 数据库字段是
TINYINT(1),Go 却传bool→ MySQL 转成整数比较,索引失效;统一用int8或uint8 - 时间字段用
time.Time直接传,别转成字符串再拼进 SQL,否则索引失效 + 类型校验开销 -
IN查询参数超 500 个时,MySQL 可能降级为普通查询,prepared statement缓存失效;分批处理,每批 ≤ 300 - 检查驱动是否启用预编译缓存:mysql 驱动默认开,postgres 驱动需显式加
?prefer_simple_protocol=false
SELECT * 在 Go 里不只是多查字段,它会 panic
表面上只是少写几个字段名,实际在 Go 中会引发三类问题:列数错位直接 panic、大文本字段触发频繁 GC、结构体未同步新增字段导致运行时报错。
- 永远显式列出字段:
SELECT id, name, status FROM users WHERE id = ? - 宽表或含
TEXT/BLOB字段时,省略无用字段可显著降低内存分配压力 - GORM 用户禁用默认全选:
db.Session(&gorm.Session{NewDB: true}).Select("id", "name").Find(&users) - 用
sql.NullString等类型接允许 NULL 的字段,别用原生string,否则 Scan 失败直接 panic
最易被跳过的一环是:没在 HTTP handler 里传 request-scoped context,而是用了 context.Background()。请求已取消,SQL 还在数据库里跑着——既拖慢 DB,又卡住连接池,且无法感知。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










