☰
Mycat 2实战:从MySQL主从复制到读写分离的完整配置指南
2026/10/1 3:43:44 网站建设 项目流程

1. 为什么我最终选了 Mycat 2:先聊聊踩过的弯路

在正式讲配置步骤之前,我想先说一下自己在做数据库读写分离时是怎么一步步走到 Mycat 2 这个方案上的。这个过程里我尝试过不少看起来更"轻量"的替代思路,但最终都因为维护成本、功能边界或者版本兼容问题放弃了。

最初的想法其实特别朴素:直接用 MySQL 自带的半同步复制,再加一层多数据源路由不就行了吗?但实际操作下来发现,应用层多数据源方案有几个绕不开的痛点:

  1. 每个 MySQL 客户端连接都要在 Java 代码里显式指定走主库还是走从库,哪怕用了AbstractRoutingDataSource这样的框架封装,SQL 语句层面的读写判定还是得自己写拦截逻辑(比如判断语句是否以SELECT开头,这条规则在外层嵌套查询、存储过程调用、FOR UPDATE语句等各种场景下很容易出错)。
  2. 一旦新增从库节点,应用配置和连接池配置都得跟着改,运维的兄弟姐妹天天追着我更新配置。
  3. 不同微服务模块对数据库的访问方式不统一,有的用 MyBatis,有的用 JPA,权限控制散落各处,排查问题的时候要去好几个项目里翻代码。

后来我去了解了专门做数据库中间件的方案,对比了 ShardingSphere 和 Mycat 2。ShardingSphere 的分片能力确实更完善,但当时我们团队的核心诉求很明确:先解决读写分离和主从切换的基础问题,暂时不需要分库分表。Mycat 2 在这一点上更轻、更直接——部署一个中间件节点,应用连接它就像连一个普通 MySQL 实例,读写分离和主从切换都收敛在中间件层,应用的 JDBC 配置几乎不用动。再加上 Mycat 2 对 MySQL 8.0 的兼容性做得比 Mycat 1.6 好不少,连接池、鉴权、字符集这些细节都更贴近现代 MySQL 的行为习惯。

我在这里给读者一个明确建议:如果你的项目目标是短期内快速落地主从读写分离,且团队成员对 ShardingSphere 不熟悉,那么 Mycat 2 的学习曲线和部署复杂度确实友好得多。团队里需要有人熟悉 Netty 和配置文本的调试方式,因为中间件层的错误排查和应用层不太一样。

另外一个很实际的原因是,Mycat 2 可以直接复用现有的 MySQL 主从复制链路。它自己不负责同步数据,只负责路由和转发。这意味着主从同步的方案可以继续沿用 MySQL 原生复制——无论是ROW格式的 binlog、半同步复制还是 GTID 模式,Mycat 2 都不干预。数据一致性这个最敏感的问题,可以继续信任 MySQL 自己的复制机制来保证。

小结一下我的选型思路:

  • 应用层多数据源方案 → 适合模块极少的项目,SQL 判定逻辑简单,但不适合有很多服务实例需要统一治理的场景。
  • ShardingSphere → 功能全面,适合整体规划分库分表,但它引入的分片规则抽象层对只有读写分离需求的项目来说显得大。
  • Mycat 2 → 轻量、专注,配置文本直观,适合已经决定用 MySQL 主从架构、只差一个统一入口的团队。

既然选定了中间件,下一步就得把手上的 MySQL 环境先理顺:搭建主从并验证同步,因为 Mycat 2 只是路由层,数据同步的根基始终是 MySQL 的复制本身。

2. 先搭建 MySQL 主从:这一步不到位的坑,后面全暴露出来

Mycat 2 本身不复制数据。读写分离的效果完全依赖 MySQL 主从复制是否健康。我在刚开始实验的时候,因为图省事,用了非常简陋的主从配置方式,结果后续调 Mycat 2 的读权重和负载均衡策略时,遇到了一堆"看起来是路由问题、其实是复制延迟问题"的怪现象。

环境规划建议如下(这里给出一套我用过的标准组合):

