数据量涨到单机跑不动、任务从几分钟变成几小时,很多人第一反应是加机器或者换个更火的框架,结果钱花了、时间没省。这篇不讲某套框架的教程,只给一条排查与取舍的顺序:先量清瓶颈在哪一层,再决定优化动作。适合数据与算法从业者自查,也适合给团队定优化优先级的人参考。
数据量涨到单机跑不动、一个例行任务从几分钟变成几小时,很多人的第一反应是加机器或者换个更火的框架。结果钱花出去了,时间没省下来,甚至因为换了技术栈又多了一堆坑。问题往往不在框架选的够不够新,而在于没搞清楚慢在哪一层。
这篇不讲某套框架怎么装怎么调,只给一条排查与取舍的顺序:先量清瓶颈在算力、数据量还是逻辑本身,再决定是改算法、换数据结构还是降规模。适合数据与算法从业者自查,也适合给团队定优化优先级的人参考。有个前提要说清楚:下面这套判断顺序针对的是「已经有基线、跑得起来但太慢」的任务,如果任务本身还在报错阶段,先修正确性再谈优化。
先定位瓶颈:算力、数据量还是逻辑本身
优化的第一步不是写代码,是测量。拿一份有代表性的采样数据把全流程跑一遍,记录时间花在哪几段:读取、中间计算、写出、序列化。多数人以为的「算法太慢」,实际是 IO 和序列化在拖后腿,改算法等于白改。
- 切分阶段计时在读取、转换、计算、写出这几个环节各打一个耗时点,跑一次全流程,看哪一段占比最高。占比最高那段才是优化对象,其他先放着。
- 区分单次慢和整体吞吐低单次任务慢,通常卡在某段热点代码;整体吞吐低,往往是并发度、调度或资源争抢的问题。两者的解法完全不同,别用同一套手段硬套。
- 量出延迟基线优化前先记下当前耗时,优化后用同一份数据复测。凭感觉说「快了不少」不算数。接口和链路层面的测速可以参照站内 /tools/speed-test 的做法,把基线固定下来。
算法层面的取舍:复杂度、近似与增量
到了算法层,先算复杂度量级再动手。同样是排序或聚合,数据规模翻一倍之后的实际表现差别,可能比换框架带来的差别大得多。很多团队换了一圈工具,却没算过自己那步操作的复杂度。判断标准很简单:先确认这步操作在数据量增长时的曲线走势,再考虑要不要重写。
- 能近似就近似:抽样、分桶、Top-K、草图计数这类方法在报表和监控场景够用,前提是业务方接受误差范围,接受不了就别用;
- 优先消除嵌套遍历和重复扫描,很多慢操作是把同一份数据来回读了好几遍;
- 把全量重算改成增量更新,只对变化的数据重跑,适合每日例行任务;如果数据本身每天全变,这条路不适用;
- 把大聚合拆成可合并的分步聚合,中间结果能提前剪枝,后续步骤就轻了。
| 场景 | 优先动的层 | 什么情况下不适用 |
|---|---|---|
| 报表类例行任务 | 增量更新、分区裁剪 | 上游数据每天全量覆盖时不适用 |
| 实时查询接口 | 索引、缓存、近似算法 | 业务要求结果绝对精确时不适用 |
| 离线特征计算 | 中间结果复用、并行度调整 | 存在强依赖链路时不适用 |
| 探索性分析 | 抽样先行、降维 | 结论要直接用于生产时不适用 |
数据结构与存储:把数据摆对位置比换算法更值
这一节经常被跳过,但它对性能的影响常常最大。检查分区、分桶、索引和列式存储有没有用上,多数慢查询靠这一步就能改善,根本不用重写算法。判断标准是看查询计划或者扫描的数据量:扫了多少行、读了多少字节。如果扫描量远大于实际需要,那就是摆放的问题,不是算法的问题。
- 分区裁剪
- 让查询只扫相关分区,而不是全表,是最便宜的一类优化
- 列式存储
- 只读用到的字段,宽表场景下收益明显,但小表上收益有限
- 中间结果控制
- 能流式处理就不要落全量中间表,能压缩就不要存冗余字段
- 口径统一
- 维度表和事实表的统计口径要一致,否则优化出来的速度建立在错口径上,越跑越偏
并行与资源:什么时候该加机器,什么时候不该
加机器是最后一步,不是第一步。先看任务是不是真的可并行:存在强依赖、全局聚合、需要统一排序的环节时,加机器只会增加协调开销,不解决问题。还要区分计算密集和 IO 密集,前者加 CPU 或 GPU 有效,后者得改网络、存储或者批大小。
- 先做分区级并行,再考虑任务级并行,粒度太细会让调度开销吃掉收益;
- 批大小要调,太小则 IO 次数多,太大则内存吃紧,这个只能测出来;
- 算成本账再决定扩容方式,租卡、按量、签合同的口径不同,到 /compute 与 /compute/tco 对照当时的数字;
- 别忘了数据倾斜,少数热点分区拖慢整个任务,加机器也救不了。
加机器解决的是「能并行但机器不够」,解决不了「本来就不该并行」。
验证与上线:优化完怎么证明没改坏
优化最大的风险是结果悄悄变了。所以验证要同时看两条:结果一致性和耗时。拿新旧两版在同一份抽样数据上跑,逐字段比对输出,两条都过才算优化成功。只快不对等于没做。
- 结果比对同一份抽样数据跑新旧两版,做逐字段或逐聚合值比对,差异要能解释清楚,解释不了就退回。
- 加监控告警给优化后的任务监控耗时、失败率和输入数据量突变,避免上线一周后悄悄退化,没人发现。
- 保留回滚路径发布拆成小批量灰度,出问题按原流程退回。自检思路可以参照 /tools/quality-check 一类的做法,把检查项固定成清单。
最后提醒一句:上面这套顺序不是每次都走完,时间紧的时候至少要走「定位瓶颈」和「结果比对」这两步,跳过定位等于盲调,跳过比对等于埋雷。
下一步想落实这套排查顺序,先去站内看看速度与链路检测的工具页,把基线量出来,再回头决定改哪一层;如果涉及扩容成本,到算力与成本对照页核对当时的数字,别凭印象做决定。→常见问题
数据量刚过单机上限,应该先换分布式框架还是先优化代码?
先优化代码和存储。多数任务在单机上的瓶颈是重复扫描、全量落盘和低效序列化,改完再评估是否真需要分布式。只有当单机内存和数据规模差距很大、且任务天然可切分时才考虑上集群,盲目分布式会把调试成本一起放大。
优化指标该看耗时还是看资源占用?
两个都要看,但优先级按场景定:面向用户或报表的链路以端到端耗时为第一指标,批量离线任务更该盯资源占用和成本。只压耗时不管成本,可能出现用三倍算力换两成提速的情况,是否划算要按当时的单价口径去站内核算页比一遍。
小团队没有专门性能工程师,从哪一步开始最有效?
从加日志和抽样计时开始,先知道时间花在哪,再动手改。常见收益最高的三件事是减少中间落盘、统一数据口径、把全量重算改成增量。这三件事不需要专门岗位,但需要有人持续盯着任务耗时和失败率的变化。