订单系统设计 —— 订单管理

时间:2024-03-18 15:33:05

一、方案背景

  订单系统是存在于各行各业的一个很基础、核心的业务系统。随着互联网的发展,订单系统的管理方案也在不断变化,订单数据规模的膨胀、用户对数据的重视,以及新的技术等都会影响订单管理方案。

1.1 考虑因素

  • 数据量: 订单的特点是只增不减,随着时间推移,订单量会越来越多;

  • 多维查询分析: 能够灵活的支持不同角色用户多种维护的查询和分析;
    订单系统设计 —— 订单管理

  • 性能: 订单管理服务需要只能高并发、低延迟;

1.2 数据特点

  订单数据具有明显的时间属性,主要呈现出两个特点:

  • 特点1:时间越久的订单被访问到的概率越低;
  • 特点2:订单规模随着时间推移不断变大,GB、TB,甚至PB等;
    订单系统设计 —— 订单管理

二、方案演进

  技术架构不是设计出来的,是演化出来的。订单管理方案也是随着业务和技术的发展,不断演化的,如下图所示。按照技术架构特点,大致分为三类:

  • MySQL架构:通过扩展库表数量来提升读写能力和存储容量;
  • MySQL + NoSQL架构:MySQL提供事务能力,NoSQL保障海量数据的存储和多维查询分析能力;
  • NewSQL架构:期待成熟的产品出现,兼容MySQL、NoSQL二者优势;
    订单系统设计 —— 订单管理

说明: 订单是个性化的数据,每个用户的订单数据都不同,不适合使用缓存;

三、MySQL架构

3.1 单库单表

  最简单的订单管理方案,适用于创业初期,便于商业模型的快速落地验证;

3.2 读写分离

解决问题: 单库性能达到极限,成为系统性能瓶颈点;
核心思想:增加从库,单点写入,多点读取(提升读性能)。目前主流方案都是走代理,比如阿里云的RDS,因为对用户完全透明,主从复制、主从切换,以及读写分离等工作由DBA来维护,如下图所示:

订单系统设计 —— 订单管理
主从同步延时问题: 正常情况下,主从同步延时都是在1ms以内。但是,当网络抖动或者数据库负载过高时,主从延时可能会变大,导致主从数据出现暂时不一致的情况。这种问题有三种解决方案:

  1. 忽略。如果业务可接受暂时的延时,则直接忽略;
  2. 产品流程上规避,尽量避免写操作后立即查询最新数据的情况。比如,订单支付完成后,会跳出支付成功页,而不是自动跳转到订单页;
  3. 强制读主库。

3.3 垂直拆分

  随着业务发展,订单表字段越来越多,职责不清晰,维护麻烦。将相同功能的字段为一组进行拆分,职责清晰,容易维护和扩展。

3.4 数据归档/冷热分离

解决问题: 单库数据量过多导致读写性能下降(RT变高);
核心思想:利用订单“长尾效应”的特点,将历史数据迁移到其它库(冷库),为热库瘦身,从而提升性能,如下图所示:
订单系统设计 —— 订单管理
注意点:

  1. 不要一次性删除大量数据(大事务,容易造成卡顿),建议按照主键顺序分批次批量删除;
  2. 这种方案需要产品设计上区分历史数据,比如前几年淘宝的“三个月前订单”选项;

3.5 分库分表

解决问题: 支持更高的并发量和数据量,解决单库性能和数据量的瓶颈;
核心思想: 数据分片,关键是分片key和分片算法。目前主流的方案有两种:路由表和哈希。

方案1:路由表

  路由表存储key与数据存储位置的映射关系, 每次查询数据时,先查询路由表获取数据位置,然后再查询数据,如下图所示。

  • 优点:分片灵活,可随意更改。比如,当出现热点分片时只需要更新路由表重新分片,当需要扩容时只需要增加路由信息。
  • 缺点:路由表为单表,且需要二次查询。
    订单系统设计 —— 订单管理

方案2:哈希

  哈希算法本身很简单,在订单场景下需要考虑的是,分库分表的key是什么,如何同时满足用户侧和商家侧的查询需求。比如,如果按照订单id分库分表,用户的订单列表和商家的订单列表则需要全表扫描,用户id和商家id也是同理。
整体思路: 订单数据一式两份,分为用户库和商家库(只读)。对于用户库,按照订单id分库分表,用户id的某几位信息加入到订单id中(“因子分表法”,见订单号设计),同时满足用户id和订单id维度的查询;对于商家库,按照商家id对订单重哈希路由,如下图所示:

  • 建立单独的商家库,是为了物理上隔离商户查询对用户的影响;

  • 商家库只读是为了单点写入,方便维护数据的一致性;

  • 如果允许商家订单数据,则商家库只维护商家与订单的映射关系(路由表);
    订单系统设计 —— 订单管理
    容量评估

  • 分库数量 = 峰值并发量 / 单库的IOPS;

  • 分表数量 = 数据量 / 单表容量(阿里Java开发规范的建议值:单表行数超过 500 万行或者单表容量超过 2GB);

四、MySQL + NoSQL架构

4.1 MySQL + ES

解决问题: 分库分表的查询能力有限,无法支持复杂灵活的查询;
核心思想: 引入ES,建立热点查询到订单ID的映射关系,用于支持复杂的查询条件。复杂查询通过ES获取对应的订单ID,再从数据库从库查询获得订单,如下图所示:
订单系统设计 —— 订单管理
注意点

  1. 不要将订单及其扩展字段全量同步到ES,会影响检索效率,只同步有强搜索需求的字段;
  2. 建议将订单及其关联表聚合成一张宽表,提升ES查询时的效率;

4.2 MySQL + ES + HBase

解决问题: 解决订单详情查询时间长问题(需要去订单表及其关联表多次查询);
核心思想: 引入HBase,将订单及其关联表全量字段导入HBase,RowKey为订单id,如下图所示:
订单系统设计 —— 订单管理

五、未来预期: NewSQL

  当前关系型数据库仍是主流的原因就在于其事务特性,NoSQL由于其高性能、高扩展和高存储量特性,成了目前技术方案的标配之一,以上所有的订单存储方案归纳起来就是MySQL + NoSQL的组合,未来NewSQL如果真正的能做到NewSQL = MySQL + NoSQL,订单的管理方案势必会被极大的简化,期待。

参考

  1. 淘宝服务端高并发分布式架构的十四次演进之路
  2. 阿里Java开发规范
  3. 有赞订单管理的三生三世与“十面埋伏”
  4. 基于Tablestore打造亿量级订单管理解决方案