节点角色主机地址端口MySQL 版本用途
主库 master192.168.1.1033068.0.32所有写操作、实时性要求高的读
从库 slave1192.168.1.1133068.0.32走 Mycat 的普通读操作
从库 slave2192.168.1.1233068.0.32走 Mycat 的普通读操作,可测试多从节点效果
Mycat 2 节点192.168.1.208066-中间件层,应用连接入口

为什么从库版本尽量和主库保持一致?我在迁移阶段试过主库 8.0、从库 5.7 的组合,caching_sha2_password认证插件会有兼容性告警,某些字符集排序规则的表现也不一致,导致主库写的字符集和从库读到的字符集之间出现细微偏差。后来我统一成 8.0.32 版本,这些问题基本就消失了。

2.1 主库需要调整的核心配置

主库的my.cnf(在 Linux 环境下,路径通常是/etc/my.cnf或/etc/mysql/my.cnf)建议设置这几项:

[mysqld] # 节点唯一ID,主库和从库必须不同 server-id = 1 # 开启binlog日志,读写分离和后续时间点恢复都依赖它 log-bin = mysql-bin # binlog格式,MySQL 8.0默认已经是ROW,不过我习惯显式写上 binlog_format = ROW # 需要记录binlog的数据库,这里是testdb binlog-do-db = testdb # 避免记录MySQL系统库的binlog,减少无意义日志量 binlog-ignore-db = mysql # 半同步复制需要的插件支持,先留着后续启用 gtid_mode = ON enforce_gtid_consistency = ON

关于binlog-do-db这个参数,我踩过一个不小的坑:如果业务库很多,在 Mycat 2 的读写分离下,某个库没被binlog-do-db覆盖,应用通过 Mycat 写入该库后,从库上永远看不到数据。这个问题不会在 MySQL 层面报错,Mycat 2 也不会感知,只有在业务反馈"某张表查不到数据"时才会被发现。排查路径绕了一大圈,最后发现是配置只覆盖了部分库。

我的建议是:如果有可能新增业务库,宁可先不配置binlog-do-db和binlog-ignore-db,让所有用户库都进 binlog,然后在从库上用replicate-wild-do-table之类参数控制要复制的表。虽然日志量会大一些,但至少不会出现"漏库"这种隐蔽问题。

2.2 从库配置与复制链路检查

从库的my.cnf配置:

[mysqld] server-id = 2 read_only = 1 # 允许复制线程写入,这一步很关键,read_only不会阻碍SQL线程应用binlog super_read_only = 1 relay-log = relay-bin log_bin = mysql-bin binlog_format = ROW gtid_mode = ON enforce_gtid_consistency = ON

配置完成之后,重启主库和从库的 MySQL 服务,然后在主库上创建复制账号:

CREATE USER 'repl'@'%' IDENTIFIED WITH mysql_native_password BY 'StrongPass123!'; GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';

在从库上执行 CHANGE MASTER 之前,务必确认主库当前 binlog 位置和 GTID 信息。用 GTID 模式会比传统 file+position 方式在链路切换时省心很多,因为它天然记录事务执行的全局位点,即使从库落后较多也能准确接着同步:

CHANGE MASTER TO MASTER_HOST='192.168.1.10', MASTER_PORT=3306, MASTER_USER='repl', MASTER_PASSWORD='StrongPass123!', MASTER_AUTO_POSITION=1; START SLAVE;

启动后立刻检查复制状态:

SHOW SLAVE STATUS\G;

最需要关注的两个字段是Slave_IO_Running: Yes和Slave_SQL_Running: Yes。如果SQL_Running是 No,查看Last_SQL_Error,绝大多数情况是主库上执行了从库无法重复的操作(比如DROP TABLE一张不存在的表,或者主从字符集不一致导致的中文插入异常)。这时可以用STOP SLAVE; SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; START SLAVE;跳过单条错误,但这只适用于测试环境,生产环境下跳过错误可能导致主从数据永久不一致,必须谨慎。

2.3 写入测试数据并验证延迟

