一、先明确分库分表场景(核心前提)
本次案例目标:
- 业务:用户订单表(
t_order)数据量激增,需做分库分表; - 分片规则:
- 分库:按
user_id取模2,路由到order_db_0/order_db_1两个数据库; - 分表:在每个库内,按
order_id取模2,路由到t_order_0/t_order_1两个表;
- 分库:按
- 分片键:分库键
user_id,分表键order_id。
简单说:一条订单数据,先通过user_id确定存在哪个库,再通过order_id确定存在该库的哪个表。
二、环境准备
1. 基础环境
- JDK 8+、Maven 3.6+、MySQL 8.0+;
- 引入依赖(和单库分表一致,核心是Sharding-JDBC核心包+MySQL驱动):
xml
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-core</artifactId>
<version>4.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>4.0.3</version>
</dependency>
2. 创建物理库和物理表
在MySQL中创建2个数据库,每个库下创建2张订单表:
sql
-- 创建第一个分库:order_db_0
CREATE DATABASE IF NOT EXISTS order_db_0;
USE order_db_0;
-- 库0的表0
CREATE TABLE t_order_0 (
order_id BIGINT NOT NULL COMMENT '订单ID',
user_id BIGINT NOT NULL COMMENT '用户ID',
order_amount DECIMAL(10,2) COMMENT '订单金额',
PRIMARY KEY (order_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 库0的表1
CREATE TABLE t_order_1 (
order_id BIGINT NOT NULL COMMENT '订单ID',
user_id BIGINT NOT NULL COMMENT '用户ID',
order_amount DECIMAL(10,2) COMMENT '订单金额',
PRIMARY KEY (order_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 创建第二个分库:order_db_1
CREATE DATABASE IF NOT EXISTS order_db_1;
USE order_db_1;
-- 库1的表0
CREATE TABLE t_order_0 (
order_id BIGINT NOT NULL COMMENT '订单ID',
user_id BIGINT NOT NULL COMMENT '用户ID',
order_amount DECIMAL(10,2) COMMENT '订单金额',
PRIMARY KEY (order_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 库1的表1
CREATE TABLE t_order_1 (
order_id BIGINT NOT NULL COMMENT '订单ID',
user_id BIGINT NOT NULL COMMENT '用户ID',
order_amount DECIMAL(10,2) COMMENT '订单金额',
PRIMARY KEY (order_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
三、核心实现:分库分表配置(两种方式)
Sharding-JDBC实现分库分表的核心是:配置多数据源 + 分别指定分库策略、分表策略。
方式1:纯Java代码配置(入门易理解)
java
import com.zaxxer.hikari.HikariDataSource;
import org.apache.shardingsphere.api.config.sharding.ShardingRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.TableRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.strategy.InlineShardingStrategyConfiguration;
import org.apache.shardingsphere.shardingjdbc.api.ShardingDataSourceFactory;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public class ShardingJdbcShardingConfig {
// 构建数据源(每个分库对应一个数据源)
private static DataSource createDataSource(String dbName) {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
// 拼接不同库的JDBC URL
dataSource.setJdbcUrl(String.format("jdbc:mysql://localhost:3306/%s?useSSL=false&serverTimezone=UTC", dbName));
dataSource.setUsername("root"); // 替换为你的MySQL用户名
dataSource.setPassword("123456"); // 替换为你的MySQL密码
dataSource.setMaximumPoolSize(10);
return dataSource;
}
// 构建分库分表的Sharding数据源
public static DataSource getShardingDataSource() throws SQLException {
// 1. 配置所有真实数据源(分库对应多个数据源)
Map<String, DataSource> dataSourceMap = new HashMap<>();
dataSourceMap.put("order_db_0", createDataSource("order_db_0"));
dataSourceMap.put("order_db_1", createDataSource("order_db_1"));
// 2. 配置t_order表的分片规则
TableRuleConfiguration orderTableRuleConfig = new TableRuleConfiguration();
// 逻辑表名:业务中使用的表名
orderTableRuleConfig.setLogicTable("t_order");
// 真实数据节点:格式 数据源名.表名,${0..1}表示0和1两个库/表
orderTableRuleConfig.setActualDataNodes("order_db_${0..1}.t_order_${0..1}");
// 3. 配置分库策略:按user_id取模2,路由到不同库
orderTableRuleConfig.setDatabaseShardingStrategyConfig(
new InlineShardingStrategyConfiguration("user_id", "order_db_${user_id % 2}")
);
// 4. 配置分表策略:按order_id取模2,路由到库内不同表
orderTableRuleConfig.setTableShardingStrategyConfig(
new InlineShardingStrategyConfiguration("order_id", "t_order_${order_id % 2}")
);
// 5. 配置全局分片规则
ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
shardingRuleConfig.getTableRuleConfigs().add(orderTableRuleConfig);
// 6. 创建Sharding数据源(核心:Sharding-JDBC自动处理路由)
return ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfig, new Properties());
}
}
方式2:YAML配置(生产推荐,易维护)
创建sharding-jdbc-sharding.yaml放在resources目录:
yaml
# 数据源配置(分库对应多个数据源)
dataSources:
order_db_0:
type: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://localhost:3306/order_db_0?useSSL=false&serverTimezone=UTC
username: root
password: 123456
order_db_1:
type: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://localhost:3306/order_db_1?useSSL=false&serverTimezone=UTC
username: root
password: 123456
# 分库分表规则配置
rules:
- !SHARDING
tables:
t_order: # 逻辑表名
actualDataNodes: order_db_${0..1}.t_order_${0..1} # 真实数据节点
databaseStrategy: # 分库策略
inline: # 行内表达式算法(简单场景首选)
shardingColumn: user_id # 分库键
algorithmExpression: order_db_${user_id % 2} # 分库规则
tableStrategy: # 分表策略
inline:
shardingColumn: order_id # 分表键
algorithmExpression: t_order_${order_id % 2} # 分表规则
defaultDataSourceName: order_db_0 # 默认数据源(兜底)
加载YAML配置的代码:
java
import org.apache.shardingsphere.shardingjdbc.api.yaml.YamlShardingDataSourceFactory;
import javax.sql.DataSource;
import java.io.File;
import java.sql.SQLException;
public class ShardingJdbcYamlConfig {
public static DataSource getShardingDataSource() throws SQLException {
String yamlPath = "src/main/resources/sharding-jdbc-sharding.yaml";
return YamlShardingDataSourceFactory.createDataSource(new File(yamlPath));
}
}
四、测试验证:插入+查询数据
编写测试代码,验证分库分表路由是否生效:
java
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.sql.DataSource;
public class ShardingJdbcTest {
public static void main(String[] args) throws SQLException {
// 1. 获取分库分表数据源(二选一:代码配置/YAML配置)
DataSource dataSource = ShardingJdbcShardingConfig.getShardingDataSource();
// DataSource dataSource = ShardingJdbcYamlConfig.getShardingDataSource();
// 2. 测试插入数据
try (Connection conn = dataSource.getConnection()) {
// 案例1:user_id=1(路由到order_db_1),order_id=10(路由到t_order_0)
String insertSql1 = "INSERT INTO t_order (order_id, user_id, order_amount) VALUES (?, ?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(insertSql1)) {
pstmt.setLong(1, 10L);
pstmt.setLong(2, 1L);
pstmt.setBigDecimal(3, new java.math.BigDecimal("100.00"));
pstmt.executeUpdate();
}
// 案例2:user_id=2(路由到order_db_0),order_id=11(路由到t_order_1)
try (PreparedStatement pstmt = conn.prepareStatement(insertSql1)) {
pstmt.setLong(1, 11L);
pstmt.setLong(2, 2L);
pstmt.setBigDecimal(3, new java.math.BigDecimal("200.00"));
pstmt.executeUpdate();
}
// 3. 测试查询数据
String querySql = "SELECT * FROM t_order WHERE user_id = ? AND order_id = ?";
// 查询案例1的数据
try (PreparedStatement pstmt = conn.prepareStatement(querySql)) {
pstmt.setLong(1, 1L);
pstmt.setLong(2, 10L);
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
System.out.println("订单ID:" + rs.getLong("order_id")
+ ",用户ID:" + rs.getLong("user_id")
+ ",金额:" + rs.getBigDecimal("order_amount"));
}
}
}
}
}
五、验证结果
运行代码后,登录MySQL检查数据:
order_db_1.t_order_0中能查到order_id=10、user_id=1的订单;order_db_0.t_order_1中能查到order_id=11、user_id=2的订单;- 代码中操作的是逻辑表
t_order,Sharding-JDBC自动完成「分库路由+分表路由」,验证成功。