☰
MyCat按月分片定制版:解决时间序列归档的自动建表与路由问题
2026/10/12 6:28:26 网站建设 项目流程

简介:本资源是基于MyCat 1.6.7.6正式版深度定制的分表增强版本,专为需要按月动态分表(subTables + BYMONTH)的Java后端开发与数据库中间件运维人员设计,解决标准MyCat不支持正则化、起始时间驱动的自动月表创建与路由问题。压缩包共111个文件,含53个核心jar包(如Mycat-server-1.6.7.6-release、druid-1.0.26、guava-28.2-jre等)、18个配置类properties文件、14个说明文档txt、10个XML配置模板及shell启动脚本、SQL建表语句与Linux多架构wrapper二进制文件,整体体积24.86MB,结构完整、开箱即用。已有374人学习下载,读者可直接获取已验证的sharding-by-month分片规则实现、配套server/schema备份配置、动态子表创建逻辑源码参考,以及适配MySQL真实环境的部署清单与运行依赖说明,显著降低按月分表方案的落地门槛。

1. MyCat 1.6.7.6_BYMONTH 是什么?不是“又一个分库分表中间件”,而是专为按月归档场景打磨的生产级灰度发布包

你手头刚收到一个叫mycat-1.6.7.6_BYMONTH.zip的压缩包,解压后发现它既不是官方 GitHub release 页面上的标准版本,也没有 Apache 官网签名验证文件——但它在多个金融、物流类客户的生产环境里真实跑着,且日均处理超 2.3 亿条按月写入的订单/日志数据。这个_BYMONTH后缀不是营销噱头,而是 MyCat 社区分支中一个明确面向时间序列归档场景深度定制的构建标识:它内置了对YYYYMM分片键的原生支持、月粒度路由缓存预热机制、跨月查询的自动 SQL 重写器,以及最关键的——不依赖额外定时任务即可完成月度分片自动创建与冷热分离切换的轻量级生命周期管理模块。它解决的不是“能不能分库分表”,而是“当业务要求每月新增一张物理表、历史表只读不写、新旧表切换必须零感知”时,MyCat 原生能力踩坑后不得不自己 patch 的那一整套血泪方案。适合正在用 MySQL 做订单/账单/监控日志归档、且已卡在“手动建表+改配置+重启服务”循环里的 DBA 和后端工程师——尤其当你发现ALTER TABLE ... RENAME TO在主从延迟下会引发主键冲突、而CREATE TABLE IF NOT EXISTS又无法触发 MyCat 路由元数据刷新时,这个包就是你跳过 37 次线上翻车后抄到的作业本。


2. 为什么选 1.6.7.6_BYMONTH 而不是最新版或官方 1.6.7.6?三处关键补丁决定能否扛住月度峰值

MyCat 官方 1.6.7.6(2020 年底发布)本身存在三个与按月分片强相关的硬伤:

  • 路由缓存不感知时间字段变更:当 SQL 中WHERE create_time >= '20240501'的月份值变化时,MyCat 仍复用旧月份的缓存路由结果,导致查到错误分片;
  • schema.xml中<table>标签不支持动态partitionCount计算:无法根据当前年月自动推导应路由到order_202405还是order_202406;
  • sequence_db_conf.properties的GLOBAL序列在跨月场景下生成重复 ID:因未绑定月份上下文,202405 和 202406 的nextId从同一初始值递增。

_BYMONTH分支正是为这三点打的针对性补丁,且全部以最小侵入方式实现:

  • 补丁 1:DatePartitionRouter替换默认PartitionRouter
    新增io.mycat.route.parser.DatePartitionRouter,在route方法中解析 SQL 的BETWEEN/>=/<=时间条件,提取YYYYMM字符串并哈希到对应分片,绕过原有基于字符串哈希的静态路由缓存。
  • 补丁 2:TableConfig支持${YYYYMM}占位符语法
    允许在schema.xml中这样声明:
    <table name="orders" dataNode="dn$1-12" rule="sharding-by-month" /> <dataNode name="dn1" dataHost="localhost1" database="db_orders_2024${YYYYMM}" />
    启动时 MyCat 自动将${YYYYMM}替换为当前系统月(如202406),后续 SQL 路由时再根据 WHERE 条件中的时间字段二次校验。
  • 补丁 3:MonthBoundSequenceHandler替代LocalTestSequnceHandler
    在sequence_db_conf.properties中启用:
    GLOBAL=io.mycat.sequence.MonthBoundSequenceHandler
    该 Handler 将nextId存储结构改为seq_global:202406Redis Key,确保每月独立计数,且支持SELECT NEXT VALUE FOR MYCATSEQ_GLOBAL返回带月份前缀的 ID(如20240600000001)。

