简介:面向使用 Java 环境搭建分布式数据库中间件的开发者与运维人员,这份 Mycat2 基础安装包提供可部署的服务端基础组件,重点缓解单机数据库在高并发、大数据量场景下的扩展与访问压力。包内共 51 个文件,整体约 1.2MB,以 jar 包、so/dll/jnilib 等跨平台依赖库、SQL 初始化脚本、JSON/XML/Properties 配置文件及 shell/bat 启动脚本为主,覆盖服务运行、参数配置、数据初始化与多平台适配等环节。已有 1072 人学习,说明其在 Mycat2 入门部署与实验环境搭建中具备一定参考价值。对于需要理解分片规则、读写分离、SQL 路由与动态扩展的读者,可借助包内配置样例和初始化脚本搭建练习环境,并据此梳理数据源、序列、集群等模块的组织方式,减少自行搜集依赖与整理目录的时间。
1. 先搞懂一件事:MyCAT2 到底是什么,为什么你的数据库需要一个「前台」
很多人第一次拿到 mycat2 基础安装包,解压完看到 conf 目录里一堆 json 文件,第一反应是「这到底是数据库还是软件」。打个比方:楼下只有一个快递柜,包裹少的时候够用,多到塞不下就得再加几个柜子;可居民不想记五个柜子分别在哪,于是需要一个前台,你只报单号,前台替你去对应的柜子取。MyCAT2 就是坐在数据库前面的那个前台,它对外表现得像一台普通的 MySQL,对内帮你把请求分发到一台或多台真实的数据库上。这份安装包给你的,就是这样一个能直接跑起来的前台服务端,装完、配完、连上,你就能用一条最普通的 select 语句,摸到后面那几台库的数据。
2. 为什么值得学:单机数据库撑不住的时候,中间件在解决什么
2.1 一个新手最熟悉的场景:数据涨到一千万行之后
刚学数据库的那阵子,几乎所有练习都长一个样:建一个库,建几张表,塞个几千行数据,select * from t_user where id = 1秒回。这个阶段你很难理解「中间件」这种东西存在的意义,因为一台机器明显够用。
麻烦是从数据量真的涨起来开始的。假设一张订单表,从十万行涨到一千万行,你会依次遇到这么几件事:查询从几十毫秒变成一两秒;你给字段加了索引,发现效果越来越差,因为索引文件本身也变大了,从内存里放得下变成要频繁读磁盘;备份从几分钟变成几个小时;磁盘快满了,想换台大点的机器,得停机、拷数据、改连接地址,业务方跟着一起等。这些现象跟 SQL 写得对不对关系不大,本质上是「一台机器扛不住了」。
常见的应对办法有三种。第一种是加硬件,加内存、换更快的盘,简单粗暴,但天花板很明显,而且成本是跳着涨的。第二种是在业务代码里自己分表,比如按用户 ID 取模,代码里判断该查哪张表。这种做法我第一次见的时候觉得挺聪明,后来维护的人换了三拨,每个人都在里面加了一段自己的逻辑,改一次需求要翻五个文件,那才是真正的血泪经验。第三种就是在数据库和应用之间加一层中间件,SQL 还是那样写,连接地址改成中间件的地址,由中间件负责把语句送到正确的机器上。MyCAT2 属于第三种。
所以学它的理由很朴素:你不需要为了扩容去大改业务代码,也不用在每张表前面都手写一套路由判断。代价是多了一个需要理解、需要运维的组件,这笔账划不划算,取决于你的数据量和你愿不愿意维护它。对新手来说,先把它跑起来、看懂它在干什么,比争论它该不该用更有价值。
2.2 MyCAT2 在中间到底干了什么活
把「前台」这个比喻拆细一点,MyCAT2 主要干三件事。
第一件是接住客户端。它自己实现了 MySQL 的网络协议,所以 Navicat、命令行 mysql、你项目里的 JDBC 驱动,连它跟连一台真 MySQL 在体感上没有区别。默认情况下它监听 8066 端口对外提供服务,另外还有一个 9066 端口是管理口,用来查状态、看配置有没有生效。这一点很关键:你的应用不需要装什么特殊 SDK,改一下 ip 和端口就行。
第二件是算路由。你发过来一条select * from t_order where user_id = 1001,它得先解析这条 SQL,知道你要查的是哪张逻辑表、条件里有没有分片字段,然后按配置好的规则算出来应该去哪个库、哪张物理表取数据。如果条件里没带分片字段,它可能要挨个库都查一遍再把结果拼起来,这就是为什么分片表设计时总要反复强调「查询要带分片键」。
第三件是合并结果。order by、limit、count(*)、group by这类操作,在单库里数据库自己就搞定了,但数据被拆到多个库之后,每个库只能给你一部分结果,剩下的排序、截断、累加得由中间件在内存里再做一次。这也是中间件最容易出性能问题的地方,翻车的往往不是路由算错,而是某个不带分片键的order by把几百万行全捞到中间件内存里排了一遍。
顺带说一句它不干的事:MyCAT2 自己不存业务数据。它是个「无状态」的转发层,真正的数据还在你后面那几台 MySQL 里。所以它挂了通常不会丢数据,但你的业务会连不上库,这也是运维上要提前想清楚的点。
2.3 学它之前,先确认手里这三样东西
第一样是 JDK。MyCAT2 是 Java 写的,运行前必须有 Java 运行环境,装 JDK 8 或更高版本,具体以你包里自带的说明为准。验证方法很简单,终端敲java -version,能打印出版本号就行。这一步看着废话,但我见过太多人卡在启动闪退,最后发现是机器上压根没装 Java,或者装了但环境变量没配。
第二样是 MySQL 的基本功。你至少得会建库建表、会写增删改查、知道主键和索引大概是干嘛的、知道事务是什么。不需要你会调优,但要能独立把一个库跑起来,因为 MyCAT2 后面接的就是它。如果你是纯新手,建议先在本地装一台 MySQL,把第 3 章第一个例子要用的表建好,再回来折腾 MyCAT2,顺序反了会很痛苦。
第三样是 Linux 的基本命令。cd、ls、tail、ps、grep这几个够用了,主要是看日志和查进程。Windows 上也能跑,启动脚本换成了 bat,但大部分资料和排错思路都是 Linux 语境,入门阶段跟着 Linux 走会顺一些。
至于分布式事务、一致性哈希、CAP 这些词,入门阶段可以先不碰。它们是你把第一个例子跑通、真的开始设计分片之后才需要补的东西,一上来就啃只会劝退。
3. 第一步怎么迈:把安装包跑成一个能连上的服务
3.1 装 JDK、解压、看清目录结构
拿到安装包后,先别急着改配置,把环境确认一遍再解压。整个流程大概是「验证 Java → 解压到固定目录 → 认目录 → 改配置 → 启动 → 连接」,一步一验证,出问题好定位。
# 1. 确认 Java 环境可用,能打印版本号才继续 java -version # 2. 解压到 /opt 下,目录名按你实际解压出来的写 tar -zxvf mycat2-xxx.tar.gz -C /opt/ # 3. 进入解压后的目录,看看有哪些东西 cd /opt/mycat ls -l第一条命令是在确认运行环境,输出里应该有类似openjdk version "1.8.x"或更高版本的字样,没有就先把 JDK 装好,否则后面启动脚本会直接退出。第二条命令把包解开到/opt,-C指定目标目录,xxx换成你实际拿到的包名。第三条命令列目录,正常情况下你会看到bin(启动脚本)、conf(配置文件)、lib(依赖的 jar)、logs(日志)这几类目录。
bin里是启动、停止脚本,conf是接下来要动的全部内容,logs是你排错时最先要看的地方。先把这三个目录记住,其余的先不用管。
# 看 conf 下都有哪些配置目录 ls -l conf输出里通常能看到datasources、schemas、users、cluster这几类目录,以及一个主配置文件mycat.yml。不同包里目录名可能略有出入,但功能是对得上的:datasources描述后端真实数据库怎么连,schemas描述对外暴露的逻辑库长什么样,users描述谁能连 MyCAT2、密码是多少,mycat.yml管端口这类服务级参数。认不出名字也没关系,按「连谁、对外叫什么、谁能连」这三件事去对号入座就行。
提示:解压路径里尽量不要带空格和中文,Windows 上也一样。这类启动脚本对路径很敏感,路径带空格导致起不来的情况非常常见。
3.2 改三个地方:数据源、逻辑库、账号密码
配置这一步是整个入门里最容易让人放弃的地方,因为文件多、字段陌生。但入门阶段你只需要动三处,其余保持模板原样。
第一处,数据源。在conf/datasources下找到默认的数据源文件(名字里一般带prototype或default),用编辑器打开,核心字段改成你自己那台 MySQL 的真实信息:
{ "dbType": "mysql", "jdbcUrl": "jdbc:mysql://127.0.0.1:3306/demo_db?useSSL=false&serverTimezone=Asia/Shanghai", "username": "demo", "password": "demo123456", "initSql": "select 1", "maxCon": 20, "minCon": 2 }jdbcUrl是唯一必须改对的一行:127.0.0.1:3306换成你 MySQL 的地址和端口,demo_db换成你实际建好的库名,后面的参数是关闭 SSL、指定时区,主要为了避免时区不一致导致时间字段差八小时。username和password是连 MySQL 用的账号密码,不是你登录 MyCAT2 的账号密码,这两个概念新手经常混。initSql是连接建立时先探一下活不活,maxCon和minCon是连接池的最大最小连接数,本地练习给 20 和 2 就够了,不用调。
第二处,逻辑库。在conf/schemas下新建或修改一个描述文件,告诉 MyCAT2「对外我叫什么名字,我的数据从哪个数据源来」:
{ "schemaName": "shop", "targetNames": ["prototype"], "defaultTargetName": "prototype" }schemaName是对外暴露的库名,客户端use shop用的就是它,跟后端真实库名demo_db可以完全不一样。targetNames表示这个逻辑库的数据来自哪些数据源,这里填的是上一步数据源文件里定义的名字(模板里通常叫prototype,你数据源文件叫什么这里就写什么)。入门阶段先一对一,后面做分片时这里才会变成多个。
第三处,账号。在conf/users下找到或新建一个用户描述文件:
{ "username": "root", "password": "123456", "schemas": ["shop"] }这里的username、password是你连 MyCAT2 时用的,跟 MySQL 那套完全独立。schemas写你能访问哪些逻辑库,要跟上面schemaName对上,写错了就会连上但看不到库。本地练习用 root/123456 没问题,上任何带真实数据的环境都要换掉。
# 主配置里确认一下服务端口,默认服务口 8066、管理口 9066 grep -n "port" conf/mycat.yml端口一般不用改,但确认一下不吃亏,尤其当你机器上已经跑了别的东西。如果 8066 被占,启动会失败,日志里会明说。
注意:JSON 文件对格式很挑剔,多一个逗号、少一个引号都会导致解析失败,服务启动时直接报错。改完先用编辑器自带的格式检查看一眼,能省掉很多来回。
3.3 启动服务,看日志确认活着
配置改完就可以启动了。启动脚本在bin下,启动之后别急着连,先确认进程真的起来了。
cd /opt/mycat # 启动服务 sh bin/mycat start # 用 status 看进程状态,脚本一般支持 start/stop/restart/status 这几个子命令 sh bin/mycat status # 启动之后看最新的日志文件,确认没有报错 ls -lt logs | head tail -n 100 logs/<最新的那个日志文件>第一条命令启动,Windows 上换成bin目录下对应的 bat 脚本。第二条确认进程状态,能看到运行中就算起来了。第三条是重点:ls -lt按时间倒序列出日志目录,找到最新的那个文件再tail看最后一百行。
日志里出现启动完成、监听端口之类的字样,说明服务活着;如果出现 JSON 解析错误、Java 异常、端口占用,基本就是你上一步配置里对应的问题,按报错关键字回去改。养成「改配置 → 重启 → 看日志」的循环,比凭感觉猜快得多。
# 用本机 MySQL 客户端连 MyCAT2,注意端口是 8066 不是 3306 mysql -h127.0.0.1 -P8066 -uroot -p123456这里四个参数要看清:-h是 MyCAT2 所在机器的地址,-P(大写)是端口,必须写 8066,写成 3306 就跑到后端真库去了,你会误以为配置没生效;-u和-p是上一步users里配的账号密码。连上之后敲show databases;,应该能看到你配的逻辑库shop。
如果你机器上没有 mysql 客户端,用任何图形化数据库工具也行,连接类型选 MySQL,主机端口账号密码填同一套。客户端并不关心对面是一台真库还是一个中间件。
3.4 第一个例子:从 MyCAT2 里查出后端数据库的表
现在跑第一个能验证全链路的例子:在真实 MySQL 里建一张表并插两条数据,然后从 MyCAT2 里查出来。整个链路是「客户端 → MyCAT2 → MySQL」,只要最后能查出数据,说明三段全通了。
第一步,直连真实 MySQL,把数据造好:
-- 直连 MySQL(3306)执行,不是在 MyCAT2 里执行 CREATE DATABASE IF NOT EXISTS demo_db DEFAULT CHARSET utf8mb4; USE demo_db; CREATE TABLE t_user ( id BIGINT PRIMARY KEY COMMENT '用户ID', user_name VARCHAR(32) NOT NULL COMMENT '用户名', city VARCHAR(32) COMMENT '城市' ) COMMENT '用户表'; INSERT INTO t_user (id, user_name, city) VALUES (1, 'alice', 'hangzhou'), (2, 'bob', 'chengdu');这段在直连 3306 的会话里执行,不是通过 MyCAT2。utf8mb4是字符集,中文和安全相关的存储都靠它;id用 BIGINT 是因为将来数据量大了,INT 可能不够用;两条 insert 是给下一步验证准备的样本数据。
第二步,通过 MyCAT2 查:
-- 下面这些在 8066 的会话里执行 SHOW DATABASES; -- 应该能看到逻辑库 shop USE shop; -- 切到逻辑库 SHOW TABLES; -- 应该能列出 t_user SELECT * FROM t_user;-- 查出上面插的两条数据SHOW DATABASES验证的是schemas配置有没有生效;USE shop能切过去说明权限配置对;SHOW TABLES和SELECT能出结果,说明「客户端 → MyCAT2 → MySQL」这条链路完全打通,数据源里的地址账号密码也是对的。
看到和直连 MySQL 一模一样的查询结果,这个例子就算跑完了。别小看这一步,它同时验证了四件事:Java 环境、服务启动、配置解析、后端连库。后面所有分片、读写分离的配置,都是在这个基础上加东西,基础没通就往上叠,出错时你根本分不清是哪一层的问题。
4. 第一个例子跑通之后:把逻辑库、分片、读写分离这几个词弄明白
4.1 逻辑库和物理库:一个「分身」的比喻
第 3 章里你已经见过一对名字:shop和demo_db。前者是对外暴露的逻辑库,后者是真实存在的物理库。两者的关系可以理解成「分身」:客户只知道shop这个名字,至于shop后面的数据到底在哪台机器、哪个库、甚至是不是被拆成了好几份,客户端一概不知,也不需要知道。
这种「名字和实体分离」的设计是中间件存在的根基。因为分离了,你才有可能在不改业务代码的前提下,把后面的demo_db从一台机器搬到三台机器上;也才有可能把同一张逻辑表,拆成t_user_0、t_user_1两张物理表,而对外仍然叫t_user。
新手最容易在这里犯迷糊的是查错地方:通过 MyCAT2 建表,到底建到哪去了?在多数配置下,你通过逻辑库执行的 DDL 会被转发到后端真实库执行,所以你在 8066 里CREATE TABLE,去 3306 里能看见。反过来,如果你在 MySQL 里手动改了一张表的名字,MyCAT2 是不会自动感知的,它只认自己配置里的那份描述。所以出现问题时的第一反应应该是「两边对照着看」,而不是只盯一头。
4.2 分片:一条数据到底被放到哪台机器上
分片听起来玄乎,本质就一句话:拿一个字段算一下,决定这条数据去哪。最常见的算法是取模。
假设你按user_id把订单拆到 4 个库,路由规则就是user_id % 4,余数是几就去几号库。写几行代码把这件事打印出来,比看任何架构图都直观:
# 只是演示路由的思路,不是 MyCAT 的内部实现 shard_count = 4 for uid in (1001, 1002, 1003, 1004, 1005): index = uid % shard_count # 取模算出落在哪个分片 print(f"user_id={uid} -> db_{index}")shard_count是分片总数,uid % shard_count就是路由结果。跑一下你会看到 1001 落到db_1,1004 落到db_0,数据被均匀摊开了。这段代码的价值在于让你建立直觉:所谓分库分表,不是把数据复制几份,而是每条数据只存在于一个分片里,靠一个确定的算式找得到它。
由此能推出两条对设计影响很大的结论。第一,查询尽量带分片键。带user_id的查询能直接算出目标库,一条语句打一台机器;不带分片键的查询,中间件只能把所有分片都问一遍,分片越多越慢。第二,分片数最好一次定好。改成 5 个分片时,原来user_id % 4的数据几乎全部要迁移,这个迁移量非常可观,所以规划分片数时要按未来一两年的量来估,宁可多留一点。
4.3 读写分离:为什么刚写完的数据有时读不到
读写分离解决的是另一类问题:写少读多的业务,主库忙着写,从库闲着。做法是把写语句发到主库,读语句发到从库,MyCAT2 根据 SQL 类型做分发,你写代码时不用区分。
听起来很美,但有个坎必须提前知道:主从之间同步是有延迟的。你刚在主库 insert 一条数据,立刻去从库查,可能查不到。这不是配置错了,是复制还没跟上。业务流程里那种「注册完立刻跳个人主页」的场景,最容易在这里翻车,用户看到空白页会以为没注册成功。
常见的处理办法有三种:对一致性要求高的读,强制走主库;写入后的一小段时间内读主库,过了窗口再切回从库;或者在前端做一次轮询重试。入门阶段你只要记住「读写分离不是免费的,它拿一致性换吞吐」,配的时候先想清楚哪些读能容忍延迟,比上来就把所有读都切到从库稳妥得多。
5. 排查与避坑:新手最容易翻车的五个地方
5.1 服务起不来:先看日志,别急着改配置
现象:敲完启动命令,终端没什么输出,status一看没起来,或者起来几秒又没了。
原因通常有两个。一是 Java 环境不对,机器上没装 JDK,或者装了但版本太低、JAVA_HOME没配好,启动脚本在第一行就退出了。二是配置文件格式错,JSON 里多一个逗号、少一个引号,解析直接失败,日志里会明确写出哪个文件第几行出问题。
解决办法是固定顺序:先java -version确认环境,再去看logs下最新的日志文件,从最后往前找第一个报错,按报错里的文件名和行号回去改。别凭感觉一次改五个地方再重启,那样即使好了你也不知道是哪一处起的作用。我第一次配的时候就是一口气改了数据源、端口、账号,结果起不来,排查了半天才发现是用户名写错了。
5.2 连上了却查不到数据
现象:客户端能连上 8066,SHOW DATABASES也能看到逻辑库,但SHOW TABLES是空的,或者查询报表不存在。
原因一般是三个。逻辑库的targetNames填的数据源名字,和数据源文件里定义的名字对不上,看着像通了,其实后面没接上;数据源的jdbcUrl里库名写成了别的库,连是连上了,看的是另一份数据;再就是大小写,某些环境下表名区分大小写,T_USER和t_user被当成两张表。
解决办法:先在 3306 直连后端库,确认表和数据确实在那儿;再把schemas里的targetNames和datasources里的名字逐个字符对一遍;最后用SHOW TABLES的结果和直连结果比对。对照法比猜快得多。
5.3 账号、端口、字符集:配置都对但结果不对
现象一:连的时候提示拒绝访问。原因是把 MyCAT2 的账号密码和 MySQL 的搞混了,users里配的那套是登录 MyCAT2 用的,datasources里那套是 MyCAT2 去连 MySQL 用的,两套完全独立,改了一处不代表另一处也对。
现象二:能连上但是连到了真库。原因多半是-P写成了 3306,或者客户端里端口填错。区别方法很直接:在 8066 里SHOW DATABASES看到的是你配的逻辑库名,在 3306 里看到的是真实库名,对一下就清楚。
现象三:查询中文显示成问号或者乱码。原因是字符集不统一,建库建表时用了utf8mb4,数据源连接串里没指定,或者客户端默认字符集不对。解决办法是把库、表、连接串三处统一成utf8mb4,然后重建表再灌数据——已经乱码写进去的数据,改配置是救不回来的。
提示:每次改完配置都要重启服务再验证,改文件不等于生效。这是新手最常见的误会之一。
6. 下一步学什么:从「能连上」到「敢设计分片」的四步路
第一个例子跑通之后,最容易犯的错是马上想上生产。我建议按四步走,每一步都有明确的验证标准,跑通了再进下一步。
| 阶段 | 学什么 | 验证方式 |
|---|---|---|
| 第一步 | 单库读写分离,一主一从 | 写一条数据,立刻查从库能复现延迟现象 |
| 第二步 | 单库内的逻辑表配置 | 通过逻辑库增删改查,去物理库对照结果一致 |
| 第三步 | 两到四个分片,带分片键查询 | 不同user_id的数据确实落在不同库,且能查全 |
| 第四步 | 全局序列、跨库查询的边界 | 知道哪些 SQL 在分片后会变慢或不被支持 |
第一步的重点不是配通,而是亲手感受主从延迟:写一条数据,马上查从库,多试几次你就能看到那个时间窗口。有了这个体感,后面设计业务时你才会自觉地给关键读操作留一条走主库的路。
第二步是把「逻辑」和「物理」的映射关系彻底搞明白。这一步不涉及多台机器,成本最低,出错了也好回滚,是练习配置最安全的沙盒。
第三步才开始碰真正的分片。选一个字段当分片键,先分两个库,数据各插几条,然后分别用带分片键和不带分片键的条件去查,对比耗时和返回结果。你会直观地看到「不带分片键的查询会打到所有分片」,这个印象比看十篇文章都深。
第四步是建立边界感。全局自增 ID 在分片后不能靠单库的自增了,得用序列或者号段;跨库 join 大部分情况下要么拆成两次查询,要么把冗余字段打倒表里;count(*)、深度分页这类操作在分片后会明显变慢。这些不是要你立刻解决,而是要知道「哪些事不能干」,避免设计阶段就埋雷。
我第一次给人配分片的时候,图省事用了默认分片数,数据灌到一半才发现分片键选错了,只能把数据全导出重灌,那天加班到很晚。从那以后我每次动分片配置之前,都强制先把分片键、分片数、未来一年的数据增长量写在纸上,确认没问题再动手。慢一点,但不会再返工。
希望这篇能帮你把第一步迈出去。
本文还有配套的精品资源,点击获取