ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Spring Boot 2.7 整合 ShardingSphere 5.2.1 分库分表与读写分离实战指南

2026/9/20 12:38:06 拓冰建站 浏览量
Spring Boot 2.7 整合 ShardingSphere 5.2.1 分库分表与读写分离实战指南 简介这是一份SpringBoot整合ShardingSphere 5.2.1的实战源码包面向需要处理海量数据的Java后端开发者。资源聚焦“分表不分库”策略通过把单表水平拆分为多张小表缓解千万级数据量下的查询瓶颈。压缩包共有16个文件其中9个Java源码为核心涵盖数据源配置、分片规则构建与自定义分片算法2个yml和2个xml文件分别管理数据源属性与MyBatis映射还包含SQL脚本、算法定义及工程忽略配置。整体包体仅15KB短小精悍适合直接导入IDE运行调试。作者从依赖引入到YAML配置、ShardingRuleBuilder注册等环节给出了可复用模板同时附分片策略与事务处理要点能帮助读者降低版本兼容问题排查成本。目前已有1949人学习是入门新版ShardingSphere分表开发的实用参考。 说实话年初在整理订单中心的时候单表数据量眼看着就要破3000万慢查询开始一个接一个冒出来DBA直接找上门问要不要做归档。当时我就琢磨着与其反复归档折腾数据生命周期不如一步到位把分库分表做扎实。市面上能选的中间件就那么几个ShardingSphere 5.2.1 是当时最新的稳定版API 和配置体系和 4.x 相比完全是两个时代的东西网上的教程又大多停留在老版本照着配根本起不来。这篇文章就把我当时整合 Spring Boot 与 ShardingSphere 5.2.1 的完整过程、配置细节和踩过的坑一次说清楚。适合直接抄作业的朋友项目正处在大数据量增长期做主从读写分离或分库分表且用的是 Spring Boot 2.x 体系的 Java 后端项目。如果你还在用 Spring Boot 1.x 或者老版 ShardingSphere 4.x建议先升级再来看本文硬套配置会死得很难看。1. 版本选型Spring Boot 2.7.x 与 5.2.1 的兼容性边界1.1 为什么是 ShardingSphere 5.2.1我在决定用哪个版本之前把官方文档的 Release Notes 从头到尾翻了一遍。5.2.1 属于 5.x 中期的一个稳定版本相比 5.1.x 修了不少分片路由和分布式事务的 bug同时它的配置模型和 SPI 扩展机制已经趋于稳定。最关键的是5.2.1 之后官方把 Spring Boot Starter 的使用方式做了调整后面的 5.3.x、5.4.x 又引入了不同的加载方式所以很多老项目的锁版本就停在 5.2.1 这个节点上。另外一点5.x 的分布式事务、数据加密、读写分离都是通过同一套 YAML 规则驱动和 4.x 那种一堆注解和 Java Config 的方案完全不同。如果你以前用过 4.x切换到 5.2.1 时需要把思维从写代码配置规则转成写声明式规则这是最大的观念变化。1.2 Spring Boot 版本别选错了ShardingSphere 5.2.1 官方适配的是 Spring Boot 2.x如果直接用 Spring Boot 3.0 以上会因为 Jakarta EE 命名空间的问题直接启动失败。我当时用的是 Spring Boot 2.7.18这也是 2.x 系列的收尾版本稳定性和各组件兼容性都验证过建议照抄。如果你的团队已经在用 Spring Boot 2.6.x问题也不大但注意 2.6 之后 Spring Boot 对循环依赖默认关闭ShardingSphere 5.2.1 在某些场景下会触发循环依赖告警保险起见还是上 2.7.x。JDK 这边我用的是 1.8理论上 11 也能跑但生产环境用 8 最稳。2. 依赖引入从 starter 到 jdbc-core 的变化2.1 只用这一个依赖就够很多老教程会让你引一堆sharding-jdbc-spring-boot-starter这个包在 4.x 之后就彻底废弃了。5.x 版本统一收口到了shardingsphere-jdbc-core-spring-boot-starter这一个依赖里面它同时包含了分库分表、读写分离、数据加密、影子库所有这些能力不用再额外加模块。dependency groupIdorg.apache.shardingsphere/groupId artifactIdshardingsphere-jdbc-core-spring-boot-starter/artifactId version5.2.1/version /dependency引入这一个依赖之后它会自动帮你把 HikariCP、MyBatis 等常用组件桥接好。我自己项目里用的连接池是 HikariCP配合这个 starter 不需要额外写配置类数据源由 ShardingSphere 统一接管和路由。2.2 日志与连接池组件别忽略我在引入依赖后顺手加了两个东西建议也照做。第一个是p6spy用来打印真实执行 SQL方便排查路由是否生效第二个是commons-pool2某些连接池模式下会需要。不过如果你只是用 HikariCPcommons-pool2可加可不加我当时加上了纯粹是图省心。还有一个特别容易漏的点如果你的项目里同时存在druid-spring-boot-starter的旧依赖一定要排除掉否则 ShardingSphere 的自动配置和 Druid 的数据源自动配置会打架表现就是日志里疯狂报错数据源初始化一会儿成功一会儿失败。dependency groupIdcom.github.gavlyukovskiy/groupId artifactIdp6spy-spring-boot-starter/artifactId version1.9.0/version /dependency3. 核心配置拆解一篇文章吃透 YAML 分片规则3.1 基础数据源定义ShardingSphere 5.2.1 的 Spring Boot 配置统一挂在spring.shardingsphere前缀下面。数据源定义语义上和 4.x 类似但 5.x 对每个数据源的类型和连接参数要求在配置里显式声明不再自动猜测。spring: shardingsphere: mode: type: Standalone repository: type: File props: path: /data/shardingsphere datasource: names: ds0, ds1 ds0: 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/ShanghaicharacterEncodingutf8 username: root password: 123456 ds1: 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/ShanghaicharacterEncodingutf8 username: root password: 123456mode.type: Standalone是 5.x 新引入的持久化模式表示分片规则只在当前进程内存中生效不依赖注册中心。如果你未来要上集群部署或者动态热更新规则可以升级到Cluster模式配合 ZooKeeper 或 etcd但对大多数中小企业项目Standalone 足够用了。3.2 数据分片规则与主键生成配置完数据源就到了整个 5.2.1 整合里最核心也最容易出错的环节分片规则。我用的是订单表t_order按订单号order_id做哈希分片两个库各分两张表也就是总共 4 张物理表。rules: sharding: tables: t_order: actual-data-nodes: ds$-{0..1}.t_order_$-{0..1} database-strategy: standard: sharding-column: order_id sharding-algorithm-name: db_hash table-strategy: standard: sharding-column: order_id sharding-algorithm-name: table_hash key-generate-strategy: column: order_id key-generator-name: snkf binding-tables: - t_order, t_order_item broadcast-tables: - t_config sharding-algorithms: db_hash: type: HASH_MOD props: sharding-count: 2 table_hash: type: HASH_MOD props: sharding-count: 2 key-generators: snkf: type: SNOWFLAKE拆开解释几个关键点。actual-data-nodes里的表达式ds$-{0..1}.t_order_$-{0..1}表示数据节点分布在 ds0 和 ds1 两个库下每个库有 t_order_0 和 t_order_1 两张表。database-strategy和table-strategy分别控制库路由和表路由。这里两个策略都用标准分片分片键都选order_id所以插入一条订单时会先根据订单号算出目标库再算出目标表。key-generate-strategy是主键生成策略我用雪花算法生成分布式主键。注意column: order_id这个配置有个隐含作用如果业务代码没有显式给主键赋值ShardingSphere 会在路由前自动生成主键并回填到插入 SQL 里这样就能保证分片键必然存在不会出现因为主键为空导致路由失败的问题。3.3 绑定表与广播表不可跳过上面配置里的binding-tables和broadcast-tables容易被新手直接忽略但生产环境真的是救命配置。绑定表说的是业务上存在关联关系的表集合比如订单表和订单明细表都按order_id分片那么它们就应该声明为绑定表。有了这个声明ShardingSphere 在做多表关联查询时会把它们路由到同一个数据节点上避免出现跨库 join性能提升非常明显。如果不配置关联查询的 SQL 会被拆成多次执行结果合并逻辑复杂不说还可能直接查不到数据。广播表则是那些每个分片库都必须完整存在的小表比如配置表、字典表。声明之后写入操作会自动广播到所有分片库查询则只路由到任意一个库读取避免出现这个库里查得到那个库里查不到的诡异问题。我当时是把t_config这种经常 join 的字典表设置成了广播表实测效果很好。4. 分片算法内建算法类型与自定义扩展4.1 内建算法到底怎么选5.2.1 里的分片算法通过type关键字声明官方内置了几种常见实现。我整理了一张表方便你快速对照选择算法类型适用场景分片键要求特点HASH_MOD通过哈希取模分片数值或字符串分布相对均匀适合随机分片MOD直接取模分片数值对连续值友好但数据倾斜风险高INLINE行表达式分片数值/字符串支持自定义表达式最灵活适合按时间或区域分片INTERVAL按时间区间分片时间类型适合按月/按周分表CLASS_BASED指定自定义算法类自定义通过反射加载你写的类我之前接手的项目里有一张日志表数据量线性增长按月分表用INTERVAL就非常合适。而订单表我坚持用HASH_MOD因为订单号的写入是随机的哈希取模后的数据分布最均匀不会出现某个库的表特别大、其他库特别小的情况。4.2 自定义分片算法的正确姿势内置算法不够用的时候可以考虑自定义算法。比如订单表想按user_id路由但又想保证某个用户的订单全部落在同一个库方便后续做单用户维度的查询优化。这种情况下我建议实现StandardShardingAlgorithm接口。public class UserHashShardingAlgorithm implements StandardShardingAlgorithmLong { Override public String doSharding(CollectionString availableTargetNames, PreciseShardingValueLong shardingValue) { long userId shardingValue.getValue(); long slot userId % 2; for (String targetName : availableTargetNames) { if (targetName.endsWith(String.valueOf(slot))) { return targetName; } } throw new UnsupportedOperationException(无法路由到目标节点userId userId); } Override public CollectionString doSharding(CollectionString availableTargetNames, RangeShardingValueLong shardingValue) { return availableTargetNames; } Override public String getType() { return USER_HASH; } Override public Properties getProps() { return null; } Override public void init(Properties props) { } }写完之后需要在META-INF/services/org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm文件里注册类的全限定名这是 5.x 的 SPI 机制。然后在 YAML 里把type写成USER_HASH就能直接用了。sharding-algorithms: user_db: type: USER_HASH这里有个很容易踩的坑自定义类必须实现init方法哪怕什么都不做也要空实现否则启动时会 SPI 加载失败。我一开始偷懒没写结果启动日志报错报得懵了半天。5. 读写分离和数据加密5.2.1 的进阶玩法5.1 读写分离配置与主从延迟问题如果你的项目不只是数据量大还有明显的读写比例不均衡问题那读写分离就是分库分表之前可以先做的优化。ShardingSphere 5.2.1 对读写分离的支持是内置的配置起来非常简洁。rules: readwrite-splitting: >rules: encrypt: tables: t_user: columns: phone: cipher-column: phone_cipher plain-column: phone_plain encryptor-name: aes_encryptor encryptors: aes_encryptor: type: AES props: aes-key-value: 4c6f7665c2a064617465这里含义是业务代码里仍然按phone列来写 SQL但实际插入的数据会加密存储到phone_cipher列查询时会从密文列解密后返回。plain-column是明文列通常只用于数据迁移的过渡期迁移完毕可以去掉。配置加密规则后所有走t_user表的读写都会经过加密拦截器。有一点容易踩坑如果表里既有存量明文数据又要启加密规则必须先做好数据迁移脚本否则老数据会因为没有对应的密文而无法被查询出来。6. 实测踩坑记录分页、事务与分布式主键6.1 分页查询的深分页与精度问题分页查询在分库分表之后会出现一些反直觉的现象。比如LIMIT 100000, 10ShardingSphere 会把每个分片里的前 100010 条数据都查出来然后在内存中合并排序最后再取第 100000 条到第 100010 条。当页码很深时这个操作的内存和耗时都是灾难级的。我在生产环境遇到过用户从后台翻到第 1000 页时接口直接超时的情况。解决思路有两个一是限制最大页码超过 200 页就拒绝查询并提示用户缩小筛选范围二是改用游标分页也就是基于id ?的方式而不是LIMIT offset。另外还有一个小坑是分页排序字段的选择。如果排序字段不是分片键ShardingSphere 必须做全局内存排序开销会增加不少。我在设计表时让大部分列表查询都按order_id排序这样路由到各分片后的数据本身就有序合并阶段就更高效。6.2 雪花算法的主键与时钟回拨分布式主键我用的是内置雪花算法雪花 ID 是 64 位的 longMyBatis Plus 的IdType.ASSIGN_ID也能兼容。但注意雪花算法依赖机器时钟如果服务器发生时钟回拨会有生成重复 ID 的风险。ShardingSphere 5.2.1 的雪花算法实现里做了简单的容错处理回拨范围较小时会等待时钟追平。我建议在部署层面加上时间同步服务避免手工调服务器时间。另外key-generators里的雪花算法可以配置max-tolerance-clock-forward-millis和worker-id等参数多实例部署时最好显式指定worker-id否则默认取 IP 段作为 workerId可能在高并发下出现碰撞。6.3 事务类型怎么选分库分表之后跨库事务是必须面对的问题。ShardingSphere 5.2.1 默认是本地事务也就是只保证单个分片内的事务跨库操作出现异常时不会自动回滚。基于这个原因凡是涉及多分片写入的操作我都在设计上规避了——要么通过分片键把相关数据路由到同一个库要么拆成多个本地事务按顺序执行。如果业务实在避不开跨库事务可以考虑引入 XA 事务。5.2.1 对 XA 的支持是通过shardingsphere-transaction-xa-core提供的配置也不复杂但 XA 的性能损耗和分布式锁等待问题在高并发场景下非常明显。我个人的建议是优先优化业务流程而不是无脑上分布式事务因为大多数跨库事务场景在仔细梳理后都能通过分片键设计收敛到单库内。7. 分片是否真正生效的验证方法配置全部完成应用也启动成功了这时候还不能掉以轻心必须验证路由是否正确。最直接的方式是看 ShardingSphere 打印出来的真实 SQL。我的做法是在配置里开启sql-showprops: sql-show: true开启后控制台会输出类似下面的内容Actual SQL: ds0 ::: select * from t_order_0 where order_id 123只要看到日志里的ds0和t_order_0符合预期就说明路由生效了。我习惯每增加一个新的分片表后先写一条插入语句再写一条按分片键查询的语句分别看插入和查询路由到了哪个库哪张表。还有一些细节实际节点的 SQL 输出里表名会被替换成物理表名比如逻辑表t_order会变成t_order_0或者t_order_1这个是正常现象。如果你在日志里发现 SQL 仍然操作的是逻辑表名且没有经过 ShardingSphere 解析那大概率是你的 SQL 里有表名或字段名带了数据库前缀或者使用了不支持的特殊语法导致解析器走了全库广播路由。8. 上线前还需要检查的几个细节最后说几个上线 checklist 级别的事情都是我真实踩过或者帮别人排查过的。第一actual-data-nodes里的库名和表名必须提前在 MySQL 中创建好ShardingSphere 只管路由不管建表。我一般会准备一个初始化 SQL 脚本在发布流程中自动执行避免新环境部署时漏建表。第二如果使用了binding-tables关联查询的两张表分片键必须完全一致。比如t_order和t_order_item都按order_id分片才能保证关联时走到同一个节点。如果分片键不同绑定表配置会导致路由结果意外日志里报错还不明显。第三连接池大小要重新评估。分库分表后每个物理库都需要独立的连接池管理ShardingSphere 会为每个数据源创建连接池所以整体连接数会翻倍。原来单库 20 个连接分两个库后可能就需要 40 个要充分评估数据库的最大连接数是否能扛住。第四也是我最有感触的一点分库分表不是银弹。它只是把单库单表的压力分散到多个节点上并没有减少总体计算量反而引入了路由、合并、分布式事务这些额外复杂度。如果你的数据量还在几千万以内先考虑索引优化、归档、冷热分离把分库分表留到真正必要的时候再用。我当时决定上 ShardingSphere 之前和 DBA、架构师做了三轮评估确认业务增长曲线确实需要才动的这个手。本文还有配套的精品资源点击获取