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自动完成「分库路由+分表路由」,验证成功。
相关推荐
哈哈老师啊2 小时前
Springboot企业办公信息化管理系统6z1v1(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库·spring boot·后端
坠金2 小时前
mysql中in和exist区别
数据库·mysql
l1t2 小时前
duckdb数据库CROSS JOIN LATERAL 中使用 EXISTS子查询的一个bug
数据库·bug
wregjru2 小时前
【C++】2.10智能指针详解
数据库·redis·mysql
2401_858286112 小时前
从Redis 8.4.0源码看快速排序(1) 宏函数min和swapcode
c语言·数据库·redis·缓存·快速排序·宏函数
茁壮成长的露露3 小时前
MongoDB单机安装
数据库·mongodb
qq_406176143 小时前
JS防抖与节流:从原理到实战的性能优化方案
服务器·数据库·php
a***59263 小时前
MySQL数据可视化实战指南
数据库·mysql·信息可视化
Maggie_ssss_supp3 小时前
LINUX-MySQL多表查询
数据库·mysql