java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > ShardingSphere-JDBC分库分表

SpringBoot集成ShardingSphere-JDBC实现分库分表

作者:喜之郎XX果冻

本文主要介绍了SpringBoot集成ShardingSphere-JDBC实现分库分表,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

前言:该文章目前只基本实现分库分表,如需更复杂功能请移步官方文档,后续会出复杂实现的笔记,如有需要请关注留言

一、ShardingSphere-JDBC 概述

作用之一:实现分库分表存储

ShardingSphere-JDBC 是 Apache ShardingSphere 的轻量级解决方案,定位为增强型 JDBC 驱动。它以 jar 包形式存在于应用程序中,通过实现 DataSource 接口,对上层应用提供透明的分库分表能力,与应用程序共享同一个 JVM 进程。

1. 核心特性

2. 与其他中间件对比

类型优点缺点
ShardingSphere-JDBC无额外部署成本,轻量级,性能损耗小与应用绑定,升级维护需修改代码
ShardingSphere-Proxy对应用透明,支持异构语言,独立部署需要额外运维,性能略低于 JDBC
MyCat/Atlas成熟稳定,社区活跃功能不如 ShardingSphere 全面

二、集成前项目架构(单数据源)

1. 依赖配置

<!-- MyBatis-Plus 依赖 -->
<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>mybatis-plus-boot-starter</artifactId>
  <version>3.4.3.4</version>
</dependency>
<!-- HikariCP 连接池 -->
<dependency>
  <groupId>com.zaxxer</groupId>
  <artifactId>hikari-cp</artifactId>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
  <groupId>com.mysql</groupId>
  <artifactId>mysql-connector-j</artifactId>
  <scope>runtime</scope>
</dependency>

2.配置文件 (application.yml)

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/enterprise_practical_project
    username: root
    password: qwer123456
    hikari:
      maximum-pool-size: 15
      minimum-idle: 5
      connection-timeout: 30000
mybatis-plus:
  mapper-locations: classpath:cn/jjcoder/mapper/*.xml
  type-aliases-package: cn.jjcoder.entity
  configuration:
    map-underscore-to-camel-case: true

3. MyBatis-Plus 配置类

@Configuration
public class MyBatisPlusConfig {


    //插件
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
        factory.setDataSource(dataSource);
        factory.setMapperLocations(new PathMatchingResourcePatternResolver()
                                   .getResources("classpath:cn/jjcoder/mapper/*.xml"));
        return factory.getObject();
    }
}

4. 架构特点

三、集成后项目架构(ShardingSphere-JDBC)

1. 新增依赖

<!-- ShardingSphere-JDBC 核心依赖 -->
<!-- https://mvnrepository.com/artifact/org.apache.shardingsphere/sharding-jdbc-spring-boot-starter -->
<dependency>
  <groupId>org.apache.shardingsphere</groupId>
  <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
  <version>5.1.2</version>
  <exclusions>
    <!-- 排除Druid自动配置 -->
    <exclusion>
      <groupId>com.alibaba</groupId>
      <artifactId>druid-spring-boot-starter</artifactId>
    </exclusion>
    <!-- 排除任务调度器 -->
    <exclusion>
      <groupId>org.apache.shardingsphere</groupId>
      <artifactId>shardingsphere-schedule-core</artifactId>
    </exclusion>
  </exclusions>
</dependency>

2. 配置文件变更

spring:
    shardingsphere:
    # 公共数据源配置(锚点)
    common-ds: &common-ds
      type: com.zaxxer.hikari.HikariDataSource
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: qwer123456
      hikari:
        minimum-idle: 5
        maximum-pool-size: 15
        idle-timeout: 30000

    datasource:
      names: main_db,ds2025
      main_db:
        <<: *common-ds  # 引用公共配置
        jdbc-url: jdbc:mysql://localhost:3306/enterprise_practical_project
      ds2025:
        <<: *common-ds
        jdbc-url: jdbc:mysql://localhost:3306/shard_db_2025
#      ds2026:
#        <<: *common-ds
#        jdbc-url: jdbc:mysql://localhost:3306/shard_db_2026
    rules:
      sharding:
        tables:
          standing_book:
            actual-data-nodes: ds$->{2025..2026}.standing_book_$->{202501..202512}
            database-strategy:
              standard:
                sharding-column: in_out_date
                sharding-algorithm-name: db-interval
            table-strategy:
              standard:
                sharding-column: in_out_date
                sharding-algorithm-name: table-interval
        sharding-algorithms:
          db-interval:
            type: INTERVAL
            props:
              datetime-pattern: "yyyy-MM-dd"
              datetime-lower: '2025-01-01'
              datetime-upper: '2026-12-31'
              sharding-suffix-pattern: 'yyyy'
              datetime-interval-amount: 1
              datetime-interval-unit: years
          table-interval:
            type: INTERVAL
            props:
              datetime-pattern: "yyyy-MM-dd"
              datetime-lower: '2025-01-01'
              datetime-upper: '2026-12-31'
              sharding-suffix-pattern: 'yyyyMM'
              datetime-interval-amount: 1
              datetime-interval-unit: months
              datetime-format-pattern: ^\d{4}-\d{2}-\d{2}$
    
mybatis-plus:
  mapper-scan:
    base-package: cn.jjcoder.mapper
  global-config:
    db-config:
      logic-delete-field: deleted # 全局逻辑删除字段名
      logic-delete-value: 1 # 逻辑已删除值
      logic-not-delete-value: 0 # 逻辑未删除值
  configuration:
    # 配置下划线与驼峰之间转换
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath:cn.jjcoder.mapper/*.xml
  type-aliases-package: cn.jjcoder.entity
    

3. MyBatis-Plus 配置类

@Configuration
public class MyBatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);// 注入的是 ShardingSphere 的数据源
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
                                          .getResources("classpath:cn/jjcoder/mapper/*.xml"));
        return sessionFactory.getObject();
    }

    /**
     * 使用 ShardingSphere 时,事务管理器需要指向 ShardingSphere 的数据源:
     * @param dataSource
     * @return
     */
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

