# 电商系统分库分表

在这里插入图片描述

# 用户业务

分片键选择:user_id

拆分策略:直接通过 user_id 进行 Hash 或取模。因为用户通常只关心自己的数据,绝大多数请求都带有 user_id,可以直接精准定位到库表

# 商品业务

分片键选择:通常按商户/店铺 ID(merchant_id / shop_id) 或 商品分类 拆分

特别注意:商品表往往更依赖分布式缓存(Redis)和搜索引擎(Elasticsearch)来抗读压力,纯数据库的分库分表反而可以相对保守,因为商品业务读多写少

# 订单业务

订单表是电商系统的“核心”,它面临着双向查询的天然矛盾

买家(用户):需要查看“我的订单列表”(需要按 user_id 查询) 卖家(商家):需要查看“我店铺的订单”(需要按 seller_id 查询)

# 基因法

如果你既想按 buyer_id 分片,又想按 order_id 单笔查询时不走全表扫描,可以使用基因法

原理:在生成 order_id(订单 ID)时,把 buyer_id(买家 ID)的后几位(比如后 4 位,即“基因”)融合成 order_id 的一部分。

效果:当按 buyer_id 查询时,根据其后 4 位正常路由。当按 order_id 查询单笔订单时,提取出 order_id 里的后 4 位基因,依然能精准路由到同一个库表

基因法(Genetics)是有局限性的,它只能完美解决“双维度”问题(比如买家维度 + 订单维度),按 seller_id 依然无法直接进行精准路由,可以采用后面的几种方案来解决

# 数据异构(C端、B端库分离)

这是大型互联网公司(如淘宝、京东)最常用的做法,把数据分2份

买家库:以 buyer_id 为分片键(配合基因法生成 order_id),专门抗 C 端用户的高并发高频读写

卖家库:以 seller_id 为分片键,专门抗 B 端商家的后台管理、订单查询

数据同步:C 端用户下单成功后,系统通过监听买家库的 Binlog(使用 Canal 等工具),异步把数据投递到消息队列,再由消费者解析并写入卖家库

优点:读写完全隔离,B 端的复杂查询绝不影响 C 端的下单性能,两端都极快

缺点:存在微秒到毫秒级的“数据同步延迟”,商家可能需要刷新一下才能看到最新订单;同时服务器成本翻倍

# 异构到 Elasticsearch

如果商家不仅要通过 seller_id 查询,还要根据“商品名称、下单时间、订单状态、物流单号”进行复杂的组合筛选,那么即使建了卖家库也玩转不动

做法:同样是通过 Binlog 异步同步,但这次不往 MySQL 存了,而是把所有订单数据实时同步到 Elasticsearch 中。

查询路由:买家查订单、根据订单 ID 查单条,走 MySQL 基因库。卖家查店铺订单、各种复杂条件筛选走 ES 统揽全局

优点:不仅解决了 seller_id 查询问题,还顺便解决了商家多条件模糊搜索的痛点

缺点:依然存在微小的同步延迟;系统架构变重,需要维护 ES 集群

# 通过映射表

如果你们的业务刚起步,数据量虽然超过了单表瓶颈,但还没到能养得起两套库或 ES 集群的程度(或者不想增加架构复杂度),可以使用映射表

做法:建立一张极其轻量级的映射表 t_seller_buyer_map,字段非常简单,只有两个:seller_id 和 buyer_id(可以再加个 order_id)。

分片键:这张映射表以 seller_id 作为分片键。

查询步骤(拆成两步):商家查订单时,系统先拿着 seller_id 去映射表里查询,找出这个商家下面有哪些 buyer_id。拿到 buyer_id 后,应用层再精准路由到买家库里去查真正的订单详情。

优点:成本低,不需要复杂的分布式数据同步机制

缺点:多了一次数据库网络 IO 交互。如果一个大商家的买家极多,映射表返回的数据量过大,这种方案就会失效