简介:这份面向 Java 后端开发与数据库运维人员的 Mycat2 基础安装包,可用于在本地或远程服务器上快速搭建分布式数据库中间件实例,适合已具备一定 Java 环境与 SQL 基础、希望实践数据分片、读写分离与 SQL 路由的中级学习者。压缩包共 51 个文件、约 1.2MB,以 SQL 初始化脚本、JSON 与 XML 配置、jar 依赖包为主,同时包含覆盖 Linux、Windows、macOS 等多平台的 wrapper 本地库与启动脚本,以及 properties 日志配置和 txt 说明文件;解压后按目录归类,即可完成服务部署与参数调整。目前已有 1075 人学习下载。借助包内现成的分片规则与数据源示例,读者可对照理解逻辑库与物理库的映射关系,动手验证主从读写分离、动态扩缩节点和故障切换,并结合日志与命令行工具观察 SQL 路由结果,省去零散搜集依赖与反复试错的成本,也便于在单机多实例或小型集群环境中做功能验证。
1. 从「一个 jar + 一堆配置」说起:mycat2 基础安装包到底装了什么
mycat2 基础安装包,是从业者对一组能直接解压运行的最小文件集合的俗称,它通常不是某个正式命名,而是核心 jar、lib 依赖目录、配置模板和启动脚本的组合。它解决的问题很具体:后端有几台 MySQL,单表写入开始吃力,业务又不想大改代码,需要一个中间层把 SQL 按规则路由到不同库表。第一次拿到这个包的人最容易犯的错,是把它当成直接双击就能运行的软件,结果脚本一闪而过、日志里只剩半行报错。这篇顺着从解压到跑通的路径讲,面向要独立部署一套分库分表中间层的后端和运维,重点是参数怎么设、失败时看哪里。
2. 装之前先把地基打稳:JDK、MySQL 节点与机器规划
基础安装包本身只有几十兆,但它对运行环境的要求比包体大得多。很多人把包解压到一台随手找的机器上,启动脚本跑完没反应,就开始怀疑包有问题,实际上八成是 JDK 版本、后端库连接权限或者端口这三类问题。这一章把安装前的核对项一次列全,后面启动和配置阶段就能少返工。
2.1 JDK 版本、JVM 参数与系统文件句柄
mycat2 是纯 Java 服务,装包之前第一件事是把 JDK 定下来。常见做法是选 JDK 8 或 JDK 11 的 LTS 版本,不要在同一台机器上混装多个大版本再靠JAVA_HOME切来切去,脚本读错 JDK 的现场极难排查。确认方式很朴素:
# 确认当前 shell 实际使用的 java 来自哪 which java java -version 2>&1 echo $JAVA_HOME逻辑说明:which java看的是 PATH 里的可执行文件,JAVA_HOME看的是脚本可能引用的变量,两者不一致是启动脚本报UnsupportedClassVersionError的常见根因。参数上,JVM 内存不要拍脑袋给满,中间件本身不存业务数据,堆内存主要消耗在连接缓冲和结果集转发上。一台 8G 内存的机器,堆给 2G 到 3G 足够,剩下的留给页缓存。
# 启动脚本里常见的 JVM 参数区,按机器规格调整 JAVA_OPTS="-Xms2g -Xmx2g \ -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m \ -XX:+UseG1GC -XX:MaxGCPauseMillis=200 \ -Dfile.encoding=UTF-8"-Xms与-Xmx设成一样,避免运行期反复扩容带来抖动;Metaspace上限必须给,默认不设上限时类加载泄漏会一路吃到系统内存;-Dfile.encoding不显式指定,中文库表名和报错日志在某些系统上会变成问号。文件句柄同样要提前放开,中间件每个后端连接、每个客户端连接都占 fd:
# 临时生效,正式环境写进 limits.conf ulimit -n 65535 ulimit -n注意:ulimit -n只对当前会话生效,用 systemd 托管时要改 unit 里的LimitNOFILE,否则重启后又回到 1024,表现为运行几小时后突然大面积报Too many open files。
2.2 后端 MySQL 节点的最小准备
中间件自己不存数据,所有查询最终落到后端 MySQL,所以后端没准备好,前面配得再对也白搭。最小准备包含三件事:建库、建专用账号、放开远程连接。专用账号不要用 root,权限收窄到具体库,后面出问题查审计日志也清楚。
-- 在后端每个 MySQL 实例上执行 CREATE DATABASE IF NOT EXISTS db_order DEFAULT CHARACTER SET utf8mb4; CREATE USER 'mycat_rw'@'%' IDENTIFIED BY '换成强密码'; GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, INDEX ON db_order.* TO 'mycat_rw'@'%'; FLUSH PRIVILEGES;字符集统一用utf8mb4,不要一半实例是utf8、一半是utf8mb4,跨库 JOIN 或排序时会出现隐式转换,索引直接失效。账号主机位写%还是具体 IP,取决于中间件所在机器的地址是否固定;写死 IP 更安全,但扩容时要记得回来改。另外确认后端实例的max_connections够用:中间件每个数据源会维持一组连接,数据源数量乘上单源最大连接数,不能超过后端上限。
SHOW VARIABLES LIKE 'max_connections'; SHOW VARIABLES LIKE 'character_set_server'; SHOW VARIABLES LIKE 'time_zone';这三条查完,心里就有底了。time_zone尤其要记下来,后面配连接串时如果两边时区不一致,datetime字段会整体偏移几个小时,这种问题查起来很折磨。
2.3 端口、目录与账号的规划清单
端口默认两个:一个给业务连接,常见是 8066;一个给管理端,常见是 9066。部署前先确认没被占用,别等启动失败再去翻日志。
# 检查端口占用,有输出说明已被占 ss -lntp | grep -E '8066|9066'目录规划上,安装目录和数据、日志目录建议分开。安装目录只放 jar、lib、配置和脚本,日志单独挂一块盘,因为 SQL 审计日志和慢日志增长很快,和安装目录挤在一起容易把磁盘写满,写满之后中间件的行为是不可预期的。
| 目录 | 用途 | 建议 |
|---|---|---|
| 安装目录 | jar、lib、bin、conf | 权限 755,属主为运行账号 |
| 日志目录 | 启动日志、SQL 日志 | 单独分区,配轮转 |
| 数据目录 | 部分版本会落本地元数据 | 定期备份 |
运行账号不要用 root,建一个普通账号,把安装目录和日志目录的属主给它。这一步多花两分钟,后面排查权限类问题时能省下几个小时。规划做完,再解压安装包,顺序不要反。
3. 解压即用的边界:目录结构与启动脚本怎么读
基础安装包解压出来,第一眼看上去文件不多,但每个目录都有明确职责,认错了就会把配置写到不生效的位置。这一章按目录树逐项拆,然后讲启动脚本的几个分支逻辑和首次启动该盯的日志位置。
3.1 目录树里每个文件分别管什么
不同小版本的目录名会有细微差异,但结构大体一致,常见形态如下:
mycat2/ ├── bin/ │ ├── mycat # 主启动脚本,start/stop/restart/status │ └── mycat.bat # Windows 版,服务器上一般用不到 ├── conf/ │ ├── server.yaml # 服务端口、用户、权限等全局项 │ ├── datasource/ # 每个后端数据源一个文件 │ ├── cluster/ # 主从或多节点映射 │ ├── schema/ # 逻辑库、逻辑表、分片规则 │ ├── sequence/ # 全局序列配置 │ └── function/ # 自定义分片函数 ├── lib/ # 依赖 jar,不要随手删 └── logs/ # 启动与运行日志conf下这几类配置是层层引用的关系:schema 里的表指向某个数据节点,数据节点由 cluster 定义,cluster 再指向 datasource 里的具体连接。任何一层名字写错,启动时未必报错,但查询会直接失败,这是后面避坑章节要重点讲的。
提示:配置文件的扩展名在不同版本里可能是
.json也可能是.yaml,以手里这个包的模板为准,不要自己改扩展名,加载器是按后缀匹配的。
3.2 启动脚本的分支逻辑与常用参数
bin/mycat本质是个 shell 包装,负责找 JDK、拼 classpath、读 JVM 参数、再拉起主类。常用子命令就几个:
# 进入安装目录后执行 cd /opt/mycat2 ./bin/mycat start # 后台启动 ./bin/mycat status # 看是否在跑 ./bin/mycat console # 前台启动,日志直接打屏,排错用 ./bin/mycat stop # 停止逻辑说明:start会把标准输出重定向到日志文件后退出,适合正式环境;console不 fork,日志直接打在终端上,第一次部署强烈建议先用它跑一遍,能立刻看到类加载失败、端口占用、配置解析异常这类问题,比事后翻日志快得多。
脚本通常会读一个MYCAT_HOME变量,没设时用脚本所在目录的上一级。如果你的部署路径带软链接,建议显式导出:
export MYCAT_HOME=/opt/mycat2否则脚本解析出的路径可能是软链接指向的真实目录,配置和日志就跑到别处去了,这种问题属于典型的排查起来没有头绪的问题。JVM 参数一般也在这个脚本里,改完记得stop再start,restart在部分脚本实现里只是简单 kill 加 start,中间没等待端口释放,启动会报地址已占用。
3.3 首次启动该盯的三个日志位置
第一次console启动,屏幕上会滚动大量框架初始化日志,不用逐行看,重点盯三段。第一段是端口绑定,看到业务端口和管理端口都成功监听,说明服务骨架起来了;第二段是配置加载,逐个数据源、集群、逻辑库打印加载结果,任何一个标红或ERROR都要停下来处理;第三段是连接池初始化,能看到对后端 MySQL 的探活结果。
# 另开一个终端验证端口确实在监听 ss -lntp | grep -E '8066|9066' # 用客户端连一下管理端口,确认服务能握手 mysql -h127.0.0.1 -P9066 -uroot -p -e "show @@version"如果console模式下一闪就退回 shell,说明主进程启动阶段就退出了,优先看屏幕上最后二三十行,通常是 JDK 版本不匹配、conf下某个文件语法错误,或者lib目录被误删了文件。定位到具体行再改,不要凭猜测连续重启,之前就有人靠反复重启把真正的那行报错冲没了,后来只能从头再来一遍。
4. 让 mycat2 真正连上库:datasource、cluster 与 schema 三层配置
安装包能启动,只说明 JVM 和脚本没问题,离“业务能连上来查数据”还差三层配置。这三层是引用关系,配置顺序也建议按 datasource、cluster、schema 来写,写完一层验证一层,比三层一起写完再一起调要快得多。
4.1 datasource:连接串、连接池与健康检查
每个后端 MySQL 实例对应一个数据源文件,文件名就是数据源名,后面 cluster 里引用的就是这个名字。下面用 YAML 形态举例,字段名以包内模板为准:
# conf/datasource/dn_order_01.yaml url: jdbc:mysql://10.0.0.11:3306/db_order?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai&useSSL=false user: mycat_rw password: 换成强密码 dbType: mysql maxCon: 50 # 单数据源最大连接数 minCon: 5 # 保底空闲连接 maxRetryCount: 3 # 建连失败重试次数 weight: 1 # 多节点时的权重url里的serverTimezone必须和后端实例的time_zone对上,对不上就是前面说的整体偏移几小时。characterEncoding写utf8mb4,不要写utf8,否则四字节字符(比如部分表情和生僻字)写入会报错或被截断。maxCon是最需要算的参数:数据源数量 ×maxCon不能超过后端max_connections,留出至少 20% 余量给运维和备份连接。minCon不要设成和maxCon一样,那等于启动就占满,后端稍有抖动就会互相拖死。
配完一个数据源,先用console模式启动看它的探活日志,确认能连上再复制模板配第二个。多个数据源之间除了url和weight,其他字段保持一致,避免出现“有的源连得上、有的连不上”这种不对等状态。
4.2 cluster:主从映射与节点切换策略
cluster 描述一组数据源怎么组成一个逻辑节点,单机就是一对一,主从就是一主一从或多从。配置里引用的名字必须是 datasource 目录下已经存在的文件名:
# conf/cluster/cluster_order.yaml masters: - dn_order_01 replicas: - dn_order_02 switchType: 1 # 1 表示主从自动切换 readBalanceType: 0 # 0 轮询,1 随机masters写主库数据源,replicas写从库,switchType决定主库挂了之后的行为。这里有个容易忽略的点:自动切换依赖中间件对后端的探活,如果网络偶尔抖动,可能触发误切,然后主从角色来回跳,业务侧看到的现象是连接时好时坏。稳妥做法是先把switchType设成不自动切换,把主从切换交给更上层的运维手段,等观测一段时间、摸清网络质量之后再开。
readBalanceType只影响读请求在多个从库之间的分摊方式,写请求永远走masters。如果业务读写比例不高,从库数量少,轮询就够了,没必要上更复杂的策略。配完这一层,启动后可以在管理端口看集群状态,确认主从节点都被识别到、角色没标反。
4.3 schema:逻辑库、分片表与全局表的落地写法
schema 是业务直接感知的一层,它定义了逻辑库名、默认数据节点,以及每张逻辑表怎么落到物理表。先配一个不分片的逻辑库跑通链路,再加分片表,这个顺序能帮你把“配置错误”和“分片规则错误”两类问题分开。
# conf/schema/schema_order.yaml schemaName: order_db defaultDataNode: cluster_order tables: - name: t_order type: sharding dataNode: cluster_order function: mod_hash # 引用 conf/function 下的分片算法 columns: - name: order_id type: bigint createTableSQL: | CREATE TABLE t_order ( order_id BIGINT NOT NULL, user_id BIGINT, amount DECIMAL(12,2), create_time DATETIME, PRIMARY KEY(order_id) );defaultDataNode给的是兜底节点,没显式配置分片的表会落到这里。type: sharding表示这是分片表,function指向分片算法,常见的取模哈希就是mod_hash,按哪个字段分片由算法配置决定,通常是主键或业务上最常用的查询条件。createTableSQL只在部分版本用于自动建表,如果后端表已经手工建好,这段可以不写或只做声明用。
分片键的选择是这套配置里最考验判断的一步。按订单号分片,按订单号查很快,按用户查就变成全路由;按用户分片则反过来。没有哪个选择能同时讨好所有查询,实际做法是先统计业务里占比最高的查询条件,用它做主分片键,剩下的查询接受一定程度的广播,或者在上层加一层映射表。全局表(比如字典表)配置时不带分片函数,每个节点都存一份,JOIN 时才不会跨节点。
三层配完,用业务账号连业务端口跑一条最简单的查询:
mysql -h127.0.0.1 -P8066 -u业务账号 -p order_db -e "select count(*) from t_order"能出结果,链路就算通了。后面再逐步加分片表、加从库,每次只改一处,出问题能立刻定位到是哪一步引入的。
5. 避坑与排查:基础安装包最容易翻车的五个点
这一章的五条都是实际部署里反复出现的,每条按现象、原因、解决写,遇到问题可以直接对照。
5.1 启动秒退,日志里只有半行
现象:./bin/mycat start回车后立刻回到命令行,status显示未运行,日志文件里只有半行或者干脆是空的。
原因:九成是 JDK 版本和编译目标不匹配,或者是JAVA_HOME指向了一个不完整的 JDK(比如只装了 JRE)。少数情况是conf下某个配置文件语法错误,加载器解析阶段直接抛异常退出,还没来得及写日志。
解决:先用./bin/mycat console前台跑,异常栈会直接打在终端上。如果是UnsupportedClassVersionError,换 JDK 大版本;如果是配置解析异常,报错信息里一般带文件名和行号,按行改;如果终端什么都没有,检查脚本里的JAVA_HOME是否指向真实存在的目录。
5.2 连得上但报表不存在
现象:客户端能连上业务端口,show databases也能看到逻辑库,但一查具体表就报table not found。
原因:schema 文件名和schemaName不一致,或者 tables 里的表名大小写与后端实际表名不匹配。在大小写敏感的文件系统上,这个差异是致命的。
解决:确认 schema 文件名、schemaName、客户端连接时用的库名三者一致;确认逻辑表名和后端物理表名的大小写完全一致;改完配置必须重启,部分配置不支持热加载。
5.3 分片键选错,查询变成了全路由
现象:单条查询平时几十毫秒,某个查询条件一换就变成几秒,后端所有分片实例同时出现慢查询。
原因:查询条件里没带分片键,中间件只能把 SQL 广播到所有节点再合并结果,数据量一大就是灾难。
解决:从慢日志里找出这类 SQL,看 WHERE 条件缺了哪个字段。能补分片键的补上;补不了的,考虑建映射表或者调整分片算法。不要指望加索引能救,广播的代价在中间件这一层,不在后端。
5.4 时间字段整体偏移几个小时
现象:写入时间和查询出来的时间差了固定的小时数,重启也不消失。
原因:中间件连接串里的时区和后端实例的time_zone不一致,或者中间件所在机器和数据库机器时区不同。
解决:把三处时区统一,连接串里显式写serverTimezone,后端实例确认time_zone值,操作系统层用timedatectl核对。统一之后历史数据不会自动修正,需要单独评估是否要写脚本订正。
5.5 跑一段时间后连接池耗尽
现象:服务刚上线正常,几小时后开始报获取连接超时,重启就能恢复,但过一阵又复发。
原因:常见是代码里有未关闭的连接,或者maxCon设得过大把后端占满,导致后端拒绝新连接,中间件这边表现为池里全是坏连接。
解决:先把maxCon调到后端能承受的范围,同时开连接池的活跃数监控;再排查业务侧是否有长事务和连接泄漏。maxRetryCount不要设太大,重试次数多只会把故障时间拉长。
6. 验证与进阶:三条 SQL 加一个压测脚本确认这套包能扛住
配置全部落地之后,别急着交付,花半小时做一轮验证,能挡掉后面大部分纠纷。我一般按三层来验:单表读写、跨分片聚合、并发压测。
# 第一层:单分片点查,应该只路由到一个节点 mysql -h127.0.0.1 -P8066 -u业务账号 -p order_db \ -e "select * from t_order where order_id = 1001" # 第二层:跨分片聚合,观察耗时是否符合数据量预期 mysql -h127.0.0.1 -P8066 -u业务账号 -p order_db \ -e "select count(*), sum(amount) from t_order where create_time > '2024-01-01'" # 第三层:管理端口看连接与节点状态 mysql -h127.0.0.1 -P9066 -u管理账号 -p -e "show @@datasource"第一层看的是分片规则有没有生效,返回行数和后端单表能对上就说明路由正确。第二层看的是广播查询的代价,如果耗时随分片数线性增长,说明聚合是在中间件内存里做的,数据量再大就要考虑预聚合。第三层看各数据源的连接数和活跃状态,有没有某个源明显偏高。
压测脚本不用复杂,用系统自带的并发工具打一轮点查就够暴露问题:
# 20 并发、总共 2000 次点查,观察错误率和平均耗时 seq 2000 | xargs -P20 -I{} mysql -h127.0.0.1 -P8066 -u业务账号 -p'密码' \ order_db -e "select * from t_order where order_id = {}" > /dev/null跑完看两件事:有没有报错,以及压测期间管理端口里连接数是否稳定。连接数持续上涨不回落,基本就是连接泄漏,早点发现比上线后发现好得多。
最后说个习惯。我早期部署这类中间件,喜欢一次把所有分片、所有从库全配上,觉得一步到位,结果每次出问题都要在几十个配置项里大海捞针。后来改成“最小可用先跑通,每次只加一个变量”,排错时间至少砍掉一半。这套基础安装包本身不复杂,难的是它背后那张配置引用网,慢一点、一次只动一处,反而最快。希望帮到你。
本文还有配套的精品资源,点击获取