资讯动态

Mycat 1.6.7.1部署实战:分库分表与读写分离配置指南

发布时间:2026/9/25 9:01:24 来源:尧图企业网站定制
简介这是 Mycat 1.6.7.1 在 CentOS7 下的 Linux 发行压缩包面向需要搭建分布式数据库中间件的中高级开发与运维人员用于解决大规模数据存储时的分库分表、读写分离与水平扩展问题。包体共95个文件整体16.74MB以42个 jar 依赖库为主辅以16个 properties 配置、10个 xml 分片规则文件、4个 sh 启动脚本及少量动态库目录结构清晰便于按模块查找。内容覆盖 server.xml 全局配置、schema.xml 表结构与分片规则、数据源定义以及哈希、范围、枚举等多种分片策略示例并附有 ZooKeeper 相关配置、dbseq.sql 初始化脚本和启动/关闭脚本可帮助读者在 CentOS7 上快速搭建具备读写分离和高可用能力的数据库集群。包内还提供典型分片算法、全局序列号生成及协调配置便于理解分布式事务与数据路由的实现方式。目前已有494人学习适合希望结合具体配置理解 Mycat 原理并落地生产环境的工程技术人员。1. 这个 tar.gz 不是数据库安装包它解压后是 MySQL 的“路由中枢”第一次看到Mycat-server-1.6.7.1-release-20190213150257-linux.tar.gz这个名字很多人以为它是个数据库安装包解压完能跑一个 mysqld。实际不是。它解压出来没有数据库内核有的是一套 Java 写的代理层你给应用一个 8066 端口当 MySQL 连Mycat 在后头把一条 SQL 按分片规则拆到多台真实 MySQL 上执行再合并结果返回。1.6.7.1 是 Mycat 1.6 系列里被生产环境用得最多的一版这个构建号对应 2019 年 2 月 13 日的 release 包。它要解决的问题很直接单库写不下了、单表数据量上亿了、主从库想读写分离但不想改应用代码。适合正在用 MySQL、想先拿中间件做分库分表和读写分离、团队里没人写过 Proxy 的读者。下面从部署开始一步步把它跑起来。2. 部署前先过三道关JDK 8、目录结构、最小启动2.1 环境判断为什么 1.6.7.1 只认 JDK 8Mycat 1.6 系列基于 JDK 8 开发tar.gz包里的启动脚本不会帮你装 Java只会去找JAVA_HOME。如果你的服务器默认装了 OpenJDK 11 或 17解压完直接mycat start大概率秒退日志里报UnsupportedClassVersionError或者类加载异常。这不是包坏了是 1.6 的老代码依赖 JDK 8 的模块结构高版本 JDK 把内部 API 封了。先做三件事确认环境再解压# 1. 确认当前 Java 版本必须是 1.8.x java -version # 2. 确认 JAVA_HOME 指向 JDK 8 路径 echo $JAVA_HOME # 3. 如果机器上装了多个 JDK编辑 /etc/profile 固定它 export JAVA_HOME/usr/lib/jvm/java-1.8.0-openjdk export PATH$JAVA_HOME/bin:$PATH参数说明java -version输出里出现1.8.0_xxx才是对的只写openjdk version 11.x就不行。JAVA_HOME是 Mycat 启动脚本bin/mycat读取的关键变量它优先用这个路径找java可执行文件。实际部署时我习惯把这个 export 写进/etc/profile.d/mycat.sh避免重启后环境变量丢失。内存方面默认配置里 JVM 堆开得比较大conf/wrapper.conf里wrapper.java.initmemory和wrapper.java.maxmemory分别控制初始堆和最大堆。2C4G 的机器上我会把maxmemory调到 2048initmemory保持 1024太小会导致分片合并查询时频繁 GC太大又会跟 MySQL 抢内存。2.2 tar.gz 解压与目录结构bin、conf、lib、logs 各自管什么解压命令很简单但目标目录要先规划好。我一般统一放到/opt/mycat而不是散落在/root或/home下方便后面做 systemd 托管和日志轮转。# 创建目标目录 mkdir -p /opt/mycat # 解压到 /opt/mycat注意 -C 参数指定目录 tar -zxvf Mycat-server-1.6.7.1-release-20190213150257-linux.tar.gz -C /opt/mycat # 解压后确认目录结构 ls -l /opt/mycattar -zxvf的四个参数z代表通过 gzip 解压x代表 extractv是 verbose 打印解压过程f后面必须跟文件名。如果你用tar -xf也能解系统会自动识别格式但在脚本里我习惯写全。解压完你会看到五个目录职责分得很清楚目录作用部署阶段关注度bin启动/停止脚本mycat还有init_zk_data等辅助脚本高conf全部配置文件schema.xml、rule.xml、server.xml最高libMycat 自身依赖的 jar以及 MySQL 连接驱动高logsmycat.log和wrapper.log高catlet自定义全局序列等扩展脚本目录低lib目录里默认带的 MySQL 驱动是 5.1.x 的版本这点先记下后面配置 MySQL 8 时这里会变成一个坑。conf里这三个文件决定了整个分片行为第 3 章逐个拆开讲。2.3 第一次启动start 之后看这 3 个信号判断成败配置一行没改的情况下可以先把服务拉起来验证环境没问题。这一步如果起不来后面配置改再多都是白搭。# 启动 /opt/mycat/bin/mycat start # 查看进程状态 /opt/mycat/bin/mycat status # 确认端口监听8066 是数据端口9066 是管理端口 netstat -lntup | grep -E 8066|9066 # 查看启动日志这一行最关键 tail -n 50 /opt/mycat/logs/wrapper.log逻辑说明mycat start通过 Tanuki Wrapper 拉起 Java 进程status返回running只代表进程在不代表服务可用。判断启动成功要看三个信号第一wrapper.log里出现mycat server startup successfully字样第二netstat能看到8066和9066都在监听第三logs/mycat.log里没有ERROR级别的堆栈。常见失败是启动后进程很快就没了status显示not running。这时wrapper.log最后几行通常会给出原因最高频的是Invalid or unsupported syntax或者找不到JAVA_HOME前者说明你还装了其他 Java 版本后者说明环境变量没生效。第一次启动别急着改配置先把这三步跑通后面才有排查的抓手。3. 核心配置三件套把分库分表写进 schema.xml、rule.xml、server.xml3.1 先分清逻辑库、数据节点、物理库三层模型Mycat 的配置之所以让新手晕是因为它有三个“库”的概念。我在培训团队时用一个例子讲明白应用连接的叫逻辑库它不对应任何真实数据库只是 Mycat 内存里的一个路由入口逻辑库下面有逻辑表逻辑表里每条数据真正存在哪里由数据节点决定数据节点指向某个 MySQL 实例上的物理库。应用 - 逻辑库(schema) - 逻辑表(table) - 数据节点(dataNode) - 物理库(database)这个tar.gz包里买不到任何物理库它只是把请求路由到你的 MySQL 上。所以部署 Mycat 之前物理库必须先建好分片表也要先在 MySQL 里建好Mycat 不会帮你建表它只负责把 SQLinsert路由到正确的物理表。理解这一点后面排查“表不存在”“Invalid datasource”时能找到方向。3.2 schema.xml 落地一个订单表拆两个物理分片直接给一个可以抄作业的最小配置。场景是订单表按order_id取模拆到两个物理库dn1和dn2两台 MySQL 分别部署在不同的机器上。?xml version1.0 encodingUTF-8? !DOCTYPE mycat:schema SYSTEM schema.dtd mycat:schema xmlns:mycathttp://io.mycat/ !-- 逻辑库应用连接的是这里 -- schema namemycat_order checkSQLschematrue sqlMaxLimit100 !-- 逻辑表order_info 拆到 dn1、dn2按 mod-long 规则分片 -- table nameorder_info dataNodedn1,dn2 rulemod-long / !-- 全局表region 每台物理库都放一份避免跨库 join -- table nameregion typeglobal dataNodedn1,dn2 / /schema !-- 数据节点dn1 指向 MySQL A 的 order_db 库 -- dataNode namedn1 dataHosthostA databaseorder_db / !-- 数据节点dn2 指向 MySQL B 的 order_db 库 -- dataNode namedn2 dataHosthostB databaseorder_db / !-- 数据主机hostA 连接 MySQL A -- dataHost namehostA maxCon200 minCon20 balance0 writeType0 dbTypemysql dbDriverjdbc heartbeatselect user()/heartbeat writeHost hostA1 urljdbc:mysql://192.168.1.10:3306/order_db usermycat_user passwordMycat123 /writeHost /dataHost !-- 数据主机hostB 连接 MySQL B -- dataHost namehostB maxCon200 minCon20 balance0 writeType0 dbTypemysql dbDriverjdbc heartbeatselect user()/heartbeat writeHost hostB1 urljdbc:mysql://192.168.1.11:3306/order_db usermycat_user passwordMycat123 /writeHost /dataHost /mycat:schema逻辑说明schema的name是应用连接时用的库名比如jdbc:mysql://mycat-host:8066/mycat_order。checkSQLschema设置为true时如果 SQL 里写成select * from mycat_order.order_infoMycat 会自动把库名前缀剥掉再路由设为false则会原样发给物理库物理库就会报Table mycat_order.order_info doesnt exist。sqlMaxLimit100是安全网没有带 limit 的查询会自动追加防止一次把几百 GB 的数据拉到前端。table标签里字段含义name是逻辑表名物理库里的表名要跟它一致dataNode用逗号分隔列出所有物理位置rule指向rule.xml里定义的分片规则typeglobal表示全局表每个分片都放一份完整数据适合配置类表查询时不会做跨库 join。dataHost里每个连接池参数都要解释maxCon是到该 MySQL 实例的最大连接数minCon是启动时保持的最小连接数。balance0代表不启用读写分离这台主机上所有请求都走writeHost。writeType0表示第一个 writeHost 是主节点。dbDriverjdbc走 JDBC 连接1.6.7.1 也支持native驱动但生产里 JDBC 更稳。3.3 rule.xml 分片算法选择mod-long、枚举、一致性哈希分片规则是 Mycat 的分水岭。选错了数据分布不均扩容和查询都难受。rule.xml里一个完整规则由tableRule和function两部分组成tableRule定义走哪个列、用哪个函数function定义函数类名和参数。!-- mod-long按分片键取模 -- tableRule namemod-long rule columnsorder_id/columns algorithmmod-long/algorithm /rule /tableRule function namemod-long classio.mycat.route.function.PartitionByMod property namecount2/property /function逻辑说明columns指定分片键这里是order_id。algorithm指向function的name。PartitionByMod是取模算法count2表示把数据平均分到 2 个节点。这个算法的优点是实现简单、路由快缺点是新增节点时存量数据得重新分布不适合频繁扩容。实际项目里我常用的分片算法有三种适用场景完全不同算法class 类名核心参数适用场景注意点mod-long 取模PartitionByModcount节点数订单、流水等增长均匀的数据扩容要迁移数据枚举分片PartitionByFileMapmapFile映射文件按地域、按业务线分类映射文件里要设defaultNode否则未知值报错一致性哈希PartitionByMurmurHashseed、count、virtualBucketTimes用户表、内容表分布均匀、扩容平滑但路由计算略慢分享一个选型经验订单、交易流水这类有明确递增主键且增长模型稳定的用mod-long最省心需要按省份或渠道隔离的场景用sharding-by-intfile把枚举值写进映射文件路由直观如果业务未来有扩容预期或者分片键枚举值不可控直接用murmur一致性哈希别在取模上硬扛。3.4 server.xml 说清账号授权与两个端口8066 与 9066server.xml管理的是 Mycat 自己的登录账号不是 MySQL 的账号。应用连 8066 时用的用户名密码由这里控制而不是物理库的账号。新增一个只读账号和修改端口操作如下system !-- Mycat 数据端口应用 SQL 走这里 -- property nameserverPort8066/property !-- Mycat 管理端口运维命令走这里 -- property namemanagerPort9066/property /system user namemycat_app property namepasswordApp123456/property property nameschemasmycat_order/property property namereadOnlytrue/property /user user namemycat_manage property namepasswordManage123456/property property nameschemasmycat_order/property /user逻辑说明serverPort和managerPort必须不能被其他进程占用改完重启生效。user标签里name是登录名password是 Mycat 认证密码schemas指向schema.xml里定义的逻辑库名多个库用逗号分隔。readOnlytrue表示该账号只能执行查询写入会报权限错误。管理端口 9066 的用途很重要后面验证分片路由全靠它-- 连接管理端口查看数据节点状态 mysql -h127.0.0.1 -P9066 -umycat_manage -pManage123456 -- 查看所有数据节点的连接状态 show datanode; -- 动态加载配置不用重启服务 reload config_all;参数说明reload config_all是运维最常用的命令它会把schema.xml、rule.xml、server.xml重新加载一遍过程里如果配置写错会加载失败并提示具体节点。需要强调的是reload与重启不一样它不会断开现有连接生产环境里优先用 reload 而不是 restart。但这个命令不是百分百无痛长事务和连接池中已建立的连接可能还持有旧 schema所以重大变更我还是建议在低峰期做。4. 避坑清单MySQL 8 认证、JDK 大版本、端口冲突、时区偏差、漏带分片键4.1 MySQL 8.x 连接报错认证插件不兼容的三种解法现象Mycat 启动正常mycat.log里出现Public Key Retrieval is not allowed或Communications link failure应用层连接报Access denied for user mycat_user但用 Navicat 连同一台 MySQL 却正常。原因MySQL 8 默认的认证插件是caching_sha2_password而 Mycat 1.6.7.1 的lib目录里自带的驱动是 MySQL 5.1.x不支持这个插件。驱动版本太老密码传输协议对不上。解决三种路径任选。第一种把 MySQL 账号的认证插件改回mysql_native_password兼容性最好-- 在 MySQL 8 物理库执行替换掉 YOUR_PASSWORD ALTER USER mycat_user% IDENTIFIED WITH mysql_native_password BY YOUR_PASSWORD; FLUSH PRIVILEGES;第二种替换 Mycat 的lib目录下连接驱动为 8.x 版本并在dataHost的url末尾追加allowPublicKeyRetrievaltrue。第三种如果物理库是 5.7 且不打算升级保持现状即可。生产上我倾向于第一种 第二种都做因为团队后面总要有人用新驱动排查。4.2 JDK 11 启动即退1.6 系列的真实兼容边界现象mycat start命令执行后进程立刻消失status显示not runningwrapper.log末尾报java.lang.UnsupportedClassVersionError或java.lang.NoClassDefFoundError。原因JDK 9 开始模块化JDK 11 起移除了 Mycat 1.6 依赖的java.se相关内部 API导致类初始化时找不到符号。这不是 Mycat 配置问题是 JDK 版本越界。解决到/usr/lib/jvm下确认有没有java-1.8.0没有就先装 OpenJDK 8然后检查bin/mycat脚本开头是否硬编码了JAVA_HOME。我的做法是把JAVA_HOME显式写进启动脚本前的export比只改/etc/profile可靠因为 systemd 托管时未必加载 profile。4.3 第二个实例起不来8066/9066 端口被占的排查路径现象在一台机器上同时跑两套 Mycat第二个实例start后进程在但netstat里看不到 8066应用连不上或者直接报BindException: Address already in use。原因server.xml里没改端口两个实例默认都绑定 8066 和 9066后启动的实例必然绑定失败。Mycat 的日志对这个问题提示得很隐蔽只会在wrapper.log里出现一行网络异常容易被忽略。解决第二个实例在server.xml的system段里把serverPort改成8067、managerPort改成9067然后重启。排查时先用lsof -i :8066确认占用进程是谁不要盲目 kill生产环境上 8066 可能就是你在用的另一个中间件。4.4 时间差 8 小时时区配置不在 Mycat 而在 dataHost URL现象应用通过 Mycat 插入订单时间数据库里存的时间比NOW()差 8 小时或者报The server time zone value ???ú??? is unrecognized。原因JDBC 驱动连接物理库时会话时区没有指定。Mycat 转发的是 SQL 本身它不做时间转换时区问题出在dataHost的连接 URL 上。解决在schema.xml每个dataHost的url里追加时区参数writeHost hostA1 urljdbc:mysql://192.168.1.10:3306/order_db?serverTimezoneAsia/Shanghaiamp;useSSLfalse usermycat_user passwordMycat123 /注意 XML 里要转义成amp;否则schema.xml解析报错。改完执行reload config_all并清理物理库连接池。这是一条血泪经验如果只调 MySQL 的global_time_zone而不同步改连接 URL过段时间 MySQL 重启或主从切换后时区又会漂回去。4.5 不带分片键的查询不报错它会在所有分片扫一遍现象有一条 SQL 明明很慢逻辑库数据也不大但执行要几十秒。看日志发现 Mycat 把它发到了所有数据节点每个节点返回全量数据再合并。原因Mycat 对于不带分片键的查询策略是全节点路由也就是说select * from order_info where status 1会同时在 dn1、dn2 上执行。如果物理表没建好索引这等于一次全表扫描乘以节点数。Mycat 不报错因为它无法判断数据在哪只能全发。解决分片键条件必须带上这是应用层规范问题。如果业务确实经常用非分片键查询常见做法是建一个按该字段分片的冗余表或者把这类表设计成全局表。改配置只能缓解根治要靠分片键设计。Mycat 日志里看到一条 SQL 被拆出多个 DATA_NODE 执行先怀疑是不是漏了分片键。5. 读写分离与高可用一个 dataHost 撑起一主一从5.1 balance 从 0 到 3读写分离的四档姿势如果分片只是把数据拆开读写分离则是把读压力卸载到从库。dataHost里的balance参数控制了读写分离的开关和力度常见配置dataHost namehostA maxCon200 minCon20 balance3 writeType0 dbTypemysql dbDriverjdbc heartbeatselect user()/heartbeat writeHost hostM1 urljdbc:mysql://192.168.1.10:3306/order_db usermycat_user passwordMycat123 readHost hostS1 urljdbc:mysql://192.168.1.12:3306/order_db usermycat_user passwordMycat123 / /writeHost /dataHost逻辑说明readHost嵌在writeHost内部语义是“M1 的从库是 S1”。balance有四个档位0表示不启用读写分离所有请求走 writeHost1表示读请求在 writeHost 和 readHost 之间轮流分发2表示读请求只发 readHostwriteHost 只处理写3表示读请求在所有节点间随机分发。生产环境用2最纯粹主库专心写从库专心读。balance1的坑在于主库同时承担读写一旦读压上来主库的写入延迟会明显上升。我的建议是一主一从结构用2一主多从想要所有节点都分担读用3除非有特殊原因否则别用1。5.2 heartbeat 与 switchType主库宕机后切换恢复Mycat 对主从状态的判断靠heartbeat配置的心跳 SQL。select user()是最轻量的探活语句它能确认连接存活但判断不了主从复制是否健康。switchType控制切换到策略1.6.7.1 里常用的是-1和1dataHost namehostA maxCon200 minCon20 balance2 writeType0 switchType1 dbTypemysql dbDriverjdbc heartbeatshow slave status/heartbeat writeHost hostM1 urljdbc:mysql://192.168.1.10:3306/order_db usermycat_user passwordMycat123 readHost hostS1 urljdbc:mysql://192.168.1.12:3306/order_db usermycat_user passwordMycat123 / /writeHost /dataHostswitchType-1意味着关闭自动切换主库挂了 Mycat 只是把该节点标记为不可用不会把写流量切到从库适合人为管控切换的场景。switchType1开启自动切换但要注意Mycat 切换的是 writeHost 角色它会把原 writeHost 标记为只读把原 readHost 提升为 writeHost。这个切换不是 MySQL 层面的主从切换它只改了 Mycat 的路由表所以物理库的主从复制必须提前用 MHA 或手动操作完成Mycat 不会帮你处理从库的提升。踩过的坑是show slave status做心跳时如果从库没有配置主从复制这条 SQL 会直接报错导致 Mycat 误判节点不可用。首次部署读写分离时先手动在从库执行一遍show slave status确认有输出再启动 Mycat。5.3 事务内读写粘连一条连接从头走到尾配置好读写分离后有个现象会让新手困惑明明balance2事务里先写后读第二条读 SQL 却还是走了主库。这不是配置没生效而是 Mycat 的事务连接粘连机制。Mycat 在一个事务内部不会释放后端连接也就是说事务中所有 SQL 都绑定在同一个 dataHost 的后端连接上。如果这个连接是 writeHost那么整个事务里的读也会走 writeHost。直到事务提交或回滚连接释放回连接池后续的普通查询才会重新按balance分配。这个设计是对的它避免了“事务里先写后读读却落到从库读到旧数据”的一致性问题。所以应用侧要注意只读业务不要包在事务里否则读写分离对这类 SQL 完全无效。一个常见优化是在注解里指定路由!-- Mycat 注解强制这条 SQL 走从库 -- /*#mycat:db_typeslave*/ select count(*) from order_info;逻辑说明/*#mycat:db_typeslave*/是 Mycat 1.6 支持的 SQL 注解执行这条 SQL 时会强制走从库不参与balance策略。适合报表查询、统计分析这类不介意延迟的读。但注解要写在 SQL 最前面且不能破坏 SQL 语义。实际项目里我不会滥用注解只有确认延迟可接受时才加。6. 上线前最后一步用 explain、日志和 reload 验证路由结果配置改完不等于分片生效我上线前习惯用一个下午做系统性验证。最直接的工具是 Mycat 的 explain 命令它能把一条 SQL 发给哪些数据节点显示出来-- 连接 8066 端口执行 explain看路由结果 mysql -h127.0.0.1 -P8066 -umycat_app -pApp123456 -Dmycat_order -e explain select * from order_info where order_id 100;期望输出类似DATA_NODEdn1, SQLselect * from order_info where order_id 100说明这条 SQL 只路由到 dn1。如果DATA_NODE同时出现dn1,dn2说明分片键条件没有生效回到第 4.5 节的排查路径。另一个验证入口是logs/mycat.log执行一条真实查询后搜索关键字route能看到 SQL 被拆分后的完整描述。这个日志在生产环境要开INFO级别DEBUG级别日志量太大只建议在压测时临时开。验证读写分离是否生效先看show datanode确认节点状态然后在主库执行show processlist看是否有来自 Mycat 的查询。从库执行show processlist能看到读流量。动态加载配置用管理端口改完schema.xml不必重启mysql -h127.0.0.1 -P9066 -umycat_manage -pManage123456 -e reload config_all;如果配置有错reload 会失败并提示哪个数据节点出错不影响当前运行状态这是 Mycat 给的后悔药。但我的习惯是改动前先备份一份cp /opt/mycat/conf/schema.xml /opt/mycat/conf/schema.xml.$(date %Y%m%d%H%M%S)演练顺序我固定下来好几套了先灌一批测试订单确认按order_id均匀落到两个物理库再开读写分离观察主从库的请求比例接着直接 kill 掉主库 MySQL 进程看 Mycat 是否按switchType把写流量切走恢复主库后再把角色切回来。这一套走完分库分表和读写分离才算真的敢交给业务。最早我部署时图省事跳过演练结果上线第二天主库宕机从库没提升业务只读不可写那次的教训让我养成了每次变更都要走一遍切换演练的习惯。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