导读:本期聚焦于小伙伴创作的《如何用 SQL 与 Grafana 实现慢查询 P99 延迟曲线和告警阈值联动?》,敬请观看详情。数据库慢查询的 P99 延迟一旦突增,往往意味着容量瓶颈或索引失效。单独看曲线很难判断何时该触发告警,固定阈值又容易产生误报。利用 Grafana 的 SQL 数据源,可以直接在查询中按时间窗口计算慢查询的 P99 分位延迟,再把计算结果同时用于时序面板和告警规则。这样曲线展示的数值与告警判断的逻辑完全同源,阈值可随历史基线动态调节。下文将说明表结构设想、P99 的 SQL 写法、Grafana 面板配置,以及让告警阈值跟随曲线分位数自动联动的具体步骤,帮助运维和开发建立稳定的慢查询监控体系。

在数据库可观测性建设中,慢查询的 P99 延迟是衡量尾部体验的关键指标。把 SQL 计算出来的 P99 曲线直接作为 Grafana 告警的判断依据,可以避免展示与告警两套逻辑不一致的问题。下面介绍从数据模型到面板联动的完整做法。

如何用 SQL 与 Grafana 实现慢查询 P99 延迟曲线和告警阈值联动?

一、慢查询日志的存储结构

要让 SQL 算出 P99,首先需要一个记录每次查询耗时的表。通常我们会把慢查询事件写入一张明细表,而不是只存聚合值,这样才能在 Grafana 里按任意时间窗口重新计算分位数。

表结构可以设计得比较简单,核心字段包括查询指纹、执行耗时、发生时间。指纹用于对同类 SQL 归类,耗时以毫秒整数保存,时间字段建议使用 UTC 时间戳。例如下面的 PostgreSQL 建表语句:

