Sharding-JDBC实现完整的分库分表步骤

一、先明确分库分表场景(核心前提)

本次案例目标:

  • 业务:用户订单表(t_order)数据量激增,需做分库分表;
  • 分片规则:
    1. 分库:按user_id取模2,路由到order_db_0/order_db_1两个数据库;
    2. 分表:在每个库内,按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检查数据:

  1. order_db_1.t_order_0 中能查到 order_id=10、user_id=1 的订单;
  2. order_db_0.t_order_1 中能查到 order_id=11、user_id=2 的订单;
  3. 代码中操作的是逻辑表t_order,Sharding-JDBC自动完成「分库路由+分表路由」,验证成功。
相关推荐
小陈工1 小时前
Python Web开发入门(十七):Vue.js与Python后端集成——让前后端真正“握手言和“
开发语言·前端·javascript·数据库·vue.js·人工智能·python
科技小花6 小时前
数据治理平台架构演进观察:AI原生设计如何重构企业数据管理范式
数据库·重构·架构·数据治理·ai-native·ai原生
一江寒逸6 小时前
零基础从入门到精通MySQL(中篇):进阶篇——吃透多表查询、事务核心与高级特性,搞定复杂业务SQL
数据库·sql·mysql
D4c-lovetrain6 小时前
linux个人心得22 (mysql)
数据库·mysql
阿里小阿希6 小时前
CentOS7 PostgreSQL 9.2 升级到 15 完整教程
数据库·postgresql
荒川之神6 小时前
Oracle 数据仓库雪花模型设计(完整实战方案)
数据库·数据仓库·oracle
做个文艺程序员6 小时前
MySQL安全加固十大硬核操作
数据库·mysql·安全
不吃香菜学java7 小时前
Redis简单应用
数据库·spring boot·tomcat·maven
一个天蝎座 白勺 程序猿7 小时前
Apache IoTDB(15):IoTDB查询写回(INTO子句)深度解析——从语法到实战的ETL全链路指南
数据库·apache·etl·iotdb
不知名的老吴7 小时前
Redis的延迟瓶颈:TCP栈开销无法避免
数据库·redis·缓存