如何使用 OpenTelemetry 将慢查询转化为可操作的可靠性指标

发布: (2026年2月4日 GMT+8 18:11)
11 分钟阅读
原文: Dev.to

看起来您只提供了来源链接,而没有贴出需要翻译的正文内容。请把您想要翻译的文本(文章正文)粘贴在这里,我就可以按照要求把它翻译成简体中文并保留原有的格式。谢谢!

介绍

慢速 SQL 查询会降低用户体验,导致连锁故障,并把本来简单的操作变成生产事故。传统的解决办法是什么?收集更多遥测数据。但更多的遥测意味着需要查看的东西更多,并不一定带来更多的理解。

我们不应把追踪视为日后可能分析的数据流,而应在决策时刻对重要的内容持明确立场。正如我们在 The Signal in the Storm 中所论述的,原始遥测只有在提取出有意义的模式后才有价值。

在本指南中,你将构建一个可重复的工作流,将 OpenTelemetry 数据库跨度转换为可在仪表盘展示并设置告警的跨度衍生指标——从而帮助你识别哪些慢、哪些最重要,以及哪些刚刚出现回退。

用例

我们将通过慢 SQL 查询来具体说明两个用例:

  • 优化 – 如果加速,哪些查询能带来最大的价值(按流量加权)?
  • 事件响应 – 哪些查询当前表现异常?

实验概述

我们将构建一个实验,在其中您的应用程序发出 OpenTelemetry 跟踪,我们将这些跟踪提炼为可操作的指标,首先进行简单的慢查询检测,然后添加流量加权影响,最后进行异常检测。

跳过理论? 请直接跳到下面的实验部分。

为什么“慢”是症状,而不是问题

一个 50 ms 的查询对报表仪表盘可能还算可以,但对结账流程来说却是灾难。正如 High Performance MySQL 所强调的,弄清楚查询慢的原因决定了修复的方向。最常见的原因包括:

问题描述
缺失或不可用的索引全表扫描;例如 SELECT * FROM orders WHERE customer_id = $1 在 10 K 行时耗时 20 ms,但在 10 M 行时会增长到分钟级。
错误的连接/聚合计划优化器误判基数,选择了错误的连接策略。
资源争用锁争用、连接池耗尽、CPU/I/O 饱和、内存压力。
计划退化参数敏感的执行计划,批量加载后统计信息陈旧。
N+1 问题许多快速查询(例如 100 × 2 ms)累计导致高延迟。
缺乏上下文数据库工具只能显示 什么 慢,却无法说明对面向用户的服务有何影响。

上下文丰富的追踪价值

分布式追踪将每个数据库跨度嵌入请求上下文(服务、端点、用户)。我们不必事后关联日志和追踪,而是可以直接从追踪中分析慢查询,并拥有完整的应用上下文。

构建模块

  • OpenTelemetry Collectordocker-otel-lgtm 配合使用(Grafana 套件:Loki、Grafana、Tempo、Mimir)。
  • 示例应用:基于 Go 的 “Album API”,从 PostgreSQL 提供音乐专辑数据,已使用 otelsql 进行仪表化。
  • 三个仪表盘:
    1. 按持续时间的查询
    2. 按流量(影响)加权的查询
    3. 异常检测

实验环境搭建

git clone https://github.com/causely-oss/slow-query-lab
cd slow-query-lab
docker-compose up -d

运行后,打开 http://localhost:3001 访问 Grafana,查看仪表盘。

仪表板 1 – 按持续时间划分的慢 SQL

TraceQL query

{ span.db.system != "" } | select(span.db.query.text, span.db.statement)
  • 按根操作(API 端点)和 SQL 语句分组。
  • 将持续时间聚合为平均值、最大值和计数。
  • 按平均持续时间排序(从慢到快)。

您将获得

  • 包含完整应用上下文的最慢查询表。
  • 出现次数计数。
  • 可点击进入单个追踪进行调试。

限制

按平均持续时间排序会忽略流量规模。一个运行 2 秒、执行 5 次的查询看起来“更糟”,而一个运行 150 毫秒、执行 1 万次的查询实际上影响的用户更多。

仪表板 2 – 流量加权影响

影响公式

Impact = Avg Duration × Count

使用相同的 TraceQL 查询,但我们添加了一个计算后的 Impact 字段并按其排序。

