【订单系统】设计核心
【订单系统】设计核心
学习核心
- 如何设计一个【订单】系统?(系统的核心功能)
- 沟通对齐
- ① 需求分析:关注订单系统的核心功能,一个订单系统的功能切入可以从商城购物切入,从用户下单、生成订单、跟踪订单状态、查看订单列表等核心功能切入,订单的不同状态也会有不同的功能
- ② 请求量分析:根据订单生成的体量级别来决定架构设计
- 如果没有明确要求订单体量,可以从一个简单的订单系统架构入手(支持日万订单级别的架构):由一个前台和一个后台串联起来构成一个完整的订单服务
- 随后可以根据基础的订单系统架构进一步臭狗改造,理解在基础架构的基础上如何实施重构以支撑更大体量级别的订单需求
- ③ 精准度分析:订单系统的精准度体现在订单状态推进的精准度,因为一个完整的订单流程可能涉及到很多子环节,那么每个环节都可能因异常而触发失败。如果对订单状态有高精准度的需求,则需要针对异常情况采取有效的规避和补偿措施以确保订单状态正常流转
- ④ 难点分析:
- 高并发、高精准度、高性能
- 整体设计
- ① 简单的订单系统架构(支持日万级别)
- ② 日百万级别的订单系统架构:
- 分库分表处理海量数据
- 引入订单引擎实现状态推进(将订单的流程拆分为一个个小任务,通过任务编排将这些任务串接成一个完成的流程)
- 引入订单缓存,提升并发度
- 要点分析(或难点分析)
- ① 存储结构:核心数据存储结构分析
- MySQL 存储核心数据
- Redis 用作订单缓存
- ② 高并发(高可用、高性能):
- 借助分库分表中间件处理海量数据,减轻单个数据库的服务压力
- 引入异步方式处理衍生任务(例如订单处理完成发货、发送通知等),提升接单服务的并发处理能力
- 引入订单引擎,通过服务流程编排确定订单执行步骤,确保每个环节的正常推进,避免丢单、卡单等异常问题,进而保证订单流程的高可用
- ③ 高精准:
- 引入订单引擎实现状态推进,确保订单状态的高精准度(核心在于通过任务状态机来控制)
- ① 存储结构:核心数据存储结构分析
- 总结陈述
学习资料
🟢【订单系统】场景核心
首先理解订单系统需要实现哪些功能,因为对于订单系统而言,完备的订单系统功能有很多,但很多都是非核心功能,实现思路也相对简单,并不是考察的重点。因此在有限的时间内不要将目光放置在设计一个完备的订单系统,而是关注订单系统核心功能和要点做扩展
例如对于用户而言一个订单的流程无非是:生成订单、查看订单列表、跟踪订单状态,基于此可以得到订单的核心功能分析如下:
🚀【订单系统】场景实战
1.沟通对齐
此处的沟通对齐方向,主核心方向是需求分析、请求量分析、精准度分析、难点/要点分析,可能还有涉及到其他的一些容量、设计等方面的对齐
① 需求分析
订单系统的核心功能
关注一个完整的订单流程:下单、订单状态跟踪、订单列表等
业务流程分析
订单系统一般可以结合购物商城去理解,从购物车提交购买开始会正式进入订单流程,当进入订单流程之后本次购物的状态就会由订单状态来进行推进(期间会经历下单、支付、发货、收货、退换货等),在整个订单流转的过程中,用户都可以在【我的订单】页面跟踪订单列表和订单详情
其中每个状态会有进一步细化的功能(衍生流程):
- 下单
- 创建订单
- 支付
- 支付失败
- 订单超时未支付
- 支付成功发送消息通知
- 发货
- 发货超时提醒
- 收货
- 确认收货
- 超时自动收货
- 退换货
- 退换货流程
- 订单列表查看
- 筛选订单列表:根据订单状态、时间等筛选
- 查看订单详情
在梳理了订单系统的核心功能时候,需要与对方对齐设计的核心功能点,抛掉一些细枝末节的东西,把握大方向。例如此处可以针对一些比较有设计点的功能进行展开分析:
- ① 下单:不同的下单请求量级会带来完全不同的系统设计方案,所以这里主要关注下单的量级问题
- ② 订单状态推进:状态推进主要关注订单状态推进过程中,出现了一些异常情况之后,如何保证订单依然能有序推进
- ③ 订单列表查询:需要确认请求量级和数量级,核心是要给出低成本且满足性能需求的方案
② 请求量分析
不同的请求量级有不同的设计难度,因此需确认请求量级好进行下一步的方案设计
- 基础:如果没有特别要求,可以先初步设计一个日万级别的订单系统,梳理各个环节的挑战
- 扩展:结合现有系统设计,讲述当场景演变为千万级别的时候应该如何对系统进行重构优化、如何升级改造等
日万级订单系统设计
千万级订单系统设计
如果是个千万级的系统,那整个系统的架构、服务的划分、功能的划分需要重新梳理设计,基于上述架构进行重构优化(在下述整体架构设计中进行扩展优化)
③ 精准度分析
是否允许少部分订单状态异常的情况?
整个订单从开始状态到结束状态需要经过很长的推进阶段,期间会存在各种数据库的修改查询,消息的收发。同时也会存在很多关联任务的执行,其中某些环节难免会出现问题,就会导致订单状态出错。此处大致例举几个常见问题以及应对方式:
① 关键逻辑不要使用读写分离的查询方式,避免从库同步延迟造成订单查询异常:比如创建订单之后要创建支付单。但是,在反查订单的时候由于主从延迟,没查询到订单信息,这就可能会造成创建支付单失败
② 关键逻辑也不要使用缓存来进行订单的查询:这样做也是为了避免因为缓存延迟造成订单反查的失败
③ 订单补偿不要粗暴地使用消息队列的方式,避免中间件引发的订单丢失:比如在进行订单状态的修改时,如果处理失败,就将这个订单信息插入到消息队列中,重新消费,以此完成订单的补偿,这种方式在发送消息和接收消息时有可能存在丢消息的情况
④ 接收消息处理失败时一定要让消息重试,避免丢失:尤其注意 return、continue 等关键字。比如,一次消费多条订单记录,一条条地进行处理,如果修改订单成功,就继续处理下一条,如果修改失败,可能会因为retrun 或 cotinue 关键字将其余的消息都丢失掉了
如果不允许订单状态出现异常的情况,则需要在系统中涉及各种补偿机制,以此应对偶尔出错的情况
④ 难点/要点分析
从高精准、高并发这两个方面切入
① 订单状态高精准:如果要求订单状态是高精准的,那就需要尽可能的考虑到各种异常情况,然后通过提前规避,或者设计事后补偿的方式来应对
② 高并发:高并发状态下,如何保证订单状态的及时有序推进,相关任务能够有序执行不堆积,不阻塞,并且不出错。会是一个比较大的难点。
2.整体设计(架构设计)
① 简单的订单系统架构(支撑日百万级)
结合图示分析,一个简单的订单系统架构包括一个前台和一个后台,前端提供了结算页面供用户进行购物结算,当后台接收到前台点击【去结算】按钮时,就会开始处理下单服务。此处大部分订单相关的功能都集中在一个服务中(下单服务),包括生成订单、订单状态推进、订单任务处理等功能。该服务功能虽然丰富,但会存在服务功能复杂、边界不清晰、性能较差等问题
订单被写入到后台的数据库中,然后异构数据到缓存中(以此提供用户在【我的订单】页面中查询订单信息),当用户支付完成之后,收银台发送消息给下单服务,进行数据库和缓存中的订单状态修改。基于此构成一个简单的订单系统架构,支撑日万级的订单量
② 日千万级的订单系统架构
==分析:==对比原有的订单服务、功能拆分,此处引入服务编排的概念,实现一种类似工作流的机制规定业务流转规则,将一个个服务接口的调用按照执行订单操作的步骤给整合成一个完整的订单流程。此处的任务就是执行订单操作的步骤,例如写订单缓存、减少库存、发送订单通知等,以及前面提到的不同的特殊业务流程,都可以看做一个个任务,这些任务经由编排构成完成的一个业务流程,即一个订单会有很多任务需要执行(一对多的关系)
整体设计流程分析:
- ① 用户在结算页点击结算,结算页调用后台的接单服务
- ② 接单服务接收到下单请求之后,会负责接单,并将订单插入到订单库,同时在一个事务里将首任务插入任务库,并通知调度起订单引擎开始执行任务
- ③ 【订单引擎】根据任务编号依次进行任务调度,更新任务状态,并由任务状态机进行任务校验补偿处理
- ④ 【订单引擎】通过调度订单管道,实现真实服务的远程调度,订单管道请求服务之后,将处理结果返回给任务引擎
- ⑤ 订单中心会在接单服务创建订单时,异步写一份订单缓存在订单中心,然后再通过数据异构的方式,再次写一份数据在订单缓存中
(1)订单"下单服务"的实现策略
借助【分库分表中间件】实施分库分表操作
此处可以理解为接单服务是度构建独立的前置订单表,和最终订单中心生成的订单数据是关联的
(2)订单引擎实现"状态推进"的策略
① 任务的创建:(任务列表)
其中的任务库由订单引擎驱动执行,任务通过订单引擎的服务编排能力生成任务队列,首任务执行成功之后,会插入第二个任务,或者,会同时插入第二个和第三个任务。如果插入任务失败,订单引擎会重新执行当前任务,执行成功之后,继续执行插入操作,这里就需要每个任务的业务处理都需要保证幂等性
② 任务调度:(订单引擎)
任务使用多线程的异步方式进行调度,并根据配置选择是串行执行还是并发执行
③ 失败任务处理:(状态机)
如果任务执行失败,订单引擎是如何重新执行失败任务的呢?这就是通过任务状态机来实现的,任务状态机就如同一个系统的守护线程,任务状态机通过识别任务的状态,来判断每个任务是执行完成,还是执行失败的,并根据状态来进行任务调度,并且,会多次执行失败的任务,重试调度的频次也会逐渐递减,当超过一定的重试次数之后,就会告警通知人工干预
④ 任务实际执行:订单管道
订单引擎真正执行调度远程服务的并非是由订单引擎来调度的,而是由订单引擎调度订单管道,订单管道去调度远程真实的服务来执行的,其原因在于任务引擎本身就是多线程的设计架构,对线程占用就比较高,而远程调度会注册很多的服务,服务调度也会启动多线程去执行,如果共同部署在同一系统,就会出现线程数过多,造成 CPU 飙升的情况
⑥ 订单缓存的实现策略:
接单服务在处理完一些业务逻辑之后,最后调用下单服务提交订单到订单中心,而在此之前,为了保证订单的及时性,在插入订单和任务之后,接单服务会先将订单通过接口写入到订单中心的缓存中,以支持用户在支付之后,在我的订单“列表中能立即查询到我的订单
3.要点分析
① 存储结构
(1)订单系统核心数据:MysqI(订单数据)
由于订单系统数据的一致性要求比较高,所以整体采用mysql来管理订单数据。后续关于订单状态的推进等,都是基于mysql进行操作,同时还会依赖数据库的事务等特性来保证并发场景下不出问题
(2)我的订单列表:Redis(订单缓存)
除了订单系统核心的订单状态推进功能,还有一个功能"我的订单”也是用户常用的功能,用户需要在前台入口查看个人的订单数据信息,由于这部分内容查询会比较频繁,并发数据的变动也是可以接受的小部分变动,所以为了提升用户的查询性能,这里需要引入Redis做一层缓存
思考对于订单缓存的存储结构设计?
此处缓存主要针对核心功能点去提升:
- ① 最核心请求量最大的就是:获取时间最近的 N个订单的信息(默认列表)
- ② 可以将订单中固定的信息一起放入 redis.list,以实现分页等功能
- ③ 将变动的信息单独用redis.hash 存储,key为orderid,value 为订单状态等,以方便状态变化的时候去更新
- ④ 最后订单列表的展示需要上面两部分信息的结合
redis 没办法支持太复杂的筛选,包括日期范围,状态筛选类型筛选等,这是一个组合功能,这些复杂筛选相对频次更低,所以一般不会基于 redis 去做,可以基于 mysql, 复杂筛选用户对延迟的容忍度更高。但是上面的核心功能,即首屏默认列表展示,就不能太慢
② 如何保证高精准度
订单整个状态推进过程是最容易出现异常的,因为一个订单关联的任务会很多,任务阶段也很多,所以某一步出错都有可能导致整个订单的整体失败。也就出现了订单系统精准度不够的问题。
所以系统如何处理失败任务就尤为关键,所以本设计中,也重点关注了这个问题。核心通过任务状态机来实现的,任务状态机就如同一个系统的守护线程,任务状态机通过识别任务的状态,来判断每个任务是执行完成,还是执行失败的,并根据状态来进行任务调度,并且,会多次执行失败的任务,重试调度的频次也会逐渐递减,当超过一定的重试次数之后,就会告警通知人工干预
③ 如何保证高可用和高性能
整个订单系统接单的核心流程,几乎全是同步执行,只有少数任务,比如发送订单通知给下游系统是采用消息异步的方式执行,以此来保证订单流程的高性能。而整个处理过程,基于订单引擎的调度,通过服务流程编排确定一个订单的执行步骤,并有效地保证每个环节的正确执行,避免订单丢单、卡单等异常问题的出现,进而保证订单流程的高可用
4.总结陈述
深刻总结
- 交易平台一直是各个公司的核心系统之一,涉及到数据流与资金流的流转,通俗说,就是钱从你这里走。那如何做到交易平台的高可用,是着实需要在细节点上下足功夫的
要点牵引
- 订单系统的核心以及难点就是:高精准度,高性能,高可用所以做系统设计的时候,将整体的中心放在了这一块的设计上面。至于订单系统一些具体的功能,例如退换货,订单超时自动关单等具体的功能就省略了
收尾请教
- 以上就是对订单系统的设计,短时间内确实会有很多考虑不周的地方,有不成熟的地方还请指教