将 LogQL、KQL 和 SPL 查询迁移到 SQL
将操作查询移至 SQL 是数据模型迁移,而不是查找和替换练习。 LogQL 从日志流和标签选择器开始,Kusto 查询语言使用表格管道,Splunk SPL 通过命令转换搜索结果。 SQL 从关系开始,使选择、分组、连接和投影变得明确。
最安全的迁移在更改语法之前保留问题和结果契约。
首先写下语义契约
对于每个查询,记录:
- 它支持的决定;
- 来源和时间窗口;
- 行或事件粒度;
- 解析的字段及其类型;
- 分组维度;
- 分子和分母;
- 空、重复、重试和迟到规则;
- 预期的列和排序顺序。
在相同的有限间隔内运行旧查询和新查询。首先比较总数,然后比较组级结果,然后比较代表性原始行。视觉上看起来相似的结果仍然可以使用不同的分母。
共享示例事件
下面的翻译假设每个完成的 API 请求有一个键入的行:
{
"timestamp": "2026-07-28T16:04:00Z",
"event_name": "api_request_completed",
"service": "checkout-api",
"route": "/v1/orders/:id",
"status_code": 503,
"latency_ms": 842,
"request_id": "req_01J...",
"release": "2026.07.28"
}
如果旧系统仅存储 "GET /v1/orders/123 returned 503 in 842ms" 等消息,请首先添加解析器或更改检测。 SQL 无法恢复从未记录过的稳定路由模板或可靠数字类型。
概念图
| 意图 | LogQL | KQL | SPL | SQL |
|---|---|---|---|---|
| 选择来源 | 流选择器 | 表表达式 | 索引和源搜索 | FROM table |
| 过滤行 | 行或标签过滤器 | where |
search 或 where |
WHERE |
| 解析字段 | 解析器表达式 | parse、extend |
rex、spath、eval |
最好在查询之前输入列 |
| 选择列 | 行格式 | project |
fields 或 table |
SELECT |
| 骨料 | 指标查询 | summarize |
stats 或 timechart |
骨料加GROUP BY |
| 管道 | ` | ` 阶段 | ` | ` 运算符 |
| 时间桶 | 范围向量 | bin() |
timechart span= |
date_trunc() |
该表映射了意图,而不是完全等同。例如,Loki标签参与流索引和基数约束; SQL 列不会自动具有相同的存储行为。
示例1:过滤最近的错误
LogQL
{service="checkout-api"} | json | status_code >= 500
KQL
ApiRequestCompleted
| where Timestamp > ago(1h)
| where Service == "checkout-api" and StatusCode >= 500
| project Timestamp, Route, StatusCode, LatencyMs, RequestId
| order by Timestamp desc
SPL
index=production service=checkout-api status_code>=500 earliest=-1h
| table _time route status_code latency_ms request_id
| sort - _time
SQL
SELECT
timestamp_utc,
route,
status_code,
latency_ms,
request_id
FROM api_request_completed
WHERE timestamp_utc >= now() - INTERVAL '1 hour'
AND service = 'checkout-api'
AND status_code >= 500
ORDER BY timestamp_utc DESC
LIMIT 200;
显式限制可防止探索性查询返回无界的事件窗口。它不是汇总报告的一部分。
示例2:按路由计算错误率
管道语言通常使分子易于查看,而分母则由早期阶段隐含。将两者保留在 SQL 结果中:
SELECT
route,
COUNT(*) AS requests,
SUM(CASE WHEN status_code >= 500 THEN 1 ELSE 0 END) AS errors,
ROUND(
100.0 * SUM(CASE WHEN status_code >= 500 THEN 1 ELSE 0 END)
/ NULLIF(COUNT(*), 0),
2
) AS error_rate_pct
FROM api_request_completed
WHERE timestamp_utc >= now() - INTERVAL '24 hours'
AND service = 'checkout-api'
GROUP BY route
ORDER BY error_rate_pct DESC, requests DESC;
不要将 LogQL count_over_time 转换为 COUNT(*),除非您确认一个已解析的日志条目对应于一个 SQL 行并且重试或多行消息不会更改粒度。
示例3:构建时间表
KQL summarize ... by bin(Timestamp, 5m)、SPL timechart span=5m 和 LogQL 范围聚合均表示时间序列。在 DataFusion SQL 中,便携式第一步是一个小时桶:
SELECT
date_trunc('hour', timestamp_utc) AS hour,
release,
COUNT(*) AS requests,
approx_percentile_cont(latency_ms, 0.95) AS p95_latency_ms
FROM api_request_completed
WHERE timestamp_utc >= now() - INTERVAL '7 days'
GROUP BY date_trunc('hour', timestamp_utc), release
ORDER BY hour ASC, release ASC;
选择引擎支持且适合事件量的存储桶。当最新的存储桶不完整时,将其排除或注释。
示例 4:用公用表表达式替换管道
管道是可读的,因为每个阶段都会转换先前的表。 SQL 公用表表达式可以保留该形状:
WITH recent_requests AS (
SELECT *
FROM api_request_completed
WHERE timestamp_utc >= now() - INTERVAL '24 hours'
AND service = 'checkout-api'
),
route_summary AS (
SELECT
route,
COUNT(*) AS requests,
SUM(CASE WHEN status_code >= 500 THEN 1 ELSE 0 END) AS errors
FROM recent_requests
GROUP BY route
)
SELECT
route,
requests,
errors,
ROUND(100.0 * errors / NULLIF(requests, 0), 2) AS error_rate_pct
FROM route_summary
WHERE requests >= 100
ORDER BY error_rate_pct DESC;
根据其含义命名阶段,而不是 step1 或 filtered。生成的查询仍然易于查看和测试。
解析是迁移风险
现有查询可能依赖于正则表达式、JSON 提取、自动字段发现或特定于供应商的搜索时解析。盘点每个派生字段:
| 派生字段 | 新事件场 | 类型 | 切换期间的回退 |
|---|---|---|---|
| 请求方式 | method |
字符串类别 | 解析旧消息 |
| 路线模板 | route |
字符串类别 | 将原始路径映射到模板 |
| 响应码 | status_code |
整数 | 转换解析值 |
| 持续时间 | latency_ms |
整数 | 标准化秒或微秒 |
| 释放 | release |
字符串类别 | 从部署元数据中丰富 |
运行双重收集,直到键入的字段在生产者中存在并且正确。不要删除旧的解析器,因为单个 happy-path 服务匹配。
需要重新设计的供应商特定功能
某些构造不应强制进入通用 SQL:
- LogQL 流标签和解包操作将存储选择与解析结合起来。
- KQL具有丰富的动态值、时间序列、异常功能。
- SPL 具有搜索时知识对象、事务和特定于命令的行为。
- 每个系统应用不同的默认时区、空语义、限制和近似聚合算法。
当供应商查询是事件或数据类型的最佳工具时,保留它。目标是针对共享问题建立可靠的 SQL 事件模型,而不是语言纯粹性。
请参阅 LogQL 查询示例、Kusto 查询运算符 和 Splunk 搜索参考 的当前第一方参考资料。
验证和切换
对于每个迁移的查询:
- 冻结代表性区间;
- 比较源行计数;
- 比较不同的操作标识符;
- 比较空值和解析失败计数;
- 比较每个输出组,而不仅仅是总计;
- 解释可接受的差异;
- 至少在一个正常的流量周期中运行两个仪表板;
- 保留旧查询的回滚链接,直到用户接受新结果。
使用 SQL 查询故障排除 解决发动机和类型问题,使用 将日志迁移到结构化事件 进行仪表部署,使用 SQL食谱 进行完整的合同和可视化输出。