统计查询的慢,很少有单一原因;优化也不是“加个缓存”就完事
统计平台上线一段时间后,性能问题一定会来:
- 看一个月的趋势,转圈十几秒;
- 按城市、渠道、品类拆开,直接超时;
- 大促期间,看板刷新越来越慢。
大多数人第一反应是“加缓存”。缓存确实有用,但如果查询本身是慢的,缓存只是把慢藏起来——第一次请求还是慢,数据一更新又慢。
这篇聊聊统计查询的性能:慢在哪、能从哪几个层面优化、哪些优化值得做、哪些是坑。
一、一次统计查询,慢在哪
先拆解一次查询的成本:
1 | 扫描数据量 × 聚合复杂度 × 分组数量 × 时间桶数量 |
四个因子乘起来,任何一个变大,查询都会变慢。
- 扫描量:时间区间越大、过滤条件越弱,扫的数据越多;
- 聚合复杂度:求和、去重、百分位,比简单计数贵得多;
- 分组数量:按城市、店铺、渠道、品类一层层拆,结果集指数级增长;
- 时间桶数量:按小时、按天、按周,桶越多,聚合次数越多。
所以优化不是“调一个参数”,而是逐个因子去降。
二、第一层:把查询范围收住
很多慢查询,不是引擎不行,而是请求本身不合理。
几条该有的约束:
- 限制时间跨度:趋势查询最多看一年,明细查询最多看三个月;
- 限制分组数量:一次最多拆两三层,不要把七八个维度全铺开;
- 限制指标数量:一次请求几十个指标,聚合规模会爆炸;
- 限制返回条数:分组结果上千条时,用分页或 topN,而不是全量返回。
约束不是“砍需求”,而是让用户知道边界:想看更大的范围,就换一种粒度(按月而不是按天),或者分批看。
三、第二层:条件下推,先缩小扫描集
统计查询通常有时间范围,但引擎如果只在聚合阶段过滤,就会先扫全量再算。
更高效的做法是把时间范围下推到查询层:
- 多个时间字段之间用并集,而不是只用一个字段;
- 过滤条件尽量在扫描阶段生效,而不是在内存里筛;
- 字段映射要能命中索引结构,避免全表扫描。
这里有个反直觉的点:条件写得越“全”,不一定越慢。把排除条件、状态条件都下推,反而能显著减少扫描量。
四、第三层:数据模型决定上限
查询优化是有上限的,上限由数据模型决定。
几个关键设计:
- 宽文档:把统计要用的字段提前拼好,查询时不做多表关联;
- 时间字段冗余:创建、支付、退款、完成,各存一份,避免查询时派生;
- 可聚合的字段类型:金额用数值、状态用枚举,不要用文本;
- 合理的分区或分片:按时间或业务维度切分,避免单点过大。
模型没设计好,后面所有优化都是打补丁。
五、第四层:执行方式
同样的查询,执行方式不同,耗时可能差几倍:
- 并发:多个指标、多个时间字段可以并行计算;
- 批量:分组查询不要每个分组发一次请求,而是一次算完再拆;
- 合并:同一个页面多个图表请求相同指标时,合并成一次查询;
- 超时与中断:慢查询要能超时中断,而不是占着资源跑到底。
并发不是越高越好:数据库连接、搜索集群的并发能力都有上限,压过头反而更慢。
六、第五层:缓存
缓存是有效的,但要用对地方:
- 只缓存纯聚合结果:带明细、带游标的查询不缓存;
- 缓存键包含全部参数:指标、时间、过滤、分组、粒度,缺一个就会串数据;
- 缓存时间要短:统计数据更新频繁,缓存太久会返回旧数字;
- 并发相同请求要合并:同一时刻多个页面请求同一指标,只发一次查询。
还要注意缓存的“冷启动”:缓存刚失效时,所有请求同时打进来,容易把下游打挂。可以加一点随机过期时间,避免同时失效。
七、预聚合:用固化换性能
如果某个报表的维度、指标、时间粒度都很固定,可以考虑预聚合:
- 提前算好“按天、按城市、按渠道”的汇总结果;
- 查询时直接读汇总表,不再扫明细;
- 代价是口径固化:维度或指标一变,就要重跑历史。
预聚合适合:
- 高频访问的固定报表;
- 数据量大、实时性要求不高的场景;
- 维度组合有限、变化不频繁的指标。
不适合:
- 探索式分析(维度组合随时变);
- 口径还在调整的指标;
- 需要下钻到明细的场景。
预聚合不是“银弹”,它是用灵活性换性能。
八、采集端也要快
统计查询慢,有时根因不在查询,而在数据写入:
- 一条订单要查十几张表,逐单串行查询,写入很慢;
- 每条数据单独写入,网络往返次数太多;
- 写入时强制刷新,每次都要等索引可见。
对应的优化:
- 拼装阶段并发执行,控制并发度,避免压垮数据库;
- 写入改成批量,一页数据一次提交;
- 刷新策略从“每条刷新”改成“一批刷新一次”。
这些优化不影响口径,但能让数据更快可用。
九、性能要能被度量
没有度量,优化就是猜。
至少要记录:
- 每次查询的耗时、扫描量、聚合规模;
- 慢查询的完整参数(指标、时间、过滤、分组);
- 查询的 P95、P99,而不是只看平均值;
- 优化前后的对比基线。
有了这些,才能判断“这次优化到底有没有用”,而不是“感觉快了一点”。
十、常见坑
- 只加缓存不改查询:第一次请求依然慢;
- 缓存键漏参数:换个筛选还返回旧数据;
- 并发开太大:数据库被打挂,整体更慢;
- 分组一次铺满:结果集爆炸,内存扛不住;
- 时间跨度不限制:一次查三年,必然超时;
- 只优化平均值:P99 依然很慢,用户体验没改善;
- 预聚合口径变了不重跑:报表数字悄悄变化;
- 写入逐条刷新:吞吐上不去;
- 慢查询不记录参数:出了问题无法复现;
- 优化没有基线:不知道有没有效果。
十一、结语
统计平台的性能优化,是一个从外到内的过程:
1 | 约束请求 → 条件下推 → 模型设计 → 执行方式 → 缓存与预聚合 |
越靠前的层次,收益越大、成本越低;越靠后,越像打补丁。
先量、再改、再验证,比“先加缓存”靠谱得多。