ShardingSphere 5.2.1 整合 Spring Boot:分库分表与读写分离避坑指南

发布时间:2026/10/20 8:05:54
ShardingSphere 5.2.1 整合 Spring Boot:分库分表与读写分离避坑指南 简介针对单表数据量过大、又希望避免分库改造的Java开发者一份SpringBoot整合ShardingSphere 5.2.1的分表不分库示例包完整演示了从依赖引入、ShardingRuleBuilder分片规则定义、分片策略选择到事务管理配置的落地路径。压缩包共16个文件9个Java文件覆盖分片规则、自定义算法及启动类2个YAML与2个XML用于多环境数据源和分片策略配置另有SQL脚本与.gitignore等整体仅15KB结构紧凑适合直接对照学习。已有1953人学习/下载资源聚焦最新版ShardingSphere的接入步骤从ShardingRuleBuilder定义到事务管理器选择均有呈现同时涉及范围分片、哈希分片及虚拟节点分片等策略并提醒数据迁移、分页查询和分布式事务等关键注意事项可帮助开发者避开数据迁移和跨分片操作等常见踩坑点在真实项目中更高效地落地分表方案。1. 一个配置前缀让大半教程停留在 4.x 时代搜索「Spring Boot 整合 ShardingSphere」的时候你会发现自己陷入了一场穿越剧跳出来的教程里配置前缀写的是spring.sharding-sphere分片算法还在用SPI的ClassBased写法甚至有些还在用早已废弃的sharding-jdbc-spring-boot-starter坐标。等你真把这些配置抄进 5.2.1 的项目里会发生一个比报错更可怕的后果——启动时一切正常但数据全部静默写进了主库的第一张物理表分片形同虚设。5.2.1 这版的核心变化在于配置前缀变成了spring.shardingsphere所有功能都以rules规则对象管理算法全面 SPI 化。这意味着网上大量 4.x 时代的教程不仅过时还会误导你。本文不考古直接给出一套以 5.2.1 为准的整合路径最小可运行配置、读写分离与强制路由、五条高频翻车现场排查以及上线前的验证清单。这套内容适合两类人一类是准备把老项目从 4.x 升级上来的开发者另一类是第一次引入分库分表、不想被旧教程带偏的新手。2. ShardingSphere 5.2.1 到底改变了什么从数据源增强到规则引擎2.1 它不是一个数据库而是一层“贴”在 JDBC 上的数据源增强层很多人第一次接触 ShardingSphere 时会误以为它是一个独立的数据库服务需要部署单独的进程。实际上 5.2.1 的 JDBC 模式做的事情非常朴素它把自己包装成一个实现了javax.sql.DataSource接口的增强数据源你的应用拿到的 DataSource 已经被替换成了 ShardingSphere 的数据源对象。当应用执行 SQL 时这个增强数据源会根据你配置的规则把一条逻辑 SQL 解析、路由、改写最终下发给真实的物理数据库执行。5.2.1 版本最重要的架构调整是把原来分散在sharding、readwrite-splitting、encrypt各处的配置项统一收拢到rules节点下。这个设计意图很明显所有能力都是规则规则可以叠加数据源可以嵌套。比如你可以同时启用分片和读写分离读写分离产生一个逻辑数据源这个逻辑数据源又可以被分片规则引用。这种嵌套能力在 4.x 里实现起来非常别扭在 5.2.1 里只是配置结构上的自然组合。理解这一层之后你就能明白为什么 5.2.1 的排错思路和 4.x 不一样。4.x 时代报错会直接指向某个 ShardingSphere 内部类5.2.1 的报错信息大部分会写在配置解析阶段——配置文件的键名拼写、引用关系、算法名称匹配这些才是 5.2.1 排错的主战场。2.2 为什么在 Spring Boot 项目里优先选 JDBC 模式的 starter 整合ShardingSphere 有 JDBC 和 Proxy 两种形态。Proxy 是一个独立的服务进程应用通过 MySQL 或 PostgreSQL 协议连上去对应用透明但多了一个必须运维的中间件节点。JDBC 模式则直接以 starter 依赖的形式嵌入应用没有额外进程不需要修改连接端口部署上就是一个普通 Spring Boot 应用。对于绝大多数 Spring Boot 单体或微服务项目JDBC 模式的整合成本最低这也是它在 Java 生态里更流行的原因。选型上有一个容易忽略的边界JDBC 模式意味着每个应用实例都要各自维护一份 ShardingSphere 数据源和连接池。如果你的微服务有三四个实例同时连接同一套分片库每个实例都会建立各自的物理连接池连接数是成倍增长的。另外如果你是异构语言团队比如一部分服务用 Go 或 Python那 JDBC 模式的方案对它们不生效这种情况应该考虑 Proxy 模式。5.2.1 的 starter 坐标已经不是老教程里的sharding-jdbc-spring-boot-starter而是shardingsphere-jdbc-spring-boot-starter。这个坐标变更也是很多人整合失败的第一道坎——依赖坐标用错Spring Boot 的自动装配完全不会触发应用启动后用的还是原来的普通数据源。2.3 三个核心概念逻辑表、数据节点、分片算法在动手写配置之前必须先建立三个概念的清晰认知否则配置里的actual-data-nodes、sharding-algorithm-name这些字段完全看不懂在表达什么。第一个概念是逻辑表。你在代码里写的t_order在业务 SQL 里查的t_order在 ShardingSphere 的配置里定义的那张表就是逻辑表。物理表叫t_order_0、t_order_1但应用层永远只感知t_order这张逻辑表的存在。第二个概念是数据节点。actual-data-nodes就是用来声明逻辑表映射到哪些物理表。5.2.1 的表达式语法是ds_$-{0..1}.t_order_$-{0..3}这个$-{}是 5.x 引入的新写法4.x 里的写法是ds_${0..1}。老教程在这个地方会出现剧烈的不一致性你要记住 5.2.1 的方言是$-{...}。第三个概念是分片算法。5.2.1 把内置算法全部 SPI 化你配置的不是一段代码而是通过type指定算法类型再通过props传参。最常用的INLINE算法接收一个algorithm-expression参数写法是一个 Groovy 表达式比如t_order_$-{order_id % 2}。这里的order_id就是分片键表达式的计算结果就是要路由到的物理表后缀。这三个概念之间的关系可以这样串起来逻辑表是应用视角数据节点是物理拓扑分片算法是路由策略。你配置的目标就是告诉 ShardingSphere——对逻辑表执行 SQL 时根据哪个字段的什么规则计算出目标物理表。2.4 什么场景下不应该用 ShardingSphere写这一节不是为了凑字数而是因为很多人整合 ShardingSphere 的动机本身就是错的。如果你现在的单表数据量在千万级以下索引设计合理读写性能还能接受那引入分库分表的收益基本为零反而要承担 SQL 改写限制和运维复杂度。另一个常见误判是项目里已经出现慢查询于是想通过分库分表来「优化查询」。ShardingSphere 解决的是数据量增长带来的写入瓶颈和单表扫描过大问题它不是慢查询优化器。一个索引可以解决的慢查询被错误地升级成分库分表方案这是我看过最典型的选型翻车。最后一种情况是分片键还没有确定。分片键是分库分表方案里最难变更的设计决策一旦线上数据落库再想换分片键基本等于重做。如果你业务上找不到一个「所有核心查询都会携带」的字段作为分片键那说明分库分表时机未到先把分片键定下来再说。3. Spring Boot 整合 5.2.1最小可运行配置与分片验证3.1 引入依赖一个 starter 搞定自动装配整合的第一步是引入官方提供的 Spring Boot starter 依赖。这里有一个容易发生的人云亦云问题网上很多教程会让你额外引入shardingsphere-core或者shardingsphere-api之类的模块但从 5.x 开始shardingsphere-jdbc-spring-boot-starter已经是面向 Spring Boot 自动装配的完整入口引入这一个就足够。dependency groupIdorg.apache.shardingsphere/groupId artifactIdshardingsphere-jdbc-spring-boot-starter/artifactId version5.2.1/version /dependency这段依赖的版本号直接对应标题里的 5.2.1不需要额外指定版本属性。Spring Boot 版本建议配套 2.6 或 2.7 系列因为 5.2.1 发布对应的是 Spring Boot 2.x 生态如果你强行把它放进 Spring Boot 3 项目里自动装配类的反射调用会出现兼容性问题后面避坑章节会展开说。还有一个细节需要注意如果你项目里已经配置了 Druid 连接池ShardingSphere 不会接管它除非你在spring.shardingsphere.datasource的type字段里明确指定com.alibaba.druid.pool.DruidDataSource。默认推荐使用 HikariCP这也是 Spring Boot 的默认连接池配置最省事。3.2 基础表结构与数据源准备在配置 ShardingSphere 之前先把物理表建好。这里用到的是最经典的两库两表场景分库键和分表键都是order_id每个库各建两张物理表四个数据节点。表结构用一份标准订单表就好不需要额外字段。CREATE TABLE t_order_0 ( id bigint NOT NULL, order_id bigint NOT NULL, user_id bigint DEFAULT NULL, amount decimal(10,2) DEFAULT NULL, status varchar(16) DEFAULT NULL, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这张表的id字段是主键在 ShardingSphere 里它由分布式主键生成器负责填充业务代码不需要给id赋值。order_id是分片键取值由业务方自己生成保证唯一即可。实际落地时t_order_1和另外两个库里的表结构保持一致注意四个物理表都要建至少一遍最好通过统一脚本批量执行。3.3 分片配置application.yml 的完整写法物理表就绪后进入核心配置环节。直接给出一份最小可运行配置这里用的是 5.2.1 的标准配置结构。spring: shardingsphere: datasource: names: ds0, ds1 ds0: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://192.168.1.10:3306/order_db_0 username: order_app password: order_app_pwd ds1: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://192.168.1.11:3306/order_db_1 username: order_app password: order_app_pwd rules: sharding: tables: t_order: actual-data-nodes: ds$-{0..1}.t_order_$-{0..1} table-strategy: standard: sharding-column: order_id sharding-algorithm-name: t_order_inline sharding-algorithms: t_order_inline: type: INLINE props: algorithm-expression: t_order_$-{order_id % 2} key-generators: snowflake: type: SNOWFLAKE default-key-generate-strategy: column: id key-generator-name: snowflake props: sql-show: true这段配置的核心逻辑逐段拆解。spring.shardingsphere.datasource下声明了两个真实数据源ds0和ds1每个数据源的连接池类型、驱动类、JDBC 地址都要完整声明。names字段用逗号分隔列出所有数据源名称这里很容易漏掉——如果names里没有写ds1即使下面配置了ds1节点也不会被加载。rules.sharding.tables.t_order是逻辑表t_order的映射。actual-data-nodes的表达式ds$-{0..1}.t_order_$-{0..1}展开后就是四个节点ds0.t_order_0、ds0.t_order_1、ds1.t_order_0、ds1.t_order_1。table-strategy声明了分表策略分片键是order_id使用的算法叫t_order_inline。这里注意我没有配置database-strategy这意味着分库也使用默认策略实际生产中经常把分库键和分表键设置为同一个字段先按字段取模分到库再按同一个字段取模分到表。sharding-algorithms是算法注册表t_order_inline这个名字是自己起的在逻辑表配置里通过sharding-algorithm-name引用。INLINE算法接收algorithm-expression表达式t_order_$-{order_id % 2}的含义是无论查哪条 SQL只要where条件里带有order_id就用它对 2 取模算出物理表后缀。至于为什么分库没有单独配置策略——当没有显式配置database-strategy时ShardingSphere 不会自动做分库所有数据仍默认落在ds0这一点踩坑率极高。key-generators注册了一个名为snowflake的雪花算法生成器然后在default-key-generate-strategy里声明主键字段是id默认使用雪花算法填充。这样你在执行 INSERT 时不需要给id传值ShardingSphere 会帮你补上。3.4 用 JdbcTemplate 验证分片路由是否真正生效配置完成后用一段最简单的 JdbcTemplate 代码验证效果。这里不引入 MyBatis 之类的 ORM避免把问题复杂化纯 JDBC 操作跑通后再换 ORM 就不容易出幺蛾子。SpringBootTest class OrderShardingVerifyTest { Autowired private JdbcTemplate jdbcTemplate; Test void insertAndQuery() { jdbcTemplate.update( INSERT INTO t_order(order_id, user_id, amount, status) VALUES (?, ?, ?, ?), 1001L, 101L, new BigDecimal(99.90), NEW ); ListMapString, Object result jdbcTemplate.queryForList( SELECT * FROM t_order WHERE order_id ?, 1001L ); System.out.println(查询结果数: result.size()); } }这段测试代码里有两点值得说明。INSERT 语句里故意没有id字段这正好验证了配置里的default-key-generate-strategy是否生效——如果雪花算法没有正确配置这条 SQL 会因主键不能为空而失败。查询语句带了order_id 1001ShardingSphere 会拿 1001 对 2 取模路由到物理表t_order_1。执行之后最重要的观察点在控制台日志。由于配置里打开了sql-show: true日志里会打印逻辑 SQL 和实际下发的 SQL。你会在日志里看到类似这样的两行输出一行是原始逻辑 SQLSELECT * FROM t_order WHERE order_id 1001另一行是真实执行的SELECT * FROM t_order_1 WHERE order_id 1001。实测这里还有一个隐藏细节——路由结果到底落在ds0还是ds1取决于你是否有分库策略。由于当前配置没有分库即使有ds0和ds1两个数据源所有流量仍然只走ds0。如果你期望看到跨库分布的效果需要在rules.sharding下补充database-strategy。4. 读写分离与强制路由5.2.1 里的逻辑数据源玩法4.1 读写分离配置readwrite-splitting 规则拆解分片只是 ShardingSphere 规则之一读写分离在 5.2.1 中是独立的readwrite-splitting规则。一个典型的配置场景是你有一主两从三个 MySQL 实例希望写入走主库、查询分摊到两个从库。spring: shardingsphere: datasource: names: primary250, replica251, replica252 primary250: type: com.zaxxer.hikari.HikariDataSource jdbc-url: jdbc:mysql://192.168.1.250:3306/order_db ... replica251: type: com.zaxxer.hikari.HikariDataSource jdbc-url: jdbc:mysql://192.168.1.251:3306/order_db ... replica252: type: com.zaxxer.hikari.HikariDataSource jdbc-url: jdbc:mysql://192.168.1.252:3306/order_db ... rules: readwrite-splitting: >public OrderDO getOrderImmediate(Long orderId) { try (HintManager hintManager HintManager.getInstance()) { hintManager.setWriteRouteOnly(); return orderMapper.selectById(orderId); } }HintManager.getInstance()拿到一个线程级的路由上下文对象setWriteRouteOnly()告诉 ShardingSphere 当前线程后续 SQL 全部路由到主库。这里有个血泪经验HintManager必须要在当前线程内关闭所以推荐用 try-with-resources 写法。一旦忘了 close线程复用时路由标记会残留后续请求可能一直走主库而且这种问题极难排查因为没有任何日志报错。还需要说明的是Transactional强制主库和HintManager.setWriteRouteOnly()的生效范围不同前者是整个事务生效后者是当前线程到close()之前生效。两者叠加时事务语义优先。理解这一点可以帮助你设计服务边界把需要强一致读的操作集中到一个单独方法里用 HintManager 强制主库把允许延迟读的操作放在无事务的普通方法里走从库。4.3 读写分离与分片规则共存逻辑数据源的嵌套引用实际大型项目里很少单独使用读写分离或分片通常两个规则叠加使用。5.2.1 对这种场景的支持方式是逻辑数据源可以互相引用这也是标题里「整合」二字的真正含义所在。rules: readwrite-splitting: >SELECT t_order_0 AS tbl, COUNT(*) AS cnt FROM t_order_0 UNION ALL SELECT t_order_1, COUNT(*) FROM t_order_1 UNION ALL SELECT t_order_2, COUNT(*) FROM t_order_2 UNION ALL SELECT t_order_3, COUNT(*) FROM t_order_3;如果分片键取模均匀四张表的行数应该大致接近。如果某张表始终为零说明分片策略或数据节点表达式存在问题需要回到actual-data-nodes的配置上排查。我在做分库分表改造时吃过最大的亏就是上线前只验证了「能查通」就放行结果流量真正大了之后才发现某个核心查询没有带分片键导致全表扫描动辄几十秒。这个教训让我养成了上线前一定对 SQL 做逐条路由验证的习惯。希望这份整合思路和踩坑记录能帮到你让你在 5.2.1 的整合路上少走这些弯路。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询