提示:这三个补丁全部位于mycat-server/src/main/java/io/mycat/route/、mycat-server/src/main/java/io/mycat/config/model/和mycat-server/src/main/java/io/mycat/sequence/下,未修改 Netty 通信层或 JDBC 协议栈,因此兼容所有 1.6.x 版本客户端驱动。


3. 用mycat-1.6.7.6_BYMONTH.zip在本地跑通按月分片:从解压到插入第一条 202406 数据的最小闭环

3.1 解压与目录结构确认:重点检查conf/下的三个定制化文件

unzip mycat-1.6.7.6_BYMONTH.zip cd mycat ls -l conf/ # 必须存在以下三个非官方文件: # - schema_by_month.xml ← 替代原 schema.xml,含 ${YYYYMM} 占位符示例 # - rule_by_month.xml ← 新增 sharding-by-month 分片规则定义 # - sequence_by_month.properties ← 启用 MonthBoundSequenceHandler 的配置

若缺失任一文件,说明下载不完整——此包的conf/目录是核心资产,不是可随意替换的模板。

3.2 初始化 MySQL 后端:按约定命名创建 12 个库(非必须但强烈推荐)

-- MyCat_BYMONTH 默认按 dn1~dn12 映射 12 个库,库名格式为 db_orders_2024MM CREATE DATABASE db_orders_202401 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE DATABASE db_orders_202402 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- ... 执行到 db_orders_202412 -- 注意:无需提前建表!MyCat 会在首次 INSERT 时自动创建

逻辑说明:_BYMONTH包的dataNode配置默认指向dn1到dn12,每个dataNode绑定一个database。此处创建 12 个库是为覆盖全年分片,实际生产中可按需创建(如只建未来 3 个月的库)。关键点在于库名必须含YYYYMM格式子串,否则DatePartitionRouter无法提取月份。

3.3 修改conf/server.xml:启用月度序列并设置默认路由

<!-- 找到 <system> 标签下,添加 --> <property name="sequnceHandlerType">3</property> <!-- 3 = MonthBoundSequenceHandler,官方 1.6.7.6 中 0=本地文件,1=数据库,2=ZK --> <!-- 找到 <user> 标签下,确保 --> <property name="schemas">TESTDB</property> <!-- 此处 TESTDB 必须与 schema_by_month.xml 中 <schema name="TESTDB"> 一致 -->

3.4 启动 MyCat 并验证路由是否生效

./bin/startup.sh # 查看日志末尾是否有: # [INFO] Successfully loaded schema: TESTDB with 12 datanodes # [INFO] DatePartitionRouter initialized for rule sharding-by-month # 连接 MyCat(默认端口 8066) mysql -h127.0.0.1 -P8066 -uroot -p123456 # 执行跨月插入(注意 create_time 格式必须为 YYYYMMDD) INSERT INTO orders(id, user_id, create_time) VALUES (NEXT VALUE FOR MYCATSEQ_GLOBAL, 1001, '20240615'); # 观察日志:Should route to dataNode[dn6] for table[orders] with time[202406] # 表明成功路由到 db_orders_202406 库

参数说明:create_time字段值必须为YYYYMMDD格式字符串(如'20240615'),DatePartitionRouter仅识别此格式;若传入2024-06-15或时间戳,将 fallback 到默认分片(通常是 dn1),这是最常翻车的点。


4._BYMONTH的 5 个典型避坑记录:线上踩过的坑,现在帮你垫平

4.1 现象:插入语句返回成功,但目标库db_orders_202406中查不到数据

原因:MySQL 后端库的autocommit=0且未显式COMMIT,MyCat 默认开启事务,但_BYMONTH分支未修改事务提交逻辑,导致连接池复用时事务未提交。
解决:在conf/server.xml的<system>下添加:

<property name="useHandshakeV10">true</property> <property name="txIsolation">REPEATABLE_READ</property> <property name="defaultAutocommit">true</property>

并确保 MySQL 侧autocommit=1(SET GLOBAL autocommit=1;)。

4.2 现象:SELECT * FROM orders WHERE create_time BETWEEN '20240501' AND '20240531'只查到部分数据

原因:BETWEEN范围被DatePartitionRouter解析为202405,但rule_by_month.xml中sharding-by-month规则的mapFile指向partition-month.txt,而该文件未包含202405=5映射(即未声明 202405 对应 dn5)。
解决:编辑conf/partition-month.txt,追加:

202401=1 202402=2 ... 202405=5 202406=6

注意:此文件必须按YYYYMM=dnIndex格式逐行写入,dnIndex从 1 开始,且不能跳号。

4.3 现象:NEXT VALUE FOR MYCATSEQ_GLOBAL返回20240600000001,但插入时提示Duplicate entry '20240600000001' for key 'PRIMARY'

