1.前言经过近一年的供应链时效领域的发展,已经形成了一套理论和两个强大的工具(估计模型和路由系统)时效性估计条款。以现货为例,通过不断升级技术方案,预估模型准确率接近90%,满足向用户公开的条件。但是在访问前台场景的过程中,前台对我们提出了界面性能的要求。以连接业务明细浮层场景为例,接口调用环节经过业务明细、竞价、交易,给我们的供应链只用了15ms。要在15ms内完成所有的业务逻辑处理,是一个不小的挑战。2.初始状态——春风得意,马蹄姬抛开业务场景,谈接口性能就是耍流氓。时效预估接口依赖的数据源很多:模型基础数据、模型自底向上数据、仓库数据、SPU类目数据、卖家信息数据等,如何快速批量获取到内存中进行逻辑运算是性能的关键改进。最先接入时效性表达的是现货业务。单现货SKU时效查询初始接口调用链接如下:根据trace分析,接口性能的瓶颈在于数据查询,而非逻辑处理。数据查询后的逻辑处理只消耗了0.6%的时间。数据查询分为外部查询和内部查询。外部查询由3个RPC调用组成(占时间的27%),内部查询由11个DB查询组成(占时间的73%)。为什么会有这么多内部查询?因为估计模型是分段的,每个分段根据不同的影响因素有不同的来回策略,所以不能聚合成一个query。单个SKU的时效查询已经达到76.5ms。以商户详情浮层页30个现货SKU的预估时效查询,一个请求需要76.5*30=2295ms,无法接受,性能提升迫在眉睫。3.优化第一轮-昨夜西风枯绿树3.1内部查询优化由于内部查询所需的预估模型数据都是离线清洗,每日同步,所以对实时性要求不高。有多种方案可供选择:序号方案描述优缺点结论1离线处理完成后,更新现有的MySQL方案,不会产生开发成本。查询性能一般达不到要求,所以不要使用。3、经过离线处理,刷入本地内存后查询性能非常好。数据量有限制。该模型的数据量约为15G。由于模型数据量增加不多,而日常同步更多的是覆盖,使用32G实例完全可以满足要求。3.2外部查询优化一一分析三个RPC查询接口,找到优化方案:序号查询描述外域优化方案原因1城市名到codeTMS本地缓存由于城市名和code的映射关系数据只有20K左右,可以在应用启动时被请求一次后放入本地缓存。另外,城市名称和代码的变化频率很低,jetcache的@CacheRefresh每8小时自动刷新一次,完全可以满足要求。而卖家信息是低频变化的数据,可以使用T+1同步到Redis3获取商品类目Redis缓存同样的商品类目数据也是低频变化的数据,使用T+1同步到Redis3。3优化后,优化后的效果非常好很明显,单个SKU时效查询的RT从76.5ms降低到了27ms。同时减少了对外域的直接依赖,一定程度上提高了稳定性。27ms还是达不到要求。目前的瓶颈是查询Redis(96%的时间都花在了上面),能否进一步优化?4.优化第二轮——腰带越宽不后悔通过上面的分析,我们可以看出,当前的耗时集中在一个接一个的RedisI/O操作上。如果将一组Redis命令组装起来,一次性传输到Redis并返回结果,可以大大减少耗时。4.1流水线原理Redis客户端执行一条命令分为以下四个过程:1)发送命令2)排队命令3)执行命令4)返回结果其中1-4称为RoundTrip时间(RTT,往返时间)。该管道通过一次向Redis服务器发送多个Redis命令,大大降低了RTT。4.2优化及效果虽然Redis提供了mget、mset等批处理接口,但是Redis不支持hget批处理操作,不支持mget和hget混批查询,只能使用pipeline。另外,我们的场景是多键读场景,允许一定比例(低概率事件)的读失败,其中一条管道读取失败(管道是非原子的),不影响时效性估计,因为有来回策略,所以很适合。由于Redis查询之间的相互依赖性,上一次查询的结果需要作为下一次查询的输入参数,所以不能将所有的Redis查询合并到一个Redis管道中。虽然最后还是有3个RedisI/O,但是7ms的RT满足要求。4.3代码//管道查询类publicclassRedisBasePipelineRegister{//存储找到的数据privateThreadLocal