目录
- 前言
- [一、为什么需要 ShardingSphere](#一、为什么需要 ShardingSphere)
- [二、什么是 ShardingSphere](#二、什么是 ShardingSphere)
- [三、ShardingSphere 解决了什么问题](#三、ShardingSphere 解决了什么问题)
- [四、ShardingSphere 的核心概念](#四、ShardingSphere 的核心概念)
-
- [1. 逻辑表](#1. 逻辑表)
- [2. 真实表](#2. 真实表)
- [3. 分片键](#3. 分片键)
- [4. 分片算法](#4. 分片算法)
- [五、Spring Boot 集成 ShardingSphere](#五、Spring Boot 集成 ShardingSphere)
-
- [1. 引入依赖](#1. 引入依赖)
- [2. 配置数据源](#2. 配置数据源)
- [3. 是否还需要 spring.datasource](#3. 是否还需要 spring.datasource)
- 六、配置真实表
- 七、按月份分表
-
- [1. 配置分片策略](#1. 配置分片策略)
- [2. sharding-column 是什么](#2. sharding-column 是什么)
- [3. 必须有 create_time 字段吗](#3. 必须有 create_time 字段吗)
- 八、配置时间分片算法
-
- [1. datetime-pattern](#1. datetime-pattern)
- [2. datetime-lower](#2. datetime-lower)
- [3. datetime-upper](#3. datetime-upper)
- [4. sharding-suffix-pattern](#4. sharding-suffix-pattern)
- [5. datetime-interval-unit](#5. datetime-interval-unit)
- [九、MyBatis 和 MyBatis Plus 如何使用](#九、MyBatis 和 MyBatis Plus 如何使用)
-
- [1. Mapper 不需要修改](#1. Mapper 不需要修改)
- [2. XML 不需要修改](#2. XML 不需要修改)
- [3. ShardingSphere 自动改写 SQL](#3. ShardingSphere 自动改写 SQL)
- 十、查询如何路由
-
- [1. 精准路由](#1. 精准路由)
- [2. 范围路由](#2. 范围路由)
- [3. 全路由](#3. 全路由)
- [十一、每个 SQL 都必须带分片键吗](#十一、每个 SQL 都必须带分片键吗)
- [十二、ShardingSphere 会自动创建表吗](#十二、ShardingSphere 会自动创建表吗)
- 十三、如何自动创建分表
-
- [1. 推荐方案](#1. 推荐方案)
- [2. 定时任务创建](#2. 定时任务创建)
- [3. Flyway 或 Liquibase](#3. Flyway 或 Liquibase)
- 十四、生产环境如何设计分表
-
- [1. 单纯按月份分表](#1. 单纯按月份分表)
- [2. 按用户 Hash 分表](#2. 按用户 Hash 分表)
- [3. 时间 + Hash](#3. 时间 + Hash)
- [十五、ShardingSphere 的优缺点](#十五、ShardingSphere 的优缺点)
- 十六、总结
前言
随着业务的发展,订单表、支付表、交易流水表等核心业务表的数据量会不断增长。当单表数据达到数千万甚至上亿级别时,查询性能、索引维护、数据备份和数据库扩容等问题都会逐渐显现。
为了解决单表数据量过大的问题,互联网系统通常会采用分库分表方案,将数据按照一定规则拆分到多个数据库或数据表中。然而,分库分表之后也会带来新的挑战,例如如何确定数据应该存储到哪张表、如何跨表查询数据、如何降低业务代码的改造成本等。
ShardingSphere 正是为了解决这些问题而诞生的。它能够在应用层和数据库之间提供透明的分片能力,让开发人员仍然像操作普通数据表一样进行开发,而无需关心底层的数据路由和 SQL 改写过程。
本文将结合 Spring Boot 和 MyBatis 项目,从分库分表的背景开始,逐步介绍 ShardingSphere 的核心概念、工作原理、项目集成方式、分片算法配置以及实际项目中的使用技巧,帮助读者快速掌握 ShardingSphere 的基本使用与设计思路。
一、为什么需要 ShardingSphere
随着业务发展,订单表、支付表、流水表的数据量会越来越大。
例如有一张支付订单表:
sql
CREATE TABLE pay_info (
id BIGINT PRIMARY KEY,
order_no VARCHAR(64),
user_id BIGINT,
amount DECIMAL(18,2),
create_time DATETIME
);
如果每天产生 100 万条数据:
text
100万/天
≈ 3000万/月
≈ 3.6亿/年
几年之后,单表可能达到几十亿数据。
此时会出现很多问题:
- 查询变慢
- 索引变大
- 备份恢复困难
- DDL操作耗时
- 数据迁移困难
因此需要进行:
text
分库分表
最常见的方案:
- 按时间分表
- 按用户分表
- 按订单号分表
而目前 Java 生态中最主流的分库分表框架之一就是 Apache ShardingSphere。
二、什么是 ShardingSphere
ShardingSphere 是 Apache 顶级项目。
它是一套数据库中间件生态,主要功能包括:
- 分库分表
- 读写分离
- 分布式事务
- 数据加密
- SQL 路由
- SQL 改写
在 Spring Boot 项目中最常见的是:
text
ShardingSphere-JDBC
其架构如下:
text
MyBatis
↓
ShardingSphere
↓
MySQL
对于业务代码来说:
text
感知不到分表的存在
三、ShardingSphere 解决了什么问题
假设数据库中有以下表:
text
pay_info_202601
pay_info_202602
pay_info_202603
...
程序中仍然写:
sql
select * from pay_info
或者:
java
payInfoMapper.insert(payInfo);
增删改查时 ShardingSphere 会自动帮你改写 SQL。
例如:
java
createTime = 2026-05-15
自动路由:
sql
insert into pay_info_202605
开发人员完全不需要手动判断表名。
四、ShardingSphere 的核心概念
1. 逻辑表
程序中看到的表:
text
pay_info
称为逻辑表。
例如:
sql
select * from pay_info
2. 真实表
数据库真正存在的表:
text
pay_info_202601
pay_info_202602
pay_info_202603
称为真实表。
3. 分片键
决定数据进入哪张表的字段。
例如:
sql
create_time
或者:
sql
user_id
或者:
sql
order_no
都可以作为分片键。
4. 分片算法
根据分片键计算目标表。
例如:
java
2026-05-15
计算得到:
text
pay_info_202605
五、Spring Boot 集成 ShardingSphere
1. 引入依赖
xml
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
</dependency>
2. 配置数据源
yaml
spring:
shardingsphere:
datasource:
names: ds
ds:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test
username: root
password: root
这里的:
yaml
url
username
password
就是实际数据库连接配置。
3. 是否还需要 spring.datasource
上面在 shardingsphere 下已经配置了数据源了,是否还需要 spring.datasource ?
通常不需要。
不要同时配置:
yaml
spring:
datasource:
和:
yaml
spring:
shardingsphere:
datasource:
否则可能出现:
text
NoUniqueBeanDefinitionException
因为 Spring 中会存在多个 DataSource Bean。
六、配置真实表
假设数据库已经存在:
text
pay_info_202601
pay_info_202602
...
pay_info_203012
配置如下:
yaml
spring:
shardingsphere:
rules:
sharding:
tables:
pay_info:
actual-data-nodes:
ds.pay_info_$->{202601..203012}
含义:
text
逻辑表:
pay_info
真实表:
pay_info_202601
pay_info_202602
...
pay_info_203012
七、按月份分表
1. 配置分片策略
yaml
table-strategy:
standard:
sharding-column: create_time
sharding-algorithm-name: pay-info-month
2. sharding-column 是什么
表示:
text
根据哪个字段决定路由
例如:
yaml
sharding-column: create_time
表示:
text
根据 create_time 决定数据进入哪张表
例如:
java
2026-05-20
自动进入:
text
pay_info_202605
3. 必须有 create_time 字段吗
不一定。
可以使用:
yaml
sharding-column: pay_time
或者:
yaml
sharding-column: order_time
只要能够表达时间即可。
八、配置时间分片算法
yaml
sharding-algorithms:
pay-info-month:
type: INTERVAL
props:
datetime-pattern: yyyy-MM-dd HH:mm:ss
datetime-lower: 2025-01-01 00:00:00
datetime-upper: 2030-12-31 23:59:59
sharding-suffix-pattern: yyyyMM
datetime-interval-unit: MONTHS
1. datetime-pattern
时间格式。
yaml
yyyy-MM-dd HH:mm:ss
例如:
java
2026-05-18 15:20:30
2. datetime-lower
开始时间:
yaml
2025-01-01 00:00:00
3. datetime-upper
结束时间:
yaml
2030-12-31 23:59:59
4. sharding-suffix-pattern
表后缀格式。
例如:
yaml
yyyyMM
生成:
text
pay_info_202605
如果:
yaml
yyyy_MM
生成:
text
pay_info_2026_05
5. datetime-interval-unit
分片粒度。
按月:
yaml
MONTHS
生成:
text
pay_info_202605
pay_info_202606
按天:
yaml
DAYS
生成:
text
pay_info_20260501
pay_info_20260502
按年:
yaml
YEARS
生成:
text
pay_info_2025
pay_info_2026
九、MyBatis 和 MyBatis Plus 如何使用
1. Mapper 不需要修改
例如:
java
@Mapper
public interface PayInfoMapper {
PayInfo selectById(Long id);
}
2. XML 不需要修改
仍然写:
xml
<select id="selectById">
select *
from pay_info
where id = #{id}
</select>
不要写:
sql
pay_info_202605
也不要写:
sql
pay_info_202606
统一写:
sql
pay_info
即可。
3. ShardingSphere 自动改写 SQL
程序:
sql
select *
from pay_info
实际执行:
sql
select *
from pay_info_202605
或者:
sql
select *
from pay_info_202605
union all
select *
from pay_info_202606
十、查询如何路由
1. 精准路由
例如:
sql
select *
from pay_info
where create_time >= '2026-05-01'
and create_time < '2026-06-01'
只查询:
text
pay_info_202605
性能最好。
2. 范围路由
例如:
sql
select *
from pay_info
where create_time between
'2026-05-01'
and
'2026-07-31'
查询:
text
pay_info_202605
pay_info_202606
pay_info_202607
3. 全路由
例如:
sql
select *
from pay_info
where status = 1
没有分片键:
sql
create_time
ShardingSphere 无法判断目标表。
于是:
text
pay_info_202601
pay_info_202602
...
pay_info_203012
全部查询。
这就是:
text
全路由
性能极差。
十一、每个 SQL 都必须带分片键吗
不是必须。
可以正常执行。
但是:
text
不带分片键
=
全路由
=
查询所有分表
因此在设计表结构时:
text
高频查询条件
最好就是分片键
这样性能最高。
十二、ShardingSphere 会自动创建表吗
不会。
ShardingSphere 负责:
text
SQL路由
不负责:
text
创建表
例如:
sql
insert into pay_info
路由到:
sql
pay_info_202606
如果:
text
pay_info_202606
不存在。
直接报错:
text
Table doesn't exist
十三、如何自动创建分表
1. 推荐方案
提前创建未来几年表。
例如:
text
pay_info_202601
...
pay_info_203012
一次建好即可。
2. 定时任务创建
假设每月1号创建未来三个月的表
(1)定时任务
java
//每个月的 1 号凌晨 2 点整执行一次
@Scheduled(cron = "0 0 2 1 * ?")
public void createFutureTables() {
//循环三次创建未来三个月的表
for (int i = 1; i <= 3; i++) {
LocalDate month = LocalDate.now().plusMonths(i);
//检查表是否存在,不存在则创建
createTableIfNotExists(month);
}
}
(2)模板表
一般会准备一个模板表:
sql
CREATE TABLE pay_info_template (
id BIGINT PRIMARY KEY,
order_no VARCHAR(64),
amount DECIMAL(18,2),
create_time DATETIME,
KEY idx_create_time(create_time)
);
(3) 使用 JdbcTemplate 创建表
java
@Autowired
private JdbcTemplate jdbcTemplate;
private void createTableIfNotExists(LocalDate month) {
String tableName = String.format(
"pay_info_%d_%02d",
month.getYear(),
month.getMonthValue()
);
String sql =
"CREATE TABLE IF NOT EXISTS "
+ tableName
+ " LIKE pay_info_template";
jdbcTemplate.execute(sql);
}
效果:
假设 当前是 2026-05 ,定时任务会执行:
sql
CREATE TABLE IF NOT EXISTS pay_info_202606 LIKE pay_info_template;
CREATE TABLE IF NOT EXISTS pay_info_202607 LIKE pay_info_template
CREATE TABLE IF NOT EXISTS pay_info_202608 LIKE pay_info_template
最终创建了:
text
pay_info_2026_06
pay_info_2026_07
pay_info_2026_08
3. Flyway 或 Liquibase
使用数据库版本管理工具自动创建。
十四、生产环境如何设计分表
1. 单纯按月份分表
text
pay_info_202605
pay_info_202606
pay_info_202607
适合:
text
按时间查询较多
2. 按用户 Hash 分表
text
pay_info_00
pay_info_01
...
pay_info_63
路由:
java
userId % 64
适合:
text
根据用户查询较多
3. 时间 + Hash
大型支付系统最常见。
例如:
text
pay_info_202605_00
pay_info_202605_01
...
pay_info_202605_15
优势:
- 控制单表大小
- 查询效率高
- 扩展能力强
十五、ShardingSphere 的优缺点
优点
- 对业务代码侵入小
- MyBatis 基本不用改
- 自动路由
- 自动聚合结果
- 支持读写分离
- 支持分布式事务
- 支持分库分表
缺点
- 配置复杂
- SQL 不宜过于复杂
- 跨分片查询成本高
- 全路由性能差
- 分页查询需要特别注意
十六、总结
ShardingSphere 本质上是一个数据库中间层。
对于开发人员来说:
text
仍然操作逻辑表
pay_info
对于数据库来说:
text
实际访问真实表
pay_info_202605
pay_info_202606
其核心思想只有一句话:
text
开发只关心逻辑表,
ShardingSphere负责将SQL路由到正确的真实表。