ShardingSphere-JDBC读写分离实战:从架构选型到踩坑复盘
最近在给一个订单系统做数据库优化单库单表的压力已经很明显了高峰时段主库CPU能冲到80%以上慢查询日志里开始频繁出现熟悉的面孔。在设计改造方案时我选了ShardingSphere-JDBC来落地读写分离整体跑下来效果不错前前后后也踩了不少坑。这篇把我从方案选型到落地实操的核心内容整理出来包括路由机制、配置细节、主从延迟处理和版本迁移陷阱希望能帮你少走弯路。本文主要围绕三个问题展开为什么需要读写分离、ShardingSphere-JDBC怎么配置才能跑起来、线上环境有哪些坑是文档里不会告诉你的。适合正在做Java应用数据库优化、想在不引入独立中间件的情况下实现读写拆分的团队参考。1. 读写分离的需求背景与方案选型1.1 单库单表的压力到底出在哪儿大部分业务系统上线初期是单库单表后期性能瓶颈通常会集中在读请求上。我见过一个典型的电商订单库读写比例能到7:3甚至8:2更极端的报表类业务能到9:1。主库既要处理事务写操作又要扛住大量查询InnoDB的锁竞争和缓冲池压力都会随之上升。尤其到了大促或者活动场景一个热门商品的查询接口流量可以瞬间翻几倍主库CPU跟着飙起来紧接着就是连接数被打满、应用侧报连接超时。解决这类问题通常路径是加缓存、做读写分离、然后才是分库分表。缓存适合抗热点读但缓存命中率不稳定穿透时流量照样打到数据库。读写分离则是从架构层面把读流量从主库上卸掉——主库只负责写从库通过binlog复制同步数据负责读。这套方案实施成本低、对业务代码侵入小是第二优先级非常值得做的事。1.2 为什么选ShardingSphere-JDBC而不是独立中间件业内做读写分离的路径有几种应用层自己写路由、部署独立数据库中间件比如MyCat、或者嵌入客户端组件比如ShardingSphere-JDBC。三者各有适用场景我最终选客户端组件核心原因是它在Java项目里接入成本最低不用额外维护一套服务。方案部署形态代码改动运维成本适合场景应用层手动路由无每个DAO都要改无简单业务、从库数量少独立中间件独立服务改连接串高需要监控和扩容多语言团队、集中管理ShardingSphere-JDBC应用内嵌JAR改数据源配置低跟着应用走Java技术栈、轻量落地应用层手动路由最大的问题是容易漏新增的查询方法如果忘记走读库流量就悄悄落在了主库时间久了主库压力还是上不去下不来。独立中间件则多了一层网络转发链路变长还引入了中间件本身的运维复杂度。ShardingSphere-JDBC直接在JDBC层拦截SQL业务代码里还是用原来的DataSource、原来MyBatis的Mapper接口只需把数据源替换成ShardingSphere包装过的逻辑数据源读写路由的活框架帮你干完了。我用的版本是5.3.x这也是目前5.x系列里比较稳的版本。如果你刚接触建议直接上5.x4.x的配置结构和API差异很大下面会专门讲版本迁移的坑。2. 环境准备主从复制与工程接入2.1 MySQL主从复制快速打通ShardingSphere-JDBC本身不负责主从数据同步它只做读写流量的路由。所以第一步是先把MySQL主从环境准备好。这里以MySQL 8.0为例GTID模式配置。主库的my.cnf需要开启binlog[mysqld] server-id 1 log-bin mysql-bin binlog_format ROW gtid_mode ON enforce_gtid_consistency ON从库的my.cnf设置不同的server-id通常还会开read_only 1防止从库被误写。主库创建复制账号CREATE USER repl% IDENTIFIED WITH mysql_native_password BY Repl123; GRANT REPLICATION SLAVE ON *.* TO repl%; FLUSH PRIVILEGES;从库执行CHANGE MASTER指令CHANGE MASTER TO MASTER_HOST 192.168.1.10, MASTER_USER repl, MASTER_PASSWORD Repl123, MASTER_AUTO_POSITION 1; START SLAVE; SHOW SLAVE STATUS\G看到Slave_IO_Running: Yes和Slave_SQL_Running: Yes两个线程都在正常运行主从链路就通了。这块建议单独用压测或者造数据验证一下延迟情况有个20秒的基准数据心里有底。2.2 引入依赖与最小化配置ShardingSphere-JDBC不是独立服务它以一个JAR包的形式嵌入应用因此不需要额外部署。Maven项目引入依赖即可dependency groupIdorg.apache.shardingsphere/groupId artifactIdshardingsphere-jdbc-core-spring-boot-starter/artifactId version5.3.2/version /dependency如果是纯Spring项目用shardingsphere-jdbc-core即可。引入依赖后做一份最小配置这个配置定义了逻辑数据源order_rw写数据源指向primary读数据源列表是replica_0和replica_1读流量走轮询算法。spring: shardingsphere: datasource: names: primary,replica-0,replica-1 primary: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.jdbc.Driver jdbc-url: jdbc:mysql://192.168.1.10:3306/order_db?useSSLfalseserverTimezoneAsia/Shanghai username: root password: root123 replica-0: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.jdbc.Driver jdbc-url: jdbc:mysql://192.168.1.11:3306/order_db?useSSLfalseserverTimezoneAsia/Shanghai username: root password: root123 replica-1: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.jdbc.Driver jdbc-url: jdbc:mysql://192.168.1.12:3306/order_db?useSSLfalseserverTimezoneAsia/Shanghai username: root password: root123 rules: readwrite-splitting: >load-balancers: readwrite_ds_weight: type: WEIGHT props: replica-0: 2 replica-1: 1这个配置表示读流量按2:1分配到replica-0和replica-1。权重适合从库机器配置不对称的场景比如一个库是SSD一个是普通SATA或者一个节点上还跑了其他业务。我实际中习惯先用轮询跑一阵看看监控里各节点负载差异再决定要不要加权重因为权重毕竟是静态配置如果流量模型本身波动大调权重的收益有限。3.3 SQL路由判定与事务级主库接管路由的核心逻辑并不复杂ShardingSphere-JDBC解析SQL语句的第一个关键字SELECT走读库INSERT、UPDATE、DELETE走主库DDL也走主库。再往深处看一步真正决定路由归属的不只是SQL类型还包括当前是否处于事务中。当你没有手动开启事务时每条SQL独立判断选择走主库还是某个从库。可一旦代码里开启了事务比如Spring的Transactional情况就变了事务边界内的所有SQL都会被强制路由到主库即使你执行的是SELECT。这个机制是安全兜底因为在事务隔离级别下如果读到数据从从库来主库刚写入的数据还没同步过去就可能出现不可重复读甚至读到不存在的数据。还有一个容易被忽略的点Transactional(readOnly true)的只读事务在ShardingSphere-JDBC里默认也会走主库。很多人以为只读事务应该读从库但框架的判定逻辑就是事务开启则主库路由并不区分你是否只读。所以如果单纯想读从库别加事务注解MyBatis默认就是每条SQL一个独立连接路由才能发挥效果。4. 实践踩坑延迟、强制路由与版本迁移4.1 写后立即读的防延迟方案读写分离落地后第一个暴露出的问题就是主从延迟。我这边遇到的具体场景是用户下单支付成功后页面跳转到订单详情页详情页查询走从库。如果主库刚commit的订单数据还没同步到从库用户看到的详情页就提示订单不存在这种体验基本属于事故级别。解决这个问题的思路不是消灭延迟因为MySQL主从延迟受网络、从库负载、大事务影响不可能完全归零。更务实的做法是区分业务场景对于写后必须能读到的强一致读场景强制这条查询走主库对于浏览历史订单、查询数据报表这类允许最终一致性的场景照常走从库。在ShardingSphere-JDBC里用HintManager可以强制某条SQL走主库try (HintManager hintManager HintManager.getInstance()) { hintManager.setWriteRouteOnly(); // 这里执行订单详情查询强制走主库 Order order orderMapper.selectById(orderId); }我在订单创建接口里就用了这个方式保证用户下单跳到详情页时数据一定可见。注意HintManager要放在try-with-resources块中用完必须关掉它基于ThreadLocal实现不关闭的话当前线程后续所有的查询都会一直走主库主库压力会被莫名放大。4.2 HintManager的坑作用域与释放HintManager的使用有几个容易踩雷的细节我一个个说。第一它只对当前线程生效。如果你在Service方法里设置了写路由然后异步线程里执行查询那个异步线程是不受影响的。第二一个线程同时只能存在一个HintManager实例如果你在同一个线程里创建了两次而没有关闭第一个第二次getInstance()会直接抛出异常。第三try-with-resources关闭不只是推荐而是强烈建议——我线上排查过一个主库CPU飙高的故障最后定位到是某位同事在代码里调了setWriteRouteOnly()但没close线程池里这个线程被复用后后续所有SQL都被强制路由到主库从库的读流量几乎归零。所以我的实践准则是强制路由的生命周期越短越好最好精确到一条SQL绝不在循环体外面长时间开着HintManager。4.3 4.x到5.x的配置迁移要点如果你的项目之前用的是ShardingSphere 4.x升级5.x时配置结构变化很大不能直接复制粘贴。最核心的变更是读写分离的规则名称从!MASTER_SLAVE换成了!READWRITE_SPLITTING对应的数据源属性也从masterDataSourceName、slaveDataSourceNames改成了writeDataSourceName、readDataSourceNames。配置项4.x5.x规则关键字!MASTER_SLAVE!READWRITE_SPLITTING主数据源属性masterDataSourceNamewriteDataSourceName从数据源属性slaveDataSourceNamesreadDataSourceNames负载均衡算法属性loadBalanceAlgorithmTypeloadBalancerName4.x里负载均衡算法是直接写在规则内部的loadBalanceAlgorithmType字段5.x则拆出了独立的load-balancers配置段可复用性更强但也意味着改动范围更大。我遇到不少朋友升级后启动报错大多是因为把4.x的配置原封不动搬过来字段对不上导致的。如果你也在做这个升级建议先用5.x的官方示例配置跑通一个最小demo再逐步迁移生产配置。4.4 连接池参数与主从差异化调优读写分离后主库和从库的流量特征完全不同连接池参数理应差异化设置。我见过很多团队新老配置一套参数包打天下导致从库连接过多浪费、主库连接又不够。实践中主库maximum-pool-size可以控制在10到20因为主库只需要处理写事务和少量强制走主库的读连接多了反而增大锁竞争。从库则可以按读流量规模调大我常用的从库maximum-pool-size是30到50。HikariCP几个参数里minimum-idle和maximum-pool-size的大小对并发影响最明显。还有个容易被忽略的JDBC参数jdbc-url里建议加上rewriteBatchedStatementstrue批量INSERT的写入性能会有明显提升这个在主库场景尤其重要。至于连接超时时间建议从库的connection-timeout稍微放宽到5000毫秒以上避免从库瞬时负载高时获取连接失败。5. 日志观测与实测验证5.1 sql-show到底能看到什么props.sql-show: true是调试阶段最有用的配置没有之一。开启后ShardingSphere-JDBC会把每条SQL的路由信息打印到日志里。下面是我截取的一段真实日志Logic SQL: SELECT * FROM t_order WHERE order_id 1001 Actual SQL: replica-0 ::: SELECT * FROM t_order WHERE order_id 1001Logic SQL是应用实际执行的SQLActual SQL前面是实际路由到的物理数据源。通过这段日志可以快速判断路由是否符合预期如果一条INSERT的Actual SQL前缀是primary没问题如果一条SELECT在开启事务的Service方法里出现前缀也是primary说明事务内读主库的机制在生效。线上环境建议把sql-show关掉它会影响性能且日志量很大。我更推荐的方式是依赖监控系统采集数据源层的指标从库和主库各自的连接数、QPS、慢查询数量都要分开看才能真正判断路由是否均衡。5.2 读写分离后带来了多少收益最后说下我这边实测的数据供参考。优化前单库直连峰值时主库QPS约3500CPU使用率接近80%。接入ShardingSphere-JDBC做一主两从读写分离后压测阶段读QPS提升到接近10000主库CPU稳定在20%到30%从库CPU在40%左右浮动。这个结果符合预期因为读流量被分摊到了两个从库主库从繁重的查询中解放出来写事务的处理能力也有了余量。需要说明的是读写分离提升的是读吞吐上限和主库稳定性它不会降低单个查询的延迟也解决不了分库分表才能解决的问题。如果某天你的读QPS再翻几倍接下来要思考的是加从库节点或者对核心表做分片规划了。最后分享一个我个人的配置习惯无论从库数量多少我都坚持在readwrite-splitting里把load-balancer-name配置为显式的负载均衡器而不是依赖框架某个隐含默认值。项目后期如果调整从库节点或者切换算法配置一处修改即可生效排查问题的时候逻辑也更清晰。ShardingSphere-JDBC在5.x之后整体稳定性和路由性能都相当能打只要把延迟控制、事务边界、Hint释放这几件大事守住读写分离落地并没有想象中那么复杂。

