
4.3 Elasticsearch-百分比、采样、移动平均、季节分解
4.3.1 百分比(Percentiles)
在监控与告警场景里,平均值往往掩盖长尾延迟。Elasticsearch 通过 percentiles 聚合把整条延迟分布切成 100 份,常用 P50、P90、P99、P99.9 四档即可看清“最慢 1 % 请求”到底慢到什么程度。
语法要点
GET latency-*/_search?size=0
{
"aggs": {
"latency_percentiles": {
"percentiles": {
"field": "duration",
"percents": [50, 90, 99, 99.9],
"keyed": true,
"tdigest": { "compression": 200 } // 精度 vs 内存权衡,默认 100
}
}
}
}
tdigest是 ES 7.x 默认算法,单分片误差 < 1 %,内存占用与compression成正比;- 若需精确值,可在索引映射里把
duration设为histogram或scaled_float+ignore_malformed,再用percentile_ranks反查“超过某阈值的请求占比”。
实战技巧
- 做 SLA 看板时,把 P99 与 P50 的差值命名为“长尾抖动”,差值突然放大即可触发告警;
- 对比灰度版本:用
filters子聚合把流量按version字段拆成 A/B 两组,再分别算 P99,一眼看出新版本是否把尾巴拖长。
4.3.2 采样(Sampler & Diversified Sampler)
全量聚合在十亿级文档上跑 P99 会直接把 data node 打爆。ES 提供两种采样聚合,用少量数据换 95 % 的精度。
1. Sampler
"aggs": {
"sample": {
"sampler": { "shard_size": 10000 }, // 每个分片最多取 1 w 条
"aggs": {
"significant_errors": {
"significant_terms": { "field": "error.message" }
}
}
}
}
- 先随机采样,再跑子聚合,显著降低内存;
- 适合日志关键词突增、错误码聚类等 exploratory 场景。
2. Diversified Sampler
在采样时再加“去重”规则,保证同一用户、同一session不会被过度代表:
"diversified_sampler": {
"shard_size": 5000,
"field": "user_id", // 每个 user_id 最多取 1 条
"max_docs_per_value": 1
}
- 做 A/B 实验时,用该聚合抽取“独立用户”延迟,可消除重度用户带来的偏差。
4.3.3 移动平均(Moving Average & Moving Function)
时序面板里最常见的降噪手段。ES 从 2.x 起内置 moving_avg 管道聚合,6.4 以后又加入更灵活的 moving_fn,支持自定义脚本。
1. 简单线性平滑
"moving_avg": {
"buckets_path": "avg_latency",
"window": 10,
"model": "linear",
"minimize": false
}
window=10表示用前后 10 个桶做滑动窗口;model可选linear、ewma、holt_winters,后者可叠加趋势与季节性。
2. 脚本化窗口(moving_fn)
"moving_fn": {
"buckets_path": "avg_latency",
"window": 7,
"script": "MovingFunctions.unweightedAvg(values)" // 也可写标准差、中位数
}
- 支持
values.length动态判断边缘桶,避免头尾缺值造成的陡降; - 若想写“环比上周同期”,可在脚本里用
values[values.length-8]做跨周期对比。
可视化落地
Kibana Lens 里把指标拖到 Y 轴 → 添加“移动平均”层,窗口大小直接下拉选择;或者 TSVB 里用 “Series Agg → Moving Average” 并勾选 “Treat gaps as zeros”,即可在 5 秒内完成平滑曲线。
4.3.4 季节分解(Seasonal Decompose & ML Forecast)
移动平均只能降噪,无法把趋势、周期、残差拆开。ES 官方推荐两条路线:
- 用
transform+pivot把数据按“周×小时” 24×7 矩阵重聚合,再手写脚本算同期平均,做减法拿到残差; - 直接上 Machine Learning 模块,一条 API 完成趋势+周期+异常检测。
方案 A:纯聚合手写分解
步骤:
- 用
date_histogram把 metrics 聚合成 1 h 桶; - 用
bucket_script计算“同期历史平均值”:
"seasonal_baseline": {
"bucket_script": {
"buckets_path": {
"hist": "history_avg", // 过去 4 周同一小时平均值
"real": "current_avg"
},
"script": "params.real - params.hist"
}
}
- 残差 > 3 σ 即认为异常。
优点:零依赖、轻量;缺点:只能处理单周期(周),且需自己维护历史基线索引。
方案 B:ML Forecast(开箱即用)
PUT _ml/anomaly-detector/service_latency
{
"analysis_config": {
"bucket_span": "1h",
"detectors": [{
"function": "mean",
"field_name": "duration",
"by_field_name": "service"
}],
"influencers": ["service", "az"]
},
"data_description": {
"time_field": "@timestamp"
},
"model_plot_config": { "enabled": true }
}
- 自动识别每日、每周、每月多重季节;
- 实时输出
lower,upper,prediction三列,残差超出边界即告警; - 支持
forecast=7d一键生成未来 7 天容量预测,直接把曲线拖进 Kibana Dashboard 即可给老板汇报。
性能调优
- 把
bucket_span设为采集周期的整数倍,避免边界抖动; - 对超大数据量启用
summary_count_field,把 1 min 原始点预聚合成 1 h 计数,模型训练提速 10 倍; - 用
calendars排除大促、节假日,否则模型会把人为峰值当成新的季节分量。
4.3.5 小结
百分比让你看到“最慢的 1 %”;采样让你用 1 % 的算力看清趋势;移动平均帮你把锯齿变平滑;季节分解则告诉你锯齿里哪些是周期、哪些是异常。四板斧组合起来,足以把 Elasticsearch 从“搜索引擎”升级成“轻量级时序分析平台”,而无需额外引入 Spark、Flink 这类重型框架。
更多技术文章见公众号: 大城市小农民
574

被折叠的 条评论
为什么被折叠?



