1. 项目概述
ShardingSphere作为Apache顶级开源项目,已经成为Java生态中分库分表方案的标杆选择。最近在帮公司重构订单系统时,我采用了ShardingSphere 5.2.1 + SpringBoot的组合方案,仅用3天就完成了从零搭建到生产验证的全流程。这套方案最吸引我的地方在于:既保留了SpringBoot的便捷性,又通过ShardingSphere实现了近乎透明的数据分片能力。
特别说明:本文基于2023年最新的ShardingSphere 5.2.1稳定版,与早期4.x版本在配置方式和功能特性上有显著差异。如果你正在寻找可直接用于生产环境的配置方案,下文提供的参数模板已经过千万级数据量的实战检验。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心需求解析
2.1 为什么要分库分表
当单表数据量突破500万行后,我们开始遇到典型的性能瓶颈:
- 查询响应时间从毫级恶化到秒级
- 索引效率下降,B+树层级加深
- 备份恢复时间超过运维窗口期
- 高峰期数据库CPU持续100%
2.2 技术选型对比
| 方案 | 侵入性 | 学习成本 | 功能完整性 | 社区支持 |
|---|---|---|---|---|
| MyCAT | 高 | 中 | 一般 | 一般 |
| Sharding-JDBC | 低 | 低 | 完善 | 活跃 |
| TDDL | 高 | 高 | 定制化 | 有限 |
| 原生SQL路由 | 极高 | 极高 | 基础 | 无 |
ShardingSphere 5.x的独特优势在于:
- 支持DistSQL - 像操作普通SQL一样管理分片规则
- 弹性伸缩能力 - 在线调整分片策略不影响业务
- 异构兼容 - 同一套API支持MySQL/Oracle/PostgreSQL等
3. 环境搭建实战
3.1 依赖配置关键点
xml复制<!-- 必须排除原生Druid数据源 -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.2.1</version>
<exclusions>
<exclusion>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 使用ShardingSphere定制版Druid -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-datasource</artifactId>
<version>5.2.1</version>
</dependency>
3.2 分片策略配置模板
yaml复制spring:
shardingsphere:
datasource:
names: ds0,ds1
ds0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://db1:3306/order_db?useSSL=false
username: root
password: 加密后的密码
ds1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://db2:3306/order_db?useSSL=false
username: root
password: 加密后的密码
rules:
sharding:
tables:
t_order:
actual-data-nodes: ds$->{0..1}.t_order_$->{0..15}
database-strategy:
standard:
sharding-column: user_id
precise-algorithm-class-name: com.example.algorithm.UserIdHashMod
table-strategy:
standard:
sharding-column: order_id
precise-algorithm-class-name: com.example.algorithm.OrderIdRangeAlgorithm
binding-tables:
- t_order,t_order_item
4. 核心算法实现
4.1 自定义分库算法
java复制public class UserIdHashMod implements StandardShardingAlgorithm<Long> {
@Override
public String doSharding(Collection<String> availableTargetNames,
PreciseShardingValue<Long> shardingValue) {
int hash = shardingValue.getValue().hashCode();
int mod = hash % availableTargetNames.size();
return "ds" + Math.abs(mod);
}
}
4.2 自定义分表算法
java复制public class OrderIdRangeAlgorithm implements StandardShardingAlgorithm<Long> {
private static final long TABLE_SPLIT_SIZE = 1000000L;
@Override
public String doSharding(Collection<String> availableTargetNames,
PreciseShardingValue<Long> shardingValue) {
long orderId = shardingValue.getValue();
long tableIndex = orderId / TABLE_SPLIT_SIZE;
return shardingValue.getLogicTableName() + "_" + tableIndex;
}
}
5. 生产环境避坑指南
5.1 分布式ID生成方案对比
| 方案 | 吞吐量 | 趋势递增 | 依赖 | 适用场景 |
|---|---|---|---|---|
| UUID | 极高 | 否 | 无 | 临时数据 |
| 数据库自增序列 | 低 | 是 | 数据库 | 小规模部署 |
| Snowflake | 高 | 是 | 时钟同步 | 通用场景 |
| Leaf-segment | 中 | 是 | DB | 中等规模业务 |
| Redis INCR | 极高 | 是 | Redis | 高频短生命周期ID |
实测建议:订单类业务优先使用改良版Snowflake(解决时钟回拨问题),用户行为日志可用UUID
5.2 必须监控的指标
-
连接池指标:
- activeConnections:活跃连接数突增可能预示慢查询
- idleConnections:连接池空闲率低于20%需扩容
-
SQL执行指标:
- shardingsphere_statistics_sql_execute_latency_millis
- shardingsphere_statistics_sql_parse_count
-
分布式事务指标:
- shardingsphere_transaction_2pc_commit_count
- shardingsphere_transaction_2pc_rollback_count
6. 性能优化实战
6.1 索引设计原则
在分片环境下,索引必须包含分片键作为首列。例如:
- 原索引:
INDEX idx_create_time (create_time) - 分片后必须改为:
INDEX idx_user_create_time (user_id, create_time)
6.2 批量插入优化
错误示范:
java复制// 每条insert单独执行,产生N次网络IO
orders.forEach(order -> orderMapper.insert(order));
正确做法:
java复制// 使用ShardingSphere提供的批量插入API
OrderBatchInsert batch = new OrderBatchInsert();
orders.forEach(batch::addInsert);
batch.execute();
7. 分布式事务处理
7.1 柔性事务选型
yaml复制spring:
shardingsphere:
rules:
sharding:
default-database-strategy:
none:
default-table-strategy:
none:
props:
sql-show: true
# 启用Seata分布式事务
proxy-frontend-flush-threshold: 128
proxy-hint-enabled: false
proxy-backend-query-fetch-size: -1
proxy-frontend-executor-size: 0
proxy-opentracing-enabled: false
proxy-backend-executor-suitable: OLAP
proxy-backend-executor-size: 20
proxy-frontend-max-connections: 0
proxy-mysql-default-version: 5.7.22
proxy-default-port: 3307
proxy-netty-backlog: 1024
proxy-transaction-type: XA
7.2 事务陷阱排查
典型问题场景:
java复制@Transactional
public void processOrder(Long orderId) {
// 操作1:更新订单状态(分片表)
orderMapper.updateStatus(orderId, "PAID");
// 操作2:记录审计日志(非分片表)
auditLogMapper.insert(new AuditLog(orderId));
// 操作3:扣减库存(另一个分片表)
inventoryMapper.deduct(orderId);
}
致命错误:混用分片表和非分片表的操作会导致XA事务失效。解决方案是使用@ShardingTransactionType注解明确事务类型。
8. 扩展实践
8.1 多租户方案集成
通过ShardingSphere的Hint机制实现租户隔离:
java复制public class TenantContext {
private static final ThreadLocal<String> CONTEXT = new ThreadLocal<>();
public static void setTenant(String tenantId) {
CONTEXT.set(tenantId);
}
public static String getTenant() {
return CONTEXT.get();
}
}
// 在Mapper调用前设置租户ID
@Before("execution(* com..mapper.*.*(..))")
public void beforeMethod(JoinPoint joinPoint) {
String tenantId = TenantContext.getTenant();
HintManager.getInstance().addDatabaseShardingValue("t_order", tenantId);
}
8.2 影子库压测方案
配置示例:
yaml复制spring:
shardingsphere:
rules:
shadow:
data-sources:
production:
source-data-source-name: ds0
shadow-data-source-name: ds0-shadow
tables:
t_order:
data-source-names: production
shadow-algorithm-names: simple-hint-algorithm
shadow-algorithms:
simple-hint-algorithm:
type: SIMPLE_HINT
props:
shadow: true
foo: bar
压测流量标记:
java复制try (HintManager hintManager = HintManager.getInstance()) {
hintManager.setShadow(true);
// 执行压测SQL...
}