相关新闻

SHL通用能力测试全解析:题型、备考与实战技巧

SHL通用能力测试全解析:题型、备考与实战技巧

秋招投出去的简历还没捂热,邮箱里就多了一封标题带“Assessment”的邮件,点开一看是SHL的链接,项目写着General Ability。很多同学第一次看到这个词有点懵,以为是某种性格测试,点进去做了几道才发现:语言推…

2026/10/5 8:10:00 阅读更多 →
头盔检测数据集处理与YOLOv5训练避坑指南

头盔检测数据集处理与YOLOv5训练避坑指南

简介:面向YOLOv5头盔目标检测学习者和安防场景开发者,这份数据集提供可直接用于模型训练的头盔佩戴图像与XML标注文件,省去自行采集、清洗和标注的繁琐环节。压缩包共160个文件,由80张JPG图片与80个XML文件一一对应组成&#xff0…

2026/10/5 8:10:00 阅读更多 →
Python模块入口揭秘:__name__与__main__的机制、时机与工程实践

Python模块入口揭秘:__name__与__main__的机制、时机与工程实践

写Python有些年头的人,大概都经历过这样一个“被问住”的时刻:新来的同事指着每个文件顶部那行if __name__ __main__:问,这到底是什么意思?不写它会怎样?大多数人的回答是“这是Python的规矩,入口文件要这…

