单表数据量超过千万级后,查询性能下降明显,索引维护成本上升。ShardingSphere作为Apache顶级项目,提供分库分表、读写分离和数据加密等能力,对应用层透明。本文以Java Spring Boot项目为例,完整演示分库分表方案的落地配置。
分片策略设计与数据分布规划
分片策略包含分片键选择和分片算法两部分。分片键应选择查询条件中出现频率最高的字段,避免跨片查询。订单系统通常以user_id作为分片键,保证同一用户的订单落在同一数据节点。
分片规模规划:假设单表500万行作为阈值,当前订单量约8000万,按user_id取模分16库16表,单表数据约30万行,满足性能要求。预留3倍增长空间。
ShardingSphere-JDBC集成配置
Maven依赖引入:
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core</artifactId>
<version>5.4.1</version>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>4.0.3</version>
</dependency>
application.yml分库分表配置:
spring:
shardingsphere:
mode:
type: Standalone
repository:
type: JDBC
datasource:
names: ds0,ds1,ds2,ds3
ds0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://mysql-0:3306/order_db_0?useSSL=false&characterEncoding=utf8
username: root
password: ${DB_PASSWORD}
pool-name: HikariCP-ds0
maximum-pool-size: 20
ds1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://mysql-1:3306/order_db_1?useSSL=false&characterEncoding=utf8
username: root
password: ${DB_PASSWORD}
maximum-pool-size: 20
ds2:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://mysql-2:3306/order_db_2?useSSL=false&characterEncoding=utf8
username: root
password: ${DB_PASSWORD}
maximum-pool-size: 20
ds3:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://mysql-3:3306/order_db_3?useSSL=false&characterEncoding=utf8
username: root
password: ${DB_PASSWORD}
maximum-pool-size: 20
rules:
# 分库分表规则
sharding:
tables:
t_order:
# 真实数据节点:4库 x 4表 = 16个分片
actual-data-nodes: ds${0..3}.t_order_${0..3}
# 数据库分片策略
database-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: db-mod-hash
# 表分片策略
table-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: table-mod-hash
# 分布式主键
key-generate-strategy:
column: order_id
key-generator-name: snowflake
# 订单明细表 - 绑定到订单表
t_order_item:
actual-data-nodes: ds${0..3}.t_order_item_${0..3}
database-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: db-mod-hash
table-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: table-mod-hash
key-generate-strategy:
column: item_id
key-generator-name: snowflake
# 绑定表关系(避免跨库JOIN)
binding-tables:
- t_order,t_order_item
# 广播表(字典表,所有库都有全量数据)
broadcast-tables:
- t_dict_order_status
- t_dict_payment_type
# 分片算法定义
sharding-algorithms:
db-mod-hash:
type: MOD
props:
sharding-count: 4
table-mod-hash:
type: MOD
props:
sharding-count: 4
# 分布式主键生成器
key-generators:
snowflake:
type: SNOWFLAKE
props:
worker-id: 1
# 读写分离规则
readwrite-splitting:
data-sources:
readwrite_ds0:
write-data-source-name: ds0
read-data-source-names: ds0-read0,ds0-read1
load-balancer-name: round-robin
readwrite_ds1:
write-data-source-name: ds1
read-data-source-names: ds1-read0,ds1-read1
load-balancer-name: round-robin
load-balancers:
round-robin:
type: ROUND_ROBIN
props:
sql-show: true # 打印实际执行的SQL(生产环境关闭)
分布式主键与跨片查询处理
ShardingSphere内置Snowflake算法生成分布式ID,配置worker-id确保集群中各节点不重复。Java实体类中标注主键生成策略:
@Data
@TableName("t_order")
public class Order {
@TableId(type = IdType.ASSIGN_ID) // 使用ShardingSphere生成
private Long orderId;
private Long userId;
private String orderNo;
private BigDecimal amount;
private Integer status;
private LocalDateTime createTime;
}
// MyBatis-Plus中无需额外配置,ShardingSphere拦截SQL自动路由
// 指定user_id的查询 - 精准路由到单分片
// SELECT * FROM t_order WHERE user_id = 10086 AND status = 1
// → SELECT * FROM t_order_2 ON ds2 WHERE user_id = 10086 AND status = 1
// 未指定user_id的查询 - 全分片扫描(生产环境避免)
// SELECT * FROM t_order WHERE status = 1 ORDER BY create_time DESC LIMIT 20
// → 16个分片各执行 LIMIT 20,归并排序后取前20
跨片查询优化策略:
// 1. 带分片键的查询 - 单分片路由
LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Order::getUserId, userId)
.eq(Order::getStatus, 1)
.orderByDesc(Order::getCreateTime);
List orders = orderMapper.selectList(wrapper);
// 2. 二级索引表 - 非分片键查询场景
// 建立order_no到user_id的映射表(不分片)
@TableName("t_order_no_mapping")
public class OrderNoMapping {
private String orderNo; // 订单号
private Long userId; // 分片键
private Long orderId; // 主键
}
// 先查映射表获取user_id,再精准路由
public Order findByOrderNo(String orderNo) {
OrderNoMapping mapping = mappingMapper.selectByOrderNo(orderNo);
if (mapping == null) return null;
return orderMapper.selectByUserIdAndOrderId(
mapping.getUserId(), mapping.getOrderId());
}
// 3. 分页查询 - 使用游标避免深度分页
// 上一页最后一条记录的create_time作为游标
wrapper.lt(Order::getCreateTime, lastCreateTime)
.orderByDesc(Order::getCreateTime)
.last("LIMIT 20");
数据迁移与扩容方案
从单库迁移到分库分表环境,采用双写加数据同步方式:
// 迁移步骤:
// 1. 应用层开启双写(新数据同时写旧库和新分片库)
// 2. 使用DataX/Canal同步存量数据到新分片库
// 3. 数据校验工具比对一致性
// 4. 灰度切读 - 按用户ID取模逐步切到新库
// 5. 全量切读后下线旧库双写
// Canal监听Binlog同步配置示例
canal:
instance:
master:
address: mysql-old:3306
journal: ""
position: ""
filter: "old_db.t_order"
sink:
type: sharding # 写入ShardingSphere分片库
扩容场景(4库扩到8库)使用一致性Hash算法替代取模,减少数据迁移量。或采用2的幂次扩容(4→8→16),每次扩容只需迁移一半数据。ShardingSphere 5.x支持在线变更分片规则,配合数据同步工具实现平滑扩容。
分布式事务与运维监控
ShardingSphere提供LOCAL、XA、BASE三种事务模式。跨库写操作推荐使用Seata AT模式(BASE事务):
// application.yml添加Seata配置
seata:
tx-service-group: order-service-group
service:
vgroup-mapping:
order-service-group: default
registry:
type: nacos
nacos:
server-addr: nacos:8848
// 业务代码中使用@GlobalTransactional
@Service
public class OrderService {
@GlobalTransactional
public void createOrder(OrderDTO dto) {
// 写入ds0的订单表
orderMapper.insert(buildOrder(dto));
// 写入ds2的库存表(跨库)
inventoryMapper.deduct(dto.getSkuId(), dto.getQty());
// 写入ds1的积分表(跨库)
pointMapper.add(dto.getUserId(), calculatePoints(dto));
}
}
监控方面,ShardingSphere暴露metrics到Prometheus,关注以下指标:每秒SQL路由数、跨片查询比例、连接池使用率、慢查询分布。跨片查询比例持续高于10%时,需要检查分片键设计或增加二级索引。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/fen-ku-fen-biao-fang-an-luo-di-shardingsphere-shi-zhan-pei/