
简介面向Spring Boot开发者这份资源针对ShardingSphere 5.2.1分表不分库场景解决单表数据量增长带来的查询性能瓶颈。项目基于Spring Boot构建代码量精炼共16个文件其中9个Java类负责分片规则构建、数据源装配与分库分表策略实现2个YAML和2个XML主要承担分片策略、数据源及MyBatis映射配置另有SQL建表脚本、分片算法定义等辅助文件包体仅15KB结构清晰简洁。内容覆盖范围分片、哈希分片与自定义分片算法的实际写法同时涉及数据迁移、分页查询和分布式事务等落地细节可帮助开发者快速掌握在Spring Boot中整合ShardingSphere 5.2.1的完整路径。已有1953人学习特别适合已具备Spring Boot基础、准备升级数据层处理能力的开发者。通过这套工程能省去从零搭建和排查配置的时间直接对照源码验证自己的分表方案为生产环境改造提供可复用的参考。1. springboot 整合 ShardingSphere 5.2.1这组关键词背后是大多数分库分表改造的起点springboot 整合 ShardingSphere 5.2.1这组关键词在分库分表的搜索场景里几乎天天出现。点进来的人大多不是想学概念而是手上有一个单体应用正在被数据量逼到墙角单表几千万行、写入变慢、查询超时又不想一步跨到分库分表中间件集群于是想在 springboot 项目里用最少代价把 ShardingSphere-JDBC 装进来。5.2.1 这个版本号则更有意思——很多博客把它标成“最新版”但它更多是 ShardingSphere 5.x 早期配置体系趋于稳定的一个节点教程存量大、坑也基本被踩熟了。这篇不打算复刻某个教程而是以 5.2.1 为基线讲清楚从依赖引入、YAML 配置、分片算法选型到读写分离、分布式事务边界的一整条落地路径顺带把启动失败、分片没生效、数据错乱这几类高频故障的排查顺序整理出来。适合两类人正在准备把单表拆成多表的 springboot 开发者以及已经整合完但被配置细节绕晕的人。你在集成过程中踩过的坑大概率都在后面直接照着核对就行。2. 从依赖到启动springboot 里的 ShardingSphere-JDBC 最小整合形态2.1 ShardingSphere-JDBC 在 springboot 项目里扮演什么角色先明确一个容易混淆的点ShardingSphere 是一套体系包含 JDBC、Proxy 两种形态。springboot 整合通常指的是 ShardingSphere-JDBC它不是一个独立服务而是一个 jar 包嵌入在应用进程内部。应用拿到的 DataSource 实际上是被 ShardingSphere 包装过的逻辑数据源SQL 经过解析、改写、路由之后再分发到背后的真实数据库连接上执行。这个模式的好处是部署零额外成本业务代码里继续用 JdbcTemplate、MyBatis-Plus 都没问题坏处是应用自身承担了分片路由的计算开销连接数也会被多分片放大。对比项ShardingSphere-JDBCShardingSphere-Proxy部署方式嵌入应用进程独立服务应用通过 JDBC 连接改造成本只改数据源配置和依赖应用侧连地址SQL 兼容性由 Proxy 承担适合场景中小团队、单应用、不想引入额外节点多应用共享分片规则、异构语言接入springboot 项目里整合 JDBC 形态核心是让 starter 接管数据源装配。5.2.1 的 starter 会自动检测配置并生成逻辑 DataSource这一步如果和 spring.datasource 自动装配冲突就会出现启动报错——这个问题后面避坑章节专门讲。2.2 最小可跑配置pom 依赖与 application.yml 一次到位先搭一个只分表不分库的最小工程验证整合链路通不通。Maven 依赖只需要一个 starterdependency groupIdorg.apache.shardingsphere/groupId artifactIdshardingsphere-jdbc-core-spring-boot-starter/artifactId version5.2.1/version /dependency然后在 application.yml 里给出最简配置。这里我用一个物理库、4 张物理表来模拟订单表拆分spring: shardingsphere: datasources: ds_order: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://127.0.0.1:3306/order_db?useSSLfalseserverTimezoneAsia/Shanghai username: root password: root rules: sharding: tables: t_order: actual-data-nodes: ds_order.t_order_${0..3} table-strategy: standard: sharding-column: order_id sharding-algorithm-name: t_order_hash sharding-algorithms: t_order_hash: type: HASH_MOD props: sharding-count: 4 props: sql-show: false这段配置的逻辑拆开看datasources 下定义的是真实物理数据源这里只有一个 ds_orderrules.sharding.tables.t_order 定义逻辑表 t_order 到物理表的映射关系actual-data-nodes 里的${0..3}是 ShardingSphere 的内联表达式启动时会展开成 t_order_0、t_order_1、t_order_2、t_order_3table-strategy 指定分片键是 order_id分片算法名叫 t_order_hash算法定义在 sharding-algorithms 里类型 HASH_MOD对分片键做哈希后对 4 取模决定落在哪张物理表。注意几个 5.2.1 的细节配置前缀是 spring.shardingsphere不是老教程里的 shardingsphereHASH_MOD 的参数名是 sharding-count不是早期 4.x 的 count如果只分表不分库actual-data-nodes 里数据源名可以直接写固定名字。启动时看到控制台打印出逻辑数据源创建成功并且没有任何 DataSource 相关的报错就说明整合链路已经通了。此时先不急着写业务代码下一步把标准分库分表规则补上才是真正能落地的形态。3. 标准分片规则落库从单表到分库分表的完整 YAML3.1 分片键与分片算法选型inline、hash_mod、interval 的适用边界分片算法选型是整个分片规则里最不能照抄的部分。5.2.1 的内置算法类型不少但实际项目里高频使用的就三类INLINE、HASH_MOD、INTERVAL。INLINE 基于 Groovy 表达式做计算配置灵活适合分片键有明显业务含义、能用表达式直接算的场景比如按 user_id 直接取模type: INLINE props: algorithm-expression: ds_${user_id % 2} allow-range-query-with-inline-sharding: false这里有个 5.2.1 经典坑INLINE 默认禁止范围查询。如果业务 SQL 里出现 WHERE user_id BETWEEN 或者 WHERE user_id 100而 user_id 是分片键ShardingSphere 会直接抛异常提示你开启 allow-range-query-with-inline-sharding。开了之后范围查询会广播到所有分片执行结果是正确的但性能代价要自己评估。HASH_MOD 是取模算法的哈希版本它对分片键先做哈希再取模避免字符串分片键直接取模带来的分布不均。INTERVAL 则按时间字段做区间分片适合流水表按月份拆分的场景。三类算法的选择逻辑不复杂分片键是整型、分布均匀直接用 HASH_MOD分片键有业务前缀、需要表达式参与用 INLINE分片键是时间字段用 INTERVAL 加时间区间配置。不要一上来就堆复杂算法能用一个取模解决的就不要引入额外依赖。3.2 标准分库分表配置一份能直接改的分片规则分库分表的标准形态是两级路由先按某个键分到库再在库内分到表。下面这份配置是某零售订单系统的常见写法两个库、每库 4 张订单表分片键都用 order_idspring: shardingsphere: datasources: ds_0: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://127.0.0.1:3306/order_db_0?useSSLfalseserverTimezoneAsia/Shanghai username: root password: root ds_1: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://127.0.0.1:3306/order_db_1?useSSLfalseserverTimezoneAsia/Shanghai username: root password: root rules: sharding: tables: t_order: actual-data-nodes: ds_${0..1}.t_order_${0..3} database-strategy: standard: sharding-column: order_id sharding-algorithm-name: order_db_hash table-strategy: standard: sharding-column: order_id sharding-algorithm-name: order_table_hash key-generate-strategy: column: order_id key-generator-name: order_snowflake t_order_item: actual-data-nodes: ds_${0..1}.t_order_item_${0..3} database-strategy: standard: sharding-column: order_id sharding-algorithm-name: order_db_hash table-strategy: standard: sharding-column: order_id sharding-algorithm-name: order_table_hash binding-tables: - t_order, t_order_item sharding-algorithms: order_db_hash: type: HASH_MOD props: sharding-count: 2 order_table_hash: type: HASH_MOD props: sharding-count: 4 key-generators: order_snowflake: type: SNOWFLAKE props: sql-show: false这份配置里值得逐项说明的有几处。actual-data-nodes 的表达式是ds_${0..1}.t_order_${0..3}展开后是 ds_0.t_order_0、ds_0.t_order_1、ds_0.t_order_2、ds_0.t_order_3、ds_1.t_order_0 一直到 ds_1.t_order_3总共 8 张物理表。分库算法 order_db_hash 对 2 取模分表算法 order_table_hash 对 4 取模最终一个 order_id 会确定唯一的物理位置。key-generate-strategy 用 SNOWFLAKE 生成分布式主键避免多分片下自增主键冲突。绑定表 binding-tables 是 join 查询性能的关键配置。t_order 和 t_order_item 使用相同的分片键、相同的分片算法绑定之后两表 join 只会在对应分片内执行不会产生笛卡尔积跨分片关联。如果不配绑定表order 和 order_item 的 join 会广播到所有分片组合SQL 数量成倍增长。这个配置项很容易被忽略等数据量上来、join 变慢的时候才会意识到。物理表结构需要提前在 8 张表中手工建好ShardingSphere 不会帮你建表。常见做法是在整合前写一份建表 SQL 脚本用循环在四个库里各执行一遍。数据写入验证建议直接写一个集成测试SpringBootTest class ShardingRouteTest { Resource private JdbcTemplate jdbcTemplate; Test void insertAndRoute() { jdbcTemplate.update(INSERT INTO t_order(order_id, user_id, amount) VALUES (?, ?, ?), 1001L, 1L, new BigDecimal(99.00)); jdbcTemplate.update(INSERT INTO t_order(order_id, user_id, amount) VALUES (?, ?, ?), 2002L, 2L, new BigDecimal(199.00)); ListMapString, Object rows jdbcTemplate.queryForList( SELECT * FROM t_order WHERE order_id IN (1001, 2002)); Assertions.assertEquals(2, rows.size()); } }这段测试的逻辑插入两条订单一条 order_id 是 1001一条是 2002然后按 order_id 查询。由于分片键出现在 WHERE 条件里ShardingSphere 能精确路由到对应物理表不会广播。如果这里查不到数据优先检查 physical 表是否建全、分片算法取模结果和表序号是否对得上。这里要特别提醒查询条件里没带分片键时ShardingSphere 不会报错而是默默广播到所有分片然后把结果合并返回。很多人在这个点上产生误解以为是查询走错了表其实是广播执行了日志里能看到真实 SQL 有多条。看日志的方法在最后一章讲。4. 读写分离与跨库事务5.2.1 里“整合”这个词的重量4.1 读写分离配置从零到一的主从路由分库分表解决了单表数据量问题但读多写少的业务很快会撞到另一个瓶颈单库读压力过大。ShardingSphere-JDBC 的读写分离在 5.2.1 里的配置路径和分片规则是并列的都在 rules 节点下。核心是先定义读写分离数据源再把它作为分片规则里的数据源引用。rules: readwrite-splitting: >dependency groupIdorg.apache.shardingsphere/groupId artifactIdshardingsphere-transaction-xa-atomikos/artifactId version5.2.1/version /dependencyprops: sql-show: false transaction-type: XAXA 的原理是两阶段提交ShardingSphere 会把跨库操作纳入同一个全局事务提交时协调所有分片。代价是事务时间变长、连接被长时间占用并发一高就容易出现连接耗尽。另一个方向是接 Seata 做最终一致需要额外部署 Seata 服务端依赖也要换成 shardingsphere-transaction-base-seata-at这个方案落地成本明显更高但长事务场景下更友好。我实际项目的选型经验是90% 的业务能通过改造 SQL 避免跨库事务比如把订单主表和明细表按同一个 order_id 分到同一分片绑定表天然保证 local 事务内完成。真正需要 XA 或 Seata 的场景很少不要在整合初期就把分布式事务当成必选项先把本地事务的边界理清楚。5. 5.2.1 整合避坑启动失败、分片没生效与数据错乱的排查5.1 启动阶段的三类故障现象一项目启动直接报No qualifying bean of type DataSource或DataSource相关自动装配冲突。原因是 pom 里同时保留了 spring-boot-starter-jdbc 的自动数据源配置和 ShardingSphere 的逻辑数据源两个 DataSource Bean 互相竞争。解决方法是排除 DataSource 自动装配让 ShardingSphere 完全接管SpringBootApplication(exclude {DataSourceAutoConfiguration.class}) public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } }现象二启动时控制台报错提示某个 sharding-algorithms 类型找不到。原因通常是两个不同版本的 ShardingSphere 依赖同时出现在依赖树里比如某个中间件传递依赖引入 5.0.0而你自己声明的 5.2.1 被它覆盖。解决方法是先用 mvn dependency:tree 查看依赖冲突然后统一用 dependencyManagement 锁定所有 shardingsphere 相关依赖的版本。现象三启动成功但一执行 SQL 就报表不存在提示的是逻辑表名或物理表名找不到。原因分两种一种是 actual-data-nodes 里的物理表没建另一种是配置里的${0..3}被当成普通字符串没有展开。第二种情况多半发生在 YAML 的键值没有用双引号包裹的场景比如${0..3}在部分 YAML 解析器里会被单引号或特殊字符干扰。解决方式是核对物理表是否存在以及把表达式单独抽出来测试展开结果。5.2 运行时的数据正确性与事务边界问题现象四配置了 INLINE 分片算法执行范围查询时直接抛异常提示开启 allow-range-query-with-inline-sharding。这个现象是 5.2.1 的预期行为不是 bug。INLINE 算法为了防误用默认关闭范围查询。按需在算法 props 里加上 allow-range-query-with-inline-sharding: true开启后要清楚这意味着所有分片都会被扫描。更合理的选择是范围查询走专门设计的分片键或在查询条件里同时带上分片键的等值条件。现象五方法标注了 Transactional但跨库更新后出现数据只写了一半。这个坑的根源有两个层次第一层是 spring 事务的经典自调用问题同类内部方法调用 this.xxx() 时注解不经过代理对象事务根本不会开启。第二层是即使事务开了LOCAL 模式只保证单库事务跨库的多个分片各自独立提交任何一个失败都不会让其他分片回滚。解决方式跨库场景先评估能否通过分片键设计收敛到单库事务必须跨库的话按上一章说的切 XA 或 Seata。顺带提醒hint 分片、上下文信息等 ThreadLocal 数据在线程池场景下不会自动透传异步任务里使用分片时要显式传递分片上下文否则路由会走默认广播。现象六配了 sql-show: true但控制台看不到任何分片 SQL 日志。原因在于 5.2.1 的 SQL 日志走独立 logger名字是 shardingsphere-sqllogback 默认不会把它打印到控制台。解决方式是在 logback 配置里显式加一个 logger级别设 INFOlogger nameshardingsphere-sql levelINFO/加了之后重启每条逻辑 SQL 背后对应的真实物理表 SQL 就会打出来这是排查分片是否生效最重要的手段。6. 一个验证技巧开启 sql-show 看清每条 SQL 真正的落库位置最后分享一个我自己每次动分片规则都会先做的事把 sql-show 打开跑几条代表性 SQL对照物理表核对落点。这个动作能避免绝大多数“配置对了但数据不知道写哪里”的焦虑。配置开启方式很简单就是把 props 里的 sql-show 设为 true同时确认 logback 里 shardingsphere-sql 的 logger 是 INFO 级别。启动后插入一条订单日志里会输出真实 SQLActual SQL: ds_0 ::: INSERT INTO t_order_2 (order_id, user_id, amount) VALUES (?, ?, ?)这条日志说明 order_id 经过 HASH_MOD 路由后落在 ds_0 的 t_order_2。核对一下你的分片键值是否真的对应到这张表就能验证算法配置是否符合预期。改动分片算法后我还习惯用 Hint 方式强制指定分片值来绕过算法干扰专门验证某一条数据是否被正确路由HintManager hintManager HintManager.getInstance(); hintManager.addDatabaseShardingValue(t_order, 1L); hintManager.addTableShardingValue(t_order, 1L); try { // 强制将 order_id 路由到 ds_1 的 t_order_1 jdbcTemplate.queryForList(SELECT * FROM t_order WHERE order_id ?, 100L); } finally { hintManager.close(); }这段代码里 HintManager 给 t_order 手工指定了库分片值和表分片值查询会无视正常的分片算法直接走指定分片。它在验证分片规则、排查路由异常时特别好用但要注意用完必须 close否则 ThreadLocal 里的值会污染后续 SQL 的路由结果。我现在的习惯是每次改完分片算法先开 sql-show 跑三条 SQL——一条精确等值查询、一条不带分片键的查询、一条 join 查询逐条核对真实落库位置和日志输出确认无误再关掉。这个习惯帮我避免过好几次上线后才发现规则写反的翻车希望帮到你。本文还有配套的精品资源点击获取