ES Rollup 预聚合:报表从 3 秒到 200ms

招聘平台每天产生海量的行为日志:谁搜了什么职位、看了哪份简历、投递了几次。这些数据要喂给运营看板和转化漏斗分析,但量级上来之后,传统聚合方式撑不住了。

一、问题:明细数据太重

早期方案是把行为日志直接灌 ES,跑聚合:

  • 数据量:单日行为日志 3000 万+ 条,保留 180 天 ≈ 5 亿+ 条
  • 查询:漏斗分析按「投递 → 简历被查看 → 约面 → 入职」多级 join 聚合
  • 现状:报表查询 3~5 秒,数据保留 180 天存储成本飙升,集群查询一多就排队

问题的本质是:明细数据只需要保留一小段热窗口,历史数据做报表时只关心聚合结果,不关心每一条明细。

二、Rollup 预聚合:把历史数据"降采样"

Elasticsearch 的 Rollup 能力本质是:按预定义的维度组合和粒度,把明细数据预先聚合成聚合桶,查询直接命中桶而不是扫明细。

d w a d e d i a e a l t k t y e l e _ y _ r h h 7 o i r i l s o s l t l t u o l o p g u g r p r j a a o m j m b : o : b 1 1 m h i n t e r m s : / /

具体做法

  1. 热窗口只留明细:行为日志明细索引只保留最近 7 天,配合 ILM 生命周期自动删除
  2. 每日滚动索引:明细按天建索引 behavior-2021-03-18,写满自动 rollover
  3. Rollup 任务定义:指定聚合维度(职位 ID、城市、渠道、行为类型)+ 粒度(1 分钟)+ 指标(count、sum 时长)
  4. 历史数据落桶:超过 7 天的明细被 Rollup Job 消费成聚合桶,原始明细归档冷存储

查询层无感

Rollup 和明细共用同一个索引别名,_search 时带上 "typed_keys" 或直接对别名查询,ES 自动路由:新数据查明细,历史数据查聚合桶。报表 SQL 不需要改。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// rollup job 定义(节选)
{
  "index_pattern": "behavior-*",
  "rollup_index": "behavior-rollup",
  "cron": "0 30 1 * * ?",
  "page_size": 1000,
  "groups": {
    "date_histogram": { "field": "timestamp", "fixed_interval": "60s" },
    "terms": { "fields": ["job_id", "city", "channel", "action_type"] }
  },
  "metrics": [
    { "field": "view_seconds", "metrics": ["sum", "avg", "max"] },
    { "field": "count", "metrics": ["sum"] }
  ]
}

三、效果

指标 优化前 优化后
报表查询耗时 3~5 秒 < 200ms(提速 80%+)
明细存储保留 180 天全量 7 天明细 + 聚合桶
存储成本 100% 降低 70%+
集群查询排队 高峰排队明显 明细索引瘦身后明显缓解

四、踩过的坑

  1. 聚合粒度不能乱设:1 分钟粒度对「近 7 天」够用,但运营要按秒看就查不到——明确「历史报表只关心分钟级以上」这个口径再上 Rollup,否则需求会打回来
  2. Rollup 任务消费有延迟:明细转桶是异步 cron,报表偶尔读到「昨天的数据还差几分钟」——加了个补偿查询:近 1 天数据直接查明细兜底
  3. 别名别乱用:Rollup 索引和明细索引共用别名后,写操作要小心——只对明细索引写,别对着别名写
  4. 维度要提前想全:Rollup 是预聚合,漏了的维度查不了。上线前和运营拉一遍「未来可能要的维度清单」,宁多勿少(多一个维度多一份存储,但少一个维度少一种分析能力)

总结

Rollup 的核心不是技术,是数据分层:热数据留明细,冷数据留聚合。预算好「报表精度」和「存储成本」的边界,Rollup 就是那把把报表从秒级拉到百毫秒级的钥匙。

如果你也在被历史数据报表拖垮,先别加机器,先算算:报表真的需要明细吗?