主库上执行:

USE testdb; CREATE TABLE IF NOT EXISTS user_info ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100), created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); INSERT INTO user_info(name) VALUES('alice'),('bob'),('carol');

等一两秒后,在从库上查:

SELECT COUNT(*) FROM testdb.user_info;

能查到 3 条,说明同步链路已经通了。然后我在主库上连续插入了十万行数据,再在从库上通过SHOW SLAVE STATUS看Seconds_Behind_Master,正常情况下应该很快归零。如果这个值持续走高或者抖动,通常不是 Mycat 2 能解决的——首先要检查从库的磁盘 IO 是不是太慢、有没有其他大查询把 CPU 占满,或者网络带宽是否被备份任务占用了。

顺手记一个排查延迟的经验:不要只看Seconds_Behind_Master这一个指标。它本质上是通过对比系统时间和中继日志时间戳计算出来的,如果主从系统时钟不一致,这个值本身就不可信。更稳的做法是直接对比主库 GTID 和从库Retrieved_Gtid_Set、Executed_Gtid_Set的差值。

-- 从库上执行,查看已获取和已执行的GTID集合差异 SHOW MASTER STATUS; SHOW SLAVE STATUS\G;

确保两边的 GTID 前缀一致、从库的Executed_Gtid_Set在持续追赶主库,这才是真正可靠的同步判断方式。MySQL 主从验证到这一步,才算具备给 Mycat 2 使用的资格。

3. 部署 Mycat 2:从下载到能连通的完整流程

Mycat 2 的部署方式和 Mycat 1.6 那一代有本质区别。1.6 是编辑schema.xml、rule.xml、server.xml这些大而全的配置文件;2.0 改成了分目录、分功能的 YAML 风格搭配少量 SQL 文件。初次上手的人如果继续搜索 1.6 的教程来配置 2.0,十个有九个会把文件结构搞混,因为很多旧教程举例太久远了。

我建议在正式部署前先明确版本。我当时用的组合是Mycat 2.0.0 稳定版(基于 Netty 4.x)加上 MySQL 8.0.32,这个组合在 GitHub 的 release 页面上能直接下载。

3.1 环境准备:JDK 版本别搞错

Mycat 2 是 Java 写的,运行环境需要 JDK 8 以上。我强烈建议用 JDK 8u202 或更新版本,因为 JVM 对 TLS 协议的默认支持影响到后续注册中心连接和 MySQL 连接的建立。如果用太老的 JDK,连接加密链路会出现 SSL 握手失败,报错信息还很绕——我记得有次一直报Could not connect to MySQL,查了半天才发现是 JDK 的 TLS 版本太老不支持 MySQL 8.0 的默认加密套件。

验证环境:

java -version

输出示例:

openjdk version "1.8.0_202" OpenJDK Runtime Environment (build 1.8.0_202-b08) OpenJDK 64-Bit Server VM (build 25.202-b08, mixed mode)

3.2 下载与目录结构

从 Mycat 官方 GitHub Releases 页面下载压缩包后,解压到/opt/mycat2:

wget https://github.com/MyCATApache/Mycat2/releases/download/mycat2-1.20-release/mycat2-1.20-release-jar-with-dependencies.jar mkdir -p /opt/mycat2/lib cp mycat2-1.20-release-jar-with-dependencies.jar /opt/mycat2/lib

Mycat 2 的启动脚本很直接,如果你下载的是完整包,里面会有bin/mycat脚本;如果是纯 jar 包,则需要自己准备启动命令。我习惯用下面这种启动方式:

cd /opt/mycat2 nohup java -Dfile.encoding=UTF-8 -Xms1g -Xmx1g \ -jar lib/mycat2-1.20-release-jar-with-dependencies.jar \ > /opt/mycat2/logs/mycat.log 2>&1 &

等 3 到 5 秒后查看日志,确认 Mycat 2 是否正常监听 8066 端口:

tail -f /opt/mycat2/logs/mycat.log netstat -tlnp | grep 8066

出现类似下面的日志说明启动成功:

