电商系统之订单系统
01
概述
订单系统作为电商系统的“纽带”贯穿了整个电商系统的关键流程。其他模块都是围绕订单系统进行完善的。订单系统的演化也是随着电商平台的业务变化而渐渐演化进化着,接出来就和你们一上去解析电商平台的“生命纽带”。
上帝视角订单系统
订单系统的作用是:管理订单类型、订单状态,搜集关于商品、优惠、用户、收货信息、支付信息等一系列的订单实时数据,进行库存更新、订单下发等一系列动作。订单系统业务的基本模型涉及用户、商品(库存)、订单、付款,订单基本流程是下订单——>减库存,这两步必须同时完成,不能下了订单不减库存(超买),或则减了库存没有生成订单(少卖)。超买店家库存不足,消费者下了单买不到东西,体验不好;少卖店家库存积压或则须要反复更改商品信息,反复麻烦,体验也不好。
02
订单基本概念
设计订单系统时包含几个大的方向须要考虑,这种内容决定了订单系统的稳定性和可持续性。
订单的多样性特征
主要来历源和操作的多样造成了订单多样性点。
订单数组
订单数组包含了订单中须要记录的信息,他的作用主要用于沟通其他系统,为下游系统提供信息根据。
订单信息
订单号作为订单辨识的标示,通常依照某种特定规则生成,依照订单的降低进行自增,同时在设计订单号的时侯考虑订单无序设置(避免竞争者或则第三方来计算订单量)。订单号后续用作订单惟一标识用于对接WMS(仓存管理系统)和TMS(运输管理系统)时的订单辨识。
订单状态
订单状态在下边章节会详尽描述
用户信息
指卖家的相关信息,包括名称、地址、手机号。O2O就会多一种情况就是自提点,这样地址则会变为自提点的地址。地址信息在后续会作用在WMS和TMS上用于区分区域和配送安排。
商品信息
商品的基本信息和库存,金额因为比较特殊所以我把金额独立在商品信息以外说,不过逻辑上似乎都属于商品信息范畴。商品信息主要影响库存更新和WMS形成。
金额信息
订单形成的商品信息,这儿面不仅要记录最终的金额,过程金额也须要记录。例如商品均摊的让利金额、支付金额,应付金额等。在后续的订单结算、退换货、财务等环节都须要使用。
时间信息
记录订单每位状态节点的触发时间。
03
订单流程
订单流程是指整个订单从形成到完成整个流转过程,包括了正向和逆向流程的过程。
正向流程
这儿面主要是涉及主流电商系统中的通用订单流程,部份细节可以依照自己平台的特殊性进行调整。
须要注意的地方
订单生成环节存在超时未支付手动取消的过程,库存的占用会在订单取消后释放。
假如选择COD(货到付款)则支付环节相应转移到订单配送以后,而过程中所有与货款相关的逻辑变为只操作金额数字,不对结算和帐户进行打退票操作。
金额平摊须要到商品
订单系统初审主要对恶意用户或则刷单情况进行处理。系统可依照白名单、黑名单、消费频次、促销品订购量方面做风控规则。假如后续会步入到人工初审,则规则上可以适当从宽。当触发规则须要进行订单退订的行为。此处设计时要当心对用户体验的损害,常常前台文案上说明当前节点是初审状态或则是等待接单。
传统电商则是通过关联第三方货运的货运信息进行跟踪。
预售等货和移仓须要弄成SOA服务,便于在交易页面估算预计时间和预计到货时间。移仓处理依赖库房的情况,也会涉及到后续分拆和合并包裹的逻辑。
订单形成时先要判定报缺情况,若果出现报缺问题则要考虑整单报缺、部分报缺、换货或则换转退的情况(库存,匆忙调拨和退票)。报缺情况分为系统报缺和实物报缺,这是承接但相对独立的两个环节。
电商系统要考虑7天无理由退款的情境,即订单状态完成后申请退款。此时主要涉及的是金额上的估算以及一些财务程序(如收据等)问题的处理。
逆向流程
逆向流程指订单发生取消、退货等情况时引起的订单流程过程。
触发逆向流程的触发主要有几种情况:
关注点
订单状态(某一节点后如订单形成后不容许取消订单)
当退单被店家拒绝后须要转到客服仲裁的环节
部份退的订单促销通常保持享用状态,但金额根据均摊的金额进行退票
订单状态
从订单状态设计目的和存在价值去剖析和理解它背后设计机制:维度及维度颗粒度大小。
1.正向和逆向流程维度
2.服务对象维度
订单推送
当状态发生变化时,须要将对应的变化情况告知给相关人员便于了解当前订单的情况,这就是订单推送的作用。
订单推送的触发依赖于状态机的变化,涉及到的信息包括
04
订单系统设计的挑战和实践
订单系统需求演化
第一步:实现订购流程
1.实现订单的创建、发货、确认等信息闭环
2.支持订单初审(早期可支持人工初审即可)
3.支持用户端显示订单相关信息
4.支持促销金额的估算
第二步:提供服务
1.提供订单分布式服务
2.支持跨平台交易单生成(即同一个大交易单内既有店家商品又有自营商品或则是多个店家的商品)
3.支持拆单、合并逻辑(配送单、支付单等)
4.提供更丰富的订单推送服务,建立订单状态
第三步:支持不同营销手段下的订单类型
平台发展到足够大的规模,提效、稳定弄成一个重要的话题。可以提供不同营销场景下的订单,如:团购、预购等。
订单系统构架的演化
第一代:简单粗鲁
第一代的问题
第一代系统因为,订单状态是在特定的服务器进行处理,假如服务一旦出现问题都会导致订单的遗失,造成订单流程难以进行下去。
总结:
1、服务单点
2、数据库单点
第二代:无状态异步驱动
第二代系统对于第一代有了挺好的提高,应用服务器不再保留订单状态,并且这样的系统设计同时也给数据库服务器导致了高频查询带来的压力,造成数据库相对比较脆弱。
总结:
状态扫描带来的负载
第三代:队列模式
第三代是对于第二代的升级,订单的状态流转不再借助高频查询数据库来获得,通过队列模式,挺好减少了数据库的压力,而且第三代仍然有问题,就是该系统中成了核心,该模块的维护都会显得很复杂,这也是构架设计的关键,没有完全的完美构架,只能得到一个平衡构架。
三代系统演化中的最佳实践
实践1:重试和补偿
实践2:幂等性
实践3:一致性实践
实践4:工作流()
无状态的,分布式布署,分布式储存工作流状态
定时器、重试、幂等性、强一致性的状态
工作流的描述和执行描述相分离,支持异步触发
系统优化
数据库读写分离
基本的原理是让主数据库处理事务性查询,而从数据库处理查询。数据库复制被拿来把事务性查询造成的变更同步到集群中的从数据库。其实,主服务器也可以提供查询服务。使用读写分离最大的作用无非是环境服务器压力。
用处
降低冗余
降低了机器的处理能力
对于读操作为主的应用微信订单系统,使用读写分离是最好的场景,由于可以确保写的服务器压力更小,而读又可以接受点时间上的延后。
读写分离提升性能之缘由
化学服务器降低,负荷降低
主从只负责各自的写和读,极大程度的减轻X锁和S锁争用
从库可配置引擎,提高查询性能以及节省系统开支
从库同步主库的数据和主库直接写还是有区别的,通过主库发送来的恢复数据,并且,最重要区别在于主库向从库发送是异步的,从库恢复数据也是异步的
读写分离适用与读远小于写的场景,假如只有一台服务器,当好多时,和会被那些访问中的数据堵塞,等待结束,并发性能不高。对于写和读比列相仿的应用,应当布署双主互相复制
可以在从库启动是降低一些参数来提升其读的性能,比如--skip-、--skip-bdb、--low--以及--delay-key-write=ALL。其实这种设置也是须要依照具体业务需求来定得,不一定能用上
平摊读取。如果我们有1主3从,不考虑上述1中提及的从库单方面设置,假定现今1分钟内有10条写入,150条读取。这么,1主3从相当于共计40条写入,而读取总量没变,因而平均出来每台服务器承当了10条写入和50条读取(主库不承当读取操作)。为此,即使写入没变,然而读取大大平摊了,增强了系统性能。另外,当读取被平摊后,又间接提升了写入的性能。所以,总体性能提升了,说白了就是拿机器和带宽换性能。
MySQL复制另外一大功能是降低冗余,提升可用性,当一台数据库服务器宕机后能通过调整另外一台从库来以最快的速率恢复服务,因而不能光看性能微信订单系统,也就是说1主1从也是可以的。
实现方案
数据库分库分表
不管是采用何种分库分表框架或则平台,其核心的思路都是将原先保存在单表中太大的数据进行分拆,将那些数据分散保存到多个数据库的多个表中,防止由于单表数据量太大给数据的访问带来读写性能的问题。所以在分库分表场景下,最重要的一个原则就是被分拆的数据尽可能的平均拆分到前端的数据库中,假如分拆的不均匀,就会形成数据访问热点,同样存在热点数据由于下降过快而又面临数据单表数据量过大的问题。
而对于数据以哪些样的经度进行分拆,你们看见好多场景中都是对业务数据的ID(大部份场景此ID是以自下降的形式)进行HASH取模的方法将数据进行平均分拆,这个简单的方法确实在好多场景下都是十分合适的分拆方式,但并不是在所有的场景中这样分拆的方法都是最优的选择。也就是说数据怎么分拆并没有所谓的金科玉律,更多的是须要结合业务数据的结构和业务场景来决定。
下边以你们最熟悉的电商订单数据分拆为例,订单是任何一个电商平台都有的业务数据,每位平台用户递交订单就会在平台前端生成订单相关的数据,通常记录一条订单数据的数据库表结构如下:
订单数据主要由三张数据库表组成,主订单表对应的就是用户的一个订单,每递交一次就会生成一条主订单表的数据。在有些情况下,用户可能在一个订单中选择不同买家的商品,而每位买家又会根据该订单中自己提供的商品估算相关的商品让利(如满100元减10元)以及根据不同的收货地址设置不同的货运配送,所以会出现子订单的相关概念,即一个主订单会由多个子订单组成,而真正对应到具体每位商品订单信息,则保存在订单详情表中。
假如一个电商平台的业务发展健康的话,订单数据是比较容易出现由于单个数据库表中的数据量过大而导致性能的困局,所以须要对他进行数据库的分拆。此时从理论上对订单分拆是可以由两个经度进行的,一个经度是通过订单ID(通常为自下降ID)取模的方法,即以订单ID为分库分表键;一个是通过卖家用户ID的经度进行哈希取模,即以卖家用户ID为分库分表键。
两种方案做一下对比:
1、如果是根据订单ID取模的方法,例如按1024取模,则可以保证主订单以及相关子订单,订单详情数据平均落入到前端1024个数据库表中,原则上挺好地满足了数据尽可能平均分拆的原则。
2、通过采用卖家ID取模的方法,例如也是根据1024取模,技术上则也能保证订单数据分拆到前端的1024个数据库表中,但这儿都会出现一个业务场景中带来的问题,就是假如有些买家是交易量十分大的,那这种买家的订单数据量(非常是订单详情表的数据量)会比其他买家要多处不少,也就是会出现数据不平均的现象,最终造成这种买家的订单数据所在的数据库会相对其他数据库提早步入数据归档(为防止在线交易数据库的数据的减小带来数据库性能的问题,通常将3个月内的订单数据保存在线交易数据库中,超过3个月的订单会归档前端专门的归档数据库)。
所以从对『数据尽可能平均分拆』这条原则来看,根据订单ID取模的形式看上去更能保证订单数据的平均分拆,但我们暂时不要那么快下推论,也要按照不同的业务场景和最佳实践角度多思索不同经度带来的异同点。
总结
电商平台的需求始终在变化,骤然订单系统的构架也会急剧变化,构架设计就是一个持续改进的过程,这篇文章还有很多细节未提到,假如你想把订单系统做的更好,须要愈发深入系统的每一个环节,例如:容灾、灾备、分流、流控都须要渐渐雕凿,在构架中没有完美的构架只有平衡的架构,不要追求单点的完美,而是要追求多点的平衡。
免责声明:部分文章信息来源于网络以及网友投稿,本站只负责对文章进行整理、排版、编辑,出于传递更多信息之目的,并不意味着赞同其观点或证实其内容的真实性,如本站文章和转稿涉及版权等问题,请作者在及时联系本站,我们会尽快为您处理。