如何使用 OpenTelemetry 将慢查询转化为可操作的可靠性指标
看起来您只提供了来源链接,而没有贴出需要翻译的正文内容。请把您想要翻译的文本(文章正文)粘贴在这里,我就可以按照要求把它翻译成简体中文并保留原有的格式。谢谢!
介绍
慢速 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 Collector 与
docker-otel-lgtm配合使用(Grafana 套件:Loki、Grafana、Tempo、Mimir)。 - 示例应用:基于 Go 的 “Album API”,从 PostgreSQL 提供音乐专辑数据,已使用
otelsql进行仪表化。 - 三个仪表盘:
- 按持续时间的查询
- 按流量(影响)加权的查询
- 异常检测
实验环境搭建
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– 如postgresqldb_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 自动化了我们构建的模式:
- 提炼 – 提取慢查询和其他症状。
- 因果模型 – 将症状与系统依赖(端点、用户、上游服务)关联。
- 根本原因识别 – 追踪因果链(例如,“搜索查询慢是因为索引被删除”)。
- 可操作的建议 – 通过 AskCausely 获取具体的后续步骤(添加索引、回滚部署、解决上游压力)。
提炼‑检测‑呈现的流水线是基础;Causely 将其扩展为大规模的完整根因分析。
结论
通过将 OpenTelemetry 数据库跨度转化为指标、应用流量加权影响评分,并叠加异常检测,您可以获得:
- 对慢查询的即时可视化,并提供完整的应用上下文。
- 基于真实用户影响的优先级排序。
- 对异常行为的自动检测。
将其与 Causely 的因果推理相结合,可实现从 症状 到 根本原因 的转变,从而加快基于数据的性能工程和事件响应。
亲自尝试: 向 Causely 询问慢查询,看看它如何将其关联到根本原因。