4. 架构特点

四、集成后效果演示

通过插入数据来演示 mybatis-plus 和ShardingSphere之间的工作流程分库分表

前言:我目前在 application.yml 配置文件中配置的需要分库分表的表为 standing_book(台账表),并且配好了多数据源的连接信息,以及具体的分片算法。所以在此之前需要保证数据库有相应的库和表,以及表的分片键类型(这点我在刚开始做的时候没注意,后续出了bug排查了好久)

保证以上之后,演示分库分表具体效果:

由于分库分表的表为standing_book,所以需给这个表插入数据才能触发,目前我的业务逻辑为 产品出入库时生成台账信息保存数据库

/**
     * 根据商品出入库生成台账
     * @param list
     * @return
     */
    @Override
    public boolean saveByProductInOrOut(List<ProductVO> list, String userName, boolean isIn) {
        String operationType = isIn ? "入库" : "出库";

        List<StandingBook> standingBooks = new ArrayList<>();
        for (ProductVO vo : list) {
            StandingBook standingBook = new StandingBook();
            standingBook.setProductId(vo.getId());   //产品id
            standingBook.setProductName(vo.getName()); //产品名称
            standingBook.setOperationType(operationType);  //操作类型
            standingBook.setOperationNum(vo.getInOrOutCount());  //入库or出库数量
            Integer newQuantity=isIn? vo.getInventory()+vo.getInOrOutCount() : vo.getInventory()-vo.getInOrOutCount();
            standingBook.setNewQuantity(newQuantity);  //出入库后库存总量
            standingBook.setOrperator(userName);
            standingBook.setCreateDatetime(LocalDateTime.now());
            standingBook.setInOutDate(LocalDate.now().toString());  //设置分片键值(shardingsphere会根据该字段值进行分片,路由到相应的库表)
            standingBooks.add(standingBook);
        }
        //该 saveOrUpdateBatchCustom 将会触发分库分表算法
        return this.saveOrUpdateBatchCustom(standingBooks);

    }
