SpringBoot3 + MyBatis-Plus 3.5.5 整合 ShardingJDBC 5.5.0 实现高可用读写分离实战指南

最近在重构公司的一个电商项目时,遇到了数据库性能瓶颈。当促销活动开始,系统瞬间涌入大量用户查询请求,主库CPU直接飙到90%以上,导致整个系统响应变慢。经过技术调研,我们决定采用ShardingJDBC实现读写分离来分担主库压力。但在整合过程中踩了不少坑,特别是SpringBoot3与ShardingJDBC 5.5.0的版本兼容性问题。本文将分享我们最终落地的完整方案,包含依赖管理、配置细节和实战技巧。

1. 环境准备与依赖管理

在开始之前,需要确认开发环境满足以下要求:

  • JDK 17+(SpringBoot3的最低要求)
  • Maven 3.6.3+
  • MySQL 8.0+(推荐使用最新稳定版)

1.1 关键依赖版本选择

SpringBoot3与ShardingJDBC的版本兼容性是个大坑。经过测试,只有ShardingJDBC 5.4.1及以上版本才能完美兼容SpringBoot3。以下是必须注意的核心依赖:

<properties>
    <java.version>17</java.version>
    <spring-boot.version>3.2.4</spring-boot.version>
    <mybatis-plus.version>3.5.5</mybatis-plus.version>
    <shardingsphere.version>5.5.0</shardingsphere.version>
</properties>

1.2 POM文件关键配置

完整的依赖配置需要考虑排除冲突的传递依赖。特别是MyBatis-Plus和ShardingJDBC自带的一些测试工具包:

<dependencies>
    <!-- SpringBoot基础依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <exclusions>
            <exclusion>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-tomcat</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    
    <!-- 使用Undertow替代Tomcat提升性能 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-undertow</artifactId>
    </dependency>
    
    <!-- MyBatis-Plus配置(注意排除冲突) -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>${mybatis-plus.version}</version>
        <exclusions>
            <exclusion>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis-spring</artifactId>
            </exclusion>
            <exclusion>
                <artifactId>mybatis</artifactId>
                <groupId>org.mybatis</groupId>
            </exclusion>
        </exclusions>
    </dependency>
    
    <!-- ShardingJDBC核心依赖 -->
    <dependency>
        <groupId>org.apache.shardingsphere</groupId>
        <artifactId>shardingsphere-jdbc</artifactId>
        <version>${shardingsphere.version}</version>
        <exclusions>
            <exclusion>
                <groupId>org.apache.shardingsphere</groupId>
                <artifactId>shardingsphere-test-util</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

提示:建议使用dependencyManagement统一管理SpringBoot相关依赖版本,避免潜在的版本冲突问题。

2. 核心配置详解

2.1 application.yml关键配置

SpringBoot应用需要做以下调整才能正确集成ShardingJDBC:

spring:
  main:
    allow-bean-definition-overriding: true  # 必须开启
  application:
    name: order-service
  datasource:
    driver-class-name: org.apache.shardingsphere.driver.ShardingSphereDriver
    url: jdbc:shardingsphere:classpath:sharding.yaml

配置项说明:

配置项 必须 说明
allow-bean-definition-overriding 允许Bean定义覆盖,解决ShardingJDBC与SpringBoot自动配置冲突
driver-class-name 使用ShardingSphere提供的驱动类
url 指向ShardingSphere的配置文件路径

2.2 sharding.yaml完整配置

这是整个读写分离的核心配置文件,需要特别注意rules部分的配置顺序:

dataSources:
  ds_master:
    driverClassName: com.mysql.cj.jdbc.Driver
    dataSourceClassName: com.zaxxer.hikari.HikariDataSource
    jdbcUrl: jdbc:mysql://master-host:3306/order_db?useSSL=false&serverTimezone=UTC
    username: root
    password: master-password
    connectionTimeout: 30000
    maximumPoolSize: 20
  ds_slave1:
    driverClassName: com.mysql.cj.jdbc.Driver
    dataSourceClassName: com.zaxxer.hikari.HikariDataSource
    jdbcUrl: jdbc:mysql://slave1-host:3306/order_db?useSSL=false&serverTimezone=UTC
    username: root
    password: slave-password
    connectionTimeout: 30000
    maximumPoolSize: 20