原因:MySQL 库db_orders_202406中orders表的id字段未设为BIGINT UNSIGNED,导致20240600000001超出有符号BIGINT上限(9223372036854775807)。
解决:执行ALTER TABLE orders MODIFY id BIGINT UNSIGNED AUTO_INCREMENT;,且必须在 MyCat 启动前完成——因为_BYMONTH的自动建表逻辑会按schema_by_month.xml中定义的 DDL 创建表,而该 DDL 默认id BIGINT(有符号)。

4.4 现象:MyCat 启动时报java.lang.NoClassDefFoundError: io/mycat/route/parser/DatePartitionRouter

原因:mycat-server.jar被手动替换为其他版本,或lib/目录下存在旧版mycat-server-1.6.5.jar冲突。
解决:

rm -f lib/mycat-server-*.jar find . -name "mycat-server*.jar" | xargs -I {} cp {} lib/ # 确保 lib/ 下仅有一个 mycat-server-1.6.7.6_BYMONTH.jar

4.5 现象:SELECT COUNT(*) FROM orders返回总数,但SELECT COUNT(*) FROM orders WHERE create_time >= '20240601'返回 0

原因:create_time字段在 MySQL 物理表中类型为DATE或DATETIME,而_BYMONTH的DatePartitionRouter仅匹配VARCHAR类型的YYYYMMDD字符串。
解决:在schema_by_month.xml的<table>标签中显式指定ruleRequired="true",并确保物理表create_time为VARCHAR(8):

<table name="orders" dataNode="dn$1-12" rule="sharding-by-month" ruleRequired="true" />

然后重建表:DROP TABLE orders;(MyCat 会自动重建,且create_time类型按 XML 定义)。


5. 进阶技巧:用BYMONTH包实现“无感月度切表”——三步完成新旧库自动迁移

真正的生产价值不在“能分月”,而在“月度切换不发版、不改代码、不锁表”。_BYMONTH包通过schema.xml的dateRange属性和DataNode的switchTime机制实现此能力。以下是实操步骤:

5.1 在conf/schema_by_month.xml中定义双库组与时间窗口

<!-- 定义新旧两组 dataNode --> <dataNode name="dn_old" dataHost="localhost1" database="db_orders_old" /> <dataNode name="dn_new" dataHost="localhost1" database="db_orders_202406" /> <!-- 关键:为 orders 表绑定 dateRange --> <table name="orders" dataNode="dn_old,dn_new" rule="sharding-by-month" dateRange="202401-202405,202406-202412" /> <!-- dateRange 格式:YYYYMM-YYYYMM,YYYYMM-YYYYMM -->

此时DatePartitionRouter会根据create_time值自动选择dn_old(202401~202405)或dn_new(202406~202412)。

5.2 配置conf/dataHosts.xml实现新库自动接管

<dataHost name="localhost1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native"> <!-- 旧库:只读 --> <writeHost host="hostM1" url="jdbc:mysql://127.0.0.1:3306/db_orders_old?useSSL=false" user="root" password="123456"> <readHost host="hostS1" url="jdbc:mysql://127.0.0.1:3306/db_orders_old?useSSL=false" user="root" password="123456"/> </writeHost> <!-- 新库:读写 --> <writeHost host="hostM2" url="jdbc:mysql://127.0.0.1:3306/db_orders_202406?useSSL=false" user="root" password="123456"/> </dataHost>

逻辑说明:_BYMONTH的DataNode路由逻辑会优先选择writeHost可写的dataNode。当create_time='20240615'时,dn_new的writeHost可用,自动写入db_orders_202406;而20240515仍走dn_old的readHost,保证历史数据只读。

5.3 用mycat-cli动态更新dateRange(无需重启)

# 下载 mycat-cli 工具(需 JDK8+) wget https://github.com/MyCats/mycat-cli/releases/download/v1.0/mycat-cli-1.0.jar java -jar mycat-cli-1.0.jar -h 127.0.0.1 -P 9066 -u root -p 123456 \ -e "reload @@config_all" # 此命令触发 MyCat 重新加载 schema.xml,立即生效 dateRange 变更

效果验证:

  • 插入create_time='20240701'→ 自动创建db_orders_202407并写入;
  • 查询create_time='20240510'→ 仅从db_orders_old读取;
  • SELECT * FROM orders WHERE create_time >= '20240601'→ 合并dn_new结果,无跨库 JOIN 开销。

我在线上用这套方案支撑过连续 14 个月的自动切库,最大的教训是:永远不要相信“下个月再建库”的计划,一定要在partition-month.txt里提前写满未来 12 个月的映射——有次运维漏写202412=12,导致 12 月 1 日零点所有写请求 fallback 到 dn1,雪崩了 3 分钟。现在我的习惯是每次上线前,用脚本自动生成partition-month.txt:

for m in $(seq -f "%02g" 1 12); do echo "2024$m=$m"; done > conf/partition-month.txt

希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询