# mybatis-plus生成的sql
2025-05-20 20:06:48.308 DEBUG --- [onPool-worker-9] c.j.mapper.StandingBookMapper.insert     : ==>  Preparing: INSERT INTO standing_book ( standing_book_id, product_id, product_name, operation_type, in_out_date, orperator, operation_num, new_quantity, create_datetime ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ? )
2025-05-20 20:06:48.316 DEBUG --- [onPool-worker-9] c.j.mapper.StandingBookMapper.insert     : ==> Parameters: f1feb93be82375630ce8f4c3a8e006f8(String), 1dbcf56794cc1cc21ea12719ea7b7a86(String), 6(String), 入库(String), 2025-05-20(String), root(String), 1(Integer), 35(Integer), 2025-05-20T20:06:48.307(LocalDateTime)

                                                                                                                                                                                                                                                                                                             # 被ShardingSphere接管()
                                                                                                                                                                                                                                                                                                             # 逻辑sql
2025-05-20 20:06:48.322  INFO --- [onPool-worker-9] ShardingSphere-SQL                       : Logic SQL: INSERT INTO standing_book  ( standing_book_id,
                                                                                                                                       product_id,
                                                                                                                                       product_name,

                                                                                                                                       operation_type,
                                                                                                                                       in_out_date,
                                                                                                                                       orperator,
                                                                                                                                       operation_num,
                                                                                                                                       new_quantity,
                                                                                                                                       create_datetime )  VALUES  ( ?,
?,
?,

?,
?,
?,
?,
?,
? )
2025-05-20 20:06:48.322  INFO --- [onPool-worker-9] ShardingSphere-SQL                       : SQLStatement: MySQLInsertStatement(setAssignment=Optional.empty, onDuplicateKeyColumns=Optional.empty)
# 实际执行sql
2025-05-20 20:06:48.322  INFO --- [onPool-worker-9] ShardingSphere-SQL                       : Actual SQL: ds2025 ::: INSERT INTO standing_book_202505  ( standing_book_id,
                                                                                                                                                          product_id,
                                                                                                                                                          product_name,

                                                                                                                                                          operation_type,
                                                                                                                                                          in_out_date,
                                                                                                                                                          orperator,
                                                                                                                                                          operation_num,
                                                                                                                                                          new_quantity,
                                                                                                                                                          create_datetime )  VALUES  (?, ?, ?, ?, ?, ?, ?, ?, ?) ::: [f1feb93be82375630ce8f4c3a8e006f8, 1dbcf56794cc1cc21ea12719ea7b7a86, 6, 入库, 2025-05-20, root, 1, 35, 2025-05-20T20:06:48.307]

通过以上控制台输出可以看到

具体执行流程如下:

  1. 应用代码调用:Service 层调用 Mapper 接口方法。
  2. MyBatis-Plus 处理:
    ■ 通过 SqlSessionFactory 获取 SqlSession。
    ■ 生成 SQL 语句。
  3. ShardingSphere 拦截:
    ■ ShardingDataSource 拦截 SQL 执行请求。
    ■ 根据分片规则计算目标库表。
    ■ 路由 SQL 到对应的物理数据源和表。
  4. 数据库执行:实际执行 SQL 并返回结果。
  5. 结果返回:结果通过 ShardingSphere 封装后返回给 MyBatis-Plus,MyBatis-Plus 将结果映射为 Java 对象返回给应用。

五、配置加载与协作流程

1. 配置加载顺序

  1. Spring Boot 启动:加载 application.yml 配置文件。
  2. ShardingSphere 自动配置
    • ShardingSphereAutoConfiguration 类解析 spring.shardingsphere 配置。
    • 创建多个物理数据源(HikariCP 连接池)。
    • 根据分片规则创建 ShardingDataSource(实现 DataSource 接口)。
    • 将 ShardingDataSource 注册为 Spring 容器中的唯一 DataSource Bean。
  3. MyBatis-Plus 自动配置
    • MybatisPlusAutoConfiguration 类自动加载。
    • 注入 ShardingSphere 提供的 DataSource Bean。
    • 创建 SqlSessionFactory 和其他 MyBatis 组件。

