ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

【电商核心业务实战】(6) 订单系统历史数据归档方案设计与实现

【电商核心业务实战】(6) 订单系统历史数据归档方案设计与实现 电商核心业务实战 · 系列文章目录(1) 电商项目核心订单系统设计与实现(2) 电商促销流程设计与实现(3) 分布式唯一ID 实战(4) 订单系统读写分离方案设计与实现(5) 订单系统分库分表方案设计与实现(6) 订单系统历史数据归档方案设计与实现(7) 电商项目订单支付实战(8) 使用RocketMQ优化订单超时取消流程(9) 分布式事务在电商项目中的应用场景分析与实战如何归档历史数据订单数据会随着时间一直累积前面我们说过预估订单的数量每个月订单2000W一年的订单数可达2.4亿三年可达7.2亿。数据量越大数据库就会越慢这是为什么我们需要理解造成这个问题的根本原因。无论是增、删、改、查中的哪个操作其本质都是查找数据因为我们需要先找到数据然后才能操作数据。无论采用的是哪种存储系统一次查询所耗费的时间都取决于如下两个因素查找的时间复杂度。数据总量。查找的时间复杂度又取决于如下两个因素查找算法。存储数据的数据结构。这两个因素也是面试问题中经常考察的知识。所以面试官并不是非要问一些用不上的问题来为难求职者这些知识点不是用不上而是求职者很多时候不知道怎么用。大多数做业务的系统采用的都是现成的数据库数据的存储结构和查找算法都是由数据库来实现的对此业务系统基本上无法做出任何改变。我们知道 MySQL 的 InnoDB 存储引擎其存储结构是 B树查找算法大多数时候是对树进行查找查找的时间复杂度就是 O(log n)这些都是固定的。我们唯一能改变的就是数据总量了。所以解决海量数据导致存储系统慢的问题方法非常简单就是一个拆字把大数据拆分成若干份小数据学名称为分片( Shard)。拆开之后每个分片里的数据就没那么多了然后让查找尽量落在某一个分片上以此来提升查找性能。存档历史订单数据订单数据一般保存在 MySQL 的订单表里说到拆分 MySQL 的表前面我们不是已经讲到了分库分表吗分库分表很多时候并不是首选的方案应该先考虑归档历史数据。以京东为例可以看到在我的订单中查询时分为了近三个月订单、今年内订单、2021年订单、2020年订单等等这就是典型的将订单数据归档处理。所谓归档也是一种拆分数据的策略。简单地说就是把大量的历史订单移到另外一张历史订单表或数据存储中。为什么这么做呢订单数据有个特点具备时间属性的并且随着系统的运行数据累计增长越来越多。但订单数据在使用上有个特点最近的数据使用最频繁超过一定时间的数据很少使用这被称之为热尾效应。因为新数据只占数据总量中很少的一部分所以把新老数据分开之后新数据的数据量就少很多查询速度也会因此快很多。虽然与之前的总量相比老数据没有减少太多但是因为老数据很少会被访问到所以即使慢一点儿也不会有太大的问题而且还可以使用其他的存储系统提升查询速度。这样拆分数据的另外一个好处是拆分订单时系统需要改动的代码非常少。对订单表的大部分操作都是在订单完成之前执行的这些业务逻辑都是完全不用修改的。即使是像退货退款这类订单完成之后的操作也是有时限的这些业务逻辑也不需要修改还是按照之前那样操作订单即可。基本上只有查询统计类的功能会查到历史订单这些都需要稍微做些调整。按照查询条件中的时间范围选择去订单表还是历史订单中查询就可以了。很多大型互联网电商在逐步发展壮大的过程中长达数年的时间采用的都是这种订单拆分的方案正如我们前面看到的京东就是如此。商城历史订单服务的实现商城历史订单的归档由 tulingmall-order-history 服务负责其中比较关键的是三个 Service既然是历史订单的归档归档到哪里去呢我们可以归档到另外的 MySQL 数据库也可以归档到另外的存储系统这个看自己的业务需求即可在我们的系统中我们选择归档到 MongoDB 数据库。对于数据的迁移归档我们总是在 MySQL 中保留3个月的订单数据超过三个月的数据则迁出。前面我们说过预估每月订单2000W一张订单下的商品平均为10个如果只保留3个月的数据则订单详情数为6亿分布到32个表中每个表容纳的记录数刚好在2000W左右这也是为什么前面的分库分表将订单表设定为32个的原因。在我们的实现中OperateDbServiceImpl 负责读取 MySQL 的订单数据和删除已迁出的订单OperateMgDbServiceImpl 负责将订单数据批量插入 MongoDBMigrateCentreServiceImpl 负责进行调度服务。在进行数据迁移的过程需要注意以下两点分布式事务考察迁移的过程我们是逐表批次删除对于每张订单表先从 MySQL 从获得指定批量的数据写入 MongoDB再从 MySQL 中删除已写入 MongoDB 的部分这里存在着一个多源的数据操作为了保证数据的一致性看起来似乎需要分布式事务。但是这里并不需要分布式事务解决的关键在于写入订单数据到 MongoDB 时我们要记住同时写入当前迁入数据的最大订单ID让这两个操作执行在同一个事务之中。这样在 MySQL 执行数据迁移时总是去 MongoDB 中获得上次处理的最大 OrderId作为本次迁移的查询起始ID当然数据写入 MongoDB 后还要记得删除 MySQL 中对应的数据。在这个过程中我们需要注意的问题是尽量不要影响线上的业务。迁移如此大量的数据或多或少都会影响数据库的性能因此应该尽量选择在闲时迁移而且每次数据库操作的记录数不宜太多。按照一般的经验对 MySQL 的操作的记录条数每次控制在10000以下是比较合适在我们的系统中缺省是2000条。更重要的是迁移之前一定要做好备份这样的话即使不小心误操作了也能用备份来恢复。如何批量删除大量数据在迁移历史订单数据的过程中还有一个很重要的细节问题如何从订单表中删除已经迁走的历史订单数据虽然我们是按时间迁出订单表中的数据但是删除最好还是按 ID 来删除并且同样要控制住每次删除的记录条数太大的数量容易遇到错误。这样每次删除的时候由于条件变成了主键比较而在 MySQL 的 InnoDB 存储引擎中表数据结构就是按照主键组织的一棵 B树同时 B树本身就是有序的因此优化后不仅查找变得非常快而且也不需要再进行额外的排序操作了。为什么要加一个排序的操作呢因为按 ID 排序后每批删除的记录基本上都是 ID 连续的一批记录由于 B树的有序性这些 ID 相近的记录在磁盘的物理文件上大致也是存放在一起的这样删除效率会比较高也便于 MySQL 回收页。关于大批量删除数据还有一点需要注意一下执行删除语句后最好能停顿一小会因为删除后肯定会牵涉到大量的 B树页面分裂和合并这个时候 MySQL 本身的负载就不小了停顿一小会可以让 MySQL 的负载更加均衡。
返回列表