☰
Mycat 1.6.7.1分库分表中间件部署配置与分片规则实战
2026/10/20 6:31:04 网站建设 项目流程

简介:这是一份面向需要在CentOS7等Linux环境部署分布式数据库中间件的开发与运维人员的Mycat 1.6.7.1安装包,解决大规模数据分库分表、读写分离与高可用问题。压缩包共95个文件,大小16.74MB,主要包含42个Java依赖库(如MySQL驱动、Druid连接池、Netty通信组件)、16个properties配置文件、14个txt规则说明、10个xml核心配置与4个启停Shell脚本,以及适配不同Linux架构的动态库,可直接用于生产或学习环境。已有494人学习下载。通过配置server.xml、schema.xml等文件,可快速规划数据节点与分片策略;包内自带多种分片规则、序列生成方案及ZooKeeper相关配置,便于理解Mycat路由机制;配套的启动脚本和日志配置能帮助读者完成基础部署与调试。适合希望从零搭建数据库中间件集群、深入掌握分片规则和连接池调优的进阶读者。

1. 从 Mycat-server-1.6.7.1-release-20190213150257-linux.tar.gz 说起:分库分表中间件的落地价值

Mycat 1.6.7.1 是我在多个生产项目里反复用过的 MySQL 分库分表中间件版本包,文件名里的 20190213150257 是 2019 年 2 月 13 日的构建时间戳,linux.tar.gz 表示面向 Linux 平台的发布产物。这个版本解决的核心问题是:当单库单表扛不住流量和数据量时,业务代码不需要大改就能把数据分布到多台 MySQL 实例上。对后端开发者来说,它把分片路由、读写分离、连接复用这些逻辑收了进去,应用层面对的仍是一个逻辑数据库。适合正在做数据量预估超过单机 MySQL 承载范围的中小型团队,或者想在不改动 SQL 的前提下完成水平拆分的存量系统。


2. 部署前的一个关键问题:JDK 与 tar 包解压的完整准备

2.1 版本与环境的匹配:为什么 JDK 版本不能乱选

Mycat 1.6.7.1 用 Java 编写,官方要求的运行环境是 JDK 1.7 或 1.8。我在实际部署时强烈建议用 JDK 1.8 的最新补丁版本,因为 1.6.7.1 的某些代码路径依赖了 Java 8 才稳定的 NIO 行为,如果直接上 JDK 11 或更高版本,会出现启动后 CPU 占用异常或者集群通信超时的问题。这类问题不报错、不崩溃,只在流量上来之后以性能劣化的方式暴露,排查成本很高。常见的做法是在服务器的 /etc/profile 或 ~/.bashrc 里固定 JAVA_HOME,然后用一个干净的 shell 窗口来启动,防止环境变量污染。

先看 JDK 是否就位:

java -version # 期望输出 1.8.0_xxx,不要用 openjdk 11 以上版本跑 1.6.7.1

终端里输出结果必须包含准确的版本行,如果系统里装了多个 JDK,可以用which java和readlink -f $(which java)确认实际指向的路径。我在某公司内部服务器上遇到过默认 java 指向 jre 而不是 jdk,导致后续mycat start脚本找不到 tools.jar 而报错。这是启动脚本里的JAVA_HOME/lib/tools.jar依赖导致的,所以环境变量里必须写 JDK 的根目录,不能写 JRE 目录。

2.2 解压 tar 包与目录结构:哪些目录可以动,哪些不能动

拿到Mycat-server-1.6.7.1-release-20190213150257-linux.tar.gz后,解压得到 mycat 根目录。常见的部署位置是/opt/mycat或/usr/local/mycat,我一般固定放在/opt/mycat并做一个软链用于版本切换。解压命令如下:

tar -zxvf Mycat-server-1.6.7.1-release-20190213150257-linux.tar.gz -C /opt/ mv /opt/mycat /opt/mycat-1.6.7.1 ln -s /opt/mycat-1.6.7.1 /opt/mycat