2. 核心组件初始化过程

ShardingSphere 初始化:

ShardingSphereAutoConfiguration
  → 解析配置文件
  → 创建多个 HikariDataSource 实例
  → 根据分片规则创建 ShardingRule
  → 将数据源和规则组装为 ShardingDataSource
  → 注册 ShardingDataSource 到 Spring 容器

MyBatis-Plus 初始化:

MybatisPlusAutoConfiguration
  → 从 Spring 容器获取唯一的 DataSource Bean(即 ShardingDataSource)
  → 创建 MybatisSqlSessionFactoryBean
  → 设置数据源、映射文件路径等配置
  → 初始化 SqlSessionFactory
  → 注册 MyBatis 的 MapperScanner

3. SQL 执行协作流程

  1. 应用代码调用:Service 层调用 Mapper 接口方法。
  2. MyBatis-Plus 处理
    • 通过 SqlSessionFactory 获取 SqlSession。
    • 生成 SQL 语句。
  3. ShardingSphere 拦截
    • ShardingDataSource 拦截 SQL 执行请求。
    • 根据分片规则计算目标库表。
    • 路由 SQL 到对应的物理数据源和表。
  4. 数据库执行:实际执行 SQL 并返回结果。
  5. 结果返回:结果通过 ShardingSphere 封装后返回给 MyBatis-Plus,MyBatis-Plus 将结果映射为 Java 对象返回给应用。

六、集成前后代码对比

1. 依赖对比

对比维度集成前集成后
核心依赖变化仅包含 MyBatis-Plus 及基础数据库连接依赖新增 ShardingSphere-JDBC 核心依赖,构建多数据源处理能力
功能扩展支持单数据源下的常规数据库操作与对象映射新增分库分表、读写分离、分布式事务等高级功能

2. 配置文件对比

在数据源配置部分

对比维度集成前集成后
数据源配置位置spring.datasource,配置单一数据源spring.shardingsphere.datasource,配置多个物理数据源
配置复杂度简单定义驱动、URL、用户名和密码等基本信息除基本信息外,还需配置多个数据源连接池参数,以及分库分表规则
新增配置项spring.shardingsphere.rules,用于定义分库分表规则,包括数据节点、分片策略和算法

3. 代码逻辑对比

从数据访问层代码逻辑来看

对比维度集成前集成后
数据访问方式MyBatis-Plus 直接操作单一数据源,简单直观MyBatis-Plus 操作 ShardingSphere 提供的逻辑数据源,底层自动处理 SQL 路由
开发关注点专注于业务 SQL 编写和对象映射无需关注复杂的 SQL 路由逻辑,专注业务逻辑,由 ShardingSphere 处理数据分片
代码改动无明显代码改动无明显代码改动,仅配置变化,对业务代码透明

4. 架构设计对比

在架构层面

对比维度集成前集成后
数据库连接关系应用直接连接单一数据库,架构简单应用通过 ShardingSphere-JDBC 连接多个数据库,形成分布式架构
数据管理方式单库内数据处理,逻辑集中ShardingSphere 管理多个物理数据源,实现数据分片存储与统一访问
架构扩展性扩展性有限,难以应对大规模数据和高并发扩展性增强,可通过增加数据源应对数据增长和高并发需求

七、关键注意事项

1. 数据源唯一性

(以下配置如果你是以代码形式配置数据源的话这么配置,如果是在application.yml中配置需在配置文件配置)

确保 Spring 容器中只有一个 DataSource Bean,即 ShardingSphere 提供的 ShardingDataSource。如果存在多个 DataSource Bean,需要通过 @Primary 注解明确指定主数据源。

若项目中存在多个数据源配置,需显式指定 ShardingSphere 的数据源为主要数据源:

示例代码

