应包装 database/sql/driver 的 driver 和 conn 接口实现代理,在 prepare/query/exec 中统一计时并记录超阈值 sql;gorm 则直接配置 slowthreshold 并启用 warn 日志级别,避免全量日志开销。

怎么用 sql.DB 拦截并记录慢 SQL
Go 原生 sql.DB 不提供钩子机制,没法直接“拦截”语句执行。必须自己包一层,核心是用 database/sql/driver 的 Driver 和 Conn 接口做代理。
常见错误是试图在 db.Query 外套个计时器——这只能测到准备阶段,不包括网络往返和数据库实际执行时间。
- 正确做法:实现一个包装
driver.Conn的结构体,在Prepare、Query、Exec等方法里统一打点 - 关键参数:慢查询阈值建议设为
200 * time.Millisecond(业务敏感度不同可调) - 注意兼容性:
sqlx或gorm底层仍走sql.DB,只要你的包装驱动注册正确,它们也能被监控到 - 示例片段:
type slowLogConn struct { driver.Conn timeout time.Duration } func (c *slowLogConn) Query(query string, args []driver.Value) (driver.Rows, error) { start := time.Now() rows, err := c.Conn.Query(query, args) if time.Since(start) > c.timeout { log.Printf("[SLOW SQL] %s | took %v", query[:min(len(query), 100)], time.Since(start)) } return rows, err }
gorm 里怎么加 SQL 执行耗时日志
如果你用的是 gorm,别自己重写驱动,它内置了 logger 接口,且支持细粒度控制。
容易踩的坑是只开了 LogMode 却没配 SlowThreshold,结果所有 SQL 都打出来,根本分不清哪条真慢。
- 必须显式设置
SlowThreshold,例如:config.SlowThreshold = 200 * time.Millisecond - 默认 logger 只打印慢 SQL 的语句和耗时,不带参数值;如需脱敏后参数,得自定义
logger.Writer实现 - 注意性能影响:开启完整日志(含参数)会触发反射和字符串拼接,压测时可能放大延迟,上线建议关掉
LogLevel到Warn或Error - 示例配置:
db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{ Logger: logger.Default.LogMode(logger.Warn). WithConfig(logger.Config{SlowThreshold: 200 * time.Millisecond}), })
为什么 context.WithTimeout 不能替代慢查询监控
很多人误以为给 db.QueryContext 加个 timeout 就等于监控慢查询——其实这是两回事。
context.WithTimeout 是控制 Go 程序等待结果的上限,不是测量 SQL 真实执行时间。数据库可能已执行 5 秒,但网络卡在返回途中,context 超时了,你却以为是“SQL 慢”,实际可能是连接池打满或防火墙丢包。
- 真实慢查询必须从数据库端发起测量:比如 MySQL 的
long_query_time+slow_query_log,或 PostgreSQL 的log_min_duration_statement - Go 层监控和 DB 层监控要对齐阈值,否则会出现“Go 认为不慢,DB 日志却记了一堆”的情况
- 如果发现大量 context cancel 但 DB 慢日志为空,优先查连接池(
db.SetMaxOpenConns)、DNS 解析、TLS 握手等基础设施问题
生产环境要不要记录完整 SQL?
不要无条件记录,尤其含参数的完整 SQL。既涉及敏感数据泄露风险,也带来可观的内存和 I/O 开销。
真正需要的不是“每条都记”,而是“能快速定位哪类语句在变慢”。重点该放在:模式匹配(如 SELECT * FROM users WHERE created_at )、表名、执行计划变化、索引缺失提示。
- 推荐做法:只记录
query[:128]+ 参数类型(如string/int64)+ 表名 + 耗时 + 错误码 - 避免记录:用户 ID、手机号、token、密码字段相关语句的参数值(哪怕脱敏也要额外 CPU)
- 如果审计强要求留痕,用数据库自身的审计日志(MySQL Enterprise Audit / PG Audit),别让应用层扛
慢查询监控不是加个计时器就完事。最常被忽略的是:没有把 Go 层耗时和数据库实际执行时间分开看,也没有和数据库自身的慢日志做交叉验证。一旦出现偏差,排查方向立刻跑偏。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