解压后目录的核心分这几块:bin目录放启动脚本和命令行工具,conf目录放核心配置文件,lib目录放依赖 jar 包,logs目录在启动后生成运行日志。conf目录是整个中间件的配置中心,schema.xml、server.xml、rule.xml三个文件决定了路由逻辑和对外表现。如果不需要切换版本,软链这步可以跳过,但建议保留,因为 Mycat 的升级通常不是小版本平滑替换,保留完整旧目录能让你在灰度失败时快速切回。

ls /opt/mycat/ # 预期看到 bin conf lib logs 等目录

注意,解压出来的目录归属一定是执行解压的用户,如果后面用 systemd 或自定义脚本托管,需要把整个 mycat 目录的 owner 改为运行用户。我踩过权限坑:启动用户对 lib 目录没有读权限时,进程能起,但一旦执行到某个异步线程加载类就会抛 NoClassDefFoundError,表象非常迷惑。

2.3 启动方式的选择:console 模式适合第一次部署

Mycat 1.6.7.1 的启动脚本在bin目录下,支持start和console两种方式。第一次部署我一定用console模式,因为它把日志直接打到当前终端,配置错误、端口占用、参数读取异常都能在第一时间看到。后台启动只截图报错日志时会漏掉标准输出里的关键堆栈。

/opt/mycat/bin/mycat console # 前台运行,Ctrl+C 可以停止,适合首次验证配置

启动成功的标志是看到Mycat Server startup successfully字样,然后终端开始输出 MyCAT 的欢迎日志。确认无误后,再Ctrl+C停掉,改用mycat start后台启动:

/opt/mycat/bin/mycat start /opt/mycat/bin/mycat status # status 命令会输出进程和端口情况

日志文件位置是logs/mycat.log和logs/wrapper.log。启动失败时先看logs/wrapper.log,JVM 层面没有被捕获的异常都在这里,比如端口被占、堆内存分配失败;mycat.log里主要是业务层面的路由和连接池信息。两个日志分开看,能避免堆栈信息混杂,这是排查的基础。


3. 三张核心配置:server.xml、schema.xml、rule.xml 的联动与参数详解

3.1 server.xml:用户权限、逻辑库与系统级参数

Mycat 对外暴露的是逻辑库和逻辑表,应用连接 Mycat 时使用 MySQL 协议,所以在server.xml里要定义一套用户账号。这套账号和后端 MySQL 账号无关,只是 Mycat 作为 MySQL Server 对应用暴露的凭证。server.xml的 user 节点里重点配置逻辑库权限,schema属性指定这个用户能看到哪个逻辑库,readOnly控制只读,dml控制 CRUD 四种操作是否允许。配置示例:

<user name="app_user" defaultAccount="true"> <property name="password">App@123456</property> <property name="schemas">order_db</property> <property name="readOnly">false</property> </user>

参数说明:name是用户名;password是明文密码,Mycat 1.6.7.1 没有加密机制,配置文件权限要收紧到仅限运维账号可读;schemas指向逻辑库,这个逻辑库必须在schema.xml里存在,否则启动直接报错;readOnly设为 true 后,该用户只能执行 SELECT 和 SHOW。权限粒度只能到库,不能到表,这是 Mycat 的边界,如果业务要求字段级权限,就不能靠 Mycat 实现。

system节点里还有几个生产必调参数:maxStringLiteralLength控制大 SQL 的解析兼容性,defaultSqlParser决定 SQL 解析器的行为。1.6.7.1 默认用的解析器对于多表 JOIN 和子查询的支持已经比较成熟,但遇到GROUP BY加聚合函数的复杂查询仍可能出现路由到所有分片再合并的情况,这不算 bug,而是分片中间件的通用取舍。

3.2 schema.xml:逻辑库到物理节点的映射

schema.xml是整个 Mycat 的架构灵魂,它定义了逻辑库、物理数据节点、后端 MySQL 实例三者之间的映射关系。一个<schema>标签代表一个逻辑库,库里的<table>标签声明了哪些表需要分片、分片键是什么、分片规则叫什么。不声明<table>的表会被当作全局表,默认每台物理库都同步一份。配置片段:

<schema name="order_db" checkSQLschema="false" sqlMaxLimit="100"> <table name="order_info" dataNode="dn1,dn2" rule="mod-long" /> <table name="global_config" type="global" dataNode="dn1,dn2" /> </schema> <dataNode name="dn1" dataHost="host1" database="order_db_1" /> <dataNode name="dn2" dataHost="host2" database="order_db_2" />

checkSQLschema设为 false 更稳妥,设为 true 时如果应用 SQL 里带了库名前缀,Mycat 会自动截掉,但在一些老版本里这个动作有性能损耗。sqlMaxLimit是防误查的兜底,客户端不带 limit 的 SELECT 会被自动追加上限,避免应用写了个全表查询直接把所有分片压垮。dataNode的database属性指向物理库名,物理库要提前在 MySQL 里建好,Mycat 不会自动建库建表。

在我用 1.6.7.1 搭建的模拟项目 X 中,遇到过一种很容易犯的错误:把表分片了,但数据导入脚本里用了INSERT ... ON DUPLICATE KEY UPDATE,这种 SQL 在分片键更新时会被 Mycat 拒绝或路由到错误节点。分片表要求写操作必须携带分片键,且不能更新分片键字段,这是规则层面写死的约束,不是参数能改的。

3.3 rule.xml:分片规则与函数的对应

rule.xml里定义了表的切分策略。每张分片表通过<tableRule>把表名、分片键、分片函数绑定在一起。分片函数是function节点,它决定了数据行的目标节点。1.6.7.1 内置了取模、范围、枚举、一致性哈希等函数,用的最多的是mod-long(取模)和partition-by-long(范围)。一个典型配置:

<tableRule name="mod-long"> <rule> <columns>id</columns> <algorithm>mod-long-function</algorithm> </rule> </tableRule> <function name="mod-long-function"> <property name="count">2</property> </function>

columns指定分片键,algorithm引用 function 名称,count是分片数量。这个数量必须和schema.xml里对应表的 dataNode 个数一致,否则启动不报错,但路由结果会出现数据落在不存在的节点上。我习惯把 count 写成分片总数,而不是某个分片区的编号,这样后续扩容时只需要新增 function 逻辑,不容易产生歧义。


4. 分片规则的实际行为:取模、范围、枚举在 1.6.7.1 里的边界条件

4.1 取模分片:简单直接,但要提前想好扩容策略

取模分片把分片键的值对分片数求余,结果对应节点编号。比如 count=2,id=5 会落到 dn1,id=6 落到 dn2。这个规则的最大优点是数据分布均匀,写入压力能平均分摊到各物理库。最大缺点是扩容要找数:从 2 个分片扩到 3 个分片,取模结果全体变化,几乎每个数据行都可能要搬家,这个过程必须用离线迁移工具或者应用双写补偿。

取模分片适合高写入量的流水型业务,比如订单流水、日志表。我在某舆情项目的存储层里用过这方案,数据量大但 key 是单调递增的自增 ID,流量均匀,节点间数据量差距不超过 5%。但如果分片键不是自增主键而是业务随机字符串,取模的分布还相对均匀;一旦分片键有明确的业务坡度(比如按时间递增),取模后的热点会漂移,需要配合索引和查询条件来规避。

4.2 范围分片:区间稳定,但容易形成数据倾斜

范围分片用枚举分区函数实现,核心思路是按分片键的数值区间划分节点。id 在 1 到 1000 之间走 dn1,1001 到 2000 走 dn2。它的好处是扩容时只需要新增节点并调整区间,存量数据不用大规模搬迁;坏处是数据分布完全取决于业务自增的节奏,如果某段时间业务爆发,一个区间灌满,其他区间闲置,中间件层面没有任何自动均衡机制。

<function name="range-long"> <property name="mapFile">partition-range.txt</property> <property name="defaultNode">0</property> </function>

mapFile指向一个文本文件,内容格式每行如0-1000=0,表示 0 到 1000 区间的数据落到节点 0;defaultNode兜底落在哪个节点,如果分片键不在此文件内的任何区间,则走默认节点。这个兜底策略很重要,不然数据会直接报 "can't find any valid datanode" 的异常。

4.3 枚举分片:按业务维度归档,适合区域性数据