@Configuration
public class DataSourceConfig {
    // 假设存在多个数据源 Bean
    @Bean
    @Primary // 指定 ShardingSphere 的数据源为主要数据源
    public DataSource shardingDataSource() throws SQLException {
        // 从 ShardingSphere 配置构建数据源
        return ShardingSphereDataSourceFactory.createDataSource(createShardingRuleConfiguration());
    }
    // 其他数据源 Bean(非主要)
    @Bean
    public DataSource anotherDataSource() {
        // 配置其他数据源
        return DataSourceBuilder.create().build();
    }
    // 构建 ShardingSphere 分片规则配置
    private ShardingRuleConfiguration createShardingRuleConfiguration() {
        // 配置分片规则...
    }
}

2. 事务管理

在使用ShardingSphere的分布式事务支持时,可通过配置事务类型属性来选择事务模式。例如,若采用XA强一致性事务,可在application.yml中添加spring.shardingsphere.props.transaction.type=XA配置;若选择柔性事务(如基于最大努力送达型的事务),则需结合具体业务场景,合理配置补偿策略和消息队列等组件,确保事务最终一致性。

对于跨库事务,需要使用分布式事务管理器,如 Seata 或 ShardingSphere 自身提供的分布式事务支持:

spring:
  shardingsphere:
    props:
      xa-transaction-manager-type: atomikos  # 使用 Atomikos XA 事务管理器

3. SQL 限制

分库分表场景下,部分复杂 SQL 可能无法执行,如:

4. 性能监控

ShardingSphere 提供内置的 SQL 监控功能,可以通过配置开启:

spring:
  shardingsphere:
    props:
      sql-show: true  # 打印 SQL
      metrics-name: prometheus  # 集成 Prometheus 监控

八、常见问题与解决方案

1. 启动时报 “SqlSessionFactory 找不到”

在 MybatisPlusConfig 配置如下bean

@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
    sessionFactory.setDataSource(dataSource);
    sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
                                      .getResources("classpath:cn/jjcoder/mapper/*.xml"));
    return sessionFactory.getObject();
}

2. SQL 执行异常,表不存在

3. 事务不生效

配置文件修改示例:

配置 Atomikos XA 事务管理器:

spring:
 shardingsphere:
   props:
     xa-transaction-manager-type: atomikos # 启用 XA 事务

Java 配置示例

定义分布式事务管理器:

@Configuration
public class TransactionConfig {
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        // 使用 Atomikos 分布式事务管理器
        JtaTransactionManager transactionManager = new JtaTransactionManager();
        transactionManager.setTransactionManager(new UserTransactionManager());
        transactionManager.setUserTransaction(new UserTransactionImp());
        return transactionManager;
    }
}

4. 性能下降明显

配置文件修改示例:

优化 HikariCP 连接池参数:

spring:
   shardingsphere:
    # 公共数据源配置(锚点)
    common-ds: &common-ds
      type: com.zaxxer.hikari.HikariDataSource
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: qwer123456
      hikari:
        maximum-pool-size: 30 # 增加最大连接数
        minimum-idle: 10 # 增加最小空闲连接数
        idle-timeout: 30000 # 空闲连接超时时间
        max-lifetime: 1800000 # 连接最大生命周期

九、总结

通过集成 ShardingSphere-JDBC,我们在不修改 MyBatis-Plus 代码的前提下,实现了数据库的分库分表能力。这种架构既保留了 MyBatis-Plus 的开发便利性,又通过 ShardingSphere 提升了系统的可扩展性和性能。关键要点包括:

  1. ShardingSphere-JDBC 作为增强型 JDBC 驱动,对上层应用透明。
  2. 集成后,MyBatis-Plus 操作的是 ShardingSphere 提供的逻辑数据源,无需关心底层分片细节。
  3. 分片规则完全通过配置文件控制,无需修改 DAO 层代码。
  4. 需要注意分布式事务、复杂 SQL 支持等特殊

到此这篇关于SpringBoot集成ShardingSphere-JDBC实现分库分表的文章就介绍到这了,更多相关ShardingSphere-JDBC分库分表内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

您可能感兴趣的文章:
阅读全文