IT老齐架构300讲笔记(056) 日千万级订单系统的高可用、高性能架构该如何设计

目录

一、场景

二、如何设计一个简单的订单系统 

三、如何避免丢单情况

四、重构优化

4.1 如何设计一个支持日万级的订单系统

4.2 千日万级的订单系统可能遇到的问题

4.3 如何设计一个支持千日万级的订单系统


一、场景

我们每天都在使用网络进行下单,购买各种各样的商品和服务,作为一名后
端服务的程序员,你有没有好奇地想过,在网络下单后,后台流程是如何处
理的,订单是如何生成的,又是如何推送到下游的各个系统的,以及在这个
过程中,订单系统是如何保证系统低延迟、高可用的,尤其是不出现丢单的
问题。

思路是这样的:首先设计一个简单的订单系统,然后分析哪几个环节会丢单,以及面对日万级的订单量如何重构优化,最后分析下,面对日千万级的订单量时,系统应该如何升级改造。

二、如何设计一个简单的订单系统 

这个订单系统很简单,只有一个前台和后台,前台有结算页提供用户去结算,当后台收到前台用户点击去结算的操作时,就会开始处理下单服务。 起初,订单被写入到后台的数据库中,然后异构数据到缓存中,以此提供用 户在“我的订单”中进行订单查询,当用户支付完成后,收银台发送消息给下单的服务,进行数据库和缓存中的订单状态的修改。这样,一个简单的订单 系统就完成了。

三、如何避免丢单情况

要考虑丢单的问题,关键点应该聚焦在写数据库、接收和发送订单消息的这些环节上。

关键逻辑不要使用读写分离的查询方式避免从库同步延迟造成订单查询异常,比如创建订单之后要创建支付单。但是在反查订单的时候由于主从延迟,没查询到订单信息,这就可能会造成创建支付单失败。

另外,关键逻辑也不要使用缓存来进行订单的查询,这样做同时是为了避免因为缓存延迟造成订单反查的失败

订单补偿不要粗暴地使用消息队列的方式,避免中间件引发的订单丢失。比如在进行订单状态的修改时,如果处理失败,就将这个订单信息插入到消息队列中,重新消费,以此完成订单的补偿,这种方式在发送消息和接收消息时有可能存在丢消息的情况。

接收消息处理失败时一定要让消息重试,避免丢失,尤其注意 return、continue 等关键字。比如,一次消费多条订单记录,一条条地进行处理,如果修改订单成功,就继续处理下一条;如果修改失败,可能会 因为 retrun 或 cotinue 关键字将其余的消息都丢失掉了。

四、重构优化

4.1 如何设计一个支持日万级的订单系统

  1. 写数据库的时候,数据库事务的粒度不要太大避免锁表,关注慢SQL
  2. 关注数据异构的性能和稳定性,尤其是在网络抖动的情况下,可能会影响用户体验
  3. 要关注订单系统的幂等性,避免出现计费的错误,影响后续操作等流程

4.2 千日万级的订单系统可能遇到的问题

首先,前面的架构设计过重依赖于数据库,而且这个数据库还是订单库,
续读写请求会给数据库造成很大的压力
,比如,修改订单状态时就需要反查
数据库,并进行订单状态的更新,这些操作在高并发写请求下,会造成数据
库资源的抢占,从而影响系统的稳定性

 

 其次,为了避免数据不一致,请求访问主要集中在主库,这样主库压力就会很大,因此,就需要实现分库分表的部署架构,下单服务为此也必须改造支持分库分表的架构设计,但由于热点数据的存在,可能导致数据库出现数据倾斜问题,引发提早的数据库扩容

 

由于下单服务耦合业务过重,使得即使是多集群的部署架构,也很难实现快速的处理响应,更何况不同业务的订单处理流程还不一样,使得系统维护性也会越来愈差。比如,创建订单时由于业务不同,数码、3C、图书 等订单包含的信息是不一样的,这就需要特殊处理,这种特殊处理逻辑与创建订单耦合在一起,系统自然就会变得越来越慢。 

4.3 如何设计一个支持千日万级的订单系统

我们将下单服务进行了服务拆分,使用单独的接单服务处理接单,使用订单引擎和订单管道处理订单业务逻辑,改用双写和数据补偿的方式处理缓存,使用缓存过期的方式控制数据量。

 当用户在结算页点击提交订单之后,接单服务会在同一个事务里,将订单插
入接单库,将首任务插入任务库,再交由订单引擎进行任务调度
。什么是任
务?任务就是执行订单操作的步骤,比如写订单缓存、减库存、发送订单通
知等,以及前面提到不同的特殊业务流程,这些都是一个个的任务。我们通
过将整个订单处理流程分解成一个一个的任务,逐个单独处理,来应对日千
万级的订单处理压力。

其中,接单库为多台数据库,通过随机的方式写入,之所以没有采用哈希等算法,其原因在于扩展能力更具灵活性,当遇到流量洪峰来临,新增数台数据库,对写入逻辑是无感的。接单库采用一主多从的部署架构,当某一台机器故障,可以通过快速切换主从,或者摘除故障机等手段进行修复

而其中的任务库由订单引擎驱动执行,任务通过订单引擎的服务编排能力生成任务队列,首任务执行成功之后,会插入第二个任务,或者,会同时插入第二个和第三个任务。如果插入任务失败,订单引擎会重新执行当前任务,执行成功之后,继续执行插入操作,这里就需要每个任务的业务处理都需要保证幂等性。任务使用多线程的异步方式进行调度,并根据配置选择是串行执行还是并发执行。

 

如果任务执行失败,订单引擎是如何重新执行失败任务的呢?这就是通过任务状态机来实现的,任务状态机就如同一个系统的守护线程,任务状态机通过识别任务的状态,来判断每个任务是执行完成,还是执行失败的,并根据状态来进行任务调度,并且,会多次执行失败的任务,重试调度的频次也会逐渐递减,当超过一定的重试次数之后,就会告警通知人工干预。

订单引擎真正执行调度远程服务的并非是由订单引擎来调度的,而是由订单引擎调度订单管道,订单管道去调度远程真实的服务来执行的,其原因在于任务引擎本身就是多线程的设计架构,对线程占用就比较高,而远程调度会注册很多的服务,服务调度也会启动多线程去执行,如果共同部署在同一系统,就会出现线程数过多,造成 CPU 飙升的情况。

订单缓存的实现策略。接单服务在处理完一些业务逻辑之后,最后调用下单服务提交订单到订单中心,而在此之前,为了保证订单的及时性,在插入订单和任务之后,接单服务会先将订单通过接口写入到订单中心的缓存中,以支持用户在支付之后,在“我的订单”列表中能立即查询到我的订单。 总地来说,订单中心接到下单服务之后,会将订单落库,再同步更新缓存, 在后续订单中心接收到台账的消息后,也会同时更新数据库和缓存,将订单 状态更新为“订单完成”。

 

 

 

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值