跑得快:别在不知道瓶颈的地方优化

📅 2026/8/24 15:04:03
跑得快:别在不知道瓶颈的地方优化
「跑得快」是四阶段的最后一步。不是因为它不重要是因为你不先把前面三个跑完你连真正的瓶颈数据都拿不到。一、提前优化的诱惑为什么人手痒提前优化是人性问题不是技术问题。你刚写完一段代码脑子里已经在想「这个循环能不能少遍历一次」「这个数据结构能不能换成更快的」「这里多查了一次数据库能不能合并」。你知道某个地方可以更快——你看到了那个可能性——手就痒。忍住。你不知道瓶颈在哪之前所有的优化都可能是错的。你花了两天把一个循环从 O(n²) 优化到 O(n log n)结果 profiling 告诉你这个循环在生产环境里只跑了几毫秒真正在吃 CPU 的是另一个你从来没注意过的组件。你优化的那个地方不是在给用户提速是在给自己找事做。「跑得快」的正确姿势是先 profiling再动手。用火焰图用慢查询日志用 metrics——找到真正的热点。不要猜要量。猜的优化是在正确的地方浪费时间量的优化是在正确的地方花时间。二、二八法则性能优化里的擒贼擒王二八法则在性能问题上普遍适用大部分的性能消耗会被消耗在个别的流程步骤或组件上。不是你系统的所有地方都慢是一两个环节在拖垮全部。关键是找到它——不是猜到它是量到它。擒贼擒王。识别出瓶颈把力气对准它。不要把时间花在次要环节上——一个冷路径优化了 50%对整体体验的影响几乎是零。一个热路径优化了 5%用户全感受到了。怎么识别瓶颈直觉会告诉你「那段代码很复杂一定很慢」但线上数据可能告诉你「那段复杂的代码处理的数据量很小实际不怎么跑」。直觉会告诉你「这段代码很简单肯定快」但线上数据可能告诉你「这段简单的代码被调了几百万次每次多花一毫秒就是几百秒的延迟」。直觉不可靠profiling 才可靠。三、优化有成本快 vs 可读代码更快往往意味着更难读。这是所有优化都要面对的交易。一个for循环拆成三个map/filter链式调用快了 5%但三个月后没人看得懂。一个递归改成迭代快了 10%但原来写递归的那个人已经离职了迭代版本没人能维护。一个 hashmap 换成 tree内存省了 30%但你要自己写比较函数每次 insert 都要调一次。不是所有优化都该做。5%、10%、30%——这些数字在业务场景下到底值不值一个后端的批处理链路每天凌晨跑一次慢 5 分钟没人计较不值得为了它把代码写成只有你一个人能看懂的版本。一个面向用户的实时查询接口多 200ms 就有用户感知值得优化但优化完之后留好注释告诉下一个接手的人你为什么这样写。关键不是「能不能优化」是「优化了值不值」。四、基础设施优先很多问题不靠改代码性能优化的第一反应永远是改代码。但很多时候瓶颈不在代码在基础设施。加个缓存效果可能比你改几百行代码更明显。加个索引查询从全表扫变成一次 B-tree 查找。调个连接池大小、调个 JVM 参数、调个超时时间——这些改动一行代码都不碰但效果可能是翻倍的。先看基础设施再看代码。代码是最后一步。代码改动的成本最高——要改逻辑、要验证正确性、要回归测试、要让同事 review。基础设施改动的成本低——一个 Spark 集群的资源配置参数改完验证一下任务能不能跑就行不用动业务逻辑。五、案例重写复杂代码 vs 调几个 Spark 参数曾经做过一个离线的编排任务流。这个任务流跑着跑着就开始出问题偶发的大数据量导致任务超时或失败。不是每次都超时——是「偶发」数据量碰巧大了就挂。但从系统角度看这已经严重影响了交付时效。最初觉得是代码的问题。那套代码逻辑确实够复杂——是很多年前写的几轮迭代下来已经没人说得清楚里面每一步在干什么。直接重写。花了不少时间把逻辑理清楚、把代码缕直、把不必要的步骤砍掉。重写完了性能确实有提升——但提升不大没到根本上去。后来换了个思路先别改代码先 profiling。结果发现瓶颈不在代码逻辑在 Spark 集群本身的配置——有几个关键参数并行度、内存分配、shuffle 分区数一直是默认值。任务超时不是代码算得慢是资源分配不合理导致 CPU 利用率上不去。把对应 Spark 集群的几个关键配置参数调整之后性能直接翻倍之前的超时问题全消失了。不是代码重写没提升——是那条路径的提升空间本身有限而真正的瓶颈在别的地方。你不是在优化最慢的地方你是在优化你最熟悉的地方。六、退出信号什么时候「跑得快」过了没人再抱怨慢了。「没人抱怨慢」是一个真实可感的信号。不是你仪表盘上的指标下来了是用户不再找你「这个怎么这么慢」。他们忘了你的系统还存在——不是真忘了是用你系统的时候不需要再花时间等它。但「跑得快」没有终点。业务在涨数据在涨这个月的瓶颈不一定是下个月的。你把这次的瓶颈处理完了下次新的瓶颈可能又在另一个环节上。性能优化是持续性的——不是一次性修完了就完了。「跑得快」的退出信号不是「你做完了」是「现在没人抱怨了」。下一次再抱怨了再走一遍profiling → 找瓶颈 → 动手。收尾架构不是一次性的设计是持续四阶段的演进。每一轮循环你手里都有比上一轮更多的信息。你做得比上一轮更准、更稳、更快——但你还是从「能跑」开始从简单开始。