INFO MycatServer start success, listen port 8066

3.3 Mycat 2 的配置核心:几个关键文件

Mycat 2 把配置拆分成了多文件结构,分布在conf/目录下。其中决定读写分离的关键文件是datasource.yaml(这里在 1.20 版本中文件名是 datasource.yml,但核心内容一致),以及schemas目录下面的schema.testdb.yaml。

我对配置结构的理解是:Mycat 2 用datasource(数据源)+ cluster(集群)+ schema(逻辑库/逻辑表)三层来定义路由。数据源直接指真实的 MySQL 地址,集群定义主从分组和读写策略,schema 定义逻辑库名和应用侧的访问方式。理解这个层次关系,后面所有配置就不是在"背参数",而是按逻辑去组织。

一个典型的数据源配置长这样(此处 YAML 语法以 Mycat 2 常见版本为例,不同小版本字段名略有差异):

datasources: master: url: jdbc:mysql://192.168.1.10:3306/testdb?useUnicode=true&characterEncoding=utf8 username: mycat_user password: MycatPass123! maxRetryCount: 3 slave1: url: jdbc:mysql://192.168.1.11:3306/testdb?useUnicode=true&characterEncoding=utf8 username: mycat_user password: MycatPass123! maxRetryCount: 3 slave2: url: jdbc:mysql://192.168.1.12:3306/testdb?useUnicode=true&characterEncoding=utf8 username: mycat_user password: MycatPass123!

注意 MySQL 的账号需要先在主库和所有从库上创建,并授予对应库的读写权限。Mycat 2 中间件本身只是拿着这套账号去连接真实 MySQL。

然后配置集群:

clusters: c0: masters: [master] replicas: [slave1, slave2] heartbeat: type: mysql url: jdbc:mysql://192.168.1.11:3306/testdb

这段配置的含义是:集群c0的主数据源是master,从数据源是slave1和slave2。heartbeat用于 Mycat 2 对后端 MySQL 做健康检查。如果主库宕机,Mycat 2 可以根据心跳感知到并触发切换逻辑。

最后是 schema 配置。在 Mycat 2 中,逻辑库和物理库的映射关系往往使用schema.testdb.yaml这类文件。为方便理解,我贴一个精简示例:

schemas: testdb: targetCluster: c0: read: c0 write: c0

其中read和write指向同一个集群c0,Mycat 2 会在c0内部分发: 写请求发到masters,读请求在replicas之间做负载均衡。

3.4 配置完成后重启并验证连通

修改配置后,需要重启 Mycat 2 使配置生效:

kill -9 $(pgrep -f mycat2-1.20-release) cd /opt/mycat2 nohup java -Dfile.encoding=UTF-8 -Xms1g -Xmx1g \ -jar lib/mycat2-1.20-release-jar-with-dependencies.jar \ > /opt/mycat2/logs/mycat.log 2>&1 &

然后用 MySQL 客户端连接 Mycat 2 的 8066 端口试试:

mysql -h 192.168.1.20 -P 8066 -u root -p

提示:Mycat 2 默认的管理用户是root,实际项目里要立刻改掉默认密码,尤其不要图方便配置成和应用共用的账号。

连接成功后执行:

SHOW DATABASES; USE testdb; SHOW TABLES;

如果能看到testdb以及之前创建的user_info表,说明逻辑库映射已经生效。至此,中间件层面已经打通,接下来才轮到读写分离的路由验证。

4. 读写分离在 Mycat 2 里的路由规则与验证方法

读写分离的核心问题只有一个:一条 SQL 进来,Mycat 2 怎么判断它该去主库还是从库?如果这个判断逻辑没弄明白,后面调试任何"读走了主库"还是"写走了从库"的疑问都会摸不着头脑。

4.1 读与写是怎么被判定的