枚举分片适合分片键是有限取值集合的业务,比如按省份、按渠道、按机房 ID。它把每个枚举值映射到一个固定节点,控制力最强,但也最容易出现节点不均。某省份数据量大,该节点永远是热点。1.6.7.1 的partition-by-enum支持枚举到节点的多对一映射,也就是多个枚举值可以指向同一个节点,这能在一定程度上缓解热点,但无法自动感知数据量。

经验上,用枚举分片时不要把节点数设得太少,否则一旦某个枚举值的数据爆发式增长,只能靠手动迁移。迁移时要注意:枚举文件改动后必须重启 Mycat 才生效,没有热加载机制。我见过某开发者用reload @@config指令做配置重载,发现枚举文件不刷新,最后手动重启才生效。这块文档写得不明确,容易耽误事。

4.4 join 和全局表:跨分片查询的正确打开方式

分片表之间的 JOIN 是 Mycat 最容易翻车的地方。两张大表都分片,且关联字段不是同一套分片键,JOIN 会在中间件层产生大量跨节点数据传输,性能不可控。常见做法是:必须 JOIN 的维度表定义为global全局表,每台物理库放一份完整副本,分片表可以与其直接 JOIN;两张分片表之间的 JOIN 要么通过冗余字段解耦,要么把 JOIN 下沉到应用层分多次查询后在内存里聚合。

全局表的问题是写放大,每次写全局表都会广播到所有节点。我在一个订单系统里把用户表设为全局表,注册高峰时写放大直接拖慢所有分片。后来改成按用户 ID 取模分片,其他表也按用户 ID 分片,这样同一个用户的数据天然落在同一节点,JOIN 就变成单节点内部 JOIN,中间件不需要跨库传输。这个设计思路比任何配置调优都重要。


5. 生产落地避坑:Mycat 1.6.7.1 的启动失败与运行异常排查清单

5.1 启动即失败:日志里只有 Java 异常,没有 Mycat 相关输出

现象:执行mycat console后,终端直接抛 java.lang.ClassNotFoundException 或 java.lang.NoClassDefFoundError,根本看不到 Mycat 自己的日志。

原因:最常发生在 JDK 版本不对或者环境变量指向了 JRE。Mycat 的启动脚本会在 classpath 里加载$JAVA_HOME/lib/tools.jar,JRE 目录下不存在该文件,启动直接失败。

解决:先执行echo $JAVA_HOME确认路径,再确认$JAVA_HOME/bin/java存在。如果系统里同时装了多个 JDK,显式在启动脚本前覆盖变量:

export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk export PATH=$JAVA_HOME/bin:$PATH /opt/mycat/bin/mycat start

5.2 进程起来了但连不上 8066 端口:端口绑定与配置库的对应关系

现象:mycat status显示进程存在,但应用连接 8066 端口超时,telnet 127.0.0.1 8066不通。

原因:8066 是 Mycat 的默认连接端口,但有可能被server.xml或wrapper.conf里的参数覆盖。还有一种情况是只启动了管理端口 9066,业务端口没起。

解决:先看logs/mycat.log里Server startup successfully附近有没有端口打印,再用netstat -anp | grep java查看实际监听端口。如果配置没被改动过但还是连不上,检查 iptables 和云平台安全组是否放行 8066。生产上遇到过 9066 通、8066 不通的情况,多半是防火墙规则没放全。

5.3 连接池报错 Connection refused 或 Too many connections

现象:应用日志表明 Mycat 能连上,但执行 SQL 时报后端 MySQL 连接失败,错误信息是Connection refused或Data source rejected establishment of connection。

原因:Mycat 与后端 MySQL 的连接池配置在dataHost节点里,maxCon参数控制每个 dataHost 的最大连接数。如果后端 MySQL 的max_connections不够,或者 balance 配置导致请求全部打到一个后端实例,就会出现连接耗尽。

解决:后端 MySQL 的max_connections要调大,同时 Mycat 的maxCon要小于它。典型配置:

<dataHost name="host1" maxCon="100" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc"> <heartbeat>select user()</heartbeat> <writeHost host="host1w" url="192.168.1.10:3306" user="mycat_user" password="xxx" /> </dataHost>

