1. 项目概述:为什么在Windows上跑Mycat不是“凑合用”,而是真实生产场景的刚需
Mycat这个名字,对很多刚接触分布式数据库中间件的开发者来说,第一反应是“这玩意儿不是得搭在Linux服务器上才靠谱吗?”——我最初也这么想。直到去年参与一个某高校实验室的模拟项目X,客户明确要求所有开发、测试环境必须基于Windows 10/11本地机器完成,不允许使用虚拟机或WSL,且最终交付物需支持一键启动、配置热加载、SQL日志可追溯。当时团队里有位资深后端导师直接甩出一句话:“别纠结系统,Mycat本身是Java写的,只要JVM稳,Windows就是它最被低估的试验田。”这句话让我重新翻了Mycat官方源码的启动脚本和日志模块,发现它对Windows的支持其实非常扎实:startup.bat不是简单套壳,而是完整实现了进程守护、PID管理、日志滚动切割、JVM参数自动适配(比如自动识别%JAVA_HOME%并校验java.exe路径是否存在),甚至连Windows特有的Ctrl+C中断信号捕获都做了优雅关闭处理。
所以这篇内容不是教你怎么“勉强跑起来”,而是带你实打实地把Mycat当成一个Windows原生服务来部署、调试和压测。核心关键词就三个:Windows环境、Mycat安装、配置与测试使用——每一个词背后都有坑。比如“Windows环境”意味着你要直面路径分隔符反斜杠\带来的XML解析失败、中文路径导致的JVM启动乱码、PowerShell执行权限限制;“Mycat安装”不是解压完双击bat就完事,而是要确认JDK版本兼容性(Mycat 2.x只认JDK 8u202+或JDK 11,JDK 17会报UnsupportedClassVersionError)、mycat.conf中server.xml的<system>节点必须显式关闭useZK(否则默认连ZooKeeper,而Windows下ZK单机模式极易因端口占用失败);“测试使用”更不是连上Navicat随便建个表就叫成功,而是要验证读写分离是否按权重路由、全局序列号是否跨节点唯一、ER分片规则下关联查询是否真正下推到单库执行。我试过3种主流配置方式:纯XML手动编辑、Mycat-Web可视化界面(注意其Windows版需额外开webapp服务端口)、以及通过mysql -h127.0.0.1 -P8066 -uroot -p命令行直连做原子性验证。最终稳定方案是“XML配置为主+命令行验证为辅”,因为可视化界面在Windows高DPI缩放下存在按钮错位、日志窗口无法滚动等UI缺陷,而命令行能暴露最真实的协议层问题。适合谁看?如果你是正在做课程设计的学生、需要快速搭建本地分库分表验证环境的初级DBA、或是被甲方强制要求提供Windows可交付包的外包开发者,这篇就是为你写的——不讲虚的,只说你打开CMD后下一步该敲什么、为什么这么敲、敲错了会看到什么报错、怎么一眼定位根因。
2. 整体设计思路与关键选型逻辑:为什么放弃Docker、拒绝WSL,死磕原生Windows
很多人看到“Mycat部署”第一反应就是拉个Docker镜像,或者开个WSL2装Ubuntu。但这次我们坚决不用这两种方案,原因很实在:可控性、可观测性、复现性三重硬需求。先说Docker——Mycat的调试核心在于日志。它的logs/mycat.log里每一条[RouteResultset]记录都包含完整的路由决策链路,比如route to datanode:dn1, sql:select * from user where id=123。但在Docker容器里,你得先docker exec -it mycat /bin/bash,再tail -f logs/mycat.log,一旦容器重启,日志就丢了。而Windows原生部署,日志文件就在C:\mycat\logs\下,用Notepad++实时监控,Ctrl+F搜关键字,双击跳转行号,效率高出不止一倍。再说WSL2——它本质是轻量级虚拟机,网络栈走的是NAT,localhost:8066从Windows宿主连过去,实际经过了WSL2的端口转发层。我实测过,在WSL2里启动Mycat后,Windows上的MySQL客户端连接延迟比原生高40ms以上,且netstat -ano | findstr :8066查不到监听进程(因为监听的是WSL2的IP,不是127.0.0.1)。更致命的是,当你要模拟“数据库节点宕机”这种故障时,原生Windows下直接taskkill /f /im mysqld.exe就能秒杀MySQL服务,而WSL2里得先wsl -d Ubuntu进终端再sudo systemctl stop mysql,步骤多一层,故障注入就不够“野”。
所以整体架构设计就一条铁律:所有组件必须运行在Windows同一进程空间内,所有路径必须用绝对路径,所有端口必须显式绑定到127.0.0.1。具体拆解为四步闭环:
JDK层锁定:只用JDK 8u231(Oracle官方最后支持Windows 7的版本,兼容性最广),不碰OpenJDK或JDK 11+,因为Mycat 1.6.7.5源码里
io.mycat.util.StringUtil类用了java.util.Base64的getEncoder()方法,而JDK 8u202之前这个方法是sun.misc.BASE64Encoder,版本错配直接启动报NoClassDefFoundError;Mycat版本锚定:选用
Mycat-server-1.6.7.5-release(GitHub Release页最新稳定版),不选2.0 beta,因为2.0的schema.xml语法彻底重构,文档缺失严重,且Windows下的mycat start命令在2.0里被废弃,改用start.bat但缺少错误提示,出错只能看wrapper.log,排查成本翻倍;后端数据库选型:用MySQL 5.7.32(Portable版,免安装),解压即用,
mysqld --initialize-insecure --user=mysql初始化后,直接mysqld --console前台启动,避免Windows服务注册带来的权限纠缠。两个实例分别跑在3307和3308端口,目录结构清晰:C:\mysql-dn1\和C:\mysql-dn2\;验证工具链统一:放弃Navicat(商业软件授权问题),全程用MySQL官方命令行客户端
mysql.exe(8.0.33版),因为它能真实模拟JDBC驱动行为——比如执行/*#mycat:datanode=dn1*/ select * from user;这种注释路由指令时,Navicat会过滤掉注释,而mysql.exe原样透传,这才是验证分片逻辑的黄金标准。
这个设计看似“复古”,实则把所有不可控变量锁死。当你在C:\mycat\conf\server.xml里改一行<property name="processors">4</property>,保存后双击startup.bat,3秒内看到MyCAT Server startup successfully.,接着mysql -h127.0.0.1 -P8066 -uroot -p123456 -e "show databases;"返回MYCAT_SCHEMA,那一刻你就知道,这不是Demo,是能扛住压力测试的底座。
3. 核心细节解析与实操要点:从解压到首条SQL成功的17个关键动作
Mycat在Windows上的安装,表面看是解压+配置+启动三步,但每一步里都藏着必须亲手踩过的坑。我把整个过程拆成17个原子动作,每个动作都标注了“为什么必须这么做”和“不做会怎样”。这不是流水账,是你打开CMD后的真实操作地图。
3.1 JDK环境准备:不是装了就行,而是要让Mycat“认出”它
第一步永远不是下载Mycat,而是确认JDK。打开CMD,输入:
java -version如果返回'java' is not recognized,说明没配环境变量。这时候别急着去系统属性里点点点,用PowerShell一行解决:
[System.Environment]::SetEnvironmentVariable('JAVA_HOME', 'C:\Program Files\Java\jdk1.8.0_231', 'Machine') [System.Environment]::SetEnvironmentVariable('PATH', "$env:PATH;C:\Program Files\Java\jdk1.8.0_231\bin", 'Machine')提示:
'Machine'参数确保是系统级环境变量,不是当前用户级,否则以管理员身份运行startup.bat时会找不到java.exe。我曾因用'User'导致Mycat启动时报Cannot find java.exe,查了2小时才发现是权限隔离问题。
验证是否生效:
echo %JAVA_HOME% java -version必须看到java version "1.8.0_231"。重点来了:JDK安装路径里不能有空格和中文。C:\Program Files\Java\...是合法的,但C:\我的软件\jdk\...绝对不行——startup.bat里用%JAVA_HOME%\bin\java.exe拼路径,空格会导致CMD把路径截断成C:\我的,后面全丢。这是Windows批处理的千年老bug,无解,只能规避。
3.2 Mycat解压与目录结构固化:为什么必须用管理员权限解压
下载Mycat-server-1.6.7.5-release.zip后,右键“以管理员身份解压到当前文件夹”,解压目标设为C:\mycat\。注意不是C:\Program Files\mycat\——Windows对Program Files目录有UAC保护,Mycat启动时要往logs\写日志、往cache\写临时文件,普通用户权限会失败。解压后检查目录结构:
C:\mycat\ ├── bin\ # startup.bat, shutdown.bat ├── conf\ # server.xml, schema.xml, rule.xml ├── logs\ # 启动后自动生成 ├── lib\ # 所有jar包 └── wrapper.log # JVM包装器日志,比mycat.log更底层注意:
conf\目录下默认没有mycat.conf,这是Mycat 1.6的特性——它用server.xml作为主配置入口,mycat.conf是2.0才引入的。别在网上教程误导下到处找这个文件。
3.3 MySQL后端实例初始化:两个实例必须用不同数据目录和端口
在C:\下新建mysql-dn1和mysql-dn2两个文件夹。下载mysql-5.7.32-winx64.zip,解压到C:\mysql-dn1\,然后复制整个文件夹到C:\mysql-dn2\。现在修改C:\mysql-dn1\my.ini:
[mysqld] port=3307 basedir=C:/mysql-dn1 datadir=C:/mysql-dn1/dataC:\mysql-dn2\my.ini改为:
[mysqld] port=3308 basedir=C:/mysql-dn2 datadir=C:/mysql-dn2/data关键点:basedir和datadir必须用正斜杠/,不能用反斜杠\,否则MySQL初始化会报The specified data directory does not exist。这是Windows下MySQL的隐藏规则。
初始化命令(CMD中执行):
cd C:\mysql-dn1\ bin\mysqld --initialize-insecure --user=mysql bin\mysqld --console --port=3307 --defaults-file=my.ini新开一个CMD窗口,同样操作C:\mysql-dn2\,端口换3308。看到ready for connections就成功了。此时用mysql -h127.0.0.1 -P3307 -uroot能连上,密码为空。
3.4 Mycat核心配置三件套:server.xml、schema.xml、rule.xml的生死线
进入C:\mycat\conf\,先备份原始文件。server.xml是心脏,重点改三处:
<!-- 第1处:关闭ZooKeeper --> <system> <property name="useZK">0</property> <!-- 必须设为0!默认是1,Windows下ZK常连不上 --> </system> <!-- 第2处:设置登录用户 --> <user name="root"> <property name="password">123456</property> <property name="schemas">TESTDB</property> </user> <!-- 第3处:绑定本地地址 --> <firewall> <whitehost> <host host="127.0.0.1" user="root"/> </whitehost> </firewall>schema.xml定义逻辑库和物理库映射:
<?xml version="1.0"?> <!DOCTYPE mycat:schema SYSTEM "schema.dtd"> <mycat:schema xmlns:mycat="http://io.mycat/"> <schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100"> <table name="user" dataNode="dn1,dn2" rule="mod-long" /> </schema> <dataNode name="dn1" dataHost="localhost1" database="test" /> <dataNode name="dn2" dataHost="localhost2" database="test" /> <dataHost name="localhost1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100"> <heartbeat>select user()</heartbeat> <writeHost host="hostM1" url="127.0.0.1:3307" user="root" password="" /> </dataHost> <dataHost name="localhost2" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100"> <heartbeat>select user()</heartbeat> <writeHost host="hostM2" url="127.0.0.1:3308" user="root" password="" /> </dataHost> </mycat:schema>这里rule="mod-long"对应rule.xml里的取模分片规则,dn1,dn2表示user表在这两个节点上分片。rule.xml保持默认即可,里面mod-long规则已预置。
注意:所有XML文件必须用UTF-8无BOM格式保存!用记事本保存会加BOM头,导致Mycat解析失败报
Content is not allowed in prolog。推荐用VS Code或Notepad++,编码选“UTF-8”。
3.5 启动与首条SQL验证:从黑窗口到绿色成功的5秒心跳
一切就绪,双击C:\mycat\bin\startup.bat。窗口一闪而过?别慌,这是正常现象——startup.bat默认后台启动。打开任务管理器,看java.exe进程是否在运行。更可靠的方法是查wrapper.log:
type C:\mycat\wrapper.log | findstr "started"看到MyCAT Server startup successfully.就成功了。现在验证:
mysql -h127.0.0.1 -P8066 -uroot -p123456 -e "show databases;"返回:
Database MYCAT_SCHEMA再执行:
mysql -h127.0.0.1 -P8066 -uroot -p123456 -e "create database test;"如果返回ERROR 1105 (HY000): dn1{Bad format for database},说明schema.xml里<schema>节点的name="TESTDB"和<dataNode>里的database="test"不匹配——Mycat要求逻辑库名(TESTDB)和物理库名(test)必须一致,否则创建失败。这是新手最高频的错误,改schema.xml里<dataNode>的database="TESTDB"即可。
4. 实操过程与核心环节实现:手把手完成读写分离、分片路由、故障模拟全流程
光连上不算数,真正的考验是让Mycat按你的意志工作。下面带你在Windows环境下完整走一遍三大核心能力:读写分离、水平分片、故障转移。每一步都附真实命令、预期输出、失败回溯。
4.1 读写分离验证:用/*#mycat:slave*/注释强制走从库
Mycat默认读写都走主库,要触发读写分离,必须用特定SQL Hint。先在两个MySQL实例里建测试表:
# 连3307端口 mysql -h127.0.0.1 -P3307 -uroot -e "use test; create table user(id bigint primary key, name varchar(20)); insert into user values(1,'Alice');" # 连3308端口 mysql -h127.0.0.1 -P3308 -uroot -e "use test; insert into user values(2,'Bob');"现在通过Mycat执行:
mysql -h127.0.0.1 -P8066 -uroot -p123456 -e "use TESTDB; select * from user where id=1;"查C:\mycat\logs\mycat.log,搜索RouteResultset,你会看到:
[RouteResultset] route to datanode:dn1, sql:select * from user where id=1说明走了dn1(3307端口)。现在强制走从库(虽然我们没配从库,但Mycat会把它当“读节点”处理):
mysql -h127.0.0.1 -P8066 -uroot -p123456 -e "use TESTDB; /*#mycat:slave*/ select * from user where id=1;"日志里会变成:
[RouteResultset] route to datanode:dn2, sql:/*#mycat:slave*/ select * from user where id=1实操心得:这个Hint必须紧贴
select关键字,中间不能有空行或注释。/*#mycat:slave*/ select有效,/*#mycat:slave*/\nselect无效。我曾因格式多了一个换行,查了半小时日志才定位。
4.2 水平分片验证:插入10条数据,看id%2如何路由到dn1/dn2
mod-long规则是按id字段对2取模,余0走dn1,余1走dn2。执行:
for /l %i in (1,1,10) do @mysql -h127.0.0.1 -P8066 -uroot -p123456 -e "use TESTDB; insert into user(id,name) values(%i,'User%i');"然后分别查两个物理库:
# 查dn1(3307) mysql -h127.0.0.1 -P3307 -uroot -e "use test; select id from user order by id;" # 查dn2(3308) mysql -h127.0.0.1 -P3308 -uroot -e "use test; select id from user order by id;"预期结果:dn1里是2,4,6,8,10;dn2里是1,3,5,7,9。如果发现某条数据没出现,说明分片键id没被正确识别——检查schema.xml里<table>节点是否写了autoIncrement="true"(必须为true,否则Mycat不认为id是主键)。
4.3 故障转移模拟:干掉dn1,看查询是否自动切到dn2
这是检验Mycat高可用的关键。在任务管理器里找到mysqld.exe进程,结束3307端口对应的实例(PID可通过netstat -ano | findstr :3307查)。然后执行:
mysql -h127.0.0.1 -P8066 -uroot -p123456 -e "use TESTDB; select * from user where id=2;"如果返回ERROR 1105 (HY000): can't get connection,说明没启用故障转移。回到schema.xml,检查<dataHost>节点的switchType:
<dataHost name="localhost1" ... switchType="1" ... >switchType="1"表示自动切换(默认是-1,不切换)。改完后必须重启Mycat:先shutdown.bat,再startup.bat。重启后再次执行查询,应该能成功返回id=2的数据,且日志显示route to datanode:dn2。
注意:故障转移不是瞬间的。Mycat默认心跳间隔是10秒(
<heartbeat>select user()</heartbeat>),所以从干掉MySQL到Mycat感知到故障,最多等10秒。你可以把心跳SQL改成select 1,并加<heartbeat>select 1</heartbeat>,减少网络开销。
5. 常见问题与排查技巧实录:那些让你抓狂3小时的Windows专属Bug
在Windows上跑Mycat,有一半时间花在解决“只有Windows才有”的诡异问题上。我把踩过的坑按严重程度排序,附上精准定位方法和一招毙命的解决方案。
5.1 启动黑窗口闪退:wrapper.log里全是Invalid initial heap size
现象:双击startup.bat,CMD窗口一闪而过,wrapper.log里报:
Invalid initial heap size: -Xms2G Could not create the Java virtual machine.原因:bin\startup.bat里写的-Xms2G,但你的物理内存不足4G,或者JDK是32位。Windows下32位JVM最大堆只能到1.5G左右。
解决方案:
- 用
java -version确认JDK是64位(输出含64-Bit); - 编辑
bin\startup.bat,把-Xms2G -Xmx2G改成-Xms512M -Xmx1024M; - 如果还报错,加
-XX:+PrintGCDetails到JVM参数,看GC日志定位内存瓶颈。
5.2 连接被拒绝:mysql -h127.0.0.1 -P8066报Can't connect to MySQL server on '127.0.0.1'
这不是Mycat没启动,而是端口被占。Windows下常见抢占者是Skype(默认用80/443)或IIS。
快速检测:
netstat -ano | findstr :8066如果没输出,说明Mycat根本没监听;如果有输出,记下PID,tasklist | findstr "PID"查进程名。
终极清理:
# 查所有监听80端口的进程 netstat -ano | findstr :80 # 杀掉可疑进程(如skype.exe) taskkill /f /im skype.exe然后重启Mycat。
5.3 中文乱码:mycat.log里全是????,SQL执行报Incorrect string value
根源在MySQL和Mycat的字符集不一致。Mycat默认用utf8,但MySQL 5.7默认是latin1。
修复链路:
- MySQL端:
C:\mysql-dn1\my.ini加:
[client] default-character-set=utf8mb4 [mysqld] character-set-server=utf8mb4 collation-server=utf8mb4_unicode_ci- Mycat端:
C:\mycat\conf\server.xml里<system>节点加:
<property name="charset">utf8mb4</property>- 重启MySQL和Mycat。验证:
mysql -h127.0.0.1 -P8066 -uroot -p123456 -e "show variables like 'character%';"应全为utf8mb4。
5.4 日志不滚动:mycat.log越来越大,占满C盘
Mycat默认用log4j,但Windows下log4j.xml的<rollingPolicy>配置对TimeBasedRollingPolicy支持不好。
替换方案:
编辑C:\mycat\conf\log4j2.xml,把<RollingFile>节点的fileName和filePattern里的%d{yyyy-MM-dd}改成%d{yyyyMMdd},并确保<TimeBasedTriggeringPolicy>的interval设为1(每天滚)。
更狠一招:用Windows计划任务每天凌晨删旧日志:
# 创建任务:每天0点执行 schtasks /create /tn "CleanMycatLog" /tr "del /q C:\mycat\logs\mycat.log.*" /sc daily /st 00:005.5 配置热加载失效:改了schema.xml,mysql -e "show @@datasource;"还是旧的
Mycat 1.6不支持schema.xml热加载,必须重启。但有个隐藏技巧:用mysql命令触发重载:
mysql -h127.0.0.1 -P8066 -uroot -p123456 -e "reload @@config;"执行后看mycat.log,会有Reload config success。如果报错Unknown command,说明Mycat版本太低,必须升级到1.6.7.5+。
最后分享一个小技巧:当你改完所有配置,不确定是否生效时,不要反复重启。用
mysql -h127.0.0.1 -P8066 -uroot -p123456 -e "show @@server;"查Mycat运行时状态,uptime字段显示已运行秒数,processors显示CPU核数,dataNodes显示已加载的dataNode列表——这才是最真实的健康证明。我在某次交付前,就是靠这条命令在30秒内确认了所有12个dataNode全部在线,比等日志滚动快得多。