资讯动态

SpringBoot集成ShardingSphere实现多数据源切换实战

发布时间:2026/10/15 20:27:16 来源:尧图企业网站定制
1. 项目概述为什么“多数据源切换”不是炫技而是真实业务里的刚需你有没有遇到过这样的场景一个电商后台系统订单数据要写进MySQL主库用户行为日志得实时落进PostgreSQL做分析而商品价格快照又必须存到Oracle里供财务对账——三套数据库结构不同、驱动不同、事务隔离级别不同但业务代码里却要像操作同一个DataSource那样自由读写或者更现实一点单库单表撑不住千万级用户订单量你决定用ShardingSphere做分库分表可上线前发现老系统里几十个定时任务、报表导出、数据校验模块全依赖原始JDBC直连改SQL成本高到团队集体沉默这时候“SpringBoot ShardingSphere 实现多数据源切换”就不是教程标题里的技术组合而是你明天晨会要拍板的救火方案。这个项目标题里藏着三个关键锚点“Java实战”说明它拒绝纯理论推演所有内容必须能跑通、能调试、能上线“SpringBoot”代表现代Java工程的默认基础设施意味着自动配置、Starter封装、Actuator监控这些开箱即用的能力必须被真正用起来而不是绕开“ShardingSphere”不是简单加个依赖就完事——它既是分库分表中间件也是多数据源路由中枢更是SQL解析与改写的底层引擎。很多人卡在第一步以为引入shardingsphere-jdbc-spring-boot-starter就能自动切库结果发现DS注解不生效、事务跨库报错、MyBatis Plus的Wrapper条件被错误重写……根本原因在于没吃透ShardingSphere的执行模型它不是代理层而是JDBC增强层所有逻辑都运行在应用进程内DataSource、Connection、Statement全由它接管。所以本篇不讲“怎么配yaml”而是带你从类加载顺序、Bean生命周期、SQL执行链路三层穿透把切换动作拆解成可验证、可打断、可回溯的操作单元。适合两类人一是刚接手遗留系统需要快速支持多库的后端开发二是准备技术选型想避开ShardingSphere常见陷阱的架构同学。接下来的内容每一行配置都有对应日志验证每一个坑我都亲手踩过三次以上。2. 整体设计思路为什么放弃AbstractRoutingDataSource而选择ShardingSphere原生路由2.1 传统方案的致命短板AbstractRoutingDataSource的“假切换”很多团队第一反应是用Spring原生的AbstractRoutingDataSource做数据源路由。写个DynamicDataSource继承它重写determineCurrentLookupKey()方法再配合ThreadLocal存当前库名看起来干净利落。我试过在某支付对账系统里这么干初期确实跑得飞快直到上线第三天凌晨收到告警对账任务失败错误日志里赫然写着org.springframework.dao.InvalidDataAccessResourceUsageException: PreparedStatementCallback; SQL [SELECT * FROM t_order WHERE status ?]; Cannot change transaction isolation level in the middle of a transaction。排查发现某个定时任务里先查了MySQL订单表设置isolation level为READ_COMMITTED紧接着又调用了PostgreSQL的日志统计接口驱动强制设为REPEATABLE_READ而AbstractRoutingDataSource在同一个事务里切换数据源时Connection对象其实没重建只是换了底层物理连接但JDBC规范明确禁止在活跃事务中修改隔离级别——这个限制在ShardingSphere里被主动规避了但在手写路由里成了定时炸弹。更隐蔽的问题是事务传播。当Service方法标注Transactional时Spring的TransactionSynchronizationManager会绑定一个DataSourceTransactionObject到当前线程。如果你在事务方法里手动调用setDataSource(pg)这个新数据源根本不会被事务管理器感知导致后续操作在非事务上下文中执行出现“部分成功部分失败”的脏数据。我们曾因此在某次大促后发现37笔订单状态不一致修复方案不是改代码而是回滚到单库模式——代价是临时关闭所有异步分析功能。2.2 ShardingSphere的执行模型为什么它能安全切换ShardingSphere-JDBC的定位是“轻量级Java框架”它的核心不是代理而是JDBC API的增强实现。当你声明shardingsphere.jdbc.datasource.namesds0,ds1时ShardingSphere实际创建的是一个ShardingSphereDataSource实例它内部持有一个MapString, DataSource缓存所有物理数据源并通过SQL解析器基于ANTLR4在Statement.execute()前完成路由决策。关键在于所有数据源切换都在SQL执行前完成且严格遵循JDBC规范。举个具体例子执行INSERT INTO t_order (id, user_id) VALUES (?, ?)时ShardingSphere的执行链路是SQL解析器识别出t_order是分片表根据user_id值计算出应路由到ds0还是ds1从缓存中取出对应物理DataSource调用getConnection()获取ConnectionConnection内部已预置了该库的事务隔离级别、网络超时等参数Statement对象由该Connection创建天然绑定到目标库。这个过程完全避开了Spring事务管理器的感知盲区——因为切换发生在JDBC层面而非Spring的DataSource抽象层。你可以用Arthas在io.shardingsphere.shardingjdbc.jdbc.core.statement.ShardingPreparedStatement#execute方法上埋点看到每次执行前都会打印[ShardingSphere-SQL] Logic SQL: INSERT INTO t_order... | Actual SQL: ds0 ::: INSERT INTO t_order...这就是它可控性的证明。2.3 方案选型对比什么时候该用ShardingSphere什么时候该退回到原生路由场景推荐方案原因同构数据库分库分表如MySQL集群ShardingSphere原生分片利用其SQL改写能力自动处理ORDER BY、GROUP BY、分页等复杂场景避免应用层拼接异构数据库混合访问MySQLPostgreSQLOracleShardingSphere多数据源路由支持自定义DatabaseType可扩展不同数据库的方言解析器简单读写分离主库写从库读Spring原生AbstractRoutingDataSourceShardingSphere的读写分离功能需额外配置负载均衡策略小项目反而增加复杂度需要强事务一致性跨库转账放弃任何中间件改用Saga或TCCShardingSphere的XA事务性能极低Seata集成成本高于直接上分布式事务框架我们最终选择ShardingSphere是因为项目需求明确要求“同一事务内可操作不同库”而ShardingSphere的sharding-sphere-jdbc-transaction-xa模块提供了基于Atomikos的XA实现。虽然性能比本地事务慢3倍但比起业务停摆这是可接受的折衷。3. 核心细节解析YAML配置、动态数据源注册与事务边界控制3.1 YAML配置的隐藏陷阱为什么datasource.name不能带下划线ShardingSphere官方文档里示例都是ds_0、ds_1这种命名但实际项目中运维同学习惯用mysql-prod、pgsql-analytics这类语义化名称。我第一次这么配启动时报错java.lang.IllegalArgumentException: Invalid datasource name: mysql-prod。翻源码发现ShardingSphere在org.apache.shardingsphere.infra.util.spi.type.typed.TypedSPIRegistry#findRegisteredService里用正则^[a-zA-Z][a-zA-Z0-9_]*$校验数据源名要求必须以字母开头且只允许字母、数字、下划线。mysql-prod里的短横线-直接触发校验失败。解决方案有两个推荐改名为mysql_prod并在application.yml里统一使用下划线风格备选自定义DataSourceNameChecker实现类重写validate方法但需在META-INF/services/org.apache.shardingsphere.infra.util.spi.type.typed.TypedSPI文件里注册维护成本高。更关键的是数据源配置顺序。ShardingSphere要求shardingsphere.jdbc.datasource块必须在shardingsphere.jdbc.rules之前加载否则规则里的actual-data-nodes: ds0.t_order_${0..1}会找不到ds0。我们曾因IDEA的YAML格式化插件自动调整了块顺序导致本地测试正常CI环境构建失败——错误日志里只有NullPointerException at org.apache.shardingsphere.sharding.rule.TableRule.findActualTableNames根本看不出是配置顺序问题。后来在CI脚本里加了校验步骤grep -A 10 shardingsphere.jdbc.datasource application.yml | grep -q names确保datasource块存在。3.2 动态注册数据源如何在运行时添加新库而不重启业务常有临时需求比如某次营销活动要单独建库存档活动结束后再归档到历史库。硬编码在YAML里显然不行。ShardingSphere提供ShardingSphereDataSourceFactory工厂类但官方文档没说清楚怎么用。实测可行的方案是Component public class DynamicDataSourceManager { Autowired private ShardingSphereDataSource dataSource; public void addDataSource(String name, DataSource newDs) { // 获取ShardingSphereDataSource内部的DataSourceMap Field field ReflectionUtils.findField(ShardingSphereDataSource.class, dataSourceMap); ReflectionUtils.makeAccessible(field); MapString, DataSource dataSourceMap (MapString, DataSource) ReflectionUtils.getField(field, dataSource); // 安全添加避免并发修改 synchronized (dataSourceMap) { if (!dataSourceMap.containsKey(name)) { dataSourceMap.put(name, newDs); // 触发规则刷新关键 refreshShardingRules(); } } } private void refreshShardingRules() { // 通过反射调用ShardingSphereDataSource的refresh方法 Method method ReflectionUtils.findMethod(ShardingSphereDataSource.class, refresh); ReflectionUtils.invokeMethod(method, dataSource); } }注意两点refresh()方法是protected的必须用ReflectionUtils调用添加后必须调用refresh否则新数据源不会被SQL解析器识别。我们在线上用这个方案动态接入了5个临时分析库平均耗时83ms不影响主业务。3.3 事务边界的精确控制Transactional失效的真相最常被问的问题“为什么我在Service方法上加了Transactional跨库操作还是不回滚”答案藏在ShardingSphere的事务类型配置里。默认情况下ShardingSphere使用LOCAL事务类型即每个物理库用独立事务根本不存在“跨库回滚”概念。必须显式配置shardingsphere: jdbc: props: # 启用XA事务 sql-show: true datasource: # ... 数据源配置 rules: - !TRANSACTION default-type: XA provider-type: Atomikos但光这样还不够。XA事务要求所有参与库都支持XA协议而某些云数据库如阿里云RDS MySQL 5.6默认关闭XA。我们曾因此在测试环境一切正常上线后发现MySQL库事务不回滚。解决方案是在RDS控制台开启xa_support参数或降级为BASE事务基于Seata的AT模式但需额外部署Seata Server。另一个坑是事务传播行为。ShardingSphere的XA事务不支持REQUIRES_NEW因为新事务会创建新的XAResource而Atomikos的XID生成策略会导致冲突。我们实测发现当方法A调用方法B且B标注Transactional(propagation Propagation.REQUIRES_NEW)时B的事务会静默失败。最终改为在B方法内手动管理事务Service public class OrderService { Autowired private TransactionTemplate transactionTemplate; public void createOrder(Order order) { // 主事务写订单库 orderMapper.insert(order); // 新事务发消息到PG日志库不依赖主事务 transactionTemplate.execute(status - { logMapper.insert(new LogRecord(order.getId())); return null; }); } }4. 实操过程从零搭建可验证的多数据源切换Demo4.1 环境准备最小可行依赖组合不要盲目复制网上教程的pom.xml。ShardingSphere版本迭代快3.x和5.x的API差异巨大。我们锁定shardingsphere-jdbc-core-spring-boot-starter:5.3.22023年稳定版对应Spring Boot 2.7.x。完整依赖如下dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- ShardingSphere核心 -- dependency groupIdorg.apache.shardingsphere/groupId artifactIdshardingsphere-jdbc-core-spring-boot-starter/artifactId version5.3.2/version /dependency !-- MySQL驱动必须8.0否则不支持XA -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency !-- PostgreSQL驱动 -- dependency groupIdorg.postgresql/groupId artifactIdpostgresql/artifactId scoperuntime/scope /dependency !-- Atomikos事务管理器 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-jta-atomikos/artifactId /dependency !-- MyBatis Plus简化CRUD -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency /dependencies特别注意spring-boot-starter-jta-atomikos必须显式引入否则ShardingSphere的XA事务无法初始化。我们曾漏掉这行启动日志里没有AtomikosTransactionManager相关输出但程序不报错直到事务测试时才发现回滚无效。4.2 数据库初始化两个库的建表语句差异处理ShardingSphere要求逻辑表在所有物理库中结构一致但MySQL和PostgreSQL的语法差异怎么办比如MySQL用BIGINT AUTO_INCREMENTPostgreSQL用SERIAL。解决方案是用JDBC标准语法建表让驱动自动适配。MySQL建表CREATE TABLE t_order ( id BIGINT NOT NULL PRIMARY KEY, user_id BIGINT NOT NULL, amount DECIMAL(10,2) );PostgreSQL建表CREATE TABLE t_order ( id BIGSERIAL PRIMARY KEY, -- 注意BIGSERIAL会自动创建sequence user_id BIGINT NOT NULL, amount NUMERIC(10,2) );关键点主键类型用BIGINT而非AUTO_INCREMENTPostgreSQL驱动会自动映射到BIGSERIAL小数类型用DECIMALMySQL和NUMERICPostgreSQL是等价的ShardingSphere的SQL解析器能识别所有表名、字段名用小写避免大小写敏感问题PostgreSQL默认小写MySQL在Linux下区分大小写。我们用Flyway做数据库版本管理在V1__init.sql里按数据库类型分文件src/main/resources/db/migration/mysql/V1__init.sql src/main/resources/db/migration/postgresql/V1__init.sql启动时通过spring.flyway.locationsclasspath:db/migration/${spring.profiles.active}动态加载。4.3 核心配置详解application.yml逐行解读# 数据源基础配置 shardingsphere: jdbc: # 必须开启SQL日志否则无法调试路由问题 props: sql-show: true # 关闭元数据刷新生产环境必须关否则每分钟扫表结构 check-table-metadata-enabled: false datasource: # 数据源名称列表顺序无关但必须与下面的ds0/ds1对应 names: ds0,ds1 # MySQL数据源 ds0: driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://localhost:3306/order_db?serverTimezoneUTCuseSSLfalse username: root password: 123456 # 连接池配置HikariCP type: com.zaxxer.hikari.HikariDataSource hikari: maximum-pool-size: 20 minimum-idle: 5 connection-timeout: 30000 # PostgreSQL数据源 ds1: driver-class-name: org.postgresql.Driver jdbc-url: jdbc:postgresql://localhost:5432/log_db username: postgres password: 123456 type: com.zaxxer.hikari.HikariDataSource hikari: maximum-pool-size: 15 minimum-idle: 3 connection-timeout: 30000 # 规则配置 rules: # 1. 分片规则这里简化为单库单表重点演示切换 - !SHARDING tables: t_order: # 逻辑表t_order实际只在ds0上存在 actual-data-nodes: ds0.t_order # 默认数据库分片策略不启用分库只做路由 default-database-strategy: none: # 2. 事务规则启用XA - !TRANSACTION default-type: XA provider-type: Atomikos # 3. 加密规则可选演示敏感字段加密 - !ENCRYPT encryptors: aes_encryptor: type: AES props: aes-key-value: 1234567890123456 tables: t_order: columns: amount: cipher-column: amount_encrypted encryptor-name: aes_encryptor重点参数说明sql-show: true开发阶段必开日志里能看到Actual SQL: ds0 ::: INSERT INTO t_order...这是验证切换是否生效的唯一依据check-table-metadata-enabled: false生产环境必须关否则ShardingSphere每5分钟执行SHOW CREATE TABLE造成数据库压力actual-data-nodes: ds0.t_order明确指定逻辑表t_order只在ds0上存在避免ShardingSphere尝试在ds1上执行SQLdefault-database-strategy: none表示不进行数据库分片所有SQL按逻辑表名路由到对应库。4.4 代码实现如何用ShardingSphereHint注解强制指定库ShardingSphere提供两种路由方式自动路由基于SQL解析和Hint路由强制指定。自动路由适合分片场景Hint路由更适合多数据源切换。例如用户中心服务需要同时读取MySQL用户表和PostgreSQL行为日志Service public class UserService { Autowired private UserMapper userMapper; // 对应MySQL Autowired private LogMapper logMapper; // 对应PostgreSQL Transactional public UserDetail getUserDetail(Long userId) { // 步骤1从MySQL查用户基本信息 User user userMapper.selectById(userId); // 步骤2强制路由到PostgreSQL查日志关键 try (HintManager hintManager HintManager.getInstance()) { hintManager.setDatabaseShardingValue(ds1); // 指定数据库名 ListLogRecord logs logMapper.selectByUserId(userId); return new UserDetail(user, logs); } } }HintManager是ShardingSphere提供的线程级路由控制工具setDatabaseShardingValue(ds1)会将当前线程的路由上下文设为ds1后续所有SQL都会被路由到PostgreSQL。注意必须用try-with-resources确保close()被调用否则路由状态会污染其他请求HintManager不支持嵌套如果A方法调用B方法B里再设Hint会覆盖A的设置日志里会显示[ShardingSphere-SQL] Actual SQL: ds1 ::: SELECT * FROM t_log WHERE user_id ?这是验证成功的标志。我们在线上用这个方案实现了“用户详情页”的秒级加载MySQL查主数据20msPostgreSQL查日志80ms总耗时控制在120ms内比原来双库串行查询200ms提升40%。5. 常见问题与排查技巧实录那些文档里不会写的坑5.1 典型问题速查表问题现象可能原因排查命令/日志关键词解决方案启动报错Cannot resolve reference to bean shardingSphereDataSourceShardingSphere Starter版本与Spring Boot不兼容检查mvn dependency:tree | grep shardingsphere升级ShardingSphere到5.3.2Spring Boot锁定2.7.18SQL日志里显示Actual SQL: ds0 ::: ...但数据库没数据物理库连接失败ShardingSphere静默降级到内存执行搜索日志Failed to get connection from data source检查jdbc-url中的端口、数据库名、用户名密码跨库事务不回滚MySQL有数据但PostgreSQL没数据XA事务未启用或数据库不支持XA搜索日志AtomikosTransactionManager、XAResource在application.yml中添加shardingsphere.jdbc.rules[1].default-type: XAShardingSphereHint不生效SQL仍路由到默认库HintManager未正确关闭线程上下文残留搜索日志Hint value is not empty严格使用try-with-resources或在finally块里调用HintManager.close()MyBatis Plus的lambdaQuery().eq(User::getId, 1)报错Column id not foundShardingSphere的SQL解析器不支持Lambda表达式生成的列名搜索日志Unsupported SQL改用query().eq(id, 1)或升级MyBatis Plus到3.5.3.15.2 独家避坑技巧三个让团队少加班20小时的经验技巧一用Arthas实时观测SQL路由路径当怀疑路由异常时别急着改代码。用Arthas attach到进程执行# 监控ShardingSphere的SQL执行方法 watch org.apache.shardingsphere.shardingjdbc.jdbc.core.statement.ShardingPreparedStatement execute {params,returnObj} -x 3 -n 5 # 查看当前线程的Hint上下文 ognl org.apache.shardingsphere.infra.hint.HintManagerHoldergetHintManager().getDatabaseShardingValues()我们曾用这个方法在5分钟内定位到一个HintManager未关闭导致的路由污染问题比翻三天日志高效得多。技巧二给每个数据源加健康检查端点ShardingSphere自带/actuator/shardingsphere端点但只返回配置不反映实时连接状态。我们扩展了ShardingSphereHealthIndicatorComponent public class MultiDataSourceHealthIndicator implements HealthIndicator { Autowired private ShardingSphereDataSource dataSource; Override public Health health() { try { // 尝试从每个物理库获取连接 MapString, DataSource dataSourceMap getDataSourceMap(); for (Map.EntryString, DataSource entry : dataSourceMap.entrySet()) { try (Connection conn entry.getValue().getConnection()) { conn.isValid(3000); // 3秒超时 } catch (Exception e) { return Health.down() .withDetail(failedDataSource, entry.getKey()) .withDetail(error, e.getMessage()) .build(); } } return Health.up().build(); } catch (Exception e) { return Health.down(e).build(); } } }上线后运维同学通过curl http://localhost:8080/actuator/health就能一眼看出哪个库断连不用登录服务器查日志。技巧三SQL日志分级输出避免生产环境IO爆炸sql-show: true在生产环境会打爆磁盘。我们改造了ShardingSphere的SQLLogger// 自定义SQL日志处理器 public class ProductionSQLLogger extends SQLLogger { Override public void logSQL(final String sql, final boolean isQuery, final long startTime, final long endTime) { if (endTime - startTime 1000) { // 只记录耗时1s的慢SQL super.logSQL(sql, isQuery, startTime, endTime); } } }然后在META-INF/services/org.apache.shardingsphere.infra.spi.type.typed.TypedSPI里注册让ShardingSphere加载我们的实现。实测线上环境日志量减少92%但关键慢SQL一个没漏。6. 性能压测与优化单机QPS从800到3200的实测记录6.1 压测环境与基准数据我们用JMeter模拟100并发用户执行混合操作70%请求GET /user/{id}MySQL查用户PostgreSQL查日志20%请求POST /orderMySQL写订单PostgreSQL写日志10%请求GET /reportMySQL聚合查询基准配置应用服务器4核8GJVM参数-Xms2g -Xmx2g -XX:UseG1GCMySQL8核16Ginnodb_buffer_pool_size10gPostgreSQL8核16Gshared_buffers4GB初始结果平均响应时间420ms错误率12.3%主要是PostgreSQL连接超时QPS8006.2 优化步骤与效果对比优化1调整HikariCP连接池参数初始配置maximum-pool-size: 20对PostgreSQL不够。PostgreSQL的连接成本高于MySQL我们按公式max_connections (CPU核心数 × 2) 有效磁盘数计算将PostgreSQL连接池调至30shardingsphere: jdbc: datasource: ds1: # PostgreSQL hikari: maximum-pool-size: 30 minimum-idle: 10 connection-timeout: 10000 # 从30s降到10s快速失败效果错误率降至2.1%QPS升至1100。优化2启用ShardingSphere的SQL执行缓存ShardingSphere默认每次SQL都重新解析对高频查询浪费CPU。开启缓存shardingsphere: jdbc: props: # 开启SQL解析缓存默认false sql-parser-cache-enabled: true # 缓存大小默认2000 sql-parser-cache-capacity: 5000效果CPU使用率下降35%QPS升至1800。优化3分离读写流量避免XA事务滥用分析日志发现85%的PostgreSQL操作是只读日志查询没必要走XA事务。我们改造了LogMapperMapper public interface LogMapper { // 不加Transactional走本地事务 Select(SELECT * FROM t_log WHERE user_id #{userId}) ListLogRecord selectByUserId(Param(userId) Long userId); // 写操作才用XA Insert(INSERT INTO t_log (...) VALUES (...)) Transactional(rollbackFor Exception.class) void insert(Param(log) LogRecord log); }效果平均响应时间降至180msQPS突破3200错误率为0。6.3 最终压测报告指标优化前优化后提升平均响应时间420ms180ms↓57%P95响应时间890ms310ms↓65%QPS8003200↑300%错误率12.3%0%↓100%CPU使用率92%48%↓48%最关键的是所有优化都不需要改业务代码全部通过配置和少量注解完成。这印证了ShardingSphere作为“框架”而非“中间件”的价值它把复杂性封装在JDBC层让业务开发者专注领域逻辑。7. 实战经验总结那些只有踩过才知道的真相我在三个不同规模的项目里落地过ShardingSphere多数据源方案从百人小团队到千人研发组织有些体会不写下来后来者可能还要再踩一遍第一永远不要相信“开箱即用”。ShardingSphere的starter确实省去了XML配置但它把错误隐藏得更深。比如sql-show: true开启后如果日志框架没配置异步Appender高并发下日志IO会拖垮整个应用。我们曾因此在大促期间服务雪崩最后发现是Logback的appender没加async标签。解决方案不是关SQL日志而是用AsyncAppender包装它或者用RollingFileAppender按大小滚动。第二监控比配置重要十倍。ShardingSphere提供了/actuator/shardingsphere端点但默认只返回静态配置。我们扩展了它加入实时指标shardingSphereDataSource.activeConnections各物理库活跃连接数shardingSphereDataSource.routeCount最近1分钟路由到各库的SQL次数shardingSphereDataSource.xaCommitTimeXA事务平均提交耗时这些指标接入Prometheus后运维同学能一眼看出“是不是PostgreSQL库扛不住了”而不是等用户投诉。第三文档里的“最佳实践”往往是特定场景的妥协。比如官方推荐用HintManager做强制路由但在高并发下HintManager.getInstance()的单例模式会成为锁瓶颈。我们实测发现当QPS超过5000时HintManager的getInstance()方法竟占到2%的CPU时间。最终改用ThreadLocal缓存public class ThreadLocalHintManager { private static final ThreadLocalHintManager HOLDER ThreadLocal.withInitial(HintManager::getInstance); public static HintManager get() { return HOLDER.get(); } public static void remove() { HOLDER.remove(); } }这个改动让QPS上限从5000提升到8000而文档里从没提过这个隐患。最后分享一个小技巧当需要快速验证某个SQL是否被正确路由时别在代码里加日志直接用ShardingSphere的SQLParserEngine解析Test public void testRoute() { SQLParserEngine engine new SQLParserEngine(MySQL); ParseTree tree engine.parse(SELECT * FROM t_order WHERE id 1, false); // 断言解析结果 assertTrue(tree instanceof SelectStatementContext); }这个单元测试能保证你的SQL语法永远被ShardingSphere支持比等上线报错再修快十倍。这个项目不是终点而是起点。当你把多数据源切换跑通后下一步自然会想能不能让前端无感地做分库分表能不能把ShardingSphere的路由能力暴露给BI工具这些延伸方向我们留到系列第二篇再聊。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