Mycat 2 对 SQL 类型的判断逻辑大致如下:

  • 事务中的 SQL:如果事务已经开启(比如执行了BEGIN或START TRANSACTION),事务内的所有操作都会路由到主库。这是为了保证事务的隔离性和一致性——如果事务里先 INSERT,再 SELECT,SELECT 跑到从库上,而这个从库复制延迟刚好没追上,就会查出旧数据,业务上就出现"刚插入的行读不到"的问题。
  • 非事务中的SELECT:默认路由到从库,参与负载均衡。前提是这条 SELECT 没有特殊标记。
  • 非事务中的INSERT/UPDATE/DELETE:默认路由到主库。
  • 带FOR UPDATE的 SELECT:虽然语法是 SELECT,但有行锁语义,必须去主库,不能因为"它是 SELECT"就送去从库。这一点在 Mycat 2 的实现中是有考虑到的。
  • 带NOW()/CURRENT_TIMESTAMP之类函数的 SELECT:如果对时间一致性要求极高,需要自己控制是否走主库。Mycat 2 不会自动判断函数的确定性,这是默认行为的一个边界。

为了在不需要改 SQL 的情况下强制读主库,Mycat 2 支持在 SQL 中加注释路由提示。注释写法大概是:

/*!mycat:db_type=master*/ SELECT * FROM user_info WHERE id = 1;

这个能力在调试阶段用处极大——当你怀疑"明明刚写入的数据为什么查不到"时,加上这个注释让查询强制走主库,如果主库能查到而从库查不到,说明复制延迟;如果主库也查不到,说明是应用逻辑或事务提交的问题,和路由无关。一注释就把问题域切干净了。

4.2 怎么确认 SQL 真的走了我预期的节点

这个问题我一直强调:不要凭感觉,要看 Mycat 2 的日志和 MySQL 侧的证据。三个方法叠加,基本能还原出 SQL 的真实路径。

方法一,看 Mycat 2 日志。在/opt/mycat2/logs/mycat.log里搜 SQL 关键字,查看前后上下文,有时能直接看到路由到的 target 数据源名。

方法二,在 MySQL 侧开启通用日志。分别在主库和从库上执行:

SET GLOBAL general_log = ON; SET GLOBAL general_log_file = '/var/log/mysql/general.log';

之后执行一条查询,再分别看主库和从库的 general log 里是否出现了这条 SQL。如果主库的 log 出现,说明它走了主库;如果从库的 log 出现,说明走了从库。这个方法比任何监控面板都直观,但生产环境别开太久——general log 每一条 SQL 都落盘,并发高时会严重影响 MySQL 性能。用几分钟验证完功能就马上关掉。

方法三,在 Mycat 2 里执行EXPLAIN类的 SQL,有时会返回路由目标信息。不同版本输出格式不同,可以试试:

EXPLAIN SELECT * FROM user_info WHERE id = 1;

4.3 实测:读写分离的完整验证过程

这里给出一份我在本地方便复现的测试脚本:

  1. 启动 Mycat 2 后,用 MySQL 客户端连上 8066。
  2. 执行写操作:
USE testdb; INSERT INTO user_info(name) VALUES ('dave');
  1. 主库上确认数据存在:
USE testdb; SELECT * FROM user_info WHERE name = 'dave';
  1. 从库上确认数据已经同步(等待 1 到 2 秒):
USE testdb; SELECT * FROM user_info WHERE name = 'dave';
  1. 在 Mycat 2 里执行一条 SELECT:
SELECT * FROM user_info WHERE name = 'dave';
  1. 打开主库和从库的 general log,看这条 SELECT 实际落在哪个节点。

实测下来,如果从库当前没有积压复制延迟,SELECT 会走从库;如果从库刚好在复制大事务(比如大量批量 UPDATE),延迟比较高,而 Mycat 2 又没有做读写分离延迟阈值控制,SELECT 也仍然可能被路由到从库。这时业务就会看到数据延迟问题。这个问题的解法不是让中间件自动切主,而是优化复制延迟本身,比如拆分大事务、把批量操作分批提交、增加从库配置等。明白这一点,很多"读写分离踩坑"其实是复制架构层面的问题,可以少走很多弯路。

4.4 多从库负载均衡:权重分配怎么设

