简介:本资源为Mycat 1.6.7.1版本的Linux发行包,面向在CentOS7环境下搭建分布式数据库集群的运维与后端开发人员,用于解决大数据场景下的水平扩展、读写分离与负载均衡问题。压缩包共95个文件,约16.74MB,以42个jar依赖库、16个properties配置、10个xml配置及4个sh脚本为主,另含so本地库与wrapper启动组件,覆盖服务运行、分片规则与连接池等核心模块。资源内含server.xml、schema.xml、rule.xml等关键配置及多种分片算法定义文件,可帮助读者理解数据节点划分、哈希与范围分片策略、数据源连接配置及服务启停脚本的使用方式。目前已有494人学习下载,适合需要快速部署Mycat中间件、研究分片与读写分离实现细节的开发者参考。
1. 拿到 Mycat-server-1.6.7.1 这个包,先搞清楚它到底解决什么问题
如果你手上正好有一个Mycat-server-1.6.7.1-release-20190213150257-linux.tar.gz,大概率是遇到了这样的场景:后端几套 MySQL 实例,业务代码里散落着一堆分库分表逻辑,改一次路由规则要动好几处代码,运维想加一个只读节点还得让开发配合发版。Mycat 这类数据库中间件的价值就在这——它把自己伪装成一个 MySQL 服务端,应用连它就像连一个普通 MySQL,分库分表、读写分离、路由规则全部下沉到中间件层配置。
这个 1.6.7.1 版本属于 Mycat 1.6 系列里比较成熟的一支,release 时间戳是 2019 年初,基于 JDK 1.7+ 运行,配置以 XML 为主,核心是schema.xml、server.xml、rule.xml三件套。它适合谁?适合那些数据库已经出现单表数据量膨胀、读写压力集中,但又不想大改业务代码的团队。不适合谁?不适合刚起步、单库单表还跑得飞快的项目,中间件本身会带来额外的连接开销和排错复杂度,属于典型的"规模到了才值得上"的东西。
这一篇不讲空泛概念,就顺着这个 tar.gz 包,从解压、配置、启动,一路讲到分片规则怎么设、踩坑怎么排,让你能真正把它跑起来并理解每个参数在干什么。
2. 解压与目录结构:先认清这个包里有什么
2.1 解压命令与目录布局
拿到 tar.gz 第一步永远是先看结构再动手,别急着改配置。用下面命令解压并查看顶层目录:
# 解压到当前目录,会生成 mycat 目录 tar -zxvf Mycat-server-1.6.7.1-release-20190213150257-linux.tar.gz # 进入目录看结构 cd mycat ls -l解压后你会看到几个关键目录:bin放启动脚本,conf放全部配置文件,lib放依赖 jar,logs放运行日志。这里有个血泪经验:不要在生产机上直接解压到有中文或空格的路径,Mycat 的启动脚本对路径里的特殊字符处理并不健壮,路径带空格会导致 classpath 拼接出错,启动直接报找不到主类。
2.2 三个核心配置文件的分工
在动手改之前,必须先把三个文件各自的职责分清楚,否则改错文件会浪费大量时间:
| 配置文件 | 职责 | 改动频率 |
|---|---|---|
| server.xml | 定义 Mycat 对外暴露的用户、密码、逻辑库名、端口 | 低 |
| schema.xml | 定义逻辑库、逻辑表、数据节点、真实物理库连接 | 高 |
| rule.xml | 定义分片算法和分片字段 | 中 |
server.xml里最容易被忽略的是<system>标签下的sequnceHandlerType,它决定全局序列号的生成方式,默认是本地文件方式,分布式环境下必须改成数据库方式,否则多节点会生成重复 ID。这个坑后面会细说。
2.3 环境依赖检查
Mycat 1.6.7.1 依赖 JDK,启动前先确认版本:
java -version # 需要 1.7 及以上,推荐 1.8如果机器上有多个 JDK,务必在bin/mycat脚本里显式指定JAVA_HOME,不要依赖系统默认。我一般会在脚本开头加一行export JAVA_HOME=/your/jdk/path,避免因为环境变量漂移导致启动时好时坏,这种问题排查起来最费劲。
3. 配置一个能跑通的分库分表最小实例
3.1 server.xml:定义逻辑库和访问账号
先配置对外访问入口。打开conf/server.xml,找到<user>标签部分,改成你自己的账号:
<user name="app_user" defaultAccount="true"> <!-- 应用连接 Mycat 时使用的密码 --> <property name="password">app_pass_123</property> <!-- 该账号能访问的逻辑库,对应 schema.xml 里的 schema name --> <property name="schemas">order_db</property> <!-- 是否只读,false 表示可读写 --> <property name="readOnly">false</property> </user>这里的schemas值order_db必须和后面schema.xml里<schema name="order_db">完全一致,大小写敏感。参数说明:name是登录用户名,password是登录密码,readOnly控制该账号是否只能读。逻辑说明:Mycat 用这套账号体系拦截应用连接,应用端配置的 JDBC 连接串里写的库名就是这里的order_db,而不是真实物理库名。
3.2 schema.xml:把逻辑表映射到真实分片
这是整个配置里最核心的文件。假设我们要把订单表t_order按用户 ID 分成两个库、每个库两张表:
<schema name="order_db" checkSQLschema="false" sqlMaxLimit="100"> <!-- 逻辑表,dataNode 指向下面的分片节点 --> <table name="t_order" dataNode="dn1,dn2" rule="mod-long" /> </schema> <!-- 定义两个数据节点,分别指向不同的物理库 --> <dataNode name="dn1" dataHost="host1" database="order_db_0" /> <dataNode name="dn2" dataHost="host2" database="order_db_1" /> <!-- 物理库连接信息 --> <dataHost name="host1" maxCon="100" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1"> <heartbeat>select user()</heartbeat> <writeHost host="hostM1" url="jdbc:mysql://127.0.0.1:3306/order_db_0" user="root" password="root_pass"> </writeHost> </dataHost> <dataHost name="host2" maxCon="100" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1"> <heartbeat>select user()</heartbeat> <writeHost host="hostM2" url="jdbc:mysql://127.0.0.1:3307/order_db_1" user="root" password="root_pass"> </writeHost> </dataHost>关键参数逐个说:sqlMaxLimit="100"是防止应用不带 limit 查询时把全表拉回来,生产环境建议设成 100 到 1000 之间;balance="0"表示不开启读写分离,所有请求走写库,如果配了从库要改成 1 或 2;switchType="1"表示写库宕机自动切换,配合heartbeat心跳检测使用。逻辑说明:应用看到的是order_db.t_order一张表,实际数据被mod-long规则分散到两个物理库的t_order表里,路由对应用完全透明。
3.3 rule.xml:分片算法的选择与参数
mod-long是内置规则,直接引用即可,但你要理解它的分片字段从哪来:
<tableRule name="mod-long"> <rule> <!-- 分片字段,必须是表里真实存在的列 --> <columns>user_id</columns> <algorithm>mod-long</algorithm> </rule> </tableRule> <function name="mod-long" class="io.mycat.route.function.PartitionByMod"> <!-- 分片数量,必须等于 dataNode 的数量 --> <property name="count">2</property> </function>这里有个必须记住的约束:count的值必须和table标签里dataNode列出的节点数量一致,写 2 个节点就填 2,填 3 会导致部分数据路由到不存在的节点,插入直接失败。columns指定的user_id必须是查询条件里会带上的字段,否则跨分片查询会退化成全节点扫描,性能反而比单库差。
3.4 启动与连接验证
配置改完,启动服务:
# 启动,-start 表示后台启动 ./bin/mycat start # 查看状态 ./bin/mycat status # 实时看日志,启动失败第一时间看这里 tail -f logs/wrapper.log启动成功后,用 MySQL 客户端连 Mycat 的默认端口 8066:
mysql -h127.0.0.1 -P8066 -uapp_user -papp_pass_123连上后执行show databases;应该能看到order_db,再use order_db; show tables;能看到t_order。此时插入一条数据,去两个物理库里分别查,就能验证分片是否生效。如果连不上,先看logs/wrapper.log里的报错,九成是配置文件的 XML 格式错误或端口被占用。
4. 分片规则与全局序列:决定系统能不能长期跑下去
4.1 常用分片算法对比与选型
mod-long只是入门,真实业务里分片字段和算法选错,后期扩容会非常痛苦。常见算法对比如下:
| 算法 | 适用场景 | 扩容难度 | 数据倾斜风险 |
|---|---|---|---|
| mod-long | 数据量均匀、分片数固定 | 高,扩容需重分布 | 低 |
| 范围分片 range | 按时间或 ID 区间 | 低,追加节点即可 | 中,热点集中 |
| 一致性哈希 | 节点频繁增减 | 低 | 低 |
| 枚举分片 | 按地区、类型等有限枚举 | 低 | 取决于枚举分布 |
我一般会这样选:如果业务能接受按时间归档,用范围分片最省心,扩容就是加节点;如果分片字段是用户 ID 且分布均匀,mod-long够用;如果节点数会动态变化,一致性哈希更稳。选型时一定要问自己一句:半年后数据翻倍,我加一个节点要不要停机迁移?这个问题的答案决定了你选哪个算法。
4.2 全局序列号的三种方案
分库分表后,自增主键不再全局唯一,必须用全局序列。Mycat 提供三种方式,在server.xml的<system>里通过sequnceHandlerType切换:
<system> <!-- 0=本地文件 1=数据库 2=时间戳 --> <property name="sequnceHandlerType">1</property> </system>本地文件方式(0)性能最好但多节点会重复,只适合单节点;数据库方式(1)需要提前在某个库建一张序列表,Mycat 通过数据库锁保证唯一,适合分布式;时间戳方式(2)实现简单但高并发下可能重复。生产环境我基本只用数据库方式,虽然多一次数据库交互,但唯一性有保障。建表语句大致如下:
CREATE TABLE MYCAT_SEQUENCE ( name VARCHAR(50) NOT NULL, current_value INT NOT NULL, increment INT NOT NULL DEFAULT 1, PRIMARY KEY (name) ) ENGINE=InnoDB;建好后插入一行初始记录,并在schema.xml里配置sequenceHandlerType对应的数据节点,Mycat 启动时会去读这张表。
4.3 ER 分片与父子表绑定
订单和订单明细这类有强关联的表,如果各自按不同字段分片,join 会变成跨库操作,性能极差。Mycat 的 ER 分片通过childTable把子表绑定到父表的分片规则上:
<table name="t_order" dataNode="dn1,dn2" rule="mod-long"> <!-- 子表跟随父表分片,join 时能下推到同一节点 --> <childTable name="t_order_item" joinKey="order_id" parentKey="order_id" /> </table>joinKey是子表里的外键列,parentKey是父表里的对应列。绑定后,同一个订单的明细一定落在和订单相同的物理节点上,join 就能在单库内完成。这个配置能省掉大量跨库查询,是分片设计里性价比很高的一步。
5. 避坑与排查:那些让新手卡半天的真实问题
5.1 启动报 "Cannot find class" 或端口占用
现象:执行./bin/mycat start后status显示未启动,wrapper.log里报找不到主类或端口被占用。原因通常是JAVA_HOME没配好,或者 8066、9066 端口已被其他进程占用。解决:先在bin/mycat里显式写死JAVA_HOME,再用netstat -tlnp | grep 8066查端口占用,杀掉冲突进程或改server.xml里的端口。
5.2 插入数据报 "can't find datanode"
现象:应用插入时报找不到数据节点。原因多半是rule.xml里count的值和schema.xml里dataNode数量不一致,或者分片字段的值算出来的下标超出了节点范围。解决:核对两处数量是否相等,并确认分片字段没有 null 值——null 值参与取模会得到意外结果。
5.3 跨分片查询返回结果不全
现象:不带分片字段的查询,返回的数据比预期少。原因:Mycat 默认只把查询路由到部分节点,或者sqlMaxLimit截断了结果。解决:确认查询条件是否包含分片字段,如果不包含,要么接受全节点扫描的性能代价,要么在业务层强制带上分片字段。sqlMaxLimit设得太小也会导致结果被截断,按业务实际需要调整。
5.4 主键重复导致插入失败
现象:多节点环境下插入报主键冲突。原因:用了本地文件方式的全局序列,或者根本没配全局序列,各节点各自自增。解决:把sequnceHandlerType改成 1,建好序列表,确保所有节点共用同一个序列源。这个坑在单机测试时不会暴露,一上多节点就翻车,属于典型的"测试环境好好的,生产就出事"。
5.5 连接数暴涨拖垮数据库
现象:Mycat 运行一段时间后物理库连接被打满。原因:dataHost里maxCon设得过大,或者应用端连接池和 Mycat 连接池叠加放大。解决:maxCon按物理库实际承受能力设置,一般单库不超过 100;同时应用端连接池要相应调小,因为应用连的是 Mycat,Mycat 再连物理库,两层连接池会相乘。这个参数关系一定要算清楚,否则数据库会被连接数压垮。
6. 进阶技巧:让 Mycat 在生产环境稳得住
跑通只是第一步,真正让 Mycat 在生产环境长期稳定,靠的是几件不起眼但很关键的事。第一件是慢查询日志的开启与定期分析,在server.xml里把sqlMaxLimit和慢查询阈值配好,定期看logs/mycat.log里执行时间超过阈值的 SQL,这些往往就是没带分片字段的全节点扫描,是性能杀手。第二件是压测验证分片均匀度,上线前用真实数据分布跑一遍,统计每个物理节点的数据量和 QPS,如果某个节点明显偏高,说明分片字段选得不好,趁数据量小赶紧换。
第三件是配置文件的版本管理。schema.xml和rule.xml一旦上线,任何改动都要走版本控制,因为这两个文件直接决定数据路由,改错一个字符就可能导致数据写错库。我习惯在每次改动前先备份,改完用diff对比,确认只动了预期的地方再重启。第四件是灰度扩容的节奏,加节点时不要一次性把流量切过去,先让新节点承接少量分片,观察稳定后再逐步迁移,避免一次性重分布把数据库压垮。
还有一个容易被忽略的点:Mycat 本身不存储数据,它只是路由层,所以它自己的高可用也要考虑。单点 Mycat 挂了整个应用就连不上数据库,常见做法是前面挂一个负载均衡,起两个 Mycat 实例,应用连负载均衡地址。这样即使一个 Mycat 实例出问题,另一个还能顶上。这套组合下来,Mycat 才算真正具备生产可用性,而不是一个"能跑通"的玩具。
我自己踩过最深的一个坑,是早期图省事用了本地文件方式的全局序列,单机测试一切正常,上线加到两个节点后开始零星报主键冲突,排查了大半天才定位到序列号生成方式。从那以后,凡是涉及分布式唯一性的配置,我都会在测试阶段就用多节点环境验证,绝不相信单机测试的结果。希望这些经验能帮你少走一段弯路,帮到你。
本文还有配套的精品资源,点击获取