冷热分离

需求分析

随着时间的推移,订单数据会逐渐增加,而在这些数据中,超过15天的订单是无法再进行变化的

因此一笔订单在15日之后的访问频率就会大大降低,为了提高订单表的查询效率,可以它转移到单独的库中

也就是将订单数据分到两个库中进行存储

  • 订单库:存储15日之内的订单(数据量较小,访问频率高,属于热数据
  • 历史订单库:存储15日之前的订单(数据量庞大,访问频率低,属于冷数据

这就是冷热分离(根据数据的特性,将数据划分为冷数据和热数据,然后针对冷热数据进行分离存储)

whiteboard_exported_image (9)

存储选型

订单冷热分离之后,通常采用MongoDB、HBase、TiDB等分布式数据库来存储冷数据

这么多数据库该如何选择呢?

假设电商项目有50万用户规模,请估算出订单数据量的规模

  • 用户活跃度:假设50万用户中有20%是活跃用户,也就是有50万 * 20% = 10万活跃用户
  • 订单频率:假设每个活跃用户每月至少下一次订单,也就是每年有10万 * 12次/年 = 120万订单
  • 订单大小:假设每个订单大约占用3KB的空间,也就是120万 * 3KB ≈ 3.6GB

对于年百万级别的数据量,比较适合的方案是使用分布式数据库来存储,首选就是TiDB

TiDB 是由PingCAP公司研发设计的开源分布式数据库,它结合了传统的关系型和非关系型数据库的最佳特性

与传统的单机数据库相比,具有以下优势:

  • 纯分布式架构,拥有良好的扩展性,支持弹性的扩缩容
  • 支持SQL,对外暴露MySQL的网络协议,并兼容大多数MySQL的语法,在大多数场景下可以直接替换MySQL
  • 默认支持高可用,在少数副本失效的情况下,数据库本身能够自动进行数据修复和故障转移,对业务透明
  • 支持ACID事务,对于一些有强一致需求的场景友好,例如:银行转账
  • 具有丰富的工具链生态,覆盖数据迁移、同步、备份等多种场景

但是它也有缺点:

  • 作为分布式数据库,对数据存储节点硬件要求比较高
  • 不支持存储过程、分区和GBK,数据写入时TiDB压力比较大
  • 分布式部署对网络要求也非常高

生产环境建议使用TIDB,但是教学环境可以使用MySQL来暂时替代

方案设计

本项目要在历史订单服务中完成两个主要的功能:

  1. 对所有终结订单(已完成、已取消、已关闭状态)进行数据统计分析
  2. 对历史订单进行存储和查询
    因此,当订单一旦终结就需要先同步到历史订单库的history_orders_sync表中等待统计分析
    当订单时间到达15日的时候,再从history_orders_sync表转移到history_orders表中做存储
whiteboard_exported_image (10)

方案实施

订单同步

根据冷热分离方案,首先将完成、取消、关闭的订单同步到历史订单库jzo2o-orders-history,涉及的表如下:

  • history_orders_sync:用于存储待迁移的订单数据(包括已完成,取消、关闭的订单)
  • history_orders_serve_sync:用于存储待迁移的已完成的服务单数据
whiteboard_exported_image (11)
  1. 当订单完成、取消、关闭时向同步表写入记录
    代码在订单状态机中:com.jzo2o.orders.base.config.OrderStateMachine.postProcessor()
  2. 当监听到订单库history_orders_sync表发生变化时写入到历史订单库history_orders_sync表
    代码在MQ监听器中:com.jzo2o.orders.history.handler.HistoryOrdersSyncHandler

冷热分离

订单完成15日后需要迁移到历史订单表

whiteboard_exported_image (12)

在历史订单同步表的sort_time字段中记录订单完成后15天的时间点
例如订单是2025-01-01 01:00:00完成的,那么sort_time字段中记录的就是2025-01-16 01:00:00
所以,只需要起一个定时任务,每天凌晨将sort_time中时间为前一天的记录迁移到历史订单表就可以
代码位置:com.jzo2o.orders.history.handler.XxlJobHandler.migrateHistoryOrders()
测试:
在测试时修改history_orders_sync表中sort_time字段值小于等于昨天日期
预期结果:
数据由history_orders_sync迁移到history_orders表

订单查询

在运营端进行订单查询的时候,就可以对冷热数据进行分别查询了
订单列表查询

  • 查询jzo2o-orders库的orders表
  • 代码在 com.jzo2o.orders.manager.controller.operation.OperationOrdersController.page()

历史订单查询

  • 查询jzo2o-orders-history库的history_orders表
  • 代码在 com.jzo2o.orders.history.controller.operation.HistoryOrdersController.queryForPage()