找回密码
 立即注册
搜索
查看: 132|回复: 0

淘宝购物车技术升级:扩容与性能优化,实现又快又丝滑的购物体验

[复制链接]

2万

主题

0

回帖

6万

积分

管理员

积分
64536
发表于 2024-12-6 05:19:37 | 显示全部楼层 |阅读模式
为了应对用户规模的增加和交易活动的激增,淘宝购物车近年来进行了一系列技术升级,主要集中在容量扩展和性能优化上。本文将简要描述这些改进以及它们如何提高购物车响应能力和用户体验,以实现“快速!”光滑的!”购物体验。主要包括:淘宝购物车扩容分析及解决方案、网络包大小及服务器并行化分析及解决方案

扩张背景

首先我们来说一下购物车的职责。购物车在预购过程中担负着促进购买的责任:对于犹豫下单的用户,通过营销等手段提高用户的决策效率。对于转化确定性高的用户来说,精准的推荐可以让他们购买更多;中台提供流畅的交易体验:折扣计算的准确性和流程的清晰性,以及各种业务形态的订单整合结算等。作为直接面向消费者的预购环节中的重要节点购物车中的体验问题非常重要且敏感。

分析往年用户反馈,用户对扩容需求强烈。

所以可以说,购物车的大幅升级刻不容缓!

对于所有用户,购物车额外购买上限将从200个(现货120个+预售80个)扩大到380个(现货300个+预售80个)。

产能扩张的挑战及解决方案

扩展挑战

每当我提到我的购物车容量时,在容量扩展功能上线之前我都会被折磨。为什么我的购物车上限只有120个? ? ?经过多轮内部讨论,我们也意识到购物车的整体拓展无论是在技术还是产品设计上都面临着巨大的挑战。聚焦于技术的挑战主要来自于对数据库的直接影响以及扩容带来的整个交易环节计算量的放大。

数据库挑战

存储瓶颈及相关影响

扩容首先带来的就是存储容量的增加。按照预计扩容至300个,存储容量将增加约2倍。在物理机资源有限的情况下,存储容量的增加会伴随着BP缓存速率的下降。这会导致其他用户的数据要从物理磁盘读取,磁盘IO将成为巨大的瓶颈,从而导致每个数据库操作的RT飙升。

扩容后,由于BP命中率下降,冷数据访问导致大量磁盘读取,数据库磁盘IO将成为巨大瓶颈。

实时计算的放大对整个交易链提出挑战

现有架构特点:“分页产品实时计算”

现有的购物车分页设计确实提高了用户进入购物车时的即时打开体验。

但遇到严重依赖实时计算结果的产品设计,例如跨店满减筛选、降价筛选、消费券筛选等,就需要计算出用户追加购买的全额金额并发送立即发送给客户。

购物车过滤的实现

跨店跨店满折报名大促销是品类+商家维度,营销规则中还包含了部分品类黑标的逻辑。但购物车跨店全折扣过滤是基于商品纬度的判断。所以这里是一个从品类+商家到产品纬度的扁平化计算流程。

为了确定某个产品是否注册了全额跨店折扣,需要调用营销进行实时计算。

通过营销统计出用户所在纬度的所有额外购买量后,购物车就可以筛选出哪些参与了跨店折扣。

传递给客户端的数据量过大,导致用户体验不佳。

预计投递300+件商品所需的数据包约为1M。在弱网络环境下,按100KB/s计算,所需传输时间约为10s。

扩容方案

提高购物车读取性能介绍

以双十一高峰期间的购物车监控为例,高峰期有明显的脉冲流量,主要流量集中在查询和检查这两类依赖读DB的操作,而涉及的流量写DB(增购、删除、更新等)明显少了。

查询购物车的SQL逻辑基本上是一个简单的语句。 SQL的查询条件主要是用户id、购买状态等,而购物车数据是按照用户纬度聚合的,所以单次请求返回的数据量不会太大。

所以我们把购物车的流量特征总结为:

典型的读多写少,SQL返回的数据不会太大,并且会因为瞬态脉冲而对RT敏感。



根据流量特征考察了各种数据库选择,并结合营销团队在双十一期间解决买家资产查询的经验,购物车选择了云原生内存数据库作为读取数据库,以提高查询性能。

读写分离

引入之后,高峰期的读流量全部消失了,写流量依然会造成db的双写,执行双写。同时有一个数据同步任务(经纬任务)进行同步。

采用db读写分离,采用双写+经纬方案保证数据同步。

预计算

购物车首页过滤的难点在于它依赖于所有产品实时计算的结果。购物车的扩大必然会给过滤带来性能压力。那么有没有办法在调用下游服务进行实时计算之前,根据一定的规则结合产品属性(产品标签、营销属性等)进行过滤,从而减少实时计算的产品数量呢?

