统计平台的性能:一次大区间查询是怎么变快的

chihqiang chihqiang 2026-11-14 约 6 分钟 2283 字

统计查询的慢,很少有单一原因;优化也不是“加个缓存”就完事

统计平台上线一段时间后,性能问题一定会来:

  • 看一个月的趋势,转圈十几秒;
  • 按城市、渠道、品类拆开,直接超时;
  • 大促期间,看板刷新越来越慢。

大多数人第一反应是“加缓存”。缓存确实有用,但如果查询本身是慢的,缓存只是把慢藏起来——第一次请求还是慢,数据一更新又慢。

这篇聊聊统计查询的性能:慢在哪、能从哪几个层面优化、哪些优化值得做、哪些是坑。


一、一次统计查询,慢在哪

先拆解一次查询的成本:

1
扫描数据量 × 聚合复杂度 × 分组数量 × 时间桶数量

四个因子乘起来,任何一个变大,查询都会变慢。

  • 扫描量:时间区间越大、过滤条件越弱,扫的数据越多;
  • 聚合复杂度:求和、去重、百分位,比简单计数贵得多;
  • 分组数量:按城市、店铺、渠道、品类一层层拆,结果集指数级增长;
  • 时间桶数量:按小时、按天、按周,桶越多,聚合次数越多。

所以优化不是“调一个参数”,而是逐个因子去降。


二、第一层:把查询范围收住

很多慢查询,不是引擎不行,而是请求本身不合理。

几条该有的约束:

  • 限制时间跨度:趋势查询最多看一年,明细查询最多看三个月;
  • 限制分组数量:一次最多拆两三层,不要把七八个维度全铺开;
  • 限制指标数量:一次请求几十个指标,聚合规模会爆炸;
  • 限制返回条数:分组结果上千条时,用分页或 topN,而不是全量返回。

约束不是“砍需求”,而是让用户知道边界:想看更大的范围,就换一种粒度(按月而不是按天),或者分批看。


三、第二层:条件下推,先缩小扫描集

统计查询通常有时间范围,但引擎如果只在聚合阶段过滤,就会先扫全量再算。

更高效的做法是把时间范围下推到查询层:

  • 多个时间字段之间用并集,而不是只用一个字段;
  • 过滤条件尽量在扫描阶段生效,而不是在内存里筛;
  • 字段映射要能命中索引结构,避免全表扫描。

这里有个反直觉的点:条件写得越“全”,不一定越慢。把排除条件、状态条件都下推,反而能显著减少扫描量。


四、第三层:数据模型决定上限

查询优化是有上限的,上限由数据模型决定。

几个关键设计:

  • 宽文档:把统计要用的字段提前拼好,查询时不做多表关联;
  • 时间字段冗余:创建、支付、退款、完成,各存一份,避免查询时派生;
  • 可聚合的字段类型:金额用数值、状态用枚举,不要用文本;
  • 合理的分区或分片:按时间或业务维度切分,避免单点过大。

模型没设计好,后面所有优化都是打补丁。


五、第四层:执行方式

同样的查询,执行方式不同,耗时可能差几倍:

  • 并发:多个指标、多个时间字段可以并行计算;
  • 批量:分组查询不要每个分组发一次请求,而是一次算完再拆;
  • 合并:同一个页面多个图表请求相同指标时,合并成一次查询;
  • 超时与中断:慢查询要能超时中断,而不是占着资源跑到底。

并发不是越高越好:数据库连接、搜索集群的并发能力都有上限,压过头反而更慢。


六、第五层:缓存

缓存是有效的,但要用对地方:

  • 只缓存纯聚合结果:带明细、带游标的查询不缓存;
  • 缓存键包含全部参数:指标、时间、过滤、分组、粒度,缺一个就会串数据;
  • 缓存时间要短:统计数据更新频繁,缓存太久会返回旧数字;
  • 并发相同请求要合并:同一时刻多个页面请求同一指标,只发一次查询。

还要注意缓存的“冷启动”:缓存刚失效时,所有请求同时打进来,容易把下游打挂。可以加一点随机过期时间,避免同时失效。


七、预聚合:用固化换性能

如果某个报表的维度、指标、时间粒度都很固定,可以考虑预聚合:

  • 提前算好“按天、按城市、按渠道”的汇总结果;
  • 查询时直接读汇总表,不再扫明细;
  • 代价是口径固化:维度或指标一变,就要重跑历史。

预聚合适合:

  • 高频访问的固定报表;
  • 数据量大、实时性要求不高的场景;
  • 维度组合有限、变化不频繁的指标。

不适合:

  • 探索式分析(维度组合随时变);
  • 口径还在调整的指标;
  • 需要下钻到明细的场景。

预聚合不是“银弹”,它是用灵活性换性能。


八、采集端也要快

统计查询慢,有时根因不在查询,而在数据写入:

  • 一条订单要查十几张表,逐单串行查询,写入很慢;
  • 每条数据单独写入,网络往返次数太多;
  • 写入时强制刷新,每次都要等索引可见。

对应的优化:

  • 拼装阶段并发执行,控制并发度,避免压垮数据库;
  • 写入改成批量,一页数据一次提交;
  • 刷新策略从“每条刷新”改成“一批刷新一次”。

这些优化不影响口径,但能让数据更快可用。


九、性能要能被度量

没有度量,优化就是猜。

至少要记录:

  • 每次查询的耗时、扫描量、聚合规模;
  • 慢查询的完整参数(指标、时间、过滤、分组);
  • 查询的 P95、P99,而不是只看平均值;
  • 优化前后的对比基线。

有了这些,才能判断“这次优化到底有没有用”,而不是“感觉快了一点”。


十、常见坑

  1. 只加缓存不改查询:第一次请求依然慢;
  2. 缓存键漏参数:换个筛选还返回旧数据;
  3. 并发开太大:数据库被打挂,整体更慢;
  4. 分组一次铺满:结果集爆炸,内存扛不住;
  5. 时间跨度不限制:一次查三年,必然超时;
  6. 只优化平均值:P99 依然很慢,用户体验没改善;
  7. 预聚合口径变了不重跑:报表数字悄悄变化;
  8. 写入逐条刷新:吞吐上不去;
  9. 慢查询不记录参数:出了问题无法复现;
  10. 优化没有基线:不知道有没有效果。

十一、结语

统计平台的性能优化,是一个从外到内的过程:

1
约束请求 → 条件下推 → 模型设计 → 执行方式 → 缓存与预聚合

越靠前的层次,收益越大、成本越低;越靠后,越像打补丁。

先量、再改、再验证,比“先加缓存”靠谱得多。

分享: Twitter Facebook
chihqiang

chihqiang

代码与生活的点滴记录