SpringBoot+MyCat读写分离与分库分表实战避坑指南
1. 项目概述为什么一个数据库架构方案值得花两周时间重做三次“SpringBootMyCat 实现读写分离、分库分表、主从同步”——这行标题在某技术社区里被点开超过1.2万次但真正跑通、压测稳定、上线不翻车的不到7%。我带过三支不同背景的开发团队落地过这个组合最短的一次是3天完成本地验证最长的一次在生产环境灰度阶段卡了11天就卡在主从延迟导致的脏读和MyCat路由规则与业务SQL语义冲突这两个点上。这不是理论题是实打实的工程题你写的每一条Select注解、每一个ORDER BY子句、每一次INSERT ... SELECT操作都在悄悄挑战MyCat的解析边界而SpringBoot启动时自动注入的DataSource可能正把你的读请求悄悄发往主库让读写分离形同虚设。这个方案解决的不是“能不能用”的问题而是“能不能稳、能不能查、能不能扩”的问题。它面向的是单库QPS持续突破3000、单表数据量逼近千万级、且业务不允许停机扩容的中型系统。适合两类人一类是正在为慢查询告警失眠的后端工程师另一类是刚接手遗留系统、发现user_order表有42个字段还带着TEXT类型的历史维护者。它不教你怎么写Hello World只告诉你当SELECT * FROM order WHERE user_id ? AND status IN (?, ?)开始拖垮整个订单页时你手里的SpringBoot应用该怎么呼吸。关键词“读写分离”“分库分表”“主从同步”不是并列关系而是三层嵌套的依赖链主从同步是地基没它读写分离就是空中楼阁读写分离是承重墙没它分库分表带来的复杂度会指数级放大分库分表才是屋顶它解决容量瓶颈但也把所有问题都暴露在阳光下。很多人一上来就猛敲schema.xml配分片规则却忘了先确认MySQL主从的Seconds_Behind_Master是否真的能长期稳定在0-50ms之间——我见过最离谱的案例DBA给的“已同步”状态其实是靠pt-heartbeat脚本每5秒人工打点伪造的真实延迟峰值超12秒结果MyCat把读请求路由过去用户刚付款就查不到订单。所以这篇内容不讲概念定义不画架构图不列官方文档参数。它是一份带血丝的实操日志从MySQL主从半同步配置的坑位编号到MyCatrule.xml里mod-long函数对负数取模的隐式转换陷阱从SpringBoot多数据源下Transactional传播行为如何穿透MyCat代理层到INSERT IGNORE在分片键缺失时触发的全表广播风暴。所有内容都来自某电商中台系统从单库单表到三库十二表的真实演进过程连报错日志截图都按生产环境脱敏标准处理过——你可以直接抄作业但建议先读完第4节的“常见问题速查表”那里列着我们踩过的17个坑其中第9个会让你在凌晨三点删掉整整两小时的调试日志。2. 整体设计思路为什么放弃ShardingSphere选MyCat又为什么必须搭配SpringBoot手动接管事务2.1 架构选型背后的三重现实约束很多团队在技术选型会上争论“ShardingSphere vs MyCat”但真实决策现场往往由三个硬性条件拍板运维成熟度约束某公司DBA团队只有2人负责23套MySQL实例他们明确拒绝学习任何需要修改MySQL配置文件如plugin_load_add或部署Java中间件的服务。MyCat作为独立Java进程可部署在专用中间件服务器DBA只需维护MySQL主从运维界面干净。而ShardingSphere-JDBC需打入每个应用JVM升级时要协调17个服务重启ShardingSphere-Proxy虽独立但其server.yaml中authentication模块与公司LDAP认证体系存在兼容性问题POC阶段就卡死。历史SQL兼容性约束存量系统存在大量动态拼接SQL例如SELECT * FROM tableName WHERE create_time dateStr 。MyCat的SQL解析器对表名变量支持更宽松通过table标签配置逻辑表映射而ShardingSphere要求表名必须为字面量否则抛UnsupportedOperationException。我们曾用AST解析器扫描全项目发现38处此类写法重写成本远超中间件适配成本。事务一致性兜底能力约束核心支付流程要求“扣减余额生成订单更新库存”强一致。MyCat的XA事务模式虽性能较差但在maxConcurrentXaRecovery1配置下配合SpringBoot的ChainedTransactionManager可实现跨库事务最终一致性补偿。而ShardingSphere的柔性事务Seata集成在当时版本对INSERT ... ON DUPLICATE KEY UPDATE语句存在回滚丢失风险压测中出现过0.3%的补偿失败率。提示MyCat不是银弹。它在高并发小事务场景如用户登录校验下因连接池复用机制问题TPS比直连MySQL低18%但在大查询聚合场景如运营报表因内置结果集合并优化响应时间反而快22%。选型前务必用sysbench模拟真实流量压测别信Benchmark网站的“百万QPS”宣传。2.2 SpringBoot为何不能只靠starter自动集成SpringBoot官方生态中并无MyCat专用Starter社区版mycat-spring-boot-starter仅封装了基础数据源配置。但实际落地中有三个关键环节必须由开发者手动接管数据源健康检查绕过MyCat代理SpringBoot Actuator的/actuator/health默认调用DataSource.getConnection()而MyCat的testOnBorrowtrue配置会导致每次健康检查都穿透到后端MySQL引发连接风暴。解决方案是自定义HealthIndicator改用MyCat管理端口默认8066的SHOW DATABASES命令检测。事务传播行为失效MyCat将BEGIN/COMMIT/ROLLBACK指令透传至后端MySQL但SpringBoot的Transactional(isolation Isolation.REPEATABLE_READ)无法控制MyCat内部的连接分配逻辑。当方法内混合执行分片表与非分片表SQL时MyCat可能将读请求路由到从库而事务上下文仍绑定在主库连接上导致Connection is closed异常。必须通过TransactionTemplate显式指定TransactionDefinition.PROPAGATION_REQUIRED并捕获SQLException做重试。分页SQL重写冲突MyCat的limitStart重写机制与MyBatis-Plus的Page对象存在兼容性问题。当Page.size20且Page.current5时MyCat生成LIMIT 80,20但MyBatis-Plus在IPage回调中会再次叠加RowBounds造成偏移量翻倍。解决方案是在MybatisPlusConfig中禁用PaginationInnerInterceptor改用MyCat原生分页需在schema.xml中设置property nameuseHandertrue/property。这些不是“高级技巧”而是上线前必须填平的坑。我见过最惨的案例某团队用社区Starter跑通了CRUD上线后发现支付回调接口在高并发下事务回滚率飙升至40%排查三天才发现是Transactional的timeout值默认30秒小于MyCat的xaRecoveryTimeout默认60秒导致XA恢复线程提前终止。2.3 主从同步半同步不是可选项而是生死线MySQL主从同步模式决定读写分离的可靠性上限。异步复制Async在主库宕机时从库可能丢失数秒甚至数分钟数据此时MyCat若将读请求路由过去用户看到的就是“刚下单就查无此单”的经典脏读。全同步Sync虽保证强一致但性能损耗达40%以上不适合OLTP场景。半同步Semi-sync是唯一平衡点——它要求至少一个从库返回ACK才提交事务既避免数据丢失又将延迟控制在毫秒级。但MySQL官方半同步插件rpl_semi_sync_master存在两个致命缺陷超时即退化为异步rpl_semi_sync_master_timeout默认10秒期间若无从库响应主库自动切回异步模式且不告警。我们在压测中故意断开从库网络发现主库在第10.002秒后开始异步写入而MyCat的heartbeat检测间隔为30秒这20秒窗口期就是数据不一致的温床。单点ACK瓶颈半同步要求“至少一个从库ACK”但实际部署中常配置双从库若其中一台网络抖动另一台需承担全部ACK压力CPU使用率飙升至95%反而拖垮整体性能。解决方案是采用增强版半同步插件mysql-semi-sync-enhancedPercona分支它支持rpl_semi_sync_master_wait_for_slave_count2强制等待N个从库ACK需从库数≥Nrpl_semi_sync_master_wait_no_slaveON当可用从库数不足N时主库阻塞而非降级配合K8s liveness探针实现自动驱逐故障节点rpl_semi_sync_master_timeout1000超时单位改为毫秒精准控制在1秒内配置实录-- 主库执行 INSTALL PLUGIN rpl_semi_sync_master SONAME semisync_master.so; SET GLOBAL rpl_semi_sync_master_enabled 1; SET GLOBAL rpl_semi_sync_master_timeout 1000; SET GLOBAL rpl_semi_sync_master_wait_for_slave_count 2; -- 从库执行 INSTALL PLUGIN rpl_semi_sync_slave SONAME semisync_slave.so; SET GLOBAL rpl_semi_sync_slave_enabled 1;注意semisync_master.so文件需从Percona Server 8.0.28版本提取MySQL官方二进制包不包含。编译安装时需指定-DWITH_SEMI_SYNCON否则INSTALL PLUGIN会报错Plugin rpl_semi_sync_master is not loaded。3. 核心细节解析MyCat配置文件的每一行都在回答一个业务问题3.1schema.xml不是XML配置而是业务分片契约MyCat的schema.xml本质是一份数据库契约协议它定义了“应用看到的逻辑结构”与“物理库表分布”之间的映射关系。很多人把它当成纯技术配置却忽略了其中每一项都在回答业务问题schema namePROD_DB checkSQLschemafalse sqlMaxLimit100checkSQLschemafalse解决的是业务代码中SELECT * FROM PROD_DB.order这类带库名的SQL。若设为trueMyCat会尝试解析PROD_DB并报错“schema not found”因为逻辑库名在MyCat中是虚拟的。设为false后MyCat自动剥离库名前缀只处理order表名——这相当于给历史代码开了个兼容白名单。table namet_order dataNodedn1,dn2,dn3 rulesharding-by-mod /dataNodedn1,dn2,dn3声明该表分布在三个物理节点但rulesharding-by-mod才是关键。MyCat内置的mod-long分片算法对order_idBIGINT取模看似简单却埋着雷当order_id为负数时如某些ORM生成的测试IDJava的%运算符返回负余数而MySQL的MOD()函数返回正余数导致路由错乱。解决方案是在function配置中启用hashSlice属性或强制业务层保证ID为正。dataNode namedn1 dataHosthost1 databaseprod_db_01 /databaseprod_db_01指定了物理库名但MyCat要求所有物理库必须存在且结构一致。我们曾因DBA在prod_db_02中误删了一个索引导致MyCat在dn2节点执行EXPLAIN时抛出Unknown column进而触发全节点广播查询TPS暴跌60%。因此必须建立物理库DDL同步流水线用pt-online-schema-change工具确保所有分片库结构原子性变更。最关键的配置在dataHost节点dataHost namehost1 maxConcurrentRead200 minConcurrentRead10 balance1 writeType0 dbTypemysql dbDrivernative heartbeatselect user()/heartbeat writeHost hostmaster1 url10.0.1.10:3306 usermycat password123456 readHost hostslave1 url10.0.1.11:3306 usermycat password123456/ readHost hostslave2 url10.0.1.12:3306 usermycat password123456/ /writeHost /dataHostbalance1表示读请求在readHost间负载均衡但MyCat的轮询算法不考虑从库延迟。我们通过在heartbeat中嵌入延迟检测SQL修复select case when global.read_only1 then (select round(seconds_behind_master*1000) from information_schema.slave_status) else 0 end as delay_ms配合自定义HeartbeatSQLChecker将延迟超500ms的从库自动踢出读列表。writeType0表示所有写操作只发往第一个writeHost这是主从架构的铁律。但若master1宕机MyCat不会自动切换writeHost需配合ZooKeeper做HA。我们采用轻量级方案在writeHost外层包裹writeHost标签用Shell脚本监听VIP漂移事件动态reload MyCat配置。3.2rule.xml分片规则是业务边界的具象化分片规则定义数据如何切分它必须与业务生命周期深度耦合。以电商订单为例sharding-by-month规则看似合理按月分表但会制造“冷热不均”大促月份表数据量是淡季的8倍导致分片倾斜。我们最终采用sharding-by-user-id-mod理由有三查询局部性92%的订单查询带user_id条件分片键与查询条件一致避免全节点广播。数据均匀性用户ID经Snowflake生成高位时间戳中位机器码低位序列号取模后分布极均匀实测12分片下最大偏差3.2%。扩展友好性未来扩容只需调整function的count属性MyCat自动重分布无需停机。rule.xml核心配置tableRule namesharding-by-user-id-mod rule columnsuser_id/columns algorithmmod-long/algorithm /rule /tableRule function namemod-long classio.mycat.route.function.PartitionByMod property namecount12/property property namehashSlice0:1/property !-- 解决负数取模问题 -- /functionhashSlice0:1是救命配置它告诉MyCat只截取user_id字符串的第0到第1位含做哈希规避了负数问题。但这也带来新约束——业务方必须保证user_id为正整数我们在SpringBoot层添加Constraint(validatedBy PositiveLongValidator.class)全局校验。另一个易被忽视的细节是autoIncrement。MyCat的table标签中autoIncrementtrue仅表示该表有自增主键但MyCat本身不生成ID。若业务代码用GeneratedValue(strategy GenerationType.IDENTITY)MyCat会将INSERT语句透传给后端MySQL由各分片库独立生成ID导致全局ID重复。正确做法是关闭MyCat的autoIncrement在SpringBoot中集成TinyID或Leaf生成分布式IDINSERT语句显式指定ID值MyCat根据ID路由到对应分片3.3 SpringBoot多数据源配置不是为了炫技而是为了逃生通道MyCat作为中间件必然成为单点故障。我们为每个核心业务域配置三套数据源mycatDataSource主数据源指向MyCat代理地址jdbc:mysql://10.0.2.5:8066/PROD_DBmasterDataSource直连主库用于MyCat故障时降级写操作slaveDataSource直连从库用于MyCat故障时降级读操作配置代码精简版Configuration public class DataSourceConfig { Bean(name mycatDataSource) ConfigurationProperties(spring.datasource.mycat) public DataSource mycatDataSource() { return DataSourceBuilder.create().build(); } Bean(name masterDataSource) ConfigurationProperties(spring.datasource.master) public DataSource masterDataSource() { return DataSourceBuilder.create().build(); } Bean(name slaveDataSource) ConfigurationProperties(spring.datasource.slave) public DataSource slaveDataSource() { return DataSourceBuilder.create().build(); } }但关键在Primary的取舍mycatDataSource必须标记为Primary否则SpringBoot JPA会找不到默认数据源。而故障切换逻辑放在DataSourceRouter中Component public class DataSourceRouter { private static final String MYCAT mycat; private static final String MASTER master; private static final String SLAVE slave; Value(${datasource.fallback.enabled:true}) private boolean fallbackEnabled; public String determineCurrentLookupKey() { if (!fallbackEnabled) return MYCAT; // 检测MyCat健康状态 if (mycatHealthCheck()) return MYCAT; // 检测主库可写性 if (masterWriteCheck()) return MASTER; // 默认走从库读 return SLAVE; } }mycatHealthCheck()不是简单ping端口而是执行SELECT 1 FROM dual并校验响应时间200msmasterWriteCheck()则插入一条带唯一索引的测试记录并立即删除。这种细粒度检测让我们在MyCat进程假死端口存活但SQL无响应时3秒内完成降级避免雪崩。4. 实操过程从本地验证到生产灰度的七步通关清单4.1 环境准备用Docker Compose搭建最小可行集群本地验证必须复现生产环境拓扑我们用Docker Compose定义四节点集群version: 3.8 services: mysql-master: image: percona:8.0.28 command: --server-id1 --log-binmysql-bin --binlog-formatROW --gtid-modeON --enforce-gtid-consistencyON --rpl_semi_sync_master_enabled1 --rpl_semi_sync_master_timeout1000 environment: MYSQL_ROOT_PASSWORD: root123 MYSQL_DATABASE: prod_db_01 ports: - 33061:3306 volumes: - ./mysql/master.cnf:/etc/my.cnf.d/master.cnf mysql-slave1: image: percona:8.0.28 command: --server-id2 --read_onlyON --relay-logmysql-relay-bin --rpl_semi_sync_slave_enabled1 environment: MYSQL_ROOT_PASSWORD: root123 MYSQL_DATABASE: prod_db_01 ports: - 33062:3306 volumes: - ./mysql/slave1.cnf:/etc/my.cnf.d/slave.cnf mysql-slave2: image: percona:8.0.28 command: --server-id3 --read_onlyON --relay-logmysql-relay-bin --rpl_semi_sync_slave_enabled1 environment: MYSQL_ROOT_PASSWORD: root123 MYSQL_DATABASE: prod_db_01 ports: - 33063:3306 volumes: - ./mysql/slave2.cnf:/etc/my.cnf.d/slave.cnf mycat: image: mycatorg/mycat2:latest ports: - 8066:8066 - 9066:9066 environment: MYCAT_HOME: /opt/mycat volumes: - ./mycat/conf:/opt/mycat/conf - ./mycat/logs:/opt/mycat/logs关键点所有MySQL容器使用Percona 8.0.28镜像确保半同步插件可用master.cnf中显式启用GTID避免传统binlog位置同步的位点漂移问题MyCat挂载宿主机conf目录便于实时修改配置并reload启动后执行主从配置# 进入mysql-master容器 docker exec -it mysql-master mysql -uroot -proot123 # 创建复制用户 CREATE USER repl% IDENTIFIED BY repl123; GRANT REPLICATION SLAVE ON *.* TO repl%; FLUSH PRIVILEGES; # 获取主库状态 SHOW MASTER STATUS; # 记录File和Position# 进入mysql-slave1容器 CHANGE MASTER TO MASTER_HOSTmysql-master, MASTER_USERrepl, MASTER_PASSWORDrepl123, MASTER_PORT3306, MASTER_LOG_FILEmysql-bin.000001, MASTER_LOG_POS156, MASTER_AUTO_POSITION1; START SLAVE; SHOW SLAVE STATUS\G # 检查Seconds_Behind_Master0注意MASTER_AUTO_POSITION1启用GTID复制避免传统方式中MASTER_LOG_POS因主库重启重置导致的同步中断。这是MyCat稳定运行的前提。4.2 MyCat配置验证三步确认法MyCat配置不是写完就生效必须通过三步验证第一步管理端口连通性验证访问http://localhost:9066MyCat管理端口用默认账号mycat/mycat登录执行show databases; show datahost;若返回PROD_DB和host1说明schema.xml加载成功若报错ERROR 3009 (HY000): Unknown database PROD_DB检查schema.xml中schema标签的name属性是否与SQL中库名完全一致区分大小写。第二步心跳检测真实性验证在MyCat日志logs/mycat.log中搜索heartbeat应看到类似2023-06-15 14:22:33 [INFO] Heartbeat response for host1:slave1, delay2ms 2023-06-15 14:22:33 [INFO] Heartbeat response for host1:slave2, delay5ms若显示delay-1说明heartbeatSQL执行失败检查从库是否开启read_only且information_schema.slave_status表可读。第三步分片路由准确性验证执行分片键查询/*#mycat:datanodedn1*/ SELECT * FROM t_order WHERE order_id 1000000000000000001;/*#mycat:datanodedn1*/是MyCat强制路由注释若返回结果说明dn1节点正常再执行SELECT * FROM t_order WHERE order_id 1000000000000000001;若返回结果且EXPLAIN显示dataNodedn1说明分片规则生效。若EXPLAIN显示dataNodedn1,dn2,dn3说明order_id未命中分片键检查rule.xml中columns是否为order_id而非user_id。4.3 SpringBoot集成绕过自动配置的四个拦截点SpringBoot 2.7对DataSource自动配置做了增强必须主动拦截拦截点1禁用HikariCP默认健康检查在application.yml中spring: datasource: hikari: connection-test-query: SELECT 1 # 关闭自动健康检查改用自定义HealthIndicator validation-timeout: 3000拦截点2覆盖MyBatis的SqlSessionFactoryBeanBean public SqlSessionFactory sqlSessionFactory(Qualifier(mycatDataSource) DataSource dataSource) throws Exception { SqlSessionFactoryBean factoryBean new SqlSessionFactoryBean(); factoryBean.setDataSource(dataSource); // 关键禁用MyBatis-Plus的分页插件改用MyCat原生分页 factoryBean.setPlugins(new Interceptor[]{}); return factoryBean.getObject(); }拦截点3事务管理器显式绑定Bean(name transactionManager) public DataSourceTransactionManager transactionManager( Qualifier(mycatDataSource) DataSource dataSource) { DataSourceTransactionManager manager new DataSourceTransactionManager(); manager.setDataSource(dataSource); // 关键设置事务超时小于MyCat的xaRecoveryTimeout manager.setDefaultTimeout(50); // 单位秒 return manager; }拦截点4Mapper扫描路径隔离MapperScan(basePackages com.example.mapper, sqlSessionFactoryRef sqlSessionFactory)避免MyBatis扫描到其他数据源的Mapper导致Invalid bound statement异常。4.4 生产灰度策略用流量染色实现零感知切换生产环境切换绝不能“一刀切”我们采用三级灰度第一级读流量染色在Nginx层添加HeaderX-MyCat-Route: readSpringBoot Filter中识别该Header强制使用slaveDataSource。灰度比例从1%开始监控从库CPU和延迟。第二级写流量染色当读流量稳定后增加HeaderX-MyCat-Route: write此时写请求走mycatDataSource读请求仍走直连从库。重点监控MyCat的processor线程数和net模块丢包率。第三级全量切换前两级稳定72小时后移除Header所有流量走MyCat。但保留DataSourceRouter中的降级开关通过Apollo配置中心动态控制fallback.enabled。灰度期间核心监控指标指标告警阈值检测方式MyCat平均响应时间200msPrometheus Grafana采集mycat_metrics从库延迟500ms自定义Exporter解析SHOW SLAVE STATUS分片路由错误率0.1%MyCat日志grep route error | wc -lXA事务恢复失败数0SELECT COUNT(*) FROM mycat_xa_recover WHERE stateFAILED5. 常见问题与排查技巧实录17个坑的填坑指南5.1 读写分离失效不是MyCat的问题是你的SQL写了不该写的Hint现象SELECT语句在MyCat日志中显示route to dn1主库节点但业务期望走从库。根因分析MyCat的读写分离策略基于SQL语义以下情况强制走主库SQL中包含FOR UPDATE或LOCK IN SHARE MODESQL中包含SELECT ... FROM ... WHERE ...但WHERE条件含NOW()、UUID()等非确定性函数SQL中包含SELECT后跟UNION且第一个子句含FOR UPDATE实操排查开启MyCat SQL日志set log_level3;管理端口执行在应用中复现问题SQL查看日志中route字段若日志显示route to master检查SQL是否含上述关键字解决方案将SELECT NOW()拆分为应用层获取时间再拼入SQL对UNION查询确保所有子句都不含锁提示使用/*#mycat:db_typeslave*/强制走从库慎用可能破坏事务一致性提示MyCat 2.x版本中db_typeslave仅在非事务上下文中生效。若在Transactional方法内使用MyCat会忽略该Hint并报WARN日志。5.2 分库分表后COUNT(*)变慢不是性能问题是架构认知偏差现象单库时SELECT COUNT(*) FROM t_order耗时20ms分库后升至1200ms。根因分析MyCat执行COUNT(*)时需向所有分片发送SELECT COUNT(*)再合并结果。12分片意味着12次网络往返1次结果集合并延迟必然升高。实操验证-- 查看MyCat执行计划 EXPLAIN SELECT COUNT(*) FROM t_order; -- 返回dataNodedn1,dn2,...,dn12解决方案业务妥协将“精确总数”改为“估算总数”用SELECT TABLE_ROWS FROM information_schema.TABLES替代架构妥协引入Redis HyperLogLog每次INSERT时PFADD hll:order:202306 {order_id}PFCOUNT hll:order:202306误差率0.81%技术妥协在MyCatschema.xml中为t_order表配置needAddLimitfalse但这要求业务层自行保证分页安全5.3 主从延迟突增不是DBA的锅是你的批量更新没加WHERE现象Seconds_Behind_Master从0ms飙升至12000ms持续15分钟。根因分析MyCat的INSERT ... SELECT语句在分片环境下会被重写为INSERT INTO t_order SELECT * FROM t_temp WHERE user_id % 12 0若t_temp表无索引从库执行该SQL时触发全表扫描导致SQL线程堵塞。实操定位在从库执行SHOW PROCESSLIST找到StateSending data的线程执行SELECT INFO FROM information_schema.PROCESSLIST WHERE ID{thread_id}获取完整SQL检查SQL中SELECT部分的表是否有有效索引解决方案批量操作前在临时表上创建复合索引ALTER TABLE t_temp ADD INDEX idx_user_status (user_id, status)改用INSERT ... VALUES (...),(...)批量插入避免SELECT子句对超大表迁移用pt-archiver分批次归档每批≤10000行5.4 MyCat内存溢出不是配置太小是你的连接没关现象MyCat进程OOM Killer被触发dmesg显示Out of memory: Kill process 12345 (java) score 852 or sacrifice child。根因分析MyCat的processor线程池默认16个每个线程维护独立连接池。若应用层Connection未正确关闭连接泄漏会累积到MyCat最终耗尽堆内存。实操诊断jstat -gc pid查看OU老年代使用率是否持续增长jmap -histo pid \| grep Connection统计连接对象数量若io.mycat.backend.mysql.nio.MySQLConnection实例数5000确认泄漏解决方案在SpringBoot中启用HikariCP连接泄漏检测spring: datasource: hikari: leak-detection-threshold: 60000 # 60秒未关闭即告警在MyCatserver.xml中设置processors8根据CPU核数÷2降低线程数强制应用层使用try-with-resourcestry (Connection conn dataSource.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { // 执行操作 } // 自动close5.5 全局序列号重复不是Snowflake的问题是你的MyCat没配idGenerator现象t_order.order_id出现重复值导致唯一索引冲突。根因分析MyCat的table标签中autoIncrementtrue仅声明存在自增但未指定ID生成器。当多个应用同时向MyCat发送INSERT INTO t_order (...) VALUES (...)无ID值时MyCat将请求透传给后端MySQL各分片库