ShardingSphere-JDBC 分库分表实战指南
一、前言
在后端开发中,当业务数据量突破千万甚至亿级时,单库单表往往成为系统性能的瓶颈——查询越来越慢,写入延迟越来越高,索引维护成本也越来越大。面对这种情况,分库分表几乎成了绕不开的技术选型。
Apache ShardingSphere 是目前 Java 生态中最成熟的分布式数据库中间件解决方案之一。其中 ShardingSphere-JDBC 是一款轻量级 Java 框架,它以 jar 包形式嵌入应用,可理解为增强版的 JDBC 驱动,完全兼容 JDBC 和各种 ORM 框架(如 MyBatis、Hibernate、JPA 等),对业务代码几乎无侵入。
本文将带你从零开始,系统掌握 ShardingSphere-JDBC 的使用方法,包括环境搭建、核心概念、分片策略配置、读写分离、数据加密以及常见避坑经验,并提供可直接复用的配置模板。
二、ShardingSphere 生态概览
Apache ShardingSphere 是一套开源的分布式数据库中间件解决方案生态圈,由三款相互独立的产品组成:
| 产品 | 定位 | 特点 |
|---|---|---|
| ShardingSphere-JDBC | 轻量级 Java 框架 | 嵌入应用内部,无需额外部署,对代码几乎无侵入 |
| ShardingSphere-Proxy | 独立部署的数据库代理服务 | 支持多语言,透明化数据库代理 |
| ShardingSphere-Sidecar(规划中) | 云原生数据库代理 | 面向 Kubernetes 环境的 Sidecar 模式 |
选择建议:如果你的技术栈统一为 Java,且希望最小化部署复杂度,ShardingSphere-JDBC 是最务实的选择。
ShardingSphere-JDBC 的核心功能包括:
- 数据分片:分库分表,水平扩展数据存储能力
- 读写分离:自动路由读写请求,提升并发能力
- 数据加密:透明化的数据加解密,保护敏感信息
- 影子库:支持压测场景的数据隔离
三、核心概念速览
在动手配置之前,必须先理解几个核心概念,否则很容易出现路由异常、数据错乱等问题。
3.1 逻辑表与真实表
- 逻辑表:开发者编写 SQL 时使用的统一表名,是一个抽象概念,不实际存储数据。例如
t_order。 - 真实表:物理数据库中实际存储数据的表,是分库分表后拆分出的具体表。例如
t_order_0、t_order_1。 - 数据节点:分片的最小物理单元,由“数据源名称 + 真实表名”组成,格式为
数据源.真实表,如ds_0.t_order_0。
3.2 分片键与分片算法
- 分片键:用于数据分片的核心字段,如
user_id、order_id。SQL 中如果缺少分片键,可能导致全路由,严重影响性能。 - 分片算法:决定数据如何分配到各个分片的计算逻辑,如取模、哈希、范围等。
3.3 绑定表
绑定表是指分片规则完全一致的主表和子表(如订单表 t_order 和订单明细表 t_order_item),两者使用相同的分片键和分片算法。配置绑定表后,ShardingSphere 在执行 JOIN 查询时会自动识别关联关系,仅将对应分片的表进行关联,避免笛卡尔积,大幅提升查询性能。
3.4 广播表
广播表是指在每个分片数据源中都完整存在的表,表结构和数据完全一致。适用于数据量不大但需与海量数据表频繁关联的场景,如字典表、配置表等。ShardingSphere 会自动将更新操作广播到所有分片,确保数据一致。
四、环境准备与项目搭建
4.1 版本选型建议
以下是经过项目实测的稳定版本组合:
| 组件 | 推荐版本 | 说明 |
|---|---|---|
| Spring Boot | 3.2.x / 2.7.x | 根据项目选择 |
| ShardingSphere-JDBC | 5.5.x | 当前稳定版本 |
| MyBatis-Plus | 3.5.x | ORM 增强框架 |
| MySQL | 8.0+ | 数据库 |
| JDK | 17+(Spring Boot 3.x) | Java 版本 |
重要提示:ShardingSphere 5.x 与 Spring Boot 2.7.x 和 3.x 的兼容性不同,务必查阅官方文档确认版本对应关系。
4.2 创建分库分表
以订单表 t_order 为例,假设采用 2 个分库(order_db_0、order_db_1),每个库中 2 张分表(t_order_0、t_order_1),共计 4 个数据节点。
-- 创建分库
CREATE DATABASE IF NOT EXISTS order_db_0;
CREATE DATABASE IF NOT EXISTS order_db_1;
-- 在 order_db_0 中创建分表
USE order_db_0;
CREATE TABLE `t_order_0` (
`id` bigint NOT NULL COMMENT '订单主键(分布式ID)',
`order_no` varchar(64) NOT NULL COMMENT '订单编号',
`user_id` bigint NOT NULL COMMENT '用户ID(分片键)',
`goods_name` varchar(255) DEFAULT NULL COMMENT '商品名称',
`order_amount` decimal(10,2) DEFAULT NULL COMMENT '订单金额',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';
CREATE TABLE `t_order_1` LIKE `t_order_0`;
-- 在 order_db_1 中执行同样的建表语句
USE order_db_1;
CREATE TABLE `t_order_0` LIKE order_db_0.t_order_0;
CREATE TABLE `t_order_1` LIKE order_db_0.t_order_0;
4.3 Maven 依赖配置
引入 ShardingSphere-JDBC 时,最头疼的往往是版本冲突,常见错误包括 ClassNotFoundException 或 NoSuchMethodError,核心问题在于传递依赖的版本不兼容。
避坑建议:不要完全依赖 Spring Boot 的 dependencyManagement,对于 ShardingSphere 的核心依赖,建议显式声明并排除可能冲突的传递依赖。
<properties>
<java.version>17</java.version>
<spring-boot.version>3.2.5</spring-boot.version>
<shardingsphere.version>5.5.2</shardingsphere.version>
<mybatis-plus.version>3.5.5</mybatis-plus.version>
</properties>
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- ShardingSphere-JDBC Spring Boot Starter -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc</artifactId>
<version>${shardingsphere.version}</version>
<exclusions>
<!-- 避免与 Spring Boot 内置的 snakeyaml 冲突 -->
<exclusion>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 显式指定 snakeyaml 版本 -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>2.0</version>
</dependency>
<!-- MyBatis-Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Lombok(可选) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
五、YAML 核心配置详解
ShardingSphere-JDBC 提供了 4 种配置方式:Java API、YAML、Spring Boot Starter 以及 Spring 命名空间配置。其中 YAML 配置 最为直观,所有规则集中在一个文件里,维护方便,是最推荐的配置方式。
5.1 基础数据源配置
Spring Boot 3.x 默认推荐使用 HikariCP 作为连接池。在 ShardingSphere 的配置中,需要为每个物理数据源单独配置连接池参数,全局的 spring.datasource.hikari 配置不会自动应用到 ShardingSphere 管理的各个数据源上。
spring:
shardingsphere:
# 开启 SQL 日志,调试必备,生产环境建议关闭
props:
sql-show: true
# 允许 Bean 定义覆盖,避免与某些 Starter 冲突
main:
allow-bean-definition-overriding: true
# 数据源配置
datasource:
names: ds0, ds1
ds0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/order_db_0?serverTimezone=Asia/Shanghai&useSSL=false
username: root
password: 123456
hikari:
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 30000
ds1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/order_db_1?serverTimezone=Asia/Shanghai&useSSL=false
username: root
password: 123456
hikari:
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 30000
5.2 分片规则配置
分片规则的核心包含三部分:数据节点定义、分库策略、分表策略。
spring:
shardingsphere:
# ... 数据源配置 ...
rules:
sharding:
# 分片算法定义
sharding-algorithms:
# 分库算法:按 user_id 取模
database-inline:
type: INLINE
props:
algorithm-expression: ds$->{user_id % 2}
# 分表算法:按 order_id 取模
table-inline:
type: INLINE
props:
algorithm-expression: t_order_$->{order_id % 2}
# 分片表配置
tables:
t_order:
# 数据节点:2 个库 × 2 张表 = 4 个节点
actual-data-nodes: ds$->{0..1}.t_order_$->{0..1}
# 分库策略
database-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: database-inline
# 分表策略
table-strategy:
standard:
sharding-column: order_id
sharding-algorithm-name: table-inline
5.3 配置项详解
| 配置项 | 说明 |
|---|---|
actual-data-nodes | 定义物理数据节点,支持行表达式,如 ds$->{0..1}.t_order_$->{0..1} 表示 ds0/ds1 下的 t_order_0/t_order_1 |
database-strategy | 分库策略,指定分片键和分片算法 |
table-strategy | 分表策略,指定分片键和分片算法 |
sharding-algorithms | 定义可复用的分片算法,支持 INLINE、MOD、HASH_MOD 等多种类型 |
六、五大分片策略详解
ShardingSphere 内置了多种分片算法,按类型可划分为:自动分片算法、标准分片算法、复合分片算法和 Hint 分片算法。下面详细拆解五种最常用的分片策略。
6.1 INLINE 策略(行表达式分片)
适用场景:最简单的分片场景,分片键为数值类型且分片规则能用简单表达式描述。
配置示例:
sharding-algorithms:
database-inline:
type: INLINE
props:
algorithm-expression: ds$->{user_id % 2}
table-inline:
type: INLINE
props:
algorithm-expression: t_order_$->{order_id % 4}
优点:配置简单,性能高。
缺点:仅支持简单的 Groovy 表达式,复杂逻辑无法实现。
6.2 STANDARD 策略(标准分片)
适用场景:需要自定义分片逻辑,但只涉及单个分片键。需要实现 StandardShardingAlgorithm 接口。
自定义分片算法类:
public class CustomDatabaseShardingAlgorithm implements StandardShardingAlgorithm<Long> {
@Override
public String doSharding(Collection<String> availableTargetNames,
PreciseShardingValue<Long> shardingValue) {
// 精确分片:根据 user_id 决定路由到哪个库
Long userId = shardingValue.getValue();
if (userId % 2 == 0) {
return availableTargetNames.stream()
.filter(name -> name.endsWith("0"))
.findFirst().orElse(null);
} else {
return availableTargetNames.stream()
.filter(name -> name.endsWith("1"))
.findFirst().orElse(null);
}
}
@Override
public Collection<String> doSharding(Collection<String> availableTargetNames,
RangeShardingValue<Long> shardingValue) {
// 范围分片:BETWEEN 查询时路由到所有节点
return availableTargetNames;
}
}
配置:
sharding-algorithms:
custom-db-algorithm:
type: CLASS_BASED
props:
strategy: STANDARD
algorithmClassName: com.example.sharding.CustomDatabaseShardingAlgorithm
6.3 COMPLEX 策略(复合分片)
适用场景:需要多个分片键联合决定路由(如同时按 user_id 和 order_id 分片)。需要实现 ComplexKeysShardingAlgorithm 接口。
public class CustomComplexShardingAlgorithm implements ComplexKeysShardingAlgorithm<Comparable<?>> {
@Override
public Collection<String> doSharding(Collection<String> availableTargetNames,
ComplexKeysShardingValue<Comparable<?>> shardingValue) {
Map<String, Collection<Comparable<?>>> columnValues = shardingValue.getColumnNameAndShardingValuesMap();
// 根据多个分片键的取值计算路由
// 返回目标数据节点集合
return availableTargetNames;
}
}
6.4 HINT 策略(强制路由)
适用场景:SQL 中无法携带分片键(如某些报表查询),或需要强制指定路由到特定分片。通过 HintManager 在代码中手动指定。
使用示例:
// 强制路由到 ds1 数据源
try (HintManager hintManager = HintManager.getInstance()) {
hintManager.setDatabaseShardingValue(1);
// 执行 SQL
List<Order> orders = orderMapper.selectList(null);
}
配置:
sharding-algorithms:
hint-algorithm:
type: HINT
tables:
t_order:
database-strategy:
hint:
sharding-algorithm-name: hint-algorithm
6.5 CLASS_BASED 策略
适用场景:需要高度自定义的分片逻辑,可实现任意类型的分片算法接口。
sharding-algorithms:
custom-algorithm:
type: CLASS_BASED
props:
strategy: STANDARD # 或 COMPLEX、HINT
algorithmClassName: com.example.sharding.CustomAlgorithm
6.6 策略选择建议
| 策略 | 推荐场景 | 复杂度 |
|---|---|---|
| INLINE | 简单取模分片,快速上手 | 低 |
| STANDARD | 单分片键,需要自定义逻辑 | 中 |
| COMPLEX | 多分片键联合分片 | 高 |
| HINT | 无分片键的查询、强制路由 | 中 |
| CLASS_BASED | 需要完全自定义的场景 | 高 |
七、进阶功能配置
7.1 广播表配置
广播表在每个分片数据源中都完整存在,适用于字典表等数据量小、变更频次低的表。
spring:
shardingsphere:
rules:
sharding:
broadcast-tables:
- sys_dict
- t_region
配置后,对广播表的 SELECT 会路由到任意数据源;INSERT/UPDATE/DELETE 会自动广播到所有数据源执行。
7.2 绑定表配置
绑定表用于优化多表关联查询性能,避免笛卡尔积。
spring:
shardingsphere:
rules:
sharding:
binding-tables:
- t_order, t_order_item
tables:
t_order:
actual-data-nodes: ds$->{0..1}.t_order_$->{0..1}
table-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: user-id-mod
t_order_item:
actual-data-nodes: ds$->{0..1}.t_order_item_$->{0..1}
table-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: user-id-mod
生效条件:
- 两表必须使用相同的分片键和分片算法
- 分片键取值严格一一对应(如
order.user_id = order_item.user_id) - 绑定关系需显式声明
7.3 读写分离配置
ShardingSphere-JDBC 支持透明化的读写分离,写操作自动路由到主库,读操作自动路由到从库。
spring:
shardingsphere:
datasource:
names: master, slave0, slave1
master:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/order_db?...
slave0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3307/order_db?...
slave1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3308/order_db?...
rules:
readwrite-splitting:
data-sources:
order_db:
type: Static
props:
write-data-source-name: master
read-data-source-names: slave0, slave1
load-balancer-name: round_robin
load-balancers:
round_robin:
type: ROUND_ROBIN
注意:ShardingSphere 不支持主从数据同步,需要自行搭建 MySQL 主从复制。
强制走主库:在读写分离场景下,如果需要读取最新数据,可以在 SQL 中添加 Hint 注释:
/* sharding hint: write */ SELECT * FROM t_order WHERE id = 123;
7.4 数据加密(数据脱敏)
ShardingSphere 提供了透明的数据加密功能,自动对敏感字段进行加解密。
spring:
shardingsphere:
rules:
encrypt:
tables:
t_user:
columns:
password:
cipher-column: password_encrypt # 密文列
plain-column: password # 明文列(可选)
encryptor-name: aes_encryptor
encryptors:
aes_encryptor:
type: AES
props:
aes-key-value: 1234567890abcdef
ShardingSphere 内置了两种加解密策略:AES 和 MD5。加密模块会自动拦截 SQL,对目标字段进行加解密处理,业务代码无需感知。
7.5 分布式主键生成
在分库分表场景下,数据库自增主键不再适用。ShardingSphere 提供了雪花算法(Snowflake)等分布式 ID 生成策略。
spring:
shardingsphere:
rules:
sharding:
tables:
t_order:
key-generate-strategy:
column: id
key-generator-name: snowflake
key-generators:
snowflake:
type: SNOWFLAKE
props:
worker-id: 1
八、完整配置示例
以下是一个包含分库分表 + 读写分离 + 数据加密的混合规则完整配置:
spring:
shardingsphere:
props:
sql-show: true
datasource:
names: ds0, ds1
ds0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/order_db_0?serverTimezone=Asia/Shanghai
username: root
password: 123456
hikari:
maximum-pool-size: 20
minimum-idle: 5
ds1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/order_db_1?serverTimezone=Asia/Shanghai
username: root
password: 123456
hikari:
maximum-pool-size: 20
minimum-idle: 5
rules:
sharding:
# 广播表
broadcast-tables:
- sys_dict
# 绑定表
binding-tables:
- t_order, t_order_item
# 分片算法
sharding-algorithms:
database-inline:
type: INLINE
props:
algorithm-expression: ds$->{user_id % 2}
table-inline:
type: INLINE
props:
algorithm-expression: t_order_$->{order_id % 2}
# 分片表
tables:
t_order:
actual-data-nodes: ds$->{0..1}.t_order_$->{0..1}
database-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: database-inline
table-strategy:
standard:
sharding-column: order_id
sharding-algorithm-name: table-inline
# 分布式主键
key-generate-strategy:
column: id
key-generator-name: snowflake
t_order_item:
actual-data-nodes: ds$->{0..1}.t_order_item_$->{0..1}
database-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: database-inline
table-strategy:
standard:
sharding-column: order_id
sharding-algorithm-name: table-inline
# 主键生成器
key-generators:
snowflake:
type: SNOWFLAKE
九、常见问题与避坑指南
9.1 分片键缺失导致全路由
问题:SQL 中没有携带分片键,ShardingSphere 会将请求广播到所有数据节点,性能极差。
解决:设计表结构时确保分片键在常用查询中都能携带。对于必须无分片键的查询,考虑使用 Hint 强制路由。
9.2 跨分片 JOIN 性能问题
问题:未配置绑定表时,JOIN 查询会产生笛卡尔积,关联次数呈指数增长。
解决:配置绑定表,确保分片键相同的主表和子表能够精准路由到同一分片。
9.3 聚合查询结果异常
问题:COUNT、SUM 等聚合函数在分片场景下,ShardingSphere 会将各分片结果合并后再计算,可能导致结果不正确。
解决:了解 ShardingSphere 对各类聚合函数的处理方式,对于复杂聚合建议在应用层处理或使用 OLAP 引擎。
9.4 版本冲突
问题:引入 ShardingSphere 后启动报 ClassNotFoundException 或 NoSuchMethodError。
解决:显式排除冲突的传递依赖(如 snakeyaml、guava),并单独引入兼容版本。
9.5 内存占用过高
问题:ANTLR 在 SQL 解析过程中会使用内部缓存,SQL 模板过多时导致堆内存增长。
解决:通过 -Xmx 参数设置合理的堆内存大小,避免 OOM。
9.6 连接池配置遗漏
问题:全局的 HikariCP 配置不会自动应用到 ShardingSphere 管理的各个数据源上。
解决:在 ShardingSphere 数据源配置中为每个物理数据源单独配置连接池参数。
十、总结
ShardingSphere-JDBC 作为 Apache 顶级项目,提供了完整的分库分表、读写分离、数据加密等解决方案,具有以下优势:
- 对业务代码几乎无侵入:只需引入依赖和配置,无需修改业务逻辑
- 兼容性强:支持任意实现 JDBC 规范的数据库和 ORM 框架
- 功能丰富:数据分片、读写分离、数据加密、影子库等功能可叠加使用
- 配置灵活:提供 Java API、YAML、Spring Boot Starter 等多种配置方式
在实际使用中,建议遵循以下最佳实践:
- 分片键设计:选择业务中最常用的查询字段作为分片键
- 避免跨分片查询:尽量让单次查询只命中一个分片
- 合理使用广播表和绑定表:优化跨分片关联查询性能
- 版本选型谨慎:确保 ShardingSphere 版本与 Spring Boot 版本兼容
- 监控与告警:对分片路由、慢查询等关键指标进行监控
希望这篇指南能够帮助你快速上手 ShardingSphere-JDBC,顺利完成分库分表的落地。
参考文档:
更多推荐




所有评论(0)