balance="0"表示不做读负载均衡,只走写库;balance="1"和balance="2"决定读写分离时如何分配读请求。writeType="0"表示写请求只发到第一个 writeHost,writeType="1"表示所有 writeHost 都参与写。如果配了多写而中间件版本对事务支持不够,会造成一致性问题,1.6.7.1 建议先把多写关掉,用主从复制解决写扩展。

5.4 心跳语句选错导致误判主从切换

现象:什么都没动,但 Mycat 日志里频繁出现switch datasource或者从库被标记为不可用。

原因:<heartbeat>标签里的检测语句决定 Mycat 怎么判断后端 MySQL 是否存活。选select 1对 SQL 权限要求最低,但它只能证明 MySQL 进程活着,无法证明该库能正确服务业务查询。如果 MySQL 处于锁定状态或半连接状态,select 1依然会成功,Mycat 误以为一切正常;反之如果用select user()且账号授权不全,心跳失败就会触发误切。

解决:心跳语句统一使用select 1,同时在后端 MySQL 侧检查 mycat_user 的权限,确保至少拥有 SHOW DATABASES 和 SELECT 权限。如果主从之间延迟大,可以在心跳里加时间字段检测,但 1.6.7.1 对主从延迟的内建处理不完善,更稳妥的手段是查看 MySQL 自己的 Seconds_Behind_Master 指标,在应用层配合重试机制。

5.5 SQL 能查到数据但与分片预期不符

现象:对分片表执行SELECT COUNT(*),结果比实际少,或者根据分片键查某条数据偶尔查不到。

原因:这是分片键选择问题,而不是 Mycat bug。分片查询的套路是:SQL 里带了分片键的条件时,Mycat 能直接路由到对应节点;不带分片键条件时,Mycat 会把 SQL 发到全部分片,然后合并结果。COUNT(*)在全分片合并时没有问题,但如果业务里用了LIMIT配合ORDER BY,且排序字段不是分片键,Mycat 可能只取每个节点排序后的前 N 条,再合并时已经丢失了全局正确顺序。

解决:业务 SQL 必须保证分片键等值条件存在;分页查询里不要用LIMIT 1000,20这种深分页,Mycat 和 MySQL 一样会全分片扫描。排序字段尽量和分片键绑定,要么就让排序只对单分片内数据有意义。这是中间件方案的通用约束,改配置解决不了,得改应用 SQL。


6. 交付验收的小技巧:用 status 和日志定位问题,而不是猜

收尾直接给一个我常用的验收习惯:每套 Mycat 环境交付前,我会把mycat status、logs/mycat.log尾部输出、netstat端口监听三项数据留档。后续运维介入时,这几份快照能省两小时的排查时间。启动成功后依次执行:

/opt/mycat/bin/mycat status tail -n 50 /opt/mycat/logs/mycat.log netstat -anp | grep -E "8066|9066"

status能看到进程 ID 和运行时长,确认不是僵尸进程;日志尾部重点看有没有异常堆栈;端口监听看有没有绑定到 127.0.0.1 而不是 0.0.0.0,后者会导致远程应用连不上。再往下看数据正确性,可以从应用层读取一条已知分片键的数据,确认它落在预期的物理库。分片键取 1 和取 2 分别测一次,就能覆盖不同节点的路由。

生产环境我会再创建一个只读监控账号,定期执行SELECT COUNT(1) FROM information_schema.tables对比各分片的数据量分布,提前发现倾斜。这个习惯挽回过一次事故:某业务分片键是自增主键,但中间有段时间导入了历史数据,导致新数据全走一个分片,另一个分片几乎空闲,应用查询响应开始抖动。如果只盯着 Mycat 层面的日志,根本看不到这个走向。

最后想说的是:Mycat 1.6.7.1 是我用过的版本里比较稳定的一个 1.6 终结版,它谈不上完美,但你对它的分片边界理解得越清楚,踩的坑就越少。每次把分片键选错、把全局表滥用、把连接池调爆,折腾下来都是一轮血泪经验。希望这篇笔记能帮你在选型和技术落地的第一个月就避开这些坑,把时间花在业务数据模型的设计上,而不是和中间件本身纠缠。

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

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

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

立即咨询