SQL 查询故障排除
SQL 查询可能无法执行、不返回任何行或生成看似合理但不正确的结果。将这些视为不同的问题。从证明表、模式和时间范围的最小读取开始,然后一步一步地恢复分析逻辑。
Telemetry 使用 Apache DataFusion SQL。即使分析思路是合理的,从 PostgreSQL、BigQuery、Snowflake、MySQL 或其他引擎复制的语法也可能需要重写。
1.读取响应类
对于同步查询API:
400通常意味着无效的JSON、缺少SQL、不兼容的SQL、缺少表或字段或者类型错误401表示 API 密钥丢失或无效403表示密钥缺少读取范围429表示请求应该等待并使用有界退避5xx可以重试有限次数
请勿重试未更改的无效 SQL。捕获安全错误文本、端点和请求 ID,但切勿仅仅因为查询失败而记录密钥。
对于异步查询,请区分 queued、running、completed、failed 和 cancelled。从 result 直接读取较小的 JSON 结果;从 download_url 下载较大的 JSON 结果和 Parquet 文件。请参阅导出查询结果。
2.证明表格和时间范围
将原始查询替换为有界样本:
SELECT *
FROM api_requests
WHERE timestamp_utc >= now() - INTERVAL '24 hours'
ORDER BY timestamp_utc DESC
LIMIT 10;
如果这不返回任何行:
- 验证规范化表名
- 扩大UTC时间范围
- 删除可选过滤器
- 检查表视图中最近的示例行和架构
- 发送具有唯一标识符的合成事件
如果合成行也丢失,请先按照 事件摄取故障排除 进行操作,然后再进一步编辑 SQL。
3. 比较字段名称和类型
不要从应用程序代码推断列名称。检查表架构,包括嵌套的点路径和精确类型。
常见错误包括:
- 查询
timestamp而不是生成的timestamp_utc - 当事件合约存储
duration_ms时使用duration - 将整数状态代码与字符串进行比较
- 在生产者更改类型后将数字标识符视为文本
- 引用没有存储模式所需的标识符形式的嵌套路径
- 聚合非数字字段
从简单的 SELECT 中的精确字段开始。然后添加比较、函数或聚合。这可以确定问题是列分辨率还是应用于列的操作。
4、分层减少查询
对于具有多个 CTE、联接和窗口的查询:
- 自行运行第一个 CTE
- 添加下一个 CTE 并检查其行颗粒
- 使用可见的标识符和行数测试每个连接
- 在连接的行正确后添加聚合
- 最后添加窗口计算
使用临时诊断列(例如 COUNT(*) 和 COUNT(DISTINCT account_id))来检测行乘法。仅在了解结果粒度后才将其删除。
5. 翻译不支持的方言语法
使用 DataFusion SQL 参考 和 经过测试的食谱库 中演示的语法。特别是:
- 使用
SUM(CASE WHEN condition THEN 1 ELSE 0 END)进行条件计数 - 使用
approx_percentile_cont(latency_ms, 0.95)近似 p95 - 使用
date_trunc('hour', timestamp_utc)作为时间段 - 使用区间算术,例如
now() - INTERVAL '24 hours' - 使用
NULLIF(denominator, 0)保护比率 - 使用
LAG(value) OVER (ORDER BY bucket)进行之前的存储桶比较
函数名称和签名是特定于方言的。在另一个数据库中具有相同用途的函数并不能证明 DataFusion 接受该拼写。
6. 诊断空结果
空结果通常是过滤器交互,而不是丢失数据。一次删除一个谓词:
SELECT
environment,
status,
COUNT(*) AS events
FROM workflow_events
WHERE timestamp_utc >= now() - INTERVAL '7 days'
GROUP BY environment, status
ORDER BY events DESC;
这公开了实际存储的值。在恢复狭窄谓词之前检查大小写、空格、空值、重命名状态、环境边界和不完整的最近周期。
对于漏斗和队列,确认每个里程碑都使用相同的身份单位,并且观察窗口足够长,以便后续步骤发生。
7. 在总计之前审核加入
写下每一行代表什么。如果双方每个标识符都包含多行,则直接联接可能会增加收入、事件计数或活跃用户数。
将每一面预先聚集到预期的颗粒上:
WITH requests AS (
SELECT
request_id,
MIN(timestamp_utc) AS requested_at
FROM request_events
GROUP BY request_id
),
outcomes AS (
SELECT
request_id,
MAX(CASE WHEN status = 'success' THEN 1 ELSE 0 END) AS succeeded
FROM outcome_events
GROUP BY request_id
)
SELECT
COUNT(*) AS requests,
SUM(COALESCE(outcomes.succeeded, 0)) AS succeeded
FROM requests
LEFT JOIN outcomes ON requests.request_id = outcomes.request_id;
比较连接前后的不同标识符。故意包含不匹配的行,而不是允许内部联接删除丢失的结果。
8. 验证比率、百分位数和窗口
比率需要分子和分母。将它们返回到百分比旁边,直到计算可信为止,并在对组进行排名之前设置最小有意义的数量。
百分位数应使用带有一个记录单位的数字字段。比较 p50 和 p95,了解是整个分布发生变化还是仅尾部发生变化。
在本系列开始时,滚动窗口的观察较少。最新的时间段可能不完整。在滚动基线上发出告警之前标记或排除这两种情况。
9. 用夹具证明预期结果
创建一个小型合成夹具,其答案可以手动计算:
- 一次成功
- 一次永久性故障
- 一次故障随后恢复
- 一个重复的标识符
- 一个空可选字段
- 一个事件正好发生在一个时间界限上
运行中间 CTE 并将最终行与预期答案进行比较。规划证明语法和类型;固定装置有助于证明解释。
查询审核清单
- 检查了精确的表和模式
- UTC 窗口和存储桶完整性已确认
- 字段类型和单位与操作相匹配
- 在每次连接之前记录行粒度
- 分子、分母和可见的最小体积
- 重试、重复、延迟事件和空值具有明确的行为
- 合成边界情况与预期输出匹配
- 保存的结果包括定义、所有者和响应
Telemetry 在 SQL测试方法 中发布引擎版本和自动检查。当您需要已知的兼容模式时,请从 API 可靠性、后台工作、产品分析 或 数据质量 中的配方开始。