新增洞察

  • 服务细分 – 哪个服务触发了每个查询。
  • 延迟分布 – 随时间可视化持续时间。
  • 按影响力排名的查询 – 优先进行优化工作。

为什么重要

高流量、适度慢的查询会比罕见但慢的查询更突出,从而为“应该先优化哪些慢查询?”提供了有依据的答案。

Source:

Dashboard 3 – 异常检测

为了解答“有什么变化?”我们使用 spanmetrics 连接器从 span 中提取指标。

Collector 配置(摘录)

connectors:
  spanmetrics:
    dimensions:
      - name: db.system
        default: "unknown"
      - name: db.query.text
      - name: db.statement
      - name: db.name
        default: "unknown"
    exemplars:
      enabled: true

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [transform, batch]
      exporters: [spanmetrics, otlphttp/lgtm]

    metrics:
      receivers: [spanmetrics]
      processors: [batch]
      exporters: [otlphttp/lgtm]

该连接器会为查询延迟生成 直方图指标,并按以下标签标记:

  • service_name – 发起服务
  • db_system – 如 postgresql
  • db_query_text / db_statement – SQL 查询(模板)
  • db_name – 数据库名称

这些指标存储在 Mimir(兼容 Prometheus)中,我们在其中使用基于 PromQL 的异常检测。

Prometheus 记录规则(来自 Grafana 的异常检测框架)

  • 基线 – 历史值的平滑平均。
  • 上/下限带 – 基线 ± N 个标准差。
  • 异常 – 当前值超出上下限带。

异常检测仪表盘

  • 将当前延迟与自适应基线带进行绘图。
  • 突出显示每个查询的异常。
  • 按查询展示细分,以便快速定位。

它如何回答“有什么变化?”

  • 一个始终慢的查询(例如基线 = 450 ms)在保持 450 ms 时 不会 触发异常。
  • 一个通常很快的查询(基线 = 50 ms)如果突升至 200 ms,则会触发异常。

指标卫生

  • 基数爆炸 – 在指标标签中使用原始 SQL 会为每个字面值创建一个序列。缓解方法:

    • 使用预处理语句(捕获模板,而非字面值)。
    • 对查询文本进行规范化。
    • 在 spanmetrics 连接器中设置 aggregation_cardinality_limit
  • 敏感数据 – 在 Collector 导出之前对敏感属性进行脱敏或删除。

  • 基线预热 – 自适应规则需要 24–48 h 的数据;先使用更宽的阈值,随后逐步收紧。

从症状到根本原因

即使使用异常检测,你看到的也是 症状。真实世界的事件通常涉及多个需要关联的症状:

  • 模式迁移后缺失索引
  • 由于统计信息陈旧导致查询计划回退
  • 并发批处理作业引起的锁争用
  • 噪声邻居导致的资源压力
  • 上游服务降级引发的重试风暴

手动排查是可能的,但耗时且难以扩展。

迎接 Causely

Causely 自动化了我们构建的模式:

  1. 提炼 – 提取慢查询和其他症状。
  2. 因果模型 – 将症状与系统依赖(端点、用户、上游服务)关联。
  3. 根本原因识别 – 追踪因果链(例如,“搜索查询慢是因为索引被删除”)。
  4. 可操作的建议 – 通过 AskCausely 获取具体的后续步骤(添加索引、回滚部署、解决上游压力)。

提炼‑检测‑呈现的流水线是基础;Causely 将其扩展为大规模的完整根因分析。

结论

通过将 OpenTelemetry 数据库跨度转化为指标、应用流量加权影响评分,并叠加异常检测,您可以获得:

  • 对慢查询的即时可视化,并提供完整的应用上下文。
  • 基于真实用户影响的优先级排序。
  • 对异常行为的自动检测。

将其与 Causely 的因果推理相结合,可实现从 症状根本原因 的转变,从而加快基于数据的性能工程和事件响应。


亲自尝试: 向 Causely 询问慢查询,看看它如何将其关联到根本原因。

Back to Blog

相关文章

阅读更多 »

当 AI 给你一巴掌

当 AI 给你当头一棒:在 Adama 中调试 Claude 生成的代码。你是否曾让 AI “vibe‑code” 一个复杂功能,却花了数小时调试细微的 bug……