Kubernetes 监控早已是标配。Prometheus + Grafana 的组合用了这么多年,PromQL 写得溜的工程师一抓一大把。但想把 CPU 使用率和请求量做关联分析,或根据资源使用情况做动态分类时,PromQL 就开始别扭了。
PromQL 擅长时序聚合,但不擅长复杂 JOIN、条件分支等能力,聚合与窗口函数的表达能力也很受限——而这些恰恰是 SQL 的强项。把两者结合起来,很多复杂分析就能变得简单直接。
GreptimeDB 的 TQL(Telemetry Query Language)正是为此设计:在 SQL 中直接嵌入 PromQL,用 CTE 组合多个查询,实现真正的混合分析。
TQL:在 SQL 中执行 PromQL
TQL 是 GreptimeDB 对 SQL 的扩展,让你在 SQL 环境里直接执行 PromQL。基本语法:
TQL EVAL (start, end, step) promql_expressionstart 和 end 支持多种时间表达式:
now()— 当前时间now() - interval '1' hour— 1 小时前date_trunc('day', now())— 今天零点'2024-01-01T00:00:00Z'— RFC3339 格式
查询过去一小时的 CPU 使用率:
TQL EVAL (now() - interval '1' hour, now(), '5m')
sum by (namespace, pod) (rate(container_cpu_usage_seconds_total[5m]))用 AS 给结果列起别名,方便后续 SQL 处理:
TQL EVAL (now() - interval '1' hour, now(), '5m')
sum by (namespace, pod) (rate(container_cpu_usage_seconds_total[5m])) AS cpu_cores结果列名变成 cpu_cores,SQL 引用更方便。
执行环境
TQL 是 SQL 扩展,任何能连 GreptimeDB 执行 SQL 的地方都能用:
- 命令行:MySQL CLI (
mysql -h host -P 4002) 或 PostgreSQL CLI (psql -h host -p 4003) - 编程语言:JDBC、Python DB-API、Go database/sql 等标准接口
- 可视化工具:Grafana(MySQL/PostgreSQL 数据源)、DBeaver、DataGrip
- HTTP API:
/v1/sql端点
K8s 监控最常见的用法是在 Grafana 中配置 MySQL 数据源指向 GreptimeDB(默认端口 4002),在 Dashboard 查询编辑器里写 TQL。PromQL 的时序能力加上 SQL 的分析能力,结果直接可视化。
如何接入 K8s 指标? 参考官方教程 Kubernetes Cluster Metrics Monitoring,了解如何通过 Prometheus Remote Write 将集群指标写入 GreptimeDB。
CTE + TQL:混合查询的核心模式
TQL 的真正威力在于和 CTE(Common Table Expression)配合。CTE 是临时结果集,可以在主查询中引用,把复杂查询拆成可读的小块。
基本模式:
WITH metrics_cte AS (
TQL EVAL (start, end, step) promql_expression AS value_alias
)
SELECT * FROM metrics_cte WHERE value_alias > threshold;几个要点:
- 时间列名取决于数据来源:Prometheus Remote Write 写入的数据用
greptime_timestamp;自定义表可能是ts或其他名字 - 建议用 AS 别名:显式指定列名比依赖自动生成更可靠
- 只支持 TQL EVAL:CTE 中不能用
TQL EXPLAIN或TQL ANALYZE
完整例子——找出 CPU 使用超过 0.5 核的 Pod,按使用量排序。
注意这里的 container_cpu_usage_seconds_total 是 Prometheus counter 类型,记录的是累计 CPU 秒数,必须用 rate() 转换成每秒使用率才有意义。纯 SQL 没法处理 counter 的速率计算,纯 PromQL 的过滤排序又不够灵活——这正是混合查询的用武之地:
WITH cpu_usage AS (
TQL EVAL (now() - interval '1' hour, now(), '5m')
sum by (namespace, pod) (rate(container_cpu_usage_seconds_total{container!=""}[5m]))
AS cpu_cores
)
SELECT
greptime_timestamp as ts,
namespace,
pod,
cpu_cores
FROM cpu_usage
WHERE cpu_cores > 0.5
ORDER BY cpu_cores DESC
LIMIT 20;PromQL 计算 CPU 使用率,SQL 过滤排序。纯 PromQL 也能实现,但 SQL 语法更直观,尤其条件复杂时。
实战场景一:资源使用分级告警
根据资源使用情况设置不同告警级别。纯 PromQL 需要多条规则,SQL 的 CASE WHEN 一步到位:
WITH cpu_usage AS (
TQL EVAL (now() - interval '1' hour, now(), '5m')
sum by (namespace, pod) (rate(container_cpu_usage_seconds_total{container!=""}[5m]))
AS cpu_cores
)
SELECT
greptime_timestamp as ts,
namespace,
pod,
ROUND(cpu_cores, 3) as cpu_cores,
CASE
WHEN cpu_cores > 2 THEN 'critical'
WHEN cpu_cores > 1 THEN 'warning'
ELSE 'normal'
END as alert_level
FROM cpu_usage
WHERE cpu_cores > 0.1
ORDER BY cpu_cores DESC;一个查询搞定分级,critical 排在前面。
实战场景二:跨指标关联分析
想知道每个服务单位请求消耗多少 CPU?用来评估代码效率或做容量规划。
把 CPU 数据和请求量关联起来,纯 PromQL 比较绕,CTE + JOIN 很自然(这里用 http_requests_total,你的集群可能是其他指标名,按实际情况替换):
WITH
cpu_data AS (
TQL EVAL (now() - interval '1' hour, now(), '5m')
sum by (pod) (rate(container_cpu_usage_seconds_total{container!=""}[5m]))
AS cpu_cores
),
request_data AS (
TQL EVAL (now() - interval '1' hour, now(), '5m')
sum by (pod) (rate(http_requests_total[5m]))
AS req_per_sec
)
SELECT
c.greptime_timestamp as ts,
c.pod,
ROUND(c.cpu_cores, 3) as cpu_cores,
ROUND(r.req_per_sec, 1) as req_per_sec,
ROUND(c.cpu_cores / r.req_per_sec * 1000, 2) as cpu_per_1k_requests
FROM cpu_data c
JOIN request_data r
ON c.greptime_timestamp = r.greptime_timestamp
AND c.pod = r.pod
WHERE r.req_per_sec > 1
ORDER BY cpu_per_1k_requests DESC;计算每 1000 个请求消耗的 CPU。数值越高,服务越"吃"资源,可能需要优化。
提示:JOIN 条件除了时间戳,还要匹配标签(这里是 pod)。确保两个 TQL 的聚合维度一致,否则 JOIN 结果会出错。
实战场景三:API 响应时间 P99 分析
SLO 监控是业界通用实践。找出 P99 延迟最高的接口,PromQL 的 histogram_quantile 算分位数,SQL 做排序过滤更方便。
这里用
http_request_duration_seconds_bucket,这是 Prometheus 社区的标准命名。如果你用的是 Istio、Nginx Ingress 等,指标名可能不同,按实际情况替换。
WITH latency_p99 AS (
TQL EVAL (now() - interval '1' hour, now(), '5m')
histogram_quantile(0.99, sum by (service, endpoint, le) (rate(http_request_duration_seconds_bucket[5m])))
AS p99_seconds
)
SELECT
greptime_timestamp as ts,
service,
endpoint,
ROUND(p99_seconds * 1000, 1) as p99_ms,
CASE
WHEN p99_seconds > 1 THEN 'SLO violated'
WHEN p99_seconds > 0.5 THEN 'at risk'
ELSE 'healthy'
END as slo_status
FROM latency_p99
WHERE p99_seconds IS NOT NULL
ORDER BY p99_seconds DESC
LIMIT 20;找出 P99 最高的 20 个接口,标注 SLO 状态。超 1 秒直接标记 SLO violated,快速定位问题。
实战场景四:Pod 重启异常检测
Pod 频繁重启是危险信号。TQL 统计重启次数,SQL 聚合做异常检测:
WITH restart_counts AS (
TQL EVAL (now() - interval '6' hour, now(), '30m')
sum by (namespace, pod) (increase(kube_pod_container_status_restarts_total[30m]))
AS restarts
)
SELECT
namespace,
pod,
CAST(SUM(restarts) AS INT) as total_restarts,
CAST(MAX(restarts) AS INT) as max_per_30m,
CAST(AVG(restarts) AS INT) as avg_per_30m
FROM restart_counts
WHERE restarts > 0
GROUP BY namespace, pod
HAVING SUM(restarts) >= 3
ORDER BY total_restarts DESC;HAVING 过滤偶发重启,聚焦真正有问题的 Pod。
最佳实践
1. 用 AS 别名让列名可预测
PromQL 自动生成的列名可能很长,且不同版本间可能变化。显式命名更健壮。
2. 写 JOIN 时确认时间列名
前面提过,Prometheus Remote Write 数据用 greptime_timestamp,自定义表可能不同。JOIN 时别写错。
3. 保持聚合维度一致
JOIN 多个 TQL CTE 时,确保 by (...) 维度一致,否则可能产生笛卡尔积或空结果。
4. 合理设置查询范围
(start, end, step) 影响数据量和性能。实时监控用短范围小 step;趋势分析用长范围大 step。
5. 善用 GreptimeDB 的 SQL 能力
GreptimeDB 基于 Apache DataFusion,提供丰富的内置函数:
- 聚合:
SUM、AVG、MIN、MAX、COUNT - 窗口:
ROW_NUMBER、RANK、LAG、LEAD - 数学:
ROUND、ABS、FLOOR、CEIL、CLAMP - 时间:
date_trunc、date_add、date_format、to_unixtime - 条件:
CASE WHEN、COALESCE、NULLIF
完整列表见 GreptimeDB SQL Functions。
小结
PromQL 擅长时序聚合,SQL 擅长关联分析和条件处理。TQL + CTE 让你:
- 用 PromQL 做时序聚合(rate、histogram_quantile、increase 等)
- 用 SQL 做过滤、JOIN、条件分支、窗口分析
- 把复杂分析拆成可读的小块
对于 K8s 监控,很多以前需要应用层处理的分析,现在可以直接在数据库层完成。
下次遇到复杂监控分析,试试混合查询。