rules:
  - !SINGLE
    tables:
      - "*.*"
    defaultDataSource: ds_master
  
  - !READWRITE_SPLITTING
    dataSources:
      readwrite_ds:
        writeDataSourceName: ds_master
        readDataSourceNames: 
          - ds_slave1
        transactionalReadQueryStrategy: PRIMARY
    loadBalancerName: round_robin
  
  loadBalancers:
    round_robin:
      type: ROUND_ROBIN

props:
  sql-show: true  # 开发环境建议开启,生产环境关闭

关键配置解析:

  1. !SINGLE规则 :必须放在第一位,用于定义默认数据源和表映射关系
  2. !READWRITE_SPLITTING规则 :定义读写分离策略
  3. loadBalancers :定义读库的负载均衡策略(支持ROUND_ROBIN和RANDOM)

3. 高级功能实现

3.1 强制走主库的注解实现

某些业务场景(如刚写入立即查询)需要强制走主库,可以通过HintManager实现:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Master {
}

@Aspect
@Component
@Slf4j
public class MasterRouteAspect {
    
    @Around("@annotation(com.yourpackage.Master)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        HintManager hintManager = null;
        try {
            hintManager = HintManager.getInstance();
            hintManager.setWriteRouteOnly();
            return joinPoint.proceed();
        } finally {
            if (hintManager != null) {
                hintManager.close();
            }
        }
    }
}

使用方法:在Service方法上添加@Master注解即可强制走主库:

@Service
public class OrderServiceImpl implements OrderService {
    
    @Master
    public Order getOrderById(Long orderId) {
        // 该方法会强制走主库查询
        return orderMapper.selectById(orderId);
    }
}

3.2 读写分离策略调优

ShardingJDBC提供了多种读写分离策略,可以根据业务特点进行配置:

rules:
  - !READWRITE_SPLITTING
    dataSources:
      readwrite_ds:
        writeDataSourceName: ds_master
        readDataSourceNames: 
          - ds_slave1
          - ds_slave2
        transactionalReadQueryStrategy: FIXED  # 可选值:PRIMARY/FIXED/DYNAMIC
    loadBalancerName: round_robin

策略对比:

策略 说明 适用场景
PRIMARY 事务内所有读操作走主库 强一致性要求高的场景
FIXED 事务内第一个读操作决定后续读操作的路由 平衡一致性与性能
DYNAMIC 每个读操作独立路由 对一致性要求不高的场景

4. 生产环境最佳实践

4.1 监控与健康检查

建议集成SpringBoot Actuator监控数据源状态:

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics
  endpoint:
    health:
      show-details: always

健康检查结果示例:

{
  "status": "UP",
  "components": {
    "db": {
      "status": "UP",
      "details": {
        "database": "MySQL",
        "master": {
          "status": "UP",
          "connection": "alive"
        },
        "slave1": {
          "status": "UP",
          "connection": "alive"
        }
      }
    }
  }
}

4.2 性能优化建议

  1. 连接池配置 :根据实际负载调整HikariCP参数

    dataSources:
      ds_master:
        maximumPoolSize: 20
        minimumIdle: 5
        idleTimeout: 600000
        maxLifetime: 1800000
    
  2. SQL打印控制 :生产环境关闭SQL打印

    props:
      sql-show: false
      sql-simple: true  # 简化日志输出
    
  3. 读写比例调整 :根据业务特点配置读库权重

    loadBalancers:
      weight_balancer:
        type: WEIGHT
        props:
          ds_slave1: 2
          ds_slave2: 1
    

4.3 常见问题排查

问题1 :启动时报 Table 'xxx' doesn't exist

解决方案:确保在rules中正确配置了!SINGLE规则,并且defaultDataSource指向正确的主库。

问题2 :读写分离不生效,所有查询都走主库

检查点:

  1. 确认sharding.yaml中!READWRITE_SPLITTING规则配置正确
  2. 检查是否在事务中执行查询(默认事务内走主库)
  3. 确认没有使用@Master注解

问题3 :性能没有明显提升

可能的优化方向:

  1. 增加从库数量
  2. 调整负载均衡策略
  3. 优化SQL查询(添加合适索引)

更多推荐