在分析现有的筛选产品功能后,对于营销筛选(跨店满减筛选、预热筛选、消费券筛选),可以考虑提前生成产品着色表,记录追加购买的特征。在进行营销计算之前,根据规则过滤掉可能参与指定活动的产品。

预先计算的链接图:

在预计算环节,监控品类信息,将折扣规则与加购表数据结合,生成加购商品特征表。当用户发起过滤请求时,和普通的渲染请求一样,会在实时购买数据中查询完整购买数据的基本信息,然后结合特征表和过滤规则,过滤掉符合条件的商品。符合规则。然后产品与后续下游服务交互进行实时计算。

端到端耗时的合成

近年来,内网用户一直抱怨淘宝购物车的性能体验不佳。用户舆论中也很少见到“购物车滞后”的反馈。在如此激烈的竞争中,购物车不仅是用户购物体验的直接界面,​​也是电商运营效率的关键。一个优秀的购物车系统应该能够承受巨大的并发请求,同时保持数据的准确性和实时性,为用户提供流畅的购物体验。

从性能数据来看,从用户在购物车中发起网络请求到客户端发生相应变化,耗时组件大致分为三个部分:“网络传输”、“服务器端时间-消费”和“客户端渲染”。从服务器端结合快速降低时间消耗的使命来看,我们的优化思路除了客户端逻辑外主要包括以下几个部分。具体细节稍后会详细阐述。

网络传输优化:

压缩数据:使用Gzip等数据压缩技术或与业务逻辑结合,减少传输数据的大小。缓存策略:适当设置缓存策略,减少不必要的网络请求和数据包大小。延迟加载:延迟加载购物车中的非关键资源,优先加载对用户体验影响最大的内容。

服务器端耗时优化:

缓存机制:使用tair等内存缓存来缓存经常访问的数据,以减轻网络传输压力。并发处理:优化代码,采用异步处理和多线程技术,提高服务器的并发处理能力。代码优化:精简代码逻辑,去除不必要的中间件和服务

网络包传输

数据包大小对网络RT的影响分析

在当今的网络环境中,上下行带宽往往不对称。这种不一致在移动网络中也很常见。为了更有效地利用有限的无线频谱资源,网络运营商往往会向下行链路分配更多带宽,以提供更好的媒体消费体验。考虑到这种上下行带宽的不一致,上行报文的优化往往比下行报文的优化带来更大的效益。由于下游通常有更多的可用带宽,即使存在一些性能瓶颈,用户也可能不会立即感受到明显的性能下降。由于上行链路固有的带宽限制,任何微小的性能提升都可以给用户体验带来显着的改善。对上行数据包进行优化,例如减少发送数据量、压缩数据包、优化传输协议、应用智能队列管理等,可以有效减少延迟,提高上传速度,从而在带宽有限的情况下最大限度地减少阻塞和等待。时间。

上游包优化的收益略大于下游包优化的收益。

购物车状态协议缓存

回到购物车场景,上行包最直观的扩展点在于请求接口中的字段:

这个长长的列表包含了页面组件、页面状态等多种信息。页面组件是客户端渲染的强依赖数据,而状态数据是服务器端业务逻辑依赖数据。

状态数据存在于上行和下行协议中,而客户端并不依赖它进行逻辑处理,因此理论上这种数据不能作为协议下发,并且在服务器的“悠久历史”中,由于缺乏为了控制这些信息,状态协议数据可以随意附加,导致这个大数据包逐渐成为上下行网络数据包的负担。

状态协议数据存在于上行和下行协议中,客户端不依赖它。

为了达到与客户端解耦的效果,最直观的方法就是将这些数据存放在缓存中。然而,根据交易流量和现有计算资源进行简单估算后,我们发现事情并没有那么简单。按照淘宝购物车峰值流量计算,整体产生的带宽将达到数十GB,在目前的规格和资源下根本不够用。

压缩和缓存

前面提到,我们的最终目标是将服务器端依赖的数据放入缓存中,与客户端完全解耦。结合业务逻辑对状态协议进行剪裁后,数据包大小得到了减小,带宽瓶颈和可用性问题确实得到了缓解。那么在将这些数据存入缓存之前,还有没有空间可以继续“挤”呢?答案当然是肯定的。



以及压缩算法:在原始模式下,状态协议是通过客户端和服务器之间直接传输来维护的,因此需要使用编码来保证数据能够正确存储和传输,而不是通过原始的二进制格式。这种方法会带来33%左右的数据扩展。确定了在缓存中存储状态数据的优化方向后,状态数据可以不再进行编码,而是可以直接存储在字节数组中。

