取号为什么越来越慢?100次数据库查询,其实只需要1次 取号为什么越来越慢100次数据库查询其实只需要1次技术重构系列 · 第3篇抖音普通订单分层架构复用 N1性能优化本系列基于老系统真实改造复盘文中客户名、地址、编码值均已化名/脱敏处理坑的类型、解题思路、踩坑过程为一线实录。系列背景见首篇。上篇聊了抖音代发的Token和地址策略这篇进入抖音普通订单。代发是仓库帮别人发货普通是店铺自己发货。听起来简单但代码层面差异不小——Token来源不同、请求格式不同、签名方式不同。好消息是代发已经搭好了分层架构普通可以直接复用。坏消息是复用不等于照搬。这篇还有一个所有平台都受益的性能优化把100次数据库查询变成1次。一、复用代发的架构但四个差异必须处理代发搭好的分层架构策略层构建请求 → 构建器组装JSON → 处理器调用API → 解析层处理响应。普通订单直接套用这套模式。但有四个核心差异差异一Token来源不同。代发用仓库的抖音账号Token普通用店铺自己的Token。打个比方代发是用仓库的钥匙开门普通是用店铺的钥匙开门。拿错钥匙门打不开API直接返回授权失败。差异二请求格式不同。代发的请求只有发件人信息因为收件人信息在店铺侧普通的请求同时包含发件人和收件人。字段名也不一样——代发用一个字段名普通用另一个。上篇的DYXD路由坑就是这个差异导致的用代发的格式去调普通的接口字段名对不上API直接报参数缺失。差异三API调用方式不同。代发是一步调用直接取号普通是两步调用先创建订单获取订单号再用订单号申请运单号。打个比方代发是直接去窗口取号普通是先登记排队再叫号取号。两步之间有依赖——第一步的排队号是第二步取号的凭证不能跳过。差异四订单渠道逻辑不同。代发只有预约取件一种渠道普通支持多种普通/预约取件/供销。不同渠道的内部编码不同传错了会导致面单上的寄件人网点错乱——小件员跑到错误的网点去取件。设计决策普通、抖店、供销三种变体共用同一套策略差异在构建器内部处理。新增抖音变体时只改构建器里的条件判断策略层和处理器层不动。二、性能优化100次查询变成1次2.1 问题为什么取号越来越慢原代码有一个严重的性能问题。系统在取号时需要知道每个商品的信息书名、重量、ISBN等用来构建API请求。原代码的做法是每处理一个商品就去数据库查一次。一个订单有10个商品就查10次。100个商品就查100次。打个比方你去超市买10样东西每拿一样就跑去收银台问一次价格。10样东西跑10趟100样东西跑100趟。超市还是那个超市但你花了10倍的时间。实际跑起来多包裹订单的取号响应时间到了秒级。用户在界面上点取号等好几秒才出结果能明显感知到卡顿。2.2 解决一次查完内存里取优化方式把100趟跑收银台改成先打印一张完整的价格清单然后按清单取东西。具体做法把所有商品的信息一次性从数据库查出来放到内存里。处理每个商品时直接从内存取不用再去数据库。实测效果10个包裹的订单取号耗时从约1200ms 降到 250ms提升79%。包裹越多提升越明显——100次查询变成1次响应从能感知的卡顿降到几乎无感。这个优化不只影响抖音——所有平台的多包裹订单都受益因为商品信息查询是所有平台共用的逻辑。就像超市的价格清单是通用的不管你买什么品牌的商品都能用同一张清单。2.3 这类问题的普遍性N1查询是遗留系统里最常见的性能问题之一。它的特点是单个商品看不出来商品多了才暴露。测试时如果只测1-2个商品的订单完全正常。但线上实际场景经常有几十甚至上百个商品的订单这时候问题就出来了。所以性能测试不能只看能不能跑通还要看跑得够不够快。三、HMAC签名给每个请求盖个章抖音API调用需要HMAC签名——每个请求都要用密钥对参数进行签名就像给每封信盖个骑缝章防止中途被篡改。原代码的签名逻辑散落在多个方法里不同接口的签名规则略有不同。重构时提取为独立工具类统一处理盖章的规则只写一次所有接口共用。踩坑点签名时有几个细节容易出错——参数的排列顺序、空字段是传空串还是不传、中文要不要编码。不同版本的抖音API这些细节可能不同。打个比方去年寄快递要求写全地址今年改了可以只写到区。如果你还按去年的规矩写快递公司可能拒收。教训签名逻辑不要凭经验写必须以当前版本官方文档为准。文档里没写清楚的细节要通过测试验证。四、订单渠道101一个不碰没事一碰就挂的字段抖音的订单渠道有多种取值1普通、54预约取件、101供销渠道。这个字段非必填大多数订单用默认值1也能跑。但供销渠道的订单必须传101否则面单上的寄件人网点会错——小件员跑到错误的网点取件客户等半天没人来。前任把101硬编码在条件判断里文档里只提了一句。不知道规则就改改完大部分订单正常因为走的不是供销渠道只有供销渠道的订单出问题——不碰它没事一碰就挂挂了还不好定位。这类坑在遗留系统里特别多字段有业务含义但不是必填大多数场景用默认值能跑只有特定场景会暴露。排查时容易误判为偶发问题实际上是规则没搞清楚。五、DYXD路由上篇讲了过程这里只补结论上篇抖音代发已经完整复盘了DYXD路由注册错误的排查过程——我凭听起来像代发的直觉注册错了处理器被用户纠正后查原代码才确认真相。这里不再重复过程只放最终结论变体编码处理器说明普通/null抖音普通默认供销抖音普通内部判断订单渠道101分销端抖音普通无特殊处理代发抖音代发独立处理器只有代发走代发其他所有DY变体走普通。一句话教训原代码是业务规则的最终裁判——它跑在线上每天几千单在验证比任何文档都可靠。不清楚业务规则就不要猜。六、回归测试抖音普通完成8次测试5次成功——是所有平台里测试通过率最高的。同一订单1小时内多次快递切换旧单清理均正确执行没有出现运单号重复或遗漏。快递/场景结果说明顺丰特快成功产品编码特快正确下发全链路通过顺丰电商标快成功产品编码切换验证通过邮政成功全链路通过申通成功全链路通过圆通成功全链路通过中通失败面单账户余额不足——业务问题非代码问题顺丰服务类型再切换被拒平台幂等限制已取号订单不允许变更服务类型特别说明顺丰两种产品编码特快/电商标快都取号成功正是这次改造的核心需求——让用户手动选择顺丰产品类型——在抖音普通链路上的直接验证。测试的目的不是证明都能通过而是发现哪些不能通过。中通余额不足是已知的业务问题比线上突然报错好得多。七、总结抖音普通的核心价值不是又对接了一个平台而是验证了分层架构的可复用性。代发搭好的模式普通直接套用只处理四个差异点。性能优化的核心也不是用了什么高级技术而是换了一种思维方式从用一次查一次变成一次性查完用的时候直接拿。这个思路不只适用于取号——任何重复查询的场景都可以用。这就是架构升级的意义——不是为了让代码好看而是为了让下一次扩展更简单、系统跑得更快。下篇预告代发验证了架构能用普通验证了架构能复用——下一篇把镜头拉远聊架构本身《从改10个文件到加3个类8个电商平台统一架构实录》。不只讲我们做对了什么还讲我们故意没做什么。你们的系统里有没有那种单个商品看不出来商品多了就变慢的情况后来是怎么优化的