Mycat 2 在集群配置里可以通过权重影响从库的选择。测试环境里可以先把两个从库的权重设成一样的,观察请求是否轮询或随机分布;也可以设成 3:1 模拟不均匀负载,感受一下中间件层面的调度效果:

clusters: c0: masters: [master] replicas: [slave1, slave2] # 权重比例示意,具体字段因版本而异 balance: 3:1

如果条件允许,可以在从库上用SHOW GLOBAL STATUS LIKE 'Com_select'分别在两个从库上多查几次,对比两个节点的Com_select增长情况,就能看到负载是否按预期分布。合理分配读写负载是读写分离功能落地的主要价值之一,验证这一步绝不能省。

5. 主从同步链路和 Mycat 2 的配合:遇到问题怎么定位

这一节想单独说说 Mycat 2 和主从同步链路的配合边界问题。我在实践中最常见的困惑是:应用报了"数据查不到",到底该怪 Mycat 2 路由错了,还是该怪复制没跟上?有了前面验证经验,大概率能快速定位。但如果主从复制链路本身被破坏,问题就复杂一层。

5.1 主从同步中断时的复制元组问题

MySQL 的主从复制是异步或半异步的,从库应用 binlog 时完全可能遇到各种中断。常见中断原因有这些:

故障类型典型报错关键词解决思路
主从 SQL 线程执行失败Last_SQL_Error查询具体错误码,根据错误手工修复或跳过
磁盘空间满Disk full清理 relay log、binlog,扩展磁盘
网络抖动导致 IO 线程重连Got fatal error 1236检查MASTER_AUTO_POSITION,必要时重新拉取
从库误写数据Duplicate entry恢复从库一致性,最稳妥做法是重建从库

如果从库 SQL 线程中断,所有后续应用写入的数据都不会同步过去。但 Mycat 2 的读写分离此时并不会自动感知从库数据不一致——它只是路由,SQL 发过去之后,从库能不能返回正确结果它管不了。这就会造成最恐怖的一种故障:某些行数据在主库存在,在从库不存在,应用通过 Mycat 2 查询时走从库,得到一个不是最新状态的数据集,而且中间件层面根本不报错。这种情况必须靠监控提前发现:定期轮询SHOW SLAVE STATUS,对Slave_SQL_Running非 YES 的情况配置告警,一旦发现从库坏了,应立即在 Mycat 2 里把这个从库暂时剔除或在从库上重建恢复。

5.2 主从切换:Mycat 2 在主库宕机时做了什么

Mycat 2 通过心跳感知主库节点是否存活。我在测试环境中做过一次模拟演练:直接在主库节点上停掉 MySQL 服务,然后从 Mycat 2 的日志里观察心跳失败和切换尝试的记录。由于我在集群配置里设置了心跳检测,Mycat 2 会标记主数据源不可用,此时如果配置允许,后续的写操作会报错或者触发备选主节点逻辑。这个行为在不同版本的 Mycat 2 中存在差异,有的版本只读配置里被标记为备选主节点时才会提升,因此生产环境中要提前确认版本文档,并做好主从切换的自动化脚本(比如通过 MHA 或 Orchestrator 做 MySQL 层面的 VIP 切换,再通知 Mycat 2 刷新配置)。

如果不想引入额外管理组件,我建议至少做到以下几点:

  • 用脚本每 10 秒轮询一次主库的 3306 端口和 MySQL 心跳表。
  • 主库不可用时,将原本指向主库的数据源配置切换为某一台数据最新、延迟最小的从库,并提升它为可写。
  • 将 Mycat 2 的配置热更新或重启,促使新配置生效。

这个过程有可能会丢失少量未同步到从库的数据,所以生产环境务必开启半同步复制并合理配置rpl_semi_sync_master_timeout,在主库等待从库确认的窗口内尽量减少数据丢失窗口。

5.3 主从延迟对读写分离的影响,以及缓解实战

主从延迟是读写分离架构绕不开的话题。同一个主库在高并发写入压力下,从库的复制线程如果跟不上,Seconds_Behind_Master 就会涨。此时应用读取实时数据(比如用户刚提交的订单),如果请求被 Mycat 2 分发到从库,就可能读到旧数据。