2026/10/5 8:10:00 阅读更多 →

最新新闻

Java零基础入门:周末大总结

Java零基础入门:周末大总结

import java.util.Scanner; public class SummaryTry { public static void main(String[] args) { 综合编程题 11:简易学生成绩管理系统(控制台版)】 结合本周所有知识点,完成以下需求: 使用 Scanner 输入3名学生的姓…

2026/10/5 9:23:55 阅读更多 →
AI辅助芯片选型:从痛点拆解到实战工作流

AI辅助芯片选型:从痛点拆解到实战工作流

芯片选型的AI工具,现在其实是个“看着热闹、用着别扭”的领域。真干过硬件的人都知道,上午还在为选一颗合适的LDO翻三个分销商网站,下午就可能因为某颗MCU的交期变成52周而推翻整版方案。最近AI工具的声量很大,但能正经回答“帮我…

2026/10/5 9:23:55 阅读更多 →
自注意力+对抗生成网络:深度子空间聚类实战解析

自注意力+对抗生成网络:深度子空间聚类实战解析

简介:一份关于基于自注意力对抗的深度子空间聚类研究的Word文档,面向机器学习、计算机视觉等领域的研究者和学习者,系统阐述如何融合自注意力机制与对抗网络来提升高维数据聚类的鲁棒性。文档从聚类基础与经典算法讲起,涵盖稀疏子…

2026/10/5 9:23:55 阅读更多 →
分数阶时滞神经网络稳定性分析:Caputo导数、LMI判据与数值验证

分数阶时滞神经网络稳定性分析:Caputo导数、LMI判据与数值验证

简介:这份PDF文献《含有离散时滞及分布时滞分数阶神经网络的渐近稳定性分析》面向从事神经网络、深度学习与数据建模方向的研究生、教师及工程技术人员,聚焦分数阶神经网络在同时含离散时滞与分布时滞情形下的渐近稳定性难题。文章在Caputo导数意义下构造…

2026/10/5 9:23:55 阅读更多 →
从爬虫到可视化:豆瓣读书Top250全栈数据项目实战

从爬虫到可视化:豆瓣读书Top250全栈数据项目实战

做这个豆瓣读书 Top250 全栈数据项目,起因其实特别朴素,就是想给简历里添一个能从头讲到尾的实战项目。python爬虫负责采数据,pandas做数据清洗,MySQL落库,Java后端提供接口,前端再用ECharts把图书数据渲染…

2026/10/5 9:23:55 阅读更多 →
Google AX 开源:用声明式 YAML 编排十亿级 AI Agent 任务

Google AX 开源:用声明式 YAML 编排十亿级 AI Agent 任务

1. 从“一天一个开源项目”聊起:为什么 AX 值得单独写一篇做 Agent 开发这两年,我最大的感受就是:写一个能跑的 Agent 不难,难的是让一千个、一万个甚至更多 Agent 稳定地跑起来、跑对、跑完还能查账。单机跑个 ReAct 循环&#x…

2026/10/5 9:22:54 阅读更多 →

日新闻

马斯克杀回智能体战场,Grok 4.5万亿参数撑腰,Cursor接手数字白领项目:用TaoToken统一Key跑通多模型Agent工作流

马斯克杀回智能体战场,Grok 4.5万亿参数撑腰,Cursor接手数字白领项目:用TaoToken统一Key跑通多模型Agent工作流

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 0:00:22 阅读更多 →
AI编程工具插件机制详解:plugin.json配置与加载失败排查指南

AI编程工具插件机制详解:plugin.json配置与加载失败排查指南

1. 从“plugins”这个词说起:它到底在解决什么问题如果你最近在折腾 AI 编程工具,尤其是 Cursor、Codex CLI、Claude Code 这类带 CLI 的编辑器或命令行助手,那你大概率绕不开一个词——plugins。这个词本身不新鲜,从浏览器到 IDE…

2026/10/5 0:00:23 阅读更多 →
第26课:OpenClaw|日志审计与问题诊断:把日志链路改到 TaoToken 的排查清单

第26课:OpenClaw|日志审计与问题诊断:把日志链路改到 TaoToken 的排查清单

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 0:00:23 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 5:06:42 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 1:10:22 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 3:06:17 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 11:40:45 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 9:43:54 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 20:14:29 阅读更多 →