
简介面向 Java 后端开发与数据库运维人员的 Mycat2 部署入门包用于快速搭建分布式数据库中间件的实验环境。Mycat2 负责数据分片、SQL 路由、读写分离与负载均衡这个包提供可直接解压运行的完整目录便于在本地或远程服务器上验证分片规则、节点状态与故障切换机制。压缩包共 51 个文件约 1.2MB其中包含 9 个 sql 初始化脚本、6 个 json 与 xml 配置、覆盖 Linux、Windows、macOS 等多平台的 so、dll、jnilib 本地库以及 mycat、mycat.bat 等启动脚本和 wrapper 组件。另外还有 schemas、datasources、clusters、sequences 等 conf 配置内容结构清晰便于按模块查阅和调整。目前已有 1072 人学习适合作为 Mycat2 入门验证与二次配置的基础参考也可用于对比不同分片策略、观察 SQL 执行日志与数据节点运行情况。1. 从一次「连不上 8066」说起mycat2 基础安装包解决的是什么问题前段时间帮人看一个查询超时的现场后端两台 MySQL一台 QPS 打满一台几乎空转典型的只做了读写分离、没做分片。这类场景里mycat2 基础安装包往往是最省事的第一步——解压、改四份配置、起 8066应用侧连的还是 MySQL 协议业务代码基本不用动。它干的事是把物理分片藏在逻辑库、逻辑表后面让应用像用单库一样用多库解决的是单表体量和连接数的天花板。适合正在给几千万行的老表找拆库方案、又不想直接上重型分布式数据库的团队也适合想先把分库分表跑通再谈治理的个人开发者。反过来SQL 花样特别多、对跨库强一致有硬要求的业务得先掂量中间件的兼容边界。下面按「配置怎么组织 → 装起来 → 拆一张表 → 踩坑 → 验证」的顺序拆。2. mycat2 的配置体系为什么把 schema.xml 换成了 YAML 与 JSON拿到安装包先别急着敲启动脚本。mycat2 和 1.x 最大的差别不在功能表上而在配置形态1.x 那套schema.xml、rule.xml、server.xml三件套在 2.x 里被拆成了server.yaml加若干份 json。不理解这个拆分后面每改一处都会怀疑人生。2.1 Calcite 接管 SQL 解析之后配置为什么要跟着变1.x 的路由靠 XML 里手写的算法声明SQL 进来自家解析器拆一遍规则表一长就容易出对不上的情况。2.x 把 SQL 解析交给了 Calcite路由计算建立在元数据之上——也就是说中间件得清楚每张逻辑表长什么样、分片键是哪列、物理表在哪个数据源上。这些信息要能被程序读写、能在线增删XML 那种「改完必须重启、结构还难校验」的形态就不合适了。换成 YAML JSON 之后有个直接好处建库建表可以走注解 SQL。连上 8066 敲一条带注解的语句就能动态加数据源、加逻辑库不用停机改文件。代价是配置文件从「一份大 XML」变成了「一堆小文件」谁管谁、谁先加载得自己捋清楚。我一般会在动手前先把conf目录整个看一遍看清楚哪几个文件是安装包自带的示例、哪几个是真正生效的。示例文件别删它是你校验字段名对不对的唯一参照。还有一个隐性成本分片算法。1.x 的rule.xml里配algorithm加algorithmExpression2.x 改成在 schema 里写function表达式语法不一样老配置搬不过来。如果你手上有一套跑着 1.x 的系统迁移的大头不在安装而在把每个分片规则重新翻译一遍并且逐条用 EXPLAIN 验证路由结果和原来一致。2.2 四类配置各管什么别改错文件文件管什么改动后是否需要重启conf/server.yaml服务端口、字符集、事务类型、集群与全局开关端口类必须重启部分开关支持重载conf/users/*.user.json登录用户、密码、可访问的逻辑库列表新建用户通常重载即可conf/datasources/*.datasource.json后端物理库的连接串、账号、连接池上下限连接串改动需重启conf/schemas/*.schema.json逻辑库、逻辑表、分片键、分片算法、物理表映射新增逻辑表多数可热加载加载关系是自下而上的数据源先能被连上schema 才有地方挂物理表schema 建好了user 里的schemas才有意义。所以排错顺序也应该反过来——应用连不上先看 user 里的库名写没写错再看 schema 有没有真的加载最后才怀疑数据源。提示conf目录里带.json后缀的文件名不要随意改文件名和内部name字段对不上时加载行为在不同小版本上表现不一致很容易查半天。2.3 装之前先把环境对齐JDK 与后端认证插件这一步跳过的人后面十有八九会在启动日志里看到一堆看不懂的异常。mycat2 是 Java 服务对 JDK 版本敏感后端如果是 MySQL 8默认认证插件也可能直接把连接挡在门外。先花两分钟确认环境# 1. 确认 JDK 版本建议 8 或 11高版本要先小流量验证 java -version # 2. 确认后端 MySQL 可达并看清版本 mysql -h 192.168.1.10 -P 3306 -uroot -p -e select version(); # 3. 如果后端是 MySQL 8把中间件用的账号切成兼容性更好的认证方式 mysql -h 192.168.1.10 -P 3306 -uroot -p -e ALTER USER mycat_rw% IDENTIFIED WITH mysql_native_password BY 换成你的密码; FLUSH PRIVILEGES; # 4. 确认字符集与时区避免中文乱码和时间字段偏移 mysql -h 192.168.1.10 -P 3306 -uroot -p -e show variables like character_set_server; show variables like lower_case_table_names; show variables like time_zone;java -version输出里如果主版本号是 17 及以上先别急着往下走用一个小库跑通全流程再说反射相关的报错在启动阶段不一定暴露往往要等到第一次执行带分片的 SQL 才炸。第三步的ALTER USER只影响中间件使用的那个账号业务账号可以不动这一步不做后面日志里大概率是一句语焉不详的认证失败。第四步的lower_case_table_names尤其关键——中间件侧和后端不一致时建表能成功查询却报表不存在这种问题靠猜是猜不出来的。3. 安装包落地从解压到第一条 SQL 跑通环境对齐之后就是常规动作了。这一段没什么玄学但顺序错了会很折磨人先起服务再想配置等于给自己制造盲区。3.1 目录结构与启动脚本先看清楚再敲命令# 解压到统一目录路径里不要带空格和中文 mkdir -p /opt/middleware unzip mycat2-basic.zip -d /opt/middleware/ cd /opt/middleware/mycat # 看一眼目录构成心里有数 ls -l # bin/ 启动、停止、状态脚本 # conf/ 全部配置重点看这里 # lib/ 依赖 jar不要手动增删 # logs/ 运行日志排错第一现场 # Linux 下脚本默认可能没有执行权限补上 chmod x bin/* # 先看状态确认当前没在跑 bin/mycat statusbin/mycat一般支持start、stop、restart、status、console几个子命令。日常用start放后台调配置阶段我建议用console前台启动——日志直接刷在终端上改一个字段重启一次反馈最快不用去logs目录里翻。chmod那一步看着多余但压缩包跨平台传递之后权限位丢失是常事报「权限不够」的时候人容易往别处想。lib目录不要手动塞 jar。有人为了让中间件连上某个特殊版本的驱动把 jar 丢进去覆盖结果类冲突报错堆了几十行。驱动版本要换走正规依赖管理别在这个目录上做实验。3.2 三个最小配置写完再启动第一次启动只需要三份东西一个后端数据源、一个逻辑库、一个能登录的用户。先写数据源{ name: ds0, dbType: mysql, instanceType: JDBC, url: jdbc:mysql://192.168.1.10:3306/order_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse, user: mycat_rw, password: 换成你的密码, minCon: 2, maxCon: 100, maxIdleCon: 20, idleTimeout: 60000, initSql: select 1, readOnly: false }name是这份数据源在中间件里的唯一标识schema 里要靠它找物理库取名尽量带业务含义。url里的serverTimezone建议显式写死不写的话时间字段在某些组合下会整体偏移八小时排查起来非常费劲。maxCon是单数据源的最大连接数不是全局的两个分片库各配 100 就是 200加中间件自身开销后端max_connections要留足余量。minCon别设 0冷启动时首批请求会明显变慢。接着写逻辑库最小版本只需要名字和默认数据源{ name: order_logic, defaultTargetName: ds0, customTables: {}, globalTables: {}, shardingTables: {} }name就是应用连接后show databases看到的库名也是 user 配置里要引用的名字。分片表先留空等连通性验证完再往里加一次只改一个变量。最后是用户{ username: app_user, password: 换成你的密码, ip: *, transactionType: xa, schemas: [order_logic] }schemas是个数组写的是逻辑库名不是后端物理库名这里写错是最常见的「连上了但看不到库」。ip生产环境不要留*按网段收紧。transactionType决定跨分片事务的处理方式本地事务够用的场景可以先按默认走涉及多库写入再评估。注意三份 json 的字段名在不同小版本上偶有增删写之前对照conf目录里自带的示例文件核一遍比照文档抄更靠谱。3.3 连库验活8066 和 9066 是两回事# 前台启动观察日志直到出现监听成功 bin/mycat console # 另开一个终端用 mysql 客户端连业务端口 mysql -h127.0.0.1 -P8066 -uapp_user -p # 进去之后先做最小验证 show databases; use order_logic; select 1;能show databases出来order_logic说明用户、逻辑库、数据源这条链已经通了。这时候show tables大概率是空的属于正常现象——逻辑表还没配中间件不会去物理库反射表结构。别在这个阶段怀疑连接有问题。8066 是业务端口应用连这个9066 是管理端口用来做运维类操作具体命令各版本差异较大以安装包内说明为准。两个端口别混用用管理端口跑业务 SQL 会得到一堆莫名其妙的错误。4. 分库分表配置实战把一张订单表拆到两个库连通性没问题接下来才是这个安装包真正值钱的地方——让一张逻辑表落到多个物理库上。这里翻车的概率远高于安装阶段因为错一步不会报错只会「结果对但很慢」或者「结果少了一半」。4.1 物理表先建好DDL 不归中间件管中间件不会替你建物理表两个分片库里的表结构必须完全一致-- 在 192.168.1.10:3306 的 order_db_0 和 order_db_1 上分别执行 CREATE TABLE orders ( order_id BIGINT NOT NULL, user_id BIGINT NOT NULL, amount DECIMAL(12,2) NOT NULL DEFAULT 0.00, status TINYINT NOT NULL DEFAULT 0, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (order_id), KEY idx_user (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;分片键我选了order_id不是user_id。理由是订单类查询九成以上都带订单号按order_id取模能做到单库命中如果按user_id分查订单号就得全路由。这里没有标准答案判断依据是「最高频的查询条件带哪一列」。索引也要跟着分片键设计order_id是主键天然带索引user_id单独留一个普通索引应对用户维度查询。两个库的表名可以相同中间件靠数据源区分表名不同反而要在 schema 里做额外映射没必要给自己加活。4.2 schema.json 的字段逐个填{ name: order_logic, defaultTargetName: ds0, customTables: {}, globalTables: {}, shardingTables: { orders: { createTableSQL: CREATE TABLE orders (order_id BIGINT NOT NULL, user_id BIGINT NOT NULL, amount DECIMAL(12,2), status TINYINT, created_at DATETIME, PRIMARY KEY(order_id)), function: mod_hash, columns: [order_id], tableCount: 2, shardingQuery: mod_hash(order_id, 2), targetNames: [ds0, ds1] } } }几个字段值得单独说。function是分片算法名mod_hash表示取模散列同族还有按范围、按日期切的算法选哪种取决于数据增长形态——订单这种持续写入、按 ID 查为主的场景取模最省心如果是按月份归档的日志表范围切更合适。columns必须写分片键写错列名不会报错但路由结果会静默变成全路由。tableCount要和后端实际的分片数对上填 2 而后端只有 1 个库写入会在一半的请求上失败。targetNames是物理数据源列表顺序和取模结果的对应关系要自己确认一遍别假设。createTableSQL不是给中间件执行用的它用于让 Calcite 知道表的字段类型和长度从而做类型推导和 SQL 校验。字段类型写窄了可能出现「明明数据能插进去查询却报类型不匹配」。提示改完 schema 后先restart再验证热加载在大改表结构时不一定完全生效重启一次能省掉一轮误判。4.3 用 EXPLAIN 验证路由而不是靠猜-- 带上分片键应该只命中一个物理库 EXPLAIN SELECT * FROM orders WHERE order_id 10086; -- 不带分片键会全路由这是预期行为不是 bug EXPLAIN SELECT * FROM orders WHERE status 1; -- 插入也看一眼目标库 EXPLAIN INSERT INTO orders(order_id, user_id, amount) VALUES (10086, 7, 99.00);EXPLAIN的输出里重点看两处一是最终下发的物理 SQL二是命中的数据源和物理表名。带order_id的查询应该只出现一个库出现两个就说明分片配置没生效或者字段没对上。不带分片键的查询必然全路由这时候要看的是中间件有没有做结果归并、归并后的排序是否正确——数据量大的时候全路由查询就是性能黑洞得从业务侧改 SQL而不是指望中间件优化。验证顺序建议是单条插入 → 按分片键查 → 按非分片键查 → 跨库聚合count、group by。每步都过一次EXPLAIN出问题时能立刻定位到是哪一类路由出了偏差比事后翻日志高效得多。5. 避坑与排查五个把我卡到半夜的现场这一段是按「现象 → 原因 → 解决」整理的实录都是我或者身边人真撞过的。装中间件这件事能一次跑通的比例其实不高大部分时间花在排错上。5.1 启动直接退出日志里是版本号相关的异常现象bin/mycat start回车之后几秒进程就没了logs目录里的启动日志出现UnsupportedClassVersionError或者一串反射相关的IllegalAccessException。原因JDK 主版本和安装包编译时使用的版本不匹配。高版本 JDK 在模块访问上有额外限制中间件的一些反射调用会被拦下来而且不一定在启动阶段暴露。解决先把 JDK 切到 8 或 11 验证功能是否正常确认可用之后再评估要不要升。切换方式我一般用环境变量临时指定不动全局JAVA_HOME# 临时指定 JDK 11 启动方便对比 export JAVA_HOME/usr/lib/jvm/java-11-openjdk export PATH$JAVA_HOME/bin:$PATH bin/mycat console如果切了版本就好了问题定性完成别再往配置上找。5.2 服务起来了但连后端报认证失败现象8066 端口能连上一执行查询就报连接后端失败日志里能看到认证相关的关键字但错误信息很含糊。原因后端 MySQL 8 默认使用caching_sha2_password中间件使用的驱动版本对这套认证流程支持不完整。解决把中间件专用账号切成mysql_native_password见第 2 章的ALTER USER只影响这一个账号。顺带检查useSSL参数内网环境通常关掉不关的话握手阶段多一轮协商某些组合下也会失败。5.3 用户能登录但show databases是空的现象mysql -P8066 -uapp_user -p登录成功执行show databases只看到系统库业务逻辑库不见了。原因user.json里的schemas写的是后端物理库名或者逻辑库配置文件根本没被加载。还有一种情况是文件名和内部name字段不一致加载时被跳过了。解决三步核对——逻辑库 json 里的name、user.json里schemas数组的值、应用连接时use的库名三者必须完全一致大小写也要一致。核完还是空的去看启动日志里有没有该 schema 的加载记录没有记录就是文件没被扫到检查文件后缀和目录层级。5.4 单条查询很快列表页却慢到超时现象按 ID 查毫秒级返回后台列表页带个状态筛选就几十秒。原因查询条件里没有分片键中间件把 SQL 广播到所有分片再把结果拉回来归并排序。分片数越多越慢而且这是设计使然不是配置能调优掉的。解决从 SQL 侧改。列表页尽量把分片键带进条件带不进去的考虑建一张按业务维度重新分片的冗余表或者把这类查询挪到独立的检索存储上。中间件不是搜索引擎别指望它把全路由变成索引扫描。改之前先EXPLAIN确认是不是真的全路由别把慢查询的锅甩错地方。5.5 改了配置不生效重启后反而更糟现象改完 schema 里的分片数查询结果和改之前一模一样顺手重启一次某些表直接查不到了。原因一部分配置支持热加载一部分只认启动时读到的值更麻烦的是改配置时把物理分片数改大了但后端库还没建对应数量的表。解决养成「改配置 → 先确认物理侧就绪 → 再重启 → 立即 EXPLAIN 验证」的固定动作。物理侧没准备好就重启等于把中间件的元数据指向了一批不存在的表。我现在的习惯是每次改分片相关配置之前先把两个库的表结构、数据分布、数据源列表写在纸上对一遍再动手。6. 进阶把读写分离接上并用压测确认没白折腾单库拆完只是第一步读多写少的业务还得把从库用起来。做法是在 schema 的targetNames里给每个分片配一组主从数据源主库readOnly设为false从库设为true中间件按语句类型分流。{ name: order_logic, defaultTargetName: ds0, replicaDatasources: [ds0_slave, ds1_slave], shardingTables: { orders: { function: mod_hash, columns: [order_id], tableCount: 2, targetNames: [ds0, ds1] } } }从库的 datasource 配置和主库一样只是url指向从库、readOnly改成true。这里有个容易忽略的点主从延迟。写完立刻查的场景读请求被分到从库就会读到旧数据表现是「刚下单查不到订单」。业务侧要么把这类查询强制走主库要么接受延迟并在交互上做兜底提示中间件本身不解决一致性问题。验证不能只看功能通不通得看数据分布对不对、性能有没有真的改善。我用得最多的是三步对照验证项做法期望结果数据分布两个物理库分别count(*)两库行数接近无单边倾斜路由正确性抽样 100 个order_id走EXPLAIN全部单库命中无全路由读写分流从库开启慢日志后跑读请求从库有查询记录主库读请求下降第一步如果出现明显倾斜多半是分片键取值分布不均比如自增 ID 取模本身均匀但业务上大量请求集中在少数几个 ID 上那就得换散列函数。第二步是全量回归改一次分片配置就跑一次别偷懒。第三步要观察一段时间因为连接池预热和从库延迟都会影响第一时间的判断。压测我一般用简单的并发脚本打 8066而不是直接打后端库——中间件自身的连接池、SQL 解析、结果归并都是开销绕过它测出来的数字没有参考价值。压测期间盯着logs目录的日志增长速度和后端库的Threads_connected中间件层的瓶颈往往先体现在连接数上而不是 CPU。从那以后我每次上新环境都会先把「JDK 版本、后端认证插件、用户 schemas 三个值、物理表是否就绪」这四项写在便签上逐条打勾再敲启动命令——这四项里任何一项没对齐后面都会以完全不相干的形式报出来。分片键的选择我也不会在开会时拍板而是拉一周的真实慢查询日志统计条件分布再决定按哪列切。希望帮到你。本文还有配套的精品资源点击获取