SELECT
server_id,
approx_top_k(log_level, 3, 100) AS top_levels,
approx_top_k(endpoint, 3, 100) AS top_endpoints
FROM service_logs
GROUP BY server_id;
输出:
server_id
top_levels
top_endpoints
1
error=5:warn=4:info=9
/api/users=9:/api/orders=4:/api/payments=5
2
info=5:warn=2:error=2
/api/users=5:/api/orders=3:/api/payments=2
输出格式:
value=count:value=count:...
value=count:value=count:...
,按频次降序排列。
参数说明
参数
类型
说明
col
col
任意可比较类型
要统计的列
k
k
INT
返回 Top K 个元素
error
error
INT
精度控制(0-100),值越大越精确但消耗更多内存
error 参数影响
-- 小数据集上差异不明显,大数据集(千万级以上)效果显著
SELECT
approx_top_k(log_level, 3, 10) AS low_accuracy,
approx_top_k(log_level, 3, 100) AS high_accuracy
FROM service_logs;
low_accuracy
high_accuracy
info=14:error=7:warn=6
info=14:error=7:warn=6
建议: 生产环境用
100
100
(最高精度);探索性分析可用
50
50
节省内存。
场景 2:数据分布直方图(approx_histogram)
问题
了解响应时间的分布情况,识别是否存在长尾或异常值。
SQL 实现
SELECT approx_histogram(response_time, 5) AS histogram
FROM service_logs;
输出:
histogram
80=80=2:80=208=10:208=336=7:336=464=6:464=600=5
输出格式:
lower=upper=count:lower=upper=count:...
lower=upper=count:lower=upper=count:...
,每个桶的起始值、结束值和计数。
解读输出
桶范围
计数
占比
80-80
2
6.7%
80-208
10
33.3%
208-336
7
23.3%
336-464
6
20.0%
464-600
5
16.7%
按服务器分组查看分布
SELECT
server_id,
approx_histogram(response_time, 4) AS histogram
FROM service_logs
GROUP BY server_id;
场景 3:近似分位数(approx_percentile)
问题
计算响应时间的 P50(中位数)、P90、P99,用于 SLA 监控。
SQL 实现
SELECT
approx_percentile(response_time, 0.5) AS p50,
approx_percentile(response_time, 0.9) AS p90,
approx_percentile(response_time, 0.99) AS p99
FROM service_logs;
输出:
p50
p90
p99
307.5
525
600
同时计算多个分位数
SELECT
approx_percentile(response_time, ARRAY[0.5, 0.9, 0.95, 0.99]) AS percentiles
FROM service_logs;
输出:
percentiles
307.5:525:550:600
输出格式: 冒号分隔的分位数值,顺序与输入数组一致。
按端点分组计算 SLA
SELECT
endpoint,
COUNT(*) AS request_count,
approx_percentile(response_time, 0.5) AS p50,
approx_percentile(response_time, 0.9) AS p90,
approx_percentile(response_time, 0.99) AS p99
FROM service_logs
GROUP BY endpoint
ORDER BY p90 DESC;
输出:
endpoint
request_count
p50
p90
p99
/api/payments
7
400
600
600
/api/orders
6
250
450
450
/api/users
14
180
420
550
/api/health
2
90
100
100
场景 4:近似值 vs 精确值对比
问题
验证近似函数的精度是否满足业务需求。
SQL 实现
SELECT
-- 精确值
percentile(response_time, 0.5) AS exact_p50,
percentile(response_time, 0.9) AS exact_p90,
-- 近似值
approx_percentile(response_time, 0.5) AS approx_p50,
approx_percentile(response_time, 0.9) AS approx_p90,
-- 误差
ROUND(ABS(percentile(response_time, 0.5) - approx_percentile(response_time, 0.5)), 1) AS p50_error,
ROUND(ABS(percentile(response_time, 0.9) - approx_percentile(response_time, 0.9)), 1) AS p90_error
FROM service_logs;
输出:
exact_p50
exact_p90
approx_p50
approx_p90
p50_error
p90_error
310
505.0
307.5
525
2.5
20.0
说明: 小数据集(30 行)上误差较明显;大数据集(百万级以上)时误差通常 < 1%。
场景 5:综合实战 — 服务性能监控看板
问题
构建一个服务性能监控查询,包含:
各服务器的请求量
Top 3 错误类型
P50/P90/P99 响应时间
响应时间分布
SQL 实现
SELECT
server_id,
COUNT(*) AS total_requests,
-- Top 3 日志级别
approx_top_k(log_level, 3, 100) AS top_levels,
-- 分位数
approx_percentile(response_time, 0.5) AS p50,
approx_percentile(response_time, 0.9) AS p90,
approx_percentile(response_time, 0.99) AS p99,
-- 分布(3 个桶)
approx_histogram(response_time, 3) AS distribution
FROM service_logs
GROUP BY server_id;
输出:
server_id
total_requests
top_levels
p50
p90
p99
distribution
1
20
info=9:error=5:warn=4
290
525
600
90=90=1:90=330=12:330=600=7
2
10
info=5:warn=2:error=2
180
400
400
80=80=1:80=253=6:253=400=3
常见问题
1.
approx_top_k
approx_top_k
的返回值格式
-- 返回的是格式化的字符串,不是 JSON 或数组
-- 如果需要结构化数据,需要用字符串函数解析
SELECT approx_top_k(level, 3, 100) AS result FROM logs;
-- 输出: error=5:warn=2:info=2
-- 错误: approx_percentile 不支持 FILTER
approx_percentile(response_time, 0.9) FILTER (WHERE server_id = 1)
-- 正确: 用 CASE WHEN 过滤
approx_percentile(CASE WHEN server_id = 1 THEN response_time END, 0.9)
4. 空值处理
-- NULL 值会被忽略,不影响统计
SELECT approx_percentile(response_time, 0.5) FROM (
SELECT NULL AS response_time UNION ALL SELECT 100 UNION ALL SELECT 200
);
-- 输出: 150(基于 100 和 200 计算)
5. 大数据集上的性能优势
数据量
percentile
percentile
耗时
approx_percentile
approx_percentile
耗时
内存消耗对比
100 万行
~5s
~1s
1:5
1 亿行
~60s
~3s
1:20
10 亿行
OOM
~15s
N/A : 可控
建议: 数据量 < 10 万行用精确函数;> 100 万行用近似函数。
性能优化建议
场景
优化策略
多分位数计算
用
ARRAY[0.5, 0.9, 0.99]
ARRAY[0.5, 0.9, 0.99]
一次计算,避免多次扫描
TopN + 分位数组合
放在同一个
SELECT
SELECT
中,共享聚合阶段
按时间窗口分析
配合
DATE_TRUNC
DATE_TRUNC
分组,避免全表扫描
近似函数 + GROUP BY
先过滤再聚合,减少输入数据量
-- 推荐: 一次计算多个分位数
SELECT
endpoint,
approx_percentile(response_time, ARRAY[0.5, 0.9, 0.99]) AS percentiles
FROM service_logs
WHERE response_time > 0 -- 先过滤
GROUP BY endpoint;
-- 不推荐: 多次扫描计算分位数
SELECT
endpoint,
approx_percentile(response_time, 0.5) AS p50,
approx_percentile(response_time, 0.9) AS p90 -- 额外扫描
FROM service_logs
GROUP BY endpoint;