订单状态(某个节点生成后不允许取消订单)
当退款被商户拒绝时,需要转至客服仲裁。
一般情况下,部分退款订单仍可享受促销,但金额将按照分配的金额进行退款。
订单状态
从设计目的和存在价值:维度和维度粒度来分析和理解订单状态背后的设计机制。
1. 正向和反向过程尺寸
2、服务对象维度
订单推送
当状态发生变化时,需要将相应的变化通知相关人员,以便了解当前订单的状态。这就是订单推送的作用。
订单推送的触发取决于状态机的变化,涉及的信息包括
04
订单系统设计的挑战与实践
订单系统要求的演变
第 1 步:实施采购流程
1.实现订单创建、发货、确认等信息闭环
2.支持订单审核(初期可支持人工审核)
3.支持客户端显示订单相关信息
4.支持促销金额计算
第二步:提供服务
1.提供订单配送服务
2.支持跨平台交易订单生成(即同一个大交易订单既包含商户产品又包含自营产品或多个商户的产品)
3.支持订单拆分合并逻辑(发货订单、付款订单等)
4.提供更丰富的订单推送服务,改善订单状态
第三步:支持不同营销方式下的订单类型
当平台发展到足够大的规模时,效率提升和稳定性成为重要课题。可提供不同营销场景下的订单,如团购、预购等。
订单系统架构的演变
第一代:简单粗暴
第一代问题
在第一代系统中,订单状态是在特定服务器上处理的。如果服务出现问题,订单将会丢失,导致订单流程无法继续。
总结:
1. 单点服务
2.数据库单点
第二代:无状态异步驱动程序
第二代系统较第一代有很大改进。应用程序服务器不再保留订单状态。但这种系统设计也给数据库服务器带来了高频查询带来的压力,使得数据库相对脆弱。
总结:
状态扫描带来的负载
第三代:队列模式
第三代是第二代的升级版。订单状态流不再依赖高频查询数据库。通过队列模式,大大减轻了数据库的压力。但是第三代仍然存在问题,就是在系统没有核心的情况下,这个模块的维护会变得非常复杂,这也是架构设计的关键。没有完全完美的架构,只有平衡的架构。
第三代系统演进的最佳实践
实践1:重试和补偿
实践2:幂等性
练习3:一致性练习
练习4:工作流()
无状态、分布式部署、分布式存储工作流状态
计时器、重试、幂等性和强一致性状态
工作流描述和执行描述分离,支持异步触发
系统优化
数据库读写分离
基本原理是让主数据库处理事务查询,而从数据库处理查询。数据库复制用于将事务查询引起的更改同步到集群中的从数据库。当然,主服务器也可以提供查询服务。采用读写分离最大的影响无非是环境服务器压力。
益处
增加冗余
提高机器加工能力
对于以读操作为主的应用,采用读写分离是最好的场景,因为这样可以保证写服务器承受较小的压力,并且读的时延是可以容忍的。
读写分离提高性能的原因
物理服务器数量增加,负载增加
和只负责自己的写和读,大大缓解了X锁和S锁的争用。
可以从数据库配置引擎,以提高查询性能并节省系统开销。
从主库同步数据和直接从主库写入是有区别的。恢复数据是从主库发送的。但最重要的区别在于,主库向从库发送数据是异步的,从库的数据恢复也是异步的。
读写分离适用于读远大于写的场景。如果服务器只有一台,多的时候就会被这些访问中的数据阻塞,等待结束,并发性能不高。对于读写比例相近的应用,应该部署双主互复制。
从库启动时可以添加一些参数来提高其读取性能,例如--skip-、--skip-bdb、--low--和--key-=ALL。当然,这些设置也需要根据具体的业务需求来确定,也不一定一定要用。
摊销读取。如果我们有1个主、3个从,不管上面1中提到的从库的单边设置如何,假设1分钟有10次写入和150次读取。那么1个和3个相当于一共写了40次,而读的总数没有变化。因此,平均每台服务器负责10次写入和50次读取(主库不负责读取操作)。因此,虽然写入保持不变,但读取却大大摊销,从而提高了系统性能。此外,当读取被摊销时,写入性能也会间接提高。因此,整体性能有所提高。说白了,就是用机器和带宽来换取性能。
复制的另一个主要功能是增加冗余并提高可用性。当一台数据库服务器宕机时,可以通过调整另一台从库来尽快恢复服务。所以不能只看性能,即1主1主。从也是可以的。
实施方案
数据库分库分表
无论使用什么样的分库分表框架或平台,其核心思想都是将太大而无法存储在单表中的数据进行拆分,将数据保存在多个数据库的多个表中,以避免因为单表数据量太大,给数据访问带来读写性能问题。因此,在分库分表的场景下,最重要的原则就是尽可能均匀地将拆分后的数据拆分到后端数据库中。如果分割不均匀,也会出现数据访问热点,热点数据也存在。因为增长速度太快,面临着单个数据表数据量过大的问题。
至于数据切分的纬度,可以看到很多场景都是利用业务数据的ID(大部分场景ID是自增的)通过HASH取模来均匀切分数据。 ,这种简单的方法确实是很多场景下非常适合的分裂方法,但并不是所有场景下的最佳选择。换句话说,如何分割数据并不存在所谓的黄金法则。更多地取决于业务数据的结构和业务场景。
我们以最熟悉的电商订单数据拆分为例。订单是任何电商平台都拥有的业务数据。每个提交订单的平台用户都会在平台后端生成订单相关数据。一般会记录一条订单数据。数据库表结构如下:
订单数据主要由三个数据库表组成。主订单表对应于用户的订单。每次提交都会在主订单表中生成一条数据。某些情况下,用户可能会在订单中选择不同卖家的产品,每个卖家会根据订单中提供的产品计算相关的产品折扣(如满100元减10元)和不同的收费。收货地址设置为不同的物流配送,所以就会出现子订单的概念,即一个主订单会由多个子订单组成,每个产品实际对应的订单信息都存储在订单明细中桌子。
如果电商平台业务发展健康,订单数据更容易造成性能瓶颈,因为单个数据库表的数据量太大,需要分库。此时理论上可以按两个纬度进行订单拆分。一纬度是根据订单ID(一般是自增ID)取模,即以订单ID作为分库、表key;另一种是通过对买家用户ID的纬度进行哈希调制,即将买家用户ID作为分片键。
让我们比较一下这两个选项:
1、如果按照订单ID取模,比如1024,可以保证主订单及相关子订单,订单明细数据平均落入1024个后端数据库表中,原则上是这样的很好地满足了要求。尽可能均匀地分割数据的原则。
2、采用买家ID取模的方式,比如按照1024取模,技术上也可以保证后端将订单数据拆分成1024张数据库表,但是这里会有一个业务场景。问题是,如果某些卖家的交易量非常大,那么这些卖家的订单数据量(尤其是订单明细表的数据量)会比其他卖家大很多,也就意味着数据会参差不齐。最终,这些卖家的订单数据所在的数据库会比其他数据库更早的进入数据存档(为了避免线上交易数据库数据增加带来的数据库性能问题,3个月内的订单数据一般保存在网上交易数据库中,超过3个月的订单将存档在后台专门存档数据库中)。
因此,从“尽可能均匀地分割数据”的原则来看,根据订单ID取模的方法似乎更能保证订单数据的均匀分割。不过,我们暂时不应该这么快下结论,我们也应该根据不同的业务场景和最佳实践的角度更多地思考不同维度带来的优势和劣势。
总结
电商平台的需求不断变化,订单系统的架构也会随之变化。架构设计是一个不断改进的过程。还有很多细节在本文中没有提及。如果想让订单系统变得更好的话,我们需要深入到系统的每一个环节,比如:容灾、容灾、导流、流控都需要慢慢细化。架构中没有完美的架构,只有平衡的架构。不追求单点的完美,但追求更多的平衡。
作者简介:李维山,世称山哥,1985年出品,现任米谋金服高级研发总监。曾就职于华为、阿里巴巴。他的座右铭是:假装成功。 《技术问答》、《中生代科技》专家组成员,运营个人公众号:科技方舟。