db.Query 比 db.QueryRow 慢主因是误用致连接未释放或结果集未消费完,引发连接池阻塞;查单行必须用 db.QueryRow,它自动管理生命周期且不泄漏游标。

为什么 db.Query 比 db.QueryRow 慢很多?
不是因为函数本身开销大,而是误用导致连接未及时释放或结果集未消费完,引发连接池阻塞。常见于只取单行却用 db.Query + 忘记 rows.Close()。
- 查单行必须用
db.QueryRow:它自动处理单行生命周期,不会泄漏游标 - 若坚持用
db.Query,务必在defer rows.Close()前检查err,否则 panic 时defer不执行 -
db.QueryRow在 scan 失败时返回sql.ErrNoRows,而db.Query的rows.Next()返回false,语义不同,别混用
如何避免 SELECT * 导致的 ORM 性能陷阱?
Go 的 database/sql 本身不解析字段,但搭配 sqlx 或 gorm 时,SELECT * 会强制读取所有列、反序列化全部字段,哪怕只用其中 2 个。
- 显式列出所需字段,例如
SELECT id, name, updated_at FROM users WHERE id = ? - 对宽表(>15 列)或含
TEXT/BLOB字段的表,省略无用字段可降低网络传输和内存分配压力 -
gorm中禁用SELECT *:设db.Session(&gorm.Session{NewDB: true})并手动写Select(),避免Find()默认全选
sql.DB.SetMaxOpenConns 和 SetMaxIdleConns 怎么配才不翻车?
这两个值不是越大越好,配错反而放大锁竞争或连接耗尽。关键看数据库服务端最大连接数和单次查询平均耗时。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 先查 MySQL 的
max_connections(如 151),Go 侧SetMaxOpenConns建议设为该值的 60%~80%,留余量给后台任务或其他服务 -
SetMaxIdleConns通常设为SetMaxOpenConns的 1/2~1/3;过大会占着空闲连接不放,导致新请求等不到连接 - 线上必须开启
SetConnMaxLifetime(如 30m):避免长连接因防火墙超时被静默断开,后续调用报driver: bad connection
为什么加了索引查询还是慢?从 EXPLAIN 到 Go 代码要连查三处
索引失效常不在 SQL,而在 Go 层参数绑定或类型不匹配,导致优化器弃用索引。
立即学习“go语言免费学习笔记(深入)”;
- MySQL 执行
EXPLAIN SELECT ... WHERE status = ?,看type是否为ref或const;若为ALL,说明没走索引 - 检查 Go 中传参类型:比如数据库字段是
TINYINT(1),却传bool,MySQL 隐式转换后索引失效;统一用int8或uint8 - 注意
IN参数数量:超过 500 个值时,预编译语句可能触发 MySQL 的max_prepared_stmt_count限制,降级为普通查询,索引也可能失效
mysql 驱动默认开启,但 postgres 驱动需显式传 prefer_simple_protocol=false,否则每次查询都重编译。


