应对延迟现状,我在项目里的做法是:

第一,做延迟阈值感知。在业务代码层面,对关键实时查询(比如用户余额、支付状态)通过前面说的/*!mycat:db_type=master*/强制走主库;对非实时列表查询(如历史订单、报表类查询)正常走从库。这个"关键性分级"的策略比让中间件去做自适应延迟感知要简单可靠得多——中间件并不知道哪条 SQL 涉及的钱不允许延迟,应用开发者最清楚。

第二,控制大事务。一条更新十万行的 SQL,虽然在主库执行得很快,但复制到从库时要按事务应用,期间其它事务也可能排队,延迟被瞬间拉高。我在项目规范里要求所有批量 UPDATE/DELETE 拆分到每批 500 到 1000 条以内,既减小锁粒度,也降低复制延迟波峰。

第三,监控Seconds_Behind_Master并通过 Prometheus 采集历史曲线。一旦延迟超过设定阈值(比如 5 秒),就触发告警,同时把对应从库暂时从 Mycat 2 的读集群中摘除,直到延迟恢复。这个摘除动作可以通过脚本修改 Mycat 2 的 datasource 配置并热更新来实现,虽然 Mycat 2 的管理命令在不同版本里略有差异,但思路是通用的。

5.4 从库数据不一致的修复:如何重建从库而不影响 Mycat 2

当从库已经出现不可逆的损坏(比如误删库、relay log 损坏),最干净的修复方式不是不断跳错,而是重新克隆主库数据。在保持 Mycat 2 配置不变的前提下,可以把出问题的从库节点从 Mycat 2 的集群配置里暂时摘除,然后在从库上重新初始化:

  1. 在主库上做一次逻辑备份(mysqldump --single-transaction --set-gtid-purged=ON)或者物理备份(如 Percona XtraBackup)。
  2. 将备份传输到从库。
  3. 在从库上恢复数据。
  4. 清空 relay log 和旧 GTID 信息,重新 CHANGE MASTER 指向主库。
  5. 启动复制并验证。

整个过程不需要动 Mycat 2 的其它配置,等从库追上主库后,再把它重新加回集群,恢复读流量。这就是配置分层带来的好处:MySQL 复制链路和 Mycat 2 路由层互不干扰,可以独立维护。

6. 从实际项目出发:Mycat 2 的配置优化与长期维护建议

走到这一步,一个能跑通的读写分离已经成型了。但如果要应用到生产环境,还有一些细节不能忽略,我按项目推进的顺序讲一下。

6.1 连接数、线程池与性能参数初调

Mycat 2 本身作为中间件,会占用一定的内存和线程资源。JVM 启动参数里我给-Xms和-Xmx各分配了 1GB,对单机小规模项目足够。如果业务并发高,需要根据连接数做加内存。一个粗略的经验值:每个后端 MySQL 连接约占 1 到 2MB 内存,Mycat 2 需要同时维护前端应用连接和后端 MySQL 连接,两者比例大约 1:1 或 1:0.8。比如 200 个应用连接,后端连接池 160 个左右,JVM 堆内存至少 2GB 起步。

连接池配置能细化的维度包括:初始连接数、最大连接数、最小空闲连接、连接超时时间、空闲检测间隔。通常在datasource.yaml里给每个数据源配置:

datasources: master: minCon: 10 maxCon: 100 slave1: minCon: 10 maxCon: 100 slave2: minCon: 10 maxCon: 100

要特别注意:后端连接池的总大小要大于应用侧并发连接数上限。否则应用并发一高,Mycat 2 里排队等后端连接,表现就是接口响应越来越慢,应用侧连接池却显示健康,排查时容易让人误判为应用代码性能问题。

6.2 账号权限与安全管理

