大规模电商系统分库分表实战经验分享
大规模电商系统分库分表实战经验分享
在互联网电商业务快速增长的背景下,单库单表架构难以支撑海量流量和庞大数据量的写入、查询压力。本文结合某大规模电商系统的真实生产环境,分享分库分表的选型思路、架构设计、Spring Boot 与 Apache ShardingSphere 集成方案,以及常见坑和优化建议,帮助后端开发者高效落地分库分表架构。
1. 业务场景描述
- 日订单量峰值:>1000 万条/天;
- 单表行数:预测可达 5 亿级别;
- 读写比:写入 7 成,查询 3 成;
- 业务热点:订单、支付及用户行为日志;
- SLA 要求:订单接口 P99 延迟 <200ms,日可用性 >99.9%。
为了保证系统的可伸缩性和高可用性,我们将核心业务数据库拆分为多个子库与子表,通过中间件透明路由以解耦应用层。
2. 技术选型过程
- 纯手写路由 vs 中间件支持:手写路由成本高、稳定性差,选择成熟中间件;
- ShardingSphere(开源、社区活跃) vs MyCat vs OceanBase:对比后选 ShardingSphere(轻量、扩展性好);
- 分库策略:按业务模块拆分(订单库、用户库、日志库)+库内垂直与水平分表;
- 分表策略:订单表按日期(
order_yyyyMM
)+订单 ID 哈希; - ID 生成:Snowflake 全局唯一;
3. 实现方案详解
3.1 数据库分片设计
-
按业务模块拆库: • db_order_0、db_order_1 ... • db_user 仅保留主表与小量辅助表; • db_log 按天或小时拆表;
-
订单表水平分表示例: CREATE TABLE
order_202309
(id
BIGINT NOT NULL,user_id
BIGINT,amount
DECIMAL(10,2),status
VARCHAR(32),created_at
DATETIME, PRIMARY KEY (id
,created_at
) ) ENGINE=InnoDB;
3.2 Spring Boot 多数据源与 ShardingSphere 配置
pom.xml 依赖:
<dependency><groupId>org.apache.shardingsphere</groupId><artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId><version>5.4.1</version>
</dependency>
application.yml:
spring:shardingsphere:datasource:names: ds0, ds1ds0:url: jdbc:mysql://127.0.0.1:3306/db_order_0username: rootpassword: rootds1:url: jdbc:mysql://127.0.0.1:3306/db_order_1username: rootpassword: rootrules:sharding:tables:`t_order`:actual-data-nodes: ds${0..1}.`order_${202301..202312}`table-strategy:inline:sharding-column: created_atalgorithm-expression: "order_${created_at.format('yyyyMM')}"database-strategy:inline:sharding-column: idalgorithm-expression: ds${id.hashCode() % 2}
Java 配置(可选):
@Configuration
public class ShardingConfig {// Spring Boot Starter 自动装配,无需手动定义 DataSource。
}
3.3 动态分片算法示例
对于复杂分片算法,可自定义 PreciseShardingAlgorithm:
public class OrderDatabaseShardingAlgorithm implements PreciseShardingAlgorithm<Long> {@Overridepublic String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingValue) {long id = shardingValue.getValue();int index = (int)(id % availableTargetNames.size());return availableTargetNames.stream().filter(name -> name.endsWith("_" + index)).findFirst().orElseThrow();}
}
在 YAML 中引用:
sharding:tables:t_order:actual-data-nodes: ds${0..1}.t_order_${0..11}database-strategy:standard:sharding-column: idsharding-algorithm-name: dbShardingAlgorithmsharding-algorithms:dbShardingAlgorithm:type: INLINEprops:algorithm-expression: ds${id % 2}
3.4 项目结构示例
order-service/
├── src/main/java/com/example/order
│ ├── config/ShardingConfig.java
│ ├── algorithm/OrderDatabaseShardingAlgorithm.java
│ ├── controller/OrderController.java
│ ├── service/OrderService.java
│ └── entity/Order.java
├── src/main/resources/application.yml
└── pom.xml
4. 踩过的坑与解决方案
-
全局唯一 ID 冲突:
- 解决:统一使用 Snowflake 或 UID Generator;保证分布式时钟同步。
-
跨库事务:
- 问题:XA 性能差;
- 解决:采用最终一致性方案 + 消息补偿;或使用 TCC/Seata 等分布式事务框架。
-
跨库 JOIN 性能问题:
- 限制跨库 JOIN;
- 使用数据中台或异步同步来实现复杂查询;
-
分片键选择不当:
- 单一日期字段导致热点库;
- 解决:组合 ID + 时间哈希,保证写入均衡;
5. 总结与最佳实践
- 选型前做好数据增长测算;
- 分库不等于分散风险,需配合备份/监控;
- 分片算法既要均衡又易于扩容;
- 业务层尽量屏蔽分库分表细节;
- 性能测试与压测必不可少;
通过本文方案,某大规模电商系统在分库分表后,日均订单 QPS 提升 3 倍以上,数据写入延迟从 100ms 降至 30ms,系统可用性稳定在 99.99% 以上。