CREATE TABLE slow_query_log (
  id BIGSERIAL PRIMARY KEY,
  query_fingerprint TEXT NOT NULL,
  duration_ms INT NOT NULL,
  created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- 为时间范围扫描创建索引,提升 Grafana 高频查询性能
CREATE INDEX idx_slow_log_time ON slow_query_log (created_at);

如果使用的是 MySQL,可以把 TIMESTAMPTZ 换成 DATETIME(3) 并加上索引。明细表会随着时间膨胀,因此线上建议配合分区表或定时归档,只保留最近 30 天明细,历史仅留小时级聚合。

有了明细数据,Grafana 的 SQL 数据源就能在查询阶段完成分位计算,而不是把原始数据拉到前端再算,这样既准又省带宽。

二、用 SQL 计算慢查询 P99 延迟

P99 表示百分之九十九的查询耗时都小于该值,也就是尾部百分之一的极端耗时。在支持 percentile 函数的数据库里可以直接算;不支持时可用有序窗口函数近似。

以 PostgreSQL 配合 TimescaleDB 或原生 percentile_cont 为例,按一分钟窗口算 P99 的语句如下。该查询同时输出时间和 P99,可直接作为 Grafana 时序面板的查询结果。

SELECT
  time_bucket('1 minute', created_at) AS ts,
  percentile_cont(0.99) WITHIN GROUP (ORDER BY duration_ms) AS p99_ms
FROM slow_query_log
WHERE created_at >= now() - interval '3 hours'
GROUP BY ts
ORDER BY ts;

对于 MySQL 8.0,没有 percentile_cont,可以用累计百分比方式近似。思路是先按耗时排序给每行编号,再找最接近 99% 位置的值。

WITH ranked AS (
  SELECT
    DATE_FORMAT(created_at, '%Y-%m-%d %H:%i:00') AS ts,
    duration_ms,
    ROW_NUMBER() OVER (
      PARTITION BY DATE_FORMAT(created_at, '%Y-%m-%d %H:%i:00')
      ORDER BY duration_ms
    ) AS rn,
    COUNT(*) OVER (
      PARTITION BY DATE_FORMAT(created_at, '%Y-%m-%d %H:%i:00')
    ) AS cnt
  FROM slow_query_log
  WHERE created_at >= NOW() - INTERVAL 3 HOUR
)
SELECT ts, MAX(duration_ms) AS p99_ms
FROM ranked
WHERE rn >= cnt * 0.99
GROUP BY ts
ORDER BY ts;

这段 SQL 的缺点是当同一分钟内数据量很小时,P99 会退化为最大值,但这在慢查询场景通常可接受。若要求严格,可把窗口放大到五分钟。

无论哪种写法,核心是把分位计算下沉到数据库。这样 Grafana 拿到的已经是 p99_ms 序列,曲线和后续告警都基于同一列,天然一致。

三、Grafana 面板展示 P99 曲线

在 Grafana 中新建 SQL 数据源对应的 Dashboard,添加 Time series 面板,把上面第二种或第三种查询粘贴进 SQL 编辑器。X 轴用 ts,Y 轴用 p99_ms,即可得到平滑的慢查询 P99 延迟曲线。

为了让曲线具备可读性,建议在面板设置里把单位改为 ms,并开启连线样式。如果希望突出异常,可以添加一条阈值线,但这只是视觉参考,真正的告警我们在下一节用同一查询完成。

{
  "targets": [
    {
      "rawSql": "SELECT ts, p99_ms FROM (/* 上文 SQL */) t",
      "format": "time_series"
    }
  ],
  "fieldConfig": {
    "defaults": {
      "unit": "ms",
      "custom": { "drawStyle": "line" }
    }
  }
}

注意 Grafana 的 SQL 数据源通常要求查询返回两列且第一列是时间。若数据库时间带时区,需在面板设置里统一时区,避免曲线错位。

当多个业务线共用一张慢查询表时,可以在 SQL 里加 query_fingerprint 过滤,为每个服务建独立面板,互不干扰。

四、告警阈值与曲线联动的实现

Grafana 的 Alerting 支持直接使用面板的 SQL 查询作为告警数据源。我们不必另写一条告警 SQL,而是复用同一个 P99 查询,这样曲线和告警判断的是完全相同的数据集。

创建 Alert rule 时选择该面板查询,设置条件为 p99_ms 在过去五分钟的平均值大于某个阈值。关键技巧是阈值不要写死,而是用 Grafana 的阈值表达式引用历史基线,例如取前一天同时段 P99 的 1.5 倍。

-- 在告警专用 SQL 中同时算当前值与基线值
WITH cur AS (
  SELECT percentile_cont(0.99) WITHIN GROUP (ORDER BY duration_ms) AS p99
  FROM slow_query_log
  WHERE created_at >= now() - interval '5 minutes'
),
base AS (
  SELECT percentile_cont(0.99) WITHIN GROUP (ORDER BY duration_ms) AS p99_base
  FROM slow_query_log
  WHERE created_at >= now() - interval '1 day'
    AND created_at < now() - interval '1 day' + interval '5 minutes'
)
SELECT
  cur.p99 AS current_p99,
  base.p99_base * 1.5 AS threshold
FROM cur, base;

在 Alert rule 里设置条件 current_p99 > threshold,即可实现阈值随历史曲线自动浮动。当业务自然增长导致基线抬高时,告警阈值同步上升,减少误报;当突然出现尖刺超过基线一点五倍,则立即触发。

这种联动的本质是:曲线面板告诉人发生了什么,告警规则用同一逻辑告诉系统何时喊人。两者同源,维护成本也低,改一次 SQL 两边一起生效。

五、常见误区与优化建议

一个典型误区是把慢查询阈值(如执行超过两秒才记录)当成 P99 的输入,导致大量亚秒级但相对变慢的查询被忽略。建议记录所有查询并按耗时打标,或至少把记录门槛设得较低,保证 P99 有统计意义。

另一个误区是在 Grafana 用 Transformation 算分位数。前端聚合无法处理大数据量,且不能与告警共享。应坚持在 SQL 内完成计算。

做法优点缺点
SQL 内算 P99 并复用给面板与告警逻辑一致,阈值可动态需数据库支持窗口函数
固定阈值告警配置简单误报漏报多
前端 Transformation 算分位不挑数据源性能差,告警不可用

如果数据库确实不支持 percentile_cont,也可以用近似聚合表:定时任务每分钟算好 P99 写入聚合表,Grafana 直接查聚合表,同样能达到曲线与告警联动效果。

最后提醒,慢查询 P99 联动告警上线后,应观察一周误报率,再微调基线倍数。倍数过小会吵,过大则失去意义,通常 1.3 到 2 之间较稳妥。

SQLGrafanaslow_query_P99修改时间:2026-08-02 11:18:36

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。