
简介本资源是基于MyCat 1.6.7.6正式版深度定制的分表增强版专为需要按月动态分表subTables的Java后端开发与数据库中间件运维人员设计解决传统MyCat不支持正则化、起始时间驱动的BYMONTH分表配置难题。压缩包共111个文件含53个核心jar包如Mycat-server-1.6.7.6-release.jar、druid-1.0.26.jar、curator-client-4.0.1.jar等、18个properties配置文件、10个xml配置模板及4个Linux启动脚本sh完整覆盖服务部署、分片规则定义、连接池与日志模块整体包体24.86MB轻量易集成。目前已有375人学习下载。用户可直接获取开箱即用的按月分表能力通过schema.xml中subTablestableName_$202101-?与subTableWayBYMONTH即可实现从指定起始月自动映射至当前月的子表路由配合预置的SQL建表脚本与动态建表逻辑显著降低分表运维成本。1. MyCat 1.6.7.6 BYMONTH 版本不是“官方补丁”而是实测可用的按月分表生产级定制包你手头正跑着一个日增百万级订单的电商后台MySQL 单表已超 2800 万行order_info查询开始抖动ALTER TABLE加个索引要锁表 47 分钟——这时候翻 MyCat 官方文档发现subTables只支持按mod、hash、range等静态规则分片压根没提“按年月自动建表、自动路由到order_202401/order_202402”这种动态能力。而这个mycat-1.6.7.6_BYMONTH.zip就是某公司线上稳定运行 14 个月的实战产物它在原生 MyCat 1.6.7.6 基础上重写了TableRule解析器与RouterUtil路由核心让subTablesorder_${yyyyMM}这种写法真正生效且支持 INSERT 自动创建目标月表、SELECT 自动下推时间范围、DELETE 按月精准清理——不是 demo不是 PoC是带完整 SQL 解析日志、分片键校验、跨月查询兜底策略的生产就绪包。适合正在被时间维度分表卡住脖子的中型业务系统尤其当你已用sharding-jdbc试过但发现其StandardShardingAlgorithm对BETWEEN 2024-01-01 AND 2024-03-31这类查询下推不干净时这个包能直接切过去跑通。2. 按月分表不是加个配置就行从源码层理解 BYMONTH 的三个关键改造点MyCat 原生分片逻辑走的是AbstractPartitionAlgorithm → RuleAlgorithm → calculate()这条链而subTables仅用于生成物理表名列表不参与路由计算。BYMONTH 版本之所以能工作是因为它在三个关键节点做了侵入式增强。下面逐层拆解不贴大段源码只聚焦你部署时必须看懂的逻辑分支。2.1 分片键识别MonthBasedColumnParse替代默认ColumnParse原版 MyCat 对WHERE create_time 2024-02-01这类条件只会提取出create_time字段名但不会解析其值是否为日期格式、是否可转为yyyyMM。BYMONTH 新增了MonthBasedColumnParse类在SQLParser解析 AST 后对WHERE子句中所有Timestamp/Date/String类型字段做二次扫描// 源码路径io.mycat.route.parser.MonthBasedColumnParse.java public class MonthBasedColumnParse extends ColumnParse { Override public void parseCondition(String columnName, Object value, ListString monthList) { if (isDateColumn(columnName)) { String yyyyMM parseToYYYYMM(value); // 支持 2024-02-15, 20240215, 1706774400000 三种输入 if (yyyyMM ! null) { monthList.add(yyyyMM); } } } }提示该解析器默认只认create_time、update_time、order_date三个字段名可配置若你的分片键叫gmt_create需在schema.xml中显式声明property namedateColumngmt_create/property否则路由会 fallback 到全表扫描。2.2 表名生成器MonthSubTableHandler动态拼接而非静态枚举原版subTablest1,t2,t3是预加载字符串数组而 BYMONTH 的MonthSubTableHandler在每次路由前实时生成!-- schema.xml 中的 table 定义 -- table nameorder_info dataNodedn1,dn2,dn3 rulebyMonth subTablesorder_${yyyyMM} dateColumncreate_time monthFormatyyyyMM /对应 Java 层调用链为RouteStrategy → getRouteResult() → MonthSubTableHandler.getSubTableName()→ 根据create_time条件值如2024-03-10→ 计算出order_202403→ 再查dataNode映射确认该表落在dn2上。关键参数说明subTablesorder_${yyyyMM}${}内必须为yyyyMM、yyyy、yyyyMMDD三者之一不支持自定义格式如yyMM会报错monthFormatyyyyMM与${}中格式严格一致不一致会导致路由失败dateColumn必须与MonthBasedColumnParse识别的字段名完全匹配大小写敏感2.3 跨月查询兜底MultiMonthRouter实现BETWEEN和IN的智能拆分当 SQL 出现WHERE create_time BETWEEN 2024-01-15 AND 2024-03-20时原版 MyCat 会因无法确定单一分片键值而路由到全部 dataNode。BYMONTH 的MultiMonthRouter会解析BETWEEN边界 → 得到起始月202401、结束月202403生成月列表[202401,202402,202403]对每个yyyyMM构造子查询SELECT * FROM order_202401 WHERE create_time 2024-01-15自动加时间过滤下推合并结果集并去重若开启needMergetrue注意此机制仅对BETWEEN、 AND 、IN (2024-01-01,2024-02-01)生效LIKE 2024%或DATE_FORMAT(create_time,%Y%m)202401不会被识别将触发全节点广播。3. 配置不是复制粘贴schema.xml与rule.xml的五处硬编码约束很多开发者解压后直接覆盖conf/目录启动报NoSuchMethodError或TableRule not found问题往往出在配置文件与 BYMONTH 版本的强耦合上。以下五处必须手工核对不能依赖旧配置迁移。3.1schema.xml中table标签的强制属性原版 MyCat 的table标签允许省略subTables但 BYMONTH 要求所有启用按月分表的 table 必须显式声明subTables、dateColumn、monthFormat缺一不可!-- ✅ 正确写法 -- table nameorder_info dataNodedn1,dn2,dn3 rulebyMonth subTablesorder_${yyyyMM} dateColumncreate_time monthFormatyyyyMM / !-- ❌ 错误写法缺少 dateColumn -- table nameorder_info dataNodedn1,dn2,dn3 rulebyMonth subTablesorder_${yyyyMM} / !-- 启动时抛异常MonthSubTableHandler requires dateColumn property --3.2rule.xml中function的 class 名必须精确匹配BYMONTH 新增了两个核心类rule.xml中function的class属性必须一字不差!-- rule.xml -- function namebyMonth classio.mycat.route.function.MonthPartionAlgorithm / !-- 注意不是 MonthPartitionAlgorithm少个 i 就报错 -- !-- 也不是 io.mycat.route.function.PartitionByMonth这是旧版社区魔改包 --同时该function下的property必须包含dateFormat注意不是datePatternfunction namebyMonth classio.mycat.route.function.MonthPartionAlgorithm property namedateFormatyyyy-MM-dd HH:mm:ss/property !-- 此格式仅用于解析字符串型日期不影响 subTables 生成 -- /function3.3dataNode定义必须与物理库表名前缀一致假设你的 MySQL 物理库名为db_order_202401、db_order_202402则dataNode的database属性必须为db_order_${yyyyMM}dataNode namedn1 dataHosthost1 databasedb_order_${yyyyMM} / !-- 若写成 databasedb_order则路由到 dn1 时会尝试连 db_order.order_202401但实际库名是 db_order_202401 -- !-- 导致报错Unknown database db_order --3.4sequence配置必须禁用或重写BYMONTH 默认关闭sequence全局序列因其与按月分表冲突若你在server.xml中启用了systemproperty namesequnceHandlerType2/property/system必须注释掉!-- server.xml -- !-- system -- !-- property namesequnceHandlerType2/property -- !-- /system -- !-- 否则启动时报SequenceHandler not compatible with MonthSubTableHandler --如需自增 ID应改用 MySQL 的AUTO_INCREMENTREPLACE INTO模式或在应用层生成雪花 ID。3.5user权限需显式授予跨库查询权限由于BETWEEN查询会路由到多个dataNodeMyCat 需要以同一账号连接所有物理库。若dn1用user1、dn2用user2则跨月查询会因权限不足中断!-- user.xml -- user nameapp_user property namepasswordmypass/property property nameschemasdb_order/property !-- 必须确保 app_user 在所有物理库db_order_202401, db_order_202402...中都存在且有 SELECT/INSERT 权限 -- /user验证命令在任一物理 MySQL 中执行SELECT user, host FROM mysql.user WHERE userapp_user; -- 应返回多行host 包含 % 或具体 IP SHOW GRANTS FOR app_user%; -- 必须包含 GRANT SELECT, INSERT ON db_order\_%.* TO app_user%4. 避坑生产环境踩过的五个真实问题与速查方案别等上线后半夜被报警电话叫醒。以下是某公司用该包支撑双十一流量峰值时记录在案的五个高频故障每一条都附带现象 → 原因 → 解决的闭环排查路径。4.1 现象INSERT 成功但数据未写入任何物理表日志无报错原因dateColumn值为NULL或非法字符串如0000-00-00MonthBasedColumnParse.parseToYYYYMM()返回null导致subTableName生成为空最终路由到nulldataNode。解决在应用层强制校验create_time非空且格式合法在 MyCat 日志中搜索getSubTableName return null关键字定位问题 SQL临时兜底在schema.xml中为该table添加defaultNodedn1属性使空值路由到默认节点4.2 现象SELECT * FROM order_info WHERE create_time 2024-02-15返回空但order_202402表中明明有数据原因monthFormatyyyyMM与dateFormatyyyy-MM-dd冲突。parseToYYYYMM(2024-02-15)得到202402但subTablesorder_${yyyyMM}生成order_202402而物理表实际名为order_202402—— 看似没问题错问题出在dataNode.databasedb_order_${yyyyMM}若物理库名为db_order_202402则database属性必须为db_order_${yyyyMM}但subTables与database的${yyyyMM}必须由同一DateFormat解析。若dateFormat设为yyyy-MM-dd则database解析出db_order_202402但subTables解析出order_202402二者匹配若dateFormat设为yyyyMMdd则subTables解析出order_20240215错误导致找不到表。解决统一dateFormat为yyyy-MM-dd并确保subTables和database中的占位符均为${yyyyMM}非${yyyyMMdd}。4.3 现象DELETE FROM order_info WHERE create_time 2024-01-01执行极慢CPU 占用 100%原因BYMONTH 对DELETE语句的跨月处理采用“逐月生成子 DELETE 串行执行”策略。若条件覆盖 36 个月2021-2024则生成 36 条DELETE FROM order_202101 WHERE ...并依次执行期间 MyCat 连接池被占满。解决业务侧改为按月分批删除DELETE ... WHERE create_time 2021-01-01 AND create_time 2021-02-01或在物理库层面执行DROP TABLE order_202101更高效但需确保无长事务修改server.xml中systemproperty nameprocessors8/property/system提升并发数默认 44.4 现象SELECT COUNT(*) FROM order_info WHERE create_time 2024-03-01结果比 MySQL 直查少 23%原因COUNT(*)聚合未下推到各月表而是 MyCat 拉取所有匹配行后在内存中计数当单月数据超 50 万行时触发bufferPool溢出部分结果被丢弃。解决强制使用SELECT COUNT(*) FROM (SELECT 1 FROM order_info WHERE create_time 2024-03-01) t触发子查询优化或在server.xml中调大systemproperty namebufferPoolChunkSize4096/property/system默认 1024终极方案改用SELECT SUM(cnt) FROM (SELECT COUNT(*) cnt FROM order_202403 UNION ALL SELECT COUNT(*) cnt FROM order_202404 ...) t4.5 现象MyCat 启动后show datanode显示dn1状态为INITING30 秒后变OFFLINE原因dataNode.databasedb_order_${yyyyMM}中的${yyyyMM}在启动时无法解析无上下文日期导致连接字符串为jdbc:mysql://host:3306/db_order_${yyyyMM}MySQL 拒绝连接。解决启动前手动创建至少一个物理库如CREATE DATABASE db_order_202401或修改dataNode为固定库名databasedb_order_init待 MyCat 启动成功后再通过mysql -h127.0.0.1 -P8066 -uapp_user -pmypass -e create schema db_order_202401创建月库血泪经验首次部署务必先停掉所有应用用mysql -h127.0.0.1 -P8066连上 MyCat执行show datanode确认全绿再放流量5. 验证不是跑个 SELECT 就完事四步法确认按月分表真正生效配完schema.xml和rule.xml很多人只执行SELECT * FROM order_info LIMIT 10就认为 OK。但真正的“生效”必须满足四个技术指标路由精准、建表自动、跨月可控、性能不降。下面是我每次上线前必做的四步验证清单漏一步都可能埋雷。5.1 第一步用explain确认路由节点与预期完全一致MyCat 1.6.7.6 BYMONTH 支持explain sql查看真实路由计划。这不是模拟是实际执行前的决策快照-- 连接 MyCat端口 8066 mysql -h127.0.0.1 -P8066 -uapp_user -pmypass -- 执行 explain EXPLAIN SELECT * FROM order_info WHERE create_time 2024-04-10;预期输出DATA_NODE SQL dn2 SELECT * FROM order_202404 WHERE create_time 2024-04-10关键检查点DATA_NODE必须是单个节点如dn2不能是dn1,dn2,dn3说明未识别到日期SQL中的表名必须为order_202404与create_time的2024-04匹配不能是order_${yyyyMM}说明subTables未生效若出现broadcast字样立即检查dateColumn是否拼写错误5.2 第二步触发自动建表验证物理库中表结构与 MyCat 一致BYMONTH 的自动建表功能在首次INSERT时触发。但很多人 INSERT 后查物理库发现表不存在——因为 MyCat 默认异步建表且需满足两个前提INSERT语句中dateColumn值必须非空且可解析见 4.1物理库需有CREATE TABLE权限GRANT CREATE ON db_order_202404.* TO app_user%验证脚本在 MyCat 中执行-- 插入测试数据确保 create_time 为未来月份避免与现有数据混淆 INSERT INTO order_info(id, user_id, amount, create_time) VALUES (999999, 1001, 99.9, 2025-01-01); -- 等待 3 秒查物理库假设 dn1 对应 host1:3306 -- 在 host1 的 MySQL 中执行 SHOW CREATE TABLE db_order_202501.order_202501;预期结果表结构必须与schema.xml中order_info的createTableSQL完全一致包括 ENGINE、CHARSET、COMMENT若createTableSQL未定义则表结构为 MyCat 内置模板id BIGINT PRIMARY KEY, ...此时需手动补全索引5.3 第三步跨月查询压力测试监控processor线程与 GCBETWEEN查询是性能黑洞高发区。用sysbench或自写脚本模拟真实流量# 模拟 50 并发查询 3 个月数据 for i in {1..50}; do mysql -h127.0.0.1 -P8066 -uapp_user -pmypass -e SELECT COUNT(*) FROM order_info WHERE create_time BETWEEN 2024-02-01 AND 2024-04-30; done wait监控指标在 MyCat 启动目录执行tail -f logs/mycat.log | grep route to确认是否真的只路由到dn1,dn2,dn33 个节点而非dn1,dn2,...,dn10说明月范围计算错误jstat -gc $(pgrep -f MyCatServer) 1s观察GCTGC 时间是否持续 200ms若是调大-Xmx4g默认 2gnetstat -anp | grep :8066 | wc -l连接数不应超过server.xml中systemproperty nameprocessors8/property/system× 1005.4 第四步对比 MyCat 与直连 MySQL 的执行计划差异最致命的坑是MyCat 路由正确但物理 SQL 执行效率暴跌。原因往往是WHERE条件未下推或索引失效。必须用EXPLAIN对比-- 在 MyCat 中执行 EXPLAIN SELECT * FROM order_info WHERE create_time 2024-03-01 AND user_id 1001; -- 在物理库 db_order_202403 中执行替换为真实库名 EXPLAIN SELECT * FROM order_202403 WHERE create_time 2024-03-01 AND user_id 1001;关键比对项指标MyCat EXPLAIN物理库 EXPLAIN是否正常typeref/rangeref/range若 MyCat 是ALL说明user_id索引未被识别keyidx_user_ididx_user_id若 MyCat 为空检查schema.xml中table是否配置needAddLimitfalserows12001200若 MyCat 是 120000说明条件未下推需加force push downhint我的习惯每次上线新分表规则我都会在测试库跑一遍这四步并把EXPLAIN截图存档。从那以后我每次改subTables配置都强制走一遍explain → insert → between → explain四步闭环宁可多花 20 分钟也不愿凌晨三点修线上慢查询。希望帮到你。本文还有配套的精品资源点击获取