sql耗时指标必须用histogram,因其支持分桶统计和分位数计算(如histogram_quantile),可准确反映90%/95%/99%延迟分布;而summary不支持多实例合并,gauge仅存瞬时值,均无法满足分布分析需求。

SQL 耗时指标必须用 Histogram,别用 Summary 或 Gauge
长耗时 SQL 的监控核心是分布分析——你不仅关心“平均多久”,更需要知道 90%、95%、99% 分位延迟是否超标。Histogram 天然支持分桶统计和分位数计算(通过 histogram_quantile),而 Summary 是客户端聚合、不支持多实例合并,Gauge 只能存当前值,完全无法反映分布特征。
常见错误是用 prometheus.NewGaugeVec 存单次 SQL 耗时,结果指标只保留最后一次值,历史毛刺全丢;或误用 Summary 导致 Prometheus 抓取到的分位数在多副本间不可靠。
-
prometheus.NewHistogramVec的Buckets必须覆盖业务真实延迟范围,比如 OLTP 场景建议:[]float64{0.01, 0.05, 0.1, 0.3, 0.5, 1.0, 3.0, 10.0}(单位:秒) - Label 必须包含可区分 SQL 类型的维度,至少包括
"sql_type"(如"select"、"update")、"table_name",避免所有 SQL 打到同一个指标下导致桶溢出 - 不要为每条 SQL 文本单独打标(如
"WHERE id = ?"),会引发高基数问题,改用归一化后的标签,例如"user_get_by_id"
如何在 database/sql 或 GORM 中注入耗时采集逻辑
不能靠日志解析或外部 APM 工具被动捕获——要直接在 SQL 执行路径里埋点,确保 100% 覆盖且无采样丢失。
对原生 database/sql,推荐用 sql.Driver 包装器(如 prometheus-sql 库)或自定义 sql.Conn 拦截;对 GORM v2+,必须使用 callbacks 或 plugin 机制,而非中间件(GORM 中间件不覆盖 Raw SQL 执行)。
- GORM 示例:注册一个 callback,在
process阶段前后记录开始/结束时间,并调用sqlDuration.WithLabelValues(sqlType, tableName).Observe(elapsed.Seconds()) - 务必在
callback.Create().After("gorm:after_create")等具体钩子点注册,避免用泛用的callback.Process()导致重复计时 - 注意事务内多语句场景:一条事务含 3 条 UPDATE,应分别计时,而不是整个事务只记一次
/metrics 端点返回空或缺失 SQL 指标
最常见原因是指标没注册进实际暴露的 registry。默认 promhttp.Handler() 只暴露 prometheus.DefaultRegisterer 里的指标,但如果你用了自定义 registry(比如 GORM 插件内部新建了一个),而没传给 promhttp.HandlerFor(),那 SQL 指标就永远不出现在 /metrics 里。
- 检查是否调用了
prometheus.MustRegister(sqlDuration)—— 如果sqlDuration是在某个包 init 函数里定义的,但该包没被 import,指标根本不会初始化 - 若用
promauto.NewHistogramVec,确认它绑定的是prometheus.DefaultRegisterer,否则需显式传入:promauto.With(prometheus.DefaultRegisterer) - curl -v http://localhost:8080/metrics | grep sql_duration_seconds_count —— 如果这条命令无输出,说明指标未注册或命名不符合规范(name 不能含大写字母、空格、点号)
为什么慢 SQL 告警总滞后?查这三处硬伤
Prometheus 默认 15s 抓取一次,但 SQL 耗时指标本身是实时更新的。告警滞后通常不是采集问题,而是 PromQL 查询或 Alertmanager 配置失当。
- 别用
rate(sql_duration_seconds_count[1m])查慢 SQL 频次——这是总量速率,毫无意义;正确写法是:histogram_quantile(0.95, sum(rate(sql_duration_seconds_bucket[1h])) by (le, sql_type, table_name)) > 1.0 - Alertmanager 的
group_wait和group_interval默认是 30s,意味着同一类慢 SQL 在 1 分钟内只发一次告警,调低它们才能提速 - Grafana 查看面板时,时间范围选 “Last 5 minutes” 但步长设成 “30s”,会导致分位数曲线锯齿严重——步长应 ≤ 抓取间隔(如设为 “15s”)
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











