一、前言

在后端开发中,当业务数据量突破千万甚至亿级时,单库单表往往成为系统性能的瓶颈——查询越来越慢,写入延迟越来越高,索引维护成本也越来越大。面对这种情况,分库分表几乎成了绕不开的技术选型。

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_0t_order_1
  • 数据节点:分片的最小物理单元,由“数据源名称 + 真实表名”组成,格式为 数据源.真实表,如 ds_0.t_order_0

3.2 分片键与分片算法

  • 分片键:用于数据分片的核心字段,如 user_idorder_id。SQL 中如果缺少分片键,可能导致全路由,严重影响性能。
  • 分片算法:决定数据如何分配到各个分片的计算逻辑,如取模、哈希、范围等。

3.3 绑定表

绑定表是指分片规则完全一致的主表和子表(如订单表 t_order 和订单明细表 t_order_item),两者使用相同的分片键和分片算法。配置绑定表后,ShardingSphere 在执行 JOIN 查询时会自动识别关联关系,仅将对应分片的表进行关联,避免笛卡尔积,大幅提升查询性能。

3.4 广播表

广播表是指在每个分片数据源中都完整存在的表,表结构和数据完全一致。适用于数据量不大但需与海量数据表频繁关联的场景,如字典表、配置表等。ShardingSphere 会自动将更新操作广播到所有分片,确保数据一致。

四、环境准备与项目搭建

4.1 版本选型建议

以下是经过项目实测的稳定版本组合:

组件推荐版本说明
Spring Boot3.2.x / 2.7.x根据项目选择
ShardingSphere-JDBC5.5.x当前稳定版本
MyBatis-Plus3.5.xORM 增强框架
MySQL8.0+数据库
JDK17+(Spring Boot 3.x)Java 版本

重要提示:ShardingSphere 5.x 与 Spring Boot 2.7.x 和 3.x 的兼容性不同,务必查阅官方文档确认版本对应关系。

4.2 创建分库分表

以订单表 t_order 为例,假设采用 2 个分库(order_db_0order_db_1),每个库中 2 张分表(t_order_0t_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 时,最头疼的往往是版本冲突,常见错误包括 ClassNotFoundExceptionNoSuchMethodError,核心问题在于传递依赖的版本不兼容。

避坑建议:不要完全依赖 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_idorder_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 内置了两种加解密策略:AESMD5。加密模块会自动拦截 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 聚合查询结果异常

问题COUNTSUM 等聚合函数在分片场景下,ShardingSphere 会将各分片结果合并后再计算,可能导致结果不正确。

解决:了解 ShardingSphere 对各类聚合函数的处理方式,对于复杂聚合建议在应用层处理或使用 OLAP 引擎。

9.4 版本冲突

问题:引入 ShardingSphere 后启动报 ClassNotFoundExceptionNoSuchMethodError

解决:显式排除冲突的传递依赖(如 snakeyaml、guava),并单独引入兼容版本。

9.5 内存占用过高

问题:ANTLR 在 SQL 解析过程中会使用内部缓存,SQL 模板过多时导致堆内存增长。

解决:通过 -Xmx 参数设置合理的堆内存大小,避免 OOM。

9.6 连接池配置遗漏

问题:全局的 HikariCP 配置不会自动应用到 ShardingSphere 管理的各个数据源上。

解决:在 ShardingSphere 数据源配置中为每个物理数据源单独配置连接池参数。

十、总结

ShardingSphere-JDBC 作为 Apache 顶级项目,提供了完整的分库分表、读写分离、数据加密等解决方案,具有以下优势:

  1. 对业务代码几乎无侵入:只需引入依赖和配置,无需修改业务逻辑
  2. 兼容性强:支持任意实现 JDBC 规范的数据库和 ORM 框架
  3. 功能丰富:数据分片、读写分离、数据加密、影子库等功能可叠加使用
  4. 配置灵活:提供 Java API、YAML、Spring Boot Starter 等多种配置方式

在实际使用中,建议遵循以下最佳实践:

  • 分片键设计:选择业务中最常用的查询字段作为分片键
  • 避免跨分片查询:尽量让单次查询只命中一个分片
  • 合理使用广播表和绑定表:优化跨分片关联查询性能
  • 版本选型谨慎:确保 ShardingSphere 版本与 Spring Boot 版本兼容
  • 监控与告警:对分片路由、慢查询等关键指标进行监控

希望这篇指南能够帮助你快速上手 ShardingSphere-JDBC,顺利完成分库分表的落地。


参考文档

  1. Apache ShardingSphere 官方文档
  2. ShardingSphere-JDBC 配置手册
  3. ShardingSphere GitHub 示例项目
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