1. 为什么你需要这份Spring Boot 3.x ShardingSphere 5.3.x的避坑指南如果你正在用Spring Boot 3.x开发项目并且数据库压力越来越大想引入读写分离来提升性能那你很可能已经踩过坑了。我最近刚把一个线上项目从Spring Boot 2.7升级到3.2同时集成了ShardingSphere 5.3.2来做MySQL的读写分离整个过程可以说是一路“披荆斩棘”。最大的感受就是版本兼容性是个大坑网上很多教程还停留在5.2.x甚至更早的版本直接套用到新版本上分分钟让你项目启动失败。Spring Boot 3.x是个大版本升级它基于Java 17并且对很多内部依赖和自动配置逻辑做了调整。而ShardingSphere在5.3.x版本做了一个非常重大的架构调整它彻底移除了对Spring Boot Starter的原生支持。这意味着如果你还按照5.2.x时代的老办法直接在pom.xml里引入一个shardingsphere-jdbc-core-spring-boot-starter然后在application.yml里写配置这条路在5.3.x下是彻底走不通的。你会遇到各种类找不到、配置不生效的奇怪问题。所以这篇文章就是为你准备的。我会以一个实战者的角度手把手带你走通Spring Boot 3.x集成ShardingSphere 5.3.x实现MySQL读写分离的完整流程。重点不是告诉你“应该怎么做”而是告诉你“我实际是怎么做的以及我遇到了哪些坑又是怎么填上的”。我们会从项目环境搭建开始一步步配置直到最终成功运行并且我会把每个容易出错的关键点都标出来。目标只有一个让你能一次成功少走弯路。2. 环境准备与依赖配置第一步就埋着“雷”万事开头难集成ShardingSphere的第一步——引入依赖就藏着好几个版本“雷区”。踩错了后面所有的努力都可能白费。2.1 核心依赖选择告别Starter拥抱新方式首先明确一个核心变化在ShardingSphere 5.3.x中官方不再提供shardingsphere-jdbc-core-spring-boot-starter这个依赖。这是与5.2.x最大的不同。如果你在Maven中央仓库搜索会发现这个artifactId的最新版本停留在了5.2.1。所以我们的依赖必须换。对于Spring Boot 3.x项目你需要引入的是最核心的JDBC驱动包dependency groupIdorg.apache.shardingsphere/groupId artifactIdshardingsphere-jdbc-core/artifactId version5.3.2/version !-- 请使用当时最新版本 -- /dependency就是这个shardingsphere-jdbc-core它包含了ShardingSphere JDBC运行所需的所有核心类。同时因为我们要连接MySQL所以数据库驱动也必不可少。Spring Boot 3.x默认的MySQL驱动版本通常没问题但建议显式声明dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency2.2 数据源依赖的“大坑”为什么不能用Druid Starter很多项目习惯用druid-spring-boot-starter来配置数据源因为它能无缝对接Spring Boot的spring.datasource配置非常方便。但是在ShardingSphere 5.3.x的配置体系下这个starter会引发冲突。ShardingSphere要求自己来完全掌控数据源的创建和初始化。如果你同时引入了Druid StarterSpring Boot的自动配置会尝试基于application.yml中的spring.datasource创建一个数据源而ShardingSphere也会基于自己的配置文件创建一套数据源。两套数据源在Spring容器中打架会导致一些奇怪的错误比如连接池参数校验失败testWhileIdle is true, validationQuery not set或者根本连不上数据库。正确的做法是只引入Druid的核心库而不是Starter。!-- 错误不要引入这个 -- !-- dependency groupIdcom.alibaba/groupId artifactIddruid-spring-boot-starter/artifactId /dependency -- !-- 正确引入这个 -- dependency groupIdcom.alibaba/groupId artifactIddruid/artifactId version1.2.18/version !-- 建议使用较新稳定版 -- /dependency2.3 隐藏的版本冲突SnakeYAML的“幽灵”这是一个非常隐蔽但几乎必现的坑尤其如果你的Spring Boot版本在2.x系列虽然我们主题是3.x但很多同学可能还在用2.7/2.8。ShardingSphere 5.3.x内部使用的YAML解析器SnakeYAML版本可能与Spring Boot内置的版本不兼容。你会遇到一个非常典型的错误日志里会提示类似“An attempt was made to call a method that does not exist...”堆栈指向ShardingSphereYamlConstructor。这就是版本冲突。解决方案是强制指定SnakeYAML的版本。无论你用Spring Boot 2.x还是3.x我都建议加上这个依赖管理确保使用一个兼容的版本如1.33或更高dependency groupIdorg.yaml/groupId artifactIdsnakeyaml/artifactId version1.33/version /dependency对于Spring Boot 3.x其内置的SnakeYAML版本可能已经较高但显式声明可以避免未来潜在的冲突是一个好的实践。3. 核心配置实战YAML文件里的“魔鬼细节”依赖搞定后就进入了最关键的配置环节。ShardingSphere 5.3.x要求我们将配置写在一个独立的YAML文件中然后在Spring Boot的主配置里引用它。这种方式更清晰也更具弹性。3.1 主配置文件引导ShardingSphere驱动首先在Spring Boot的application.yml或application.properties中你需要进行一个非常关键的配置将数据源驱动指向ShardingSphere的驱动类并通过URL指定我们的独立配置文件。这意味着你要彻底清空或注释掉之前所有spring.datasource下的配置比如url,username,password因为它们现在由ShardingSphere管理。# application.yml spring: datasource: # 关键1驱动类必须使用ShardingSphere提供的驱动 driver-class-name: org.apache.shardingsphere.driver.ShardingSphereDriver # 关键2URL格式固定指向你的ShardingSphere配置文件 url: jdbc:shardingsphere:classpath:sharding-config.yaml # 注意这里不再需要 username, password 等配置它们在下方的独立配置文件中定义这个url的格式是固定的jdbc:shardingsphere:classpath:[你的配置文件路径]。我习惯在resources目录下直接创建一个sharding-config.yaml文件。3.2 独立配置文件上定义真实数据源现在我们来创建核心的sharding-config.yaml文件。第一部分是定义真实的物理数据源。这里以一主一从为例使用我们上面引入的Druid连接池。# sharding-config.yaml dataSources: # 定义主库数据源名称可以自定义这里叫master master: # 数据源完整类名对应Druid dataSourceClassName: com.alibaba.druid.pool.DruidDataSource # 数据库驱动 driverClassName: com.mysql.cj.jdbc.Driver # 数据库连接URL - 注意Druid的属性是url不是jdbcUrl url: jdbc:mysql://192.168.1.100:3306/your_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: your_master_password # 以下是Druid连接池的其他可选参数 initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 # 定义从库数据源名称可以自定义这里叫slave1 slave1: dataSourceClassName: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.1.101:3306/your_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: your_slave_password initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000这里有一个超级大坑属性名不同连接池的配置属性名可能不同。比如HikariCP用的是jdbcUrl而Druid用的是url。如果你错误地写成了jdbcUrl启动时就会报url not set的错误。一定要根据你使用的dataSourceClassName对应的连接池文档来填写属性。3.3 独立配置文件下配置读写分离规则数据源定义好后接下来就是配置读写分离规则。这是逻辑层面的配置我们会定义一个逻辑数据源名称比如readwrite_ds应用代码中使用的就是这个名字。# sharding-config.yaml (接上一部分) rules: # 使用‘!’加规则类型来声明一个规则 - !READWRITE_SPLITTING dataSources: # 定义逻辑数据源名称应用代码中就用这个‘readwrite_ds’ readwrite_ds: # 静态读写分离策略适用于从库地址固定的场景 staticStrategy: # 写请求路由到哪个真实数据源 writeDataSourceName: master # 读请求可以路由到哪些真实数据源数组格式或逗号分隔字符串 readDataSourceNames: - slave1 # - slave2 # 如果你有多个从库可以继续添加 # 负载均衡算法名称与下面的loadBalancers配置对应 loadBalancerName: round_robin # 负载均衡算法定义 loadBalancers: # 定义一个名为‘round_robin’的负载均衡器 round_robin: type: ROUND_ROBIN # 轮询算法 # 你也可以定义其他算法比如随机‘RANDOM’ # random: # type: RANDOM这里藏着第二个大坑配置项的命名风格我实测在5.3.2版本中配置项必须使用驼峰命名法camelCase。比如writeDataSourceName、readDataSourceNames、loadBalancerName。如果你不小心参考了某些过时的文档或示例写成了下划线风格如write_data_source_name启动时就会抛出Unable to find property static_strategy之类的解析错误让你排查半天。这一点官方文档在某些地方存在描述不一致务必以实际可运行的驼峰命名为准。3.4 开启SQL日志调试必备在开发阶段为了确认读写分离是否生效强烈建议开启SQL显示功能。这能让你在控制台看到每条SQL语句被路由到了哪个真实数据源master 或 slave。# sharding-config.yaml (接上一部分) props: # 显示SQL日志包含逻辑SQL和实际路由到的数据源 sql-show: true4. 避坑大全那些让我熬夜的报错与解决方案配置写完了启动项目才是“试金石”。下面是我在集成过程中遇到的几个典型错误以及它们的解决方案。4.1 报错dataSource-1 init error或url not set错误现象应用启动失败日志中明确提示数据源初始化错误原因是url属性没有设置。问题根源这几乎可以肯定是sharding-config.yaml中dataSources部分的连接池属性名写错了。正如前面强调的对于Druid连接地址的属性名是url对于HikariCP属性名是jdbcUrl。如果你定义的数据源类型是com.alibaba.druid.pool.DruidDataSource却写了jdbcUrlDruid内部就找不到url这个属性从而初始化失败。解决方案仔细核对dataSourceClassName与你填写的属性名是否匹配。最稳妥的方法是去该连接池的官方文档查看数据源配置的属性列表。4.2 报错Unable to find property static_strategy错误现象启动时抛出YAML解析异常提示找不到static_strategy、write_data_source_name等属性。问题根源配置项的命名风格错误。在ShardingSphere 5.3.x的YAML配置中规则内部的属性如staticStrategywriteDataSourceName需要使用驼峰命名法。很多从旧版本迁移过来的文档或者官方文档中部分描述性文字用了下划线导致误解。解决方案将读写分离规则中的所有配置项改为驼峰形式。对照我上面给出的示例检查你的staticStrategy、writeDataSourceName、readDataSourceNames、loadBalancerName这几个键是否正确。4.3 现象控制台出现了HikariCP连接池的日志问题现象明明只配置了Druid但启动日志里却出现了HikariPool-1 - Starting...和HikariPool-1 - Start completed.这样的信息。问题根源这是ShardingSphere内部的一个已知行为不算错误但容易让人困惑。ShardingSphere驱动在初始化时可能会创建一个默认的HikariCP数据源用于内部管理即使你没有配置它。只要你的业务SQL正确路由到了Druid管理的主从库并且运行正常这个HikariCP数据源的出现就可以忽略不影响功能。解决方案无需解决属于正常现象。如果你实在觉得干扰可以尝试在sharding-config.yaml的每个数据源配置中显式设置一些Druid特有的连接池属性如validationQuery: SELECT 1这能更明确地表明Druid被成功创建和使用。4.4 报错No qualifying bean of type javax.sql.DataSource available错误现象应用启动失败Spring上下文创建失败提示找不到DataSource类型的Bean。问题根源Spring Boot的自动配置还在尝试基于spring.datasource创建自己的数据源Bean但相关的必要属性如url已经被我们移除了。或者其他一些第三方库如某些监控组件自动注入了对DataSource的依赖但ShardingSphere提供的DataSourceBean可能在某些条件下不符合它们的预期。解决方案首先确保你的application.yml中已经彻底清空了所有spring.datasource下的子属性除了我们指定的driver-class-name和url。如果问题依旧可以尝试在启动类上排除Spring Boot默认的数据源自动配置SpringBootApplication(exclude {DataSourceAutoConfiguration.class}) public class YourApplication { public static void main(String[] args) { SpringApplication.run(YourApplication.class, args); } }但请注意排除后要确保所有数据库操作都通过ShardingSphere的逻辑数据源进行。5. 功能验证与高级考量你的读写分离真的生效了吗配置完成且启动成功不代表万事大吉。我们还需要验证读写分离是否真的按预期工作了。5.1 基础验证查看SQL日志这是最直接的方法。确保props.sql-show: true已开启然后在应用中执行一些操作执行一条INSERT或UPDATE语句。在控制台日志中你应该能看到这条SQL并且其上下文信息会显示数据源为master。执行一条SELECT语句。在日志中你应该能看到这条SQL被路由到了slave1或者你在readDataSourceNames中配置的从库名称。如果日志显示所有SQL都走向了master那就要检查配置了很可能负载均衡算法配置有误或者从库数据源定义有问题。5.2 强制走主库应对“写完即读”的场景读写分离引入了一个经典问题在同一个事务或方法中如果我先写入INSERT紧接着就要读取SELECT这条刚写入的数据由于主从同步有毫秒级延迟读请求可能会被路由到从库从而读不到刚写入的数据。ShardingSphere提供了Hint强制路由机制来解决这个问题。你可以通过编程方式强制让接下来的操作走主库。首先需要在配置中启用Hint管理器默认通常是启用的。然后在代码中这样使用import org.apache.shardingsphere.api.hint.HintManager; public class SomeService { public void writeThenRead() { // 获取HintManager实例 try (HintManager hintManager HintManager.getInstance()) { // 设置强制走主库 hintManager.setWriteRouteOnly(); // 执行写入操作 yourRepository.insert(data); // 执行读取操作此时即使它是SELECT也会被强制路由到主库 YourEntity result yourRepository.findById(data.getId()); } // try-with-resources会自动关闭HintManager清除强制路由设置 } }HintManager实现了AutoCloseable接口使用try-with-resources语法可以确保它在使用后被正确清理避免污染后续的SQL路由。5.3 多从库与负载均衡策略上面的例子只用了一个从库。在生产环境中我们通常会配置多个从库来分担读压力。配置起来很简单只需在readDataSourceNames列表中添加即可readDataSourceNames: - slave1 - slave2 - slave3负载均衡算法除了轮询ROUND_ROBIN还可以选择随机RANDOM或者根据权重WEIGHT进行分配。例如配置权重负载均衡loadBalancers: weight_lb: type: WEIGHT props: slave1: 2 slave2: 3 slave3: 5这意味着slave1、slave2、slave3在承担读请求时的权重比为2:3:5。slave3机器配置最好承担更多的读请求。5.4 关于事务的一些思考ShardingSphere的读写分离默认是在非事务环境下根据SQL类型读/写进行路由。但在Transactional声明的事务内部为了保持数据一致性默认所有操作都会路由到主库。因为事务可能包含混合的读写操作如果读操作被路由到从库而写操作在主库一旦事务回滚主从之间的数据状态就会变得复杂且不一致。这一点需要特别注意。如果你的业务场景是“在事务内先读后写”并且你能够接受主从延迟或者通过其他手段保证同时希望读操作依然能从从库读取以减轻主库压力那么就需要更精细地设计事务边界或者探索ShardingSphere提供的一致性读等高级特性但这会引入额外的复杂度。对于大多数应用保持事务内走主库是简单可靠的选择。6. 从5.2.x升级到5.3.x的迁移要点如果你有一个正在使用ShardingSphere 5.2.x Spring Boot Starter的项目想要升级到5.3.x和Spring Boot 3.x整个迁移路径可以概括为以下几个步骤依赖替换将shardingsphere-jdbc-core-spring-boot-starter依赖移除替换为shardingsphere-jdbc-core。同时处理好Druid和SnakeYAML的依赖。配置迁移这是工作量最大的部分。你需要将原来散落在application.yml中的ShardingSphere配置通常在spring.shardingsphere节点下全部提取出来整理成一个独立的YAML配置文件如sharding-config.yaml。配置格式转换将Spring Boot风格的配置点分隔符如spring.shardingsphere.datasource.names转换为ShardingSphere原生YAML格式树状结构。特别注意属性名的变化如urlvsjdbcUrl和规则配置的语法使用!标签。主配置修改在application.yml中将数据源驱动和URL指向新的独立配置文件。测试与验证全面测试应用的启动、增删改查操作利用sql-show功能验证路由是否正确特别注意事务场景下的行为是否符合预期。整个升级过程的核心思想是从“与Spring Boot深度集成”的模式转变为“将ShardingSphere作为独立JDBC驱动”的模式。这种转变使得ShardingSphere的配置更加标准化和独立减少了与特定Spring Boot版本的耦合从长远看是更有利于维护的。虽然迁移初期有学习成本但一旦掌握你会发现这种配置方式更加清晰和强大。