编码原理:编码将每组3字节(共24位)原始数据分为4个单元,每个单元6​​位。由于每 6 位只能表示 64 个状态 (2^6 = 64),因此编码选择一组 64 个字符来表示这些状态。该字符集通常包括大小写英文字母(AZ、az)、数字(0-9)、加号(+)和斜线(/)。在实际编码过程中,处理器将查找该字符集并将每个 6 位值映射到相应的字符。数据膨胀:考虑到在大多数字符编码(如 ASCII、UTF-8)中,一个字符通常占用 8 位(1 个字节)。编码时,每4个字符代表3个字节的原始数据。这意味着每个原始字节在编码后占用 8 * 4 / 3 = 10.666... 位。换句话说,原始数据的大小将增加大约 1/3 (33%)。

我们还以原始长度36KB(未压缩数据)测试了各种压缩算法的性能。尽管某些算法(例如)可以实现更高的压缩比,但从综合解压和压缩时间来看,gzip 仍然是最好的。不错的选择。

购物车状态缓存模式:经过上述优化后,可以进一步减小网络传输数据包中状态数据的大小,从而减少带宽和存储压力。客户端与服务器之间状态数据的交互方式也由原来的网络传输方式转变为与客户端解耦的服务器端缓存方式。客户端的上游数据不再持有状态数据,业务状态的计算完全交给服务器。如果缓存超时或异常,则通知客户端降级为网络传输(原始模式)。

流媒体 API

经过上面的优化,我们发现还剩下一个“坑”点。为了保证用户操作在异常情况下能够上传一份状态数据,下行包中仍然保留了全量的状态协议。这时我们需要在保证覆盖能力的同时,去掉下行报文中的状态协议。

流媒体API简介

参考业界的流式API模型,有不同的看法,主要有以下三种:

:多条上行对应一条下行; :一条上行对应多条下行; :端云双向流媒体服务;

流媒体API实现的是“一上行多下行”。请求模型如下:

引入流式传输后

通过引入流API,将下游数据包分为主数据包和子数据包。状态协议位于子包中,其余数据保留在主包中。客户端收到主包后即可执行渲染逻辑,相当于状态数据的网络传输时间,不再影响用户的端到端体验。

服务器接口

接口耗时分析

服务器端耗时比例:

从用户的操作数据来看,大多数用户操作的商品数量较少。在这些情况下,服务器的性能是可以接受的,这意味着在服务器上花费的时间对购物车中的这部分用户影响很小。但随着商品数量的增加,服务器端耗时占端到端耗时的比例不断上升,这意味着服务器端性能对购物车用户深度的影响更大。

产品数据较少时:服务器的优化点在于发送的数据量,这个问题在网络包优化中已经解决。

当产品数据量较大时:服务器的优化点是减少时间消耗

并行变换

购物车服务器端逻辑的一大特点是平均商品数量较多。历史上,使用简单的直接遍历来计算项目数量。解决逻辑过多最有效的方法自然是做并行处理。购物车流程根据业务特征连接各种独立的域活动。简化的流程图如下:

基于业务回顾整个流程,我们会发现全串行流程的低效率之处:

有些节点之间没有严格的顺序关系。例如,可以在注入产品标签后执行查询服务数据,而服务数据目前只有在视图层构建时才有用。库存查询、营销计算、服务数据查询等下游业务相互独立,属于低效的串行查询。购物车中的平均商品数量太大,这使得与商品数量相关的节点的内部循环效率非常低。典型的例子包括注入产品标签和构建视图层。

找到问题后,我们最终基于以下实现了购物车的并行改造:

主要针对可以拆分为多个子任务的大型任务进行优化。其核心技术原理基于“工作窃取”算法。每个工作线程都有自己的任务队列。当一个线程完成自己队列中的任务时,它可以从其他线程队列尾部窃取任务以继续工作。这样可以保持所有工作线程高效运行并避免线程空闲。和 等子类支持任务拆分(fork)和结果合并(join)。

这种面向多子任务并支持并行化的方法非常适合购物车场景中的多商品处理,例如:

多商品任务的并行处理:购物车的一大特点是平均商品数量较多。每个产品标签注入、单视图分组构建等操作都可以作为子任务独立执行。任务可分割性:购物车中的每个商品处理逻辑都是独立的,这使得任务很容易分割成小任务并行执行。提高响应性能:采用多核处理器可以同时执行多个产品的处理任务,从而减少整体处理时间,提高系统的响应速度。动态任务调度:“工作窃取”算法可以在运行时动态重新平衡任务负载。如果某些项目的处理需要很长时间,它可以确保其他线程不会空闲并帮助处理剩余的工作。

并行优化后的购物车流程简化图如下:

注入产品标签和视图层构建,从产品维度拆分子任务,并使用并行化。聚合一些仅在视图层且相互独立的下游数据节点,内部进行并行查询。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|【远见汇智】 ( 京ICP备20013102号-17 )

GMT+8, 2025-5-6 19:19 , Processed in 0.058018 second(s), 20 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表