真实互联网公司常见的订单分库分表架构设计。这个设计基本是很多大厂(电商、外卖、支付系统)使用的模式。🚀
我会从 设计目标 → 分片键选择 → 分库分表结构 → 表结构设计 → 查询优化 → 扩容方案 一步一步讲。
一、订单系统的真实规模
假设一个电商系统:
| 指标 | 数量 |
|---|---|
| 每天订单 | 1000万 |
| 每年订单 | 36亿 |
| 订单表数据 | 几十亿 |
如果只有一张表:
order
问题:
-
表太大(几十亿行)
-
索引巨大
-
查询慢
-
MySQL 扛不住
所以必须:
分库 + 分表
二、常见分库分表结构
典型架构:
Application
│
ShardingSphere
│
┌───────────┼───────────┐
│ │ │
DB0 DB1 DB2 DB3
order_0..7 order_0..7 order_0..7 order_0..7
总表数量:
4库 × 8表 = 32张订单表
逻辑表:
order
真实表:
db0.order_0
db0.order_1
...
db3.order_7
三、为什么订单要按 user_id 分片(重点)
很多人第一反应:
按 order_id 分片
但 大厂基本不会这样做。
原因:
1 用户订单查询最多
用户最常见操作:
查看我的订单
SQL:
SELECT * FROM order WHERE user_id = ?
如果按 user_id 分片:
user_id % 32
只查:
1张表
如果按 order_id 分片:
必须扫描32张表
性能很差。
2 用户订单列表
例如:
用户订单列表
SQL:
SELECT * FROM order
WHERE user_id = 1001
ORDER BY create_time DESC
LIMIT 10
按 user_id 分片:
只查一个表
速度极快。
四、分片算法设计
假设:
4库
8表
总分片数:
32
算法:
dbIndex = user_id % 4
tableIndex = user_id % 8
最终表:
db{dbIndex}.order_{tableIndex}
例子:
user_id = 1001
计算:
1001 % 4 = 1
1001 % 8 = 1
路由:
db1.order_1
五、ShardingSphere 配置示例
spring:
shardingsphere:
datasource:
names: ds0,ds1,ds2,ds3
rules:
sharding:
tables:
order:
actual-data-nodes: ds-\>{0..3}.order_->{0..7}
database-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: db_inline
table-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: order_inline
sharding-algorithms:
db_inline:
type: INLINE
props:
algorithm-expression: ds$->{user_id % 4}
order_inline:
type: INLINE
props:
algorithm-expression: order_$->{user_id % 8}
六、订单表真实结构(非常重要)
互联网订单表一般不会只存 ID。
典型结构:
CREATE TABLE order_0 (
id BIGINT PRIMARY KEY,
user_id BIGINT,
user_name VARCHAR(64),
shop_id BIGINT,
shop_name VARCHAR(128),
product_id BIGINT,
product_name VARCHAR(128),
price DECIMAL(10,2),
status INT,
create_time DATETIME
);
注意:
user_name
shop_name
product_name
这些都是:
冗余字段
原因:
避免 JOIN。
七、订单系统为什么大量冗余字段
如果不冗余:
SELECT *
FROM order o
JOIN user u
JOIN product p
在分库环境:
非常慢
互联网解决方案:
订单快照
订单生成时:
复制用户信息
复制商品信息
所以订单表里会有:
buyer_name
buyer_phone
product_name
shop_name
好处:
订单查询无需 join
八、订单表必须有的索引
订单系统最常见查询:
1 用户订单
WHERE user_id
索引:
(user_id, create_time)
2 单个订单
WHERE id
主键:
id
3 商家订单
WHERE shop_id
索引:
(shop_id, create_time)
九、主键生成(雪花算法)
订单 ID 不用自增。
因为:
分库后无法自增
一般用:
Snowflake
64位ID:
时间戳 + 机器ID + 序列号
ShardingSphere 自带:
key-generators:
snowflake:
type: SNOWFLAKE
优点:
全局唯一
高性能
十、历史订单如何处理
订单表会越来越大。
解决方案:
冷热数据分离
例如:
order_hot (最近3个月)
order_hist (历史订单)
查询:
最近订单 → hot
历史订单 → hist
十一、未来扩容问题
假设:
32表
不够用了。
扩容:
32 → 64
问题:
旧数据怎么办?
解决:
一致性哈希
或
双写迁移
很多公司用:
ShardingSphere Scaling
在线迁移数据。
十二、完整订单系统架构
真实架构:
API
│
Order Service
│
ShardingSphere
│
┌───────────┼───────────┐
│ │ │
DB0 DB1 DB2
order_0..7 order_0..7 order_0..7
配合:
Redis 订单缓存
Kafka 订单消息
ES 订单搜索
十三、真实互联网订单查询流程
用户打开订单列表:
APP
│
API
│
OrderService
│
ShardingSphere
│
db1.order_3
只查:
1张表
速度:
毫秒级
十四、总结(核心设计)
互联网订单系统核心设计:
1 分片键
user_id
原因:
用户查询最多
2 分库分表
4库 × 8表
3 冗余字段
避免:
跨库 join
4 雪花ID
保证:
全局唯一
十五、一个真正的架构经验(非常重要)
很多新系统一开始:
只建一张 order 表
等数据上亿:
再拆分
这会非常痛苦。
正确方式:
一开始就设计分库分表
即使:
只有几万数据