Mycat 2 提供独立权限层。我在配置逻辑库访问时只创建一个业务账号,授权仅到testdb逻辑库的 DML/DDL 权限,不开放管理端。生产环境有几个明确的红线:

  • 不把 Mycat 2 的管理端口暴露在公网。
  • 修改默认管理密码,密码使用独立的强密码,与应用账号密码不同。
  • 在 MySQL 主从节点上,给 Mycat 2 使用的连接账号最小化授权。不要把 root 密码放进 Mycat 2 的配置里。

6.3 监控体系与巡检脚本

光搭好架构不算完,后面维护才是大头。我维护的读写分离环境中,至少做了以下三个监控点:

  1. Mycat 2 本身的存活与端口监听。脚本或监控软件每 30 秒探测 8066 端口,连续两次失败就告警。
  2. MySQL 主从复制健康度。每 60 秒执行一次SHOW SLAVE STATUS,检测Slave_IO_Running和Slave_SQL_Running是否都等于 Yes,以及Seconds_Behind_Master是否超过阈值。
  3. 数据一致性抽检。每周从主库和从库里各取一张大表的部分行做哈希校验,从根上发现隐性不一致。比如对user_info这张表按主键取前 1000 行,计算CRC32(CONCAT_WS(...)),两边比对。

如果发现不一致,按照时间的先后顺序排查:先看是否有 DDL 语句触发从库重建、是否有 MySQL 升级导致复制行为变化、是否有大量无主键表在复制时出现特殊问题。这类问题的根因往往是设计层面的,不是单纯的 Mycat 2 配置问题。

6.4 灰度切换和回滚预案

所有架构调整都存在风险。我在切换当天准备的预案可以概括为:最小变更、快速回滚、业务降级兜底。

具体操作是:

  • 提前用业务账号连接 Mycat 2,执行一轮完整的 CRUD 冒烟测试。
  • 在应用侧准备好两套数据库地址配置:一套直连 MySQL 主库(绕过中间件),一套走 Mycat 2。切换失败时可以 5 分钟内切回直连模式。
  • 切换窗口选择在业务低峰期,切换后观察至少 30 分钟错误日志和慢查询。

值得一提的还有一个容易被忽略的运维细节:Mycat 2 所在主机的时间要和 MySQL 主从节点保持一致(NTP 同步),否则日志的时间戳、审计的先后顺序、甚至某些超时判断都会产生偏差。这个虽然不影响 SQL 路由正确性,但在故障复盘时会带来很多误导。

7. 最后分享几个我实测后的心得体会

文章到这里,核心内容已经写完了。最后说几句掏心窝的话。

第一个体会,读写分离真正难的不是中间件配置,而是业务对一致性的容忍度分级。同一张表,实时行情数据和历史统计数据的读取要求完全不同。在 Mycat 2 的配置里,其实没有特别优雅的"按 SQL 自动判定实时性"的黑科技,最可控的方法还是应用自己统一管理强制走主库的 SQL 清单。配置一个MasterRouteRequired这样的标注或者常量列表,把需要强一致的查询放进去,比在中间件层面做各种猜测可靠得多。

第二个体会,主从同步是读写分离的地基,而大多数人只盯着中间件。从库磁盘 IO、网络延迟、大事务批处理,每一项对读写分离整体效果的影响都不小于路由策略本身。至少在我的实践里,出现"读写分离后数据库变慢"的案例,查到最后往往是复制延迟导致大量请求重试,而非中间件路由损失了性能。

第三个体会,不要一上来就追求全自动化。Mycat 2 的配置热更新、主从自动切换,这些听起来很美好,但如果团队还没有积累足够的运维经验,反而会在故障时增加判断复杂度。先从半自动做起:监控告警自动化,切换操作先手工脚本化,等每一次切换的流程都理顺了,再尝试加入自动决策逻辑。稳妥永远是生产环境的第一原则。

至此,从 MySQL 主从搭建、Mycat 2 部署配置,到读写分离路由验证、故障定位和长期维护,整条链路已经梳理完毕。这篇文章里的步骤和坑都是我自己动手跑过的,如果你也在做同类架构,希望它能帮你省掉几次通宵排障的时间。

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

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

立即咨询