我刚在测试环境里搭完一套 MySQL 主从集群,正准备把业务读写分离的流量切过去,发现手里没有合适的路由层。手动改连接串切主从这种事,一两次还能忍,养成依赖就麻烦了——哪天凌晨主库宕了,还得爬起来挨个改应用配置。MySQL Router 8 就是来解决这个问题的,它是 MySQL 官方出的轻量级中间件,负责把 SQL 请求按规则转发到对应的 MySQL 实例。这篇笔记就记录我这次安装 MySQL Router 8 的完整过程,包含版本选择、安装包获取、配置文件的写法、启动验证,以及我踩过的几个坑,给同样在折腾 MySQL 读写分离的朋友做个参考。
1. 为什么我会去装 MySQL Router——安装前必须想清楚的事
很多人看到"安装 MySQL Router"几个字就直接去下载安装了,装上之后却不知道怎么配,配完也不知道对不对。我建议在动手之前先花五分钟想清楚:你的架构里到底需不需要这个东西,它解决了什么问题。
1.1 手动切库切出阴影后的反思
我之前负责的一个项目,数据库用的是 MySQL 主从架构,主库负责写入,从库负责查询。平时倒是没觉得有什么问题,直到有一次主库磁盘满了,写入直接卡死。当时我手忙脚乱地登录服务器,查状态、改应用配置、重启服务,前后折腾了将近四十分钟,业务中断时间远远超过了预期。
事后复盘发现,问题的根源不是主库故障本身,而是故障处理链路太长:应用通过固定的 IP 和端口连接数据库,主从切换意味着要改配置、要重启,还得保证切换后连接池里的旧连接被清掉。如果中间加一层路由,应用只感知到一个虚拟的数据库地址,真正连接哪台实例由路由层决定,那切换就只是路由器上的一次配置更新而已。
1.2 MySQL Router 在读写分离架构里的位置
MySQL Router 是 MySQL 官方提供的一个轻量级中间件,在 8.0 系列里已经比较成熟了。它最典型的用法是搭配 InnoDB Cluster 使用,作为集群的流量入口;也可以像我这样,在没有部署 InnoDB Cluster 的普通主从架构里,手动配置读写分离规则,让 Router 把写请求转发到主库、读请求转发到从库。
它的工作方式可以简单理解为:应用连接 Router 的某个端口(比如 6446),Router 收到 SQL 后,根据端口号对应的路由规则,把请求转发到后端的 MySQL 实例。应用完全感知不到后端拓扑的变化。
| 组件 | 作用 | 是否必须 |
|---|---|---|
| MySQL Router | 路由层,接收应用请求并转发 | 本次安装对象 |
| MySQL 主实例 | 处理写请求 | 必须 |
| MySQL 从实例 | 处理读请求 | 可选,但通常都有 |
| 应用 | 只连接 Router,不直接连数据库 | 必须改造连接配置 |
想清楚这些之后,再动手安装就不会盲目了。接下来的操作步骤,都是围绕"装一个能用的 Router 出来"这个目标展开的。
2. 安装前的环境规划与版本选择
这个环节看着不起眼,却是最容易出问题的。MySQL Router 8 对操作系统、glibc 版本、后端 MySQL 版本都有要求,不满足的话装上了也起不来。
2.1 我的环境信息和版本选型思路
这次安装的实验环境是这样的:
- 操作系统:CentOS 7.9(内核 3.10,glibc 2.17)
- 后端 MySQL:MySQL 8.0.32(主)+ MySQL 8.0.32(从)
- 安装方式:RPM 包安装
- Router 版本:8.0.33
为什么选 8.0.33?当时 8.0.33 是 8.0 系列里比较新的稳定版,而 MySQL Router 8.0 与 MySQL 8.0 的兼容性最好。如果你后端的 MySQL 还是 5.7,那可以选 Router 2.x 的版本,8.0 的 Router 主要面向 8.0 系列优化,虽然也能勉强兼容 5.7,但部分集群功能用不了,没必要冒这个险。
2.2 三种安装方式,我为什么放弃了二进制解压
MySQL Router 8 常见的安装方式有三种:RPM 包、DEB 包、二进制解压包。如果你是 Debian/Ubuntu 系统,直接用 apt 装 DEB 包就行;我是 CentOS,所以优先考虑 RPM。
有人可能会问:为什么不直接下载二进制 tar 包解压然后用?我也试过,二进制包解压后目录结构很清楚,理论上放到 /usr/local/mysql-router 下面就能跑。但它有一个麻烦的地方——手动维护 PATH 环境变量、systemd 服务文件和目录权限,而且升级的时候要重新适配路径。用 RPM 包的话,systemd 服务文件、配置文件目录、日志轮转都是自动配置好的,省心很多。
从官网下载 RPM 包的时候要注意区分平台版本。同样是 el7 的包,不要下成 el8 的,虽然有些情况下也能装上,但依赖关系很容易出问题。我这次用的是:
mysql-router-community-8.0.33-1.el7.x86_64.rpm2.3 安装 RPM 包时可能碰到的依赖问题
CentOS 7 自带的 yum 源里没有 MySQL Router,所以我从官网把 RPM 包下载到本地之后,用 yum 本地安装的方式搞定依赖:
yum localinstall -y mysql-router-community-8.0.33-1.el7.x86_64.rpm用 yum localinstall 而不是 rpm -ivh 的关键原因是:yum 会自动解决依赖关系。MySQL Router 8 依赖 libaio、perl 等一些基础库,如果你的机器之前装过 MySQL 相关组件,这些依赖大概率已经在系统里了,但保不齐缺某一个,用 rpm 直接装的话会直接报错中断,用 yum 就能自动从配置好的源里补上。
装完验证一下:
mysqlrouter --version正常情况下会输出类似这样的信息:
MySQL Router Ver 8.0.33 for Linux on x86_64 (MySQL Community - GPL)到这里安装环节基本结束,但距离"能用"还有一步——配置。
3. 解压版安装的另一种选择,以及目录结构解析
如果你实在不想用 RPM 包,或者你的环境是内网隔离、不方便配置 yum 源,那 MySQL Router 的二进制解压包也是可以用的。我这次虽然最后用了 RPM,但在测试过程中也折腾过解压版,把两种方式的差异和踩坑点一起记录下来,免得大家重复走弯路。
3.1 手动解压版的操作流程
解压版的使用思路很简单:把 tar 包解压到一个固定的目录,然后手动配置环境变量和服务文件。
tar -xzf mysql-router-8.0.33-linux-glibc2.17-x86_64.tar.gz mv mysql-router-8.0.33-linux-glibc2.17-x86_64 /usr/local/mysql-router解压之后,核心的目录结构是这样的:
| 路径 | 作用 |
|---|---|
| /usr/local/mysql-router/bin/ | mysqlrouter 主程序、mysqlrouter_passwd 工具等 |
| /usr/local/mysql-router/etc/ | 配置文件目录,默认找 mysqlrouter.conf |
| /usr/local/mysql-router/lib/ | 依赖的动态链接库 |
| /usr/local/mysql-router/share/ | 文档和示例文件 |
关键点在 lib 目录。解压版不会自动把依赖库路径写入系统的 ld.so.conf,如果你直接运行 bin/mysqlrouter,很可能会遇到:
error while loading shared libraries: libmysqlrouter.so: cannot open shared object file: No such file or directory这时候需要手动把 lib 目录加入动态链接库搜索路径,或者在运行前设置环境变量:
export LD_LIBRARY_PATH=/usr/local/mysql-router/lib3.2 解压版和 RPM 版该如何选择
从我个人的使用体验来看,两种方式各有优劣。RPM 版的好处是安装位置统一、有现成的 systemd 服务、升级卸载方便;坏处是如果你用的不是 RedHat 系 Linux,就得换 DEB 或者其他方式。解压版的好处是通用性强,只要 glibc 版本匹配,几乎任何 Linux 发行版都能跑;坏处是安装后的链路维护成本稍微高一些,需要自己写 systemd 文件。
如果你是在 Docker 容器里用,解压版反而更灵活——直接把解压后的目录打进镜像,用 ENTRYPOINT 启动 mysqlrouter 进程就行,不需要 systemd。我之前在容器化环境里就是这么干的。不过这次在 CentOS 7 物理机上,我最终选择了 RPM 版,因为省事、稳定。
3.3 装完之后先看看默认配置长什么样
RPM 安装完成后,/etc/mysqlrouter/mysqlrouter.conf 是默认的主配置文件。这个文件默认内容是纯注释的,基本没什么实际配置,但可以从里面看到官方预留的配置节结构。先用下面这条命令看看有哪些配置项是被支持的:
mysqlrouter --help输出信息里会列出所有可用的配置参数,比如 bind_address、bind_port、destinations、routing_strategy 等等。我建议装完先跑一下这个命令,心里有个底,后面写配置的时候就不容易发懵。
4. 写配置文件:从 Bootstrap 到手动配置的取舍
MySQL Router 8 的配置方式有两种:一种是用mysqlrouter --bootstrap命令自动生成配置,另一种是手动编写配置文件。很多教程默认只讲 Bootstrap 方式,但实际工作中手动配置的需求也很常见,所以我两种都说说。
4.1 Bootstrap 方式:一句话生成配置
Bootstrap 是 MySQL Router 官方推荐的方式来配置 InnoDB Cluster 的路由。它会连接现有的 MySQL 集群主实例,自动探测集群拓扑,然后生成一份完整的 mysqlrouter.conf。
假设我的 MySQL 集群的管理账号是root,密码是Root@123456,集群主实例 IP 是192.168.1.10,那么 Bootstrap 命令就是:
mysqlrouter --bootstrap root@192.168.1.10 --directory=/tmp/router1执行后,MySQL Router 会自动连接这个实例,检测到这是一个 InnoDB Cluster,然后生成一份可以直接启动的配置。Bootstrap 方式生成的文件里,[metadata_cache]和[routing]等核心节都是自动填好的,不需要手工编写。
4.2 手动配置:普通主从架构下的读写分离规则
Bootstrap 方式虽然方便,但有一个前提条件——你的后端必须是一个 InnoDB Cluster,也就是说至少有三个节点、部署了 Group Replication。如果像我一样只是一个传统的主从复制架构(Master-Slave),直接 Bootstrap 是行不通的,这时候就需要手动写配置文件。
我最终使用的配置是这样写的:
[DEFAULT] logging_folder=/var/log/mysqlrouter runtime_folder=/var/run/mysqlrouter config_folder=/etc/mysqlrouter bind_address=0.0.0.0 connect_timeout=30 read_timeout=30 write_timeout=30 [logging] level=INFO [routing:read_write] bind_address=0.0.0.0 bind_port=6446 mode=read-write destinations=192.168.1.10:3306,192.168.1.11:3306 routing_strategy=first-available [routing:read_only] bind_address=0.0.0.0 bind_port=6447 mode=read-only destinations=192.168.1.10:3306,192.168.1.11:3306 routing_strategy=round-robin这里解释一下几个关键配置的作用:
[routing:read_write]和[routing:read_only]是两个路由规则节,名称可以自己定义,但一个端口对应一条规则。bind_port=6446和bind_port=6447是 Router 对外提供服务的端口,一个用于读写流量,一个用于只读流量。destinations是后端 MySQL 实例列表,用逗号分隔。routing_strategy是路由策略。first-available表示优先使用列表中的第一个可用的实例,写请求只会发到主库;round-robin表示轮询分发,适合多从库场景下的读请求负载均衡。
在这个配置下,应用连接192.168.1.100:6446写入时,Router 会把流量转发到主库;连接192.168.1.100:6447查询时,Router 会在两个实例之间轮询,把压力分散到从库上。
4.3 配置写好之后先别急着启动,用这个命令检查语法
MySQL Router 提供了一条专门的配置检查命令:
mysqlrouter --config=/etc/mysqlrouter/mysqlrouter.conf --config-check这条命令不会启动 Router,只会读取配置文件并检查语法和参数是否合法。如果配置有问题,会直接报错并提示具体行号;如果没有问题,什么都不输出,静默退出。
我刚开始配置的时候,把routing_strategy拼成了round_robin而不是round-robin,用--config-check检查的时候它提示了错误,但直接启动的话可能要看日志才能发现。所以建议每次改完配置都跑一遍这个检查命令,比启动实例再翻日志效率高得多。
5. 用 systemd 托管服务,实现开机自启与守护进程管理
配置检查通过后,就可以把 MySQL Router 作为系统服务跑起来了。RPM 安装方式会自动在/etc/systemd/system/下生成mysqlrouter.service文件。相比手动在后台用 nohup 跑进程,用 systemd 管理的好处很明显:支持开机自启、进程崩溃后自动重启、日志统一走 journald。
5.1 启动服务与查看状态
依次执行下面的命令:
systemctl daemon-reload systemctl enable mysqlrouter systemctl start mysqlrouter systemctl status mysqlrouterdaemon-reload是让 systemd 重新加载配置文件,新装的 service 文件必须先执行这一步才能被识别。
启动后查看状态,正常情况会显示active (running)。如果状态不是这样,用journalctl -u mysqlrouter查看日志:
journalctl -u mysqlrouter -n 50 --no-pager5.2 验证端口监听是否正常
Router 进程跑起来之后,还要确认一下端口有没有真的监听。用ss命令查看:
ss -tlnp | grep mysqlrouter正常输出应该同时看到 6446 和 6447 两个端口被监听,进程名是 mysqlrouter。如果只能看到其中一个端口,说明对应的路由规则可能配置有问题,或者配置节没写完整——比如[routing:read_only]这个节少了bind_port,Router 启动时会直接跳过这条规则。
5.3 用 MySQL 客户端实测读写分离效果
端口监听正常只是第一步,路由逻辑到底对不对,还得实测。用 mysql 客户端分别连接两个端口:
mysql -h 192.168.1.100 -P 6446 -u test_user -p -e "select @@server_id, now();" mysql -h 192.168.1.100 -P 6447 -u test_user -p -e "select @@server_id, now();"两次命令返回的@@server_id如果不同,说明路由规则生效了:写连接走 6446 被转发到主库,读连接走 6447 在多个实例间轮询。如果两个端口返回的结果完全相同,可能是路由规则里的destinations配错了,或者两个实例的 server_id 设置相同,需要重点排查。
6. 对照实际验证结果,看读写分离到底有没有生效
配置启动完成之后,验证环节不能省。很多人在这一步图省事,只看systemctl status显示 running 就认为大功告成,结果应用连上去发现所有流量都打到了同一台实例上,读写分离根本没生效。
6.1 怎么从结果反推路由规则是否正常
最直接的办法就是在 Router 机器上打开日志监控,然后从应用端发起几个典型的读写操作,观察日志里每条连接被转发到了哪个后端地址。
MySQL Router 的日志默认在/var/log/mysqlrouter/mysqlrouter.log,如果配置文件中[logging] level=DEBUG,日志里会输出每次路由决策的详细信息。我第一次实测的时候发现写请求总是被转发到第一台实例,这是first-available策略的预期行为;读请求在两个实例之间交替,这是round-robin策略的正常表现。
6.2 各种路由策略下的表现对比
这里补充一下路由策略的选择心得。first-available的字面意思是"优先可用",它会从 destinations 列表的第一个地址开始尝试,如果第一个不可用就换第二个。这个策略非常适合写流量,因为主库只有一台,必须保证所有写请求都落到主库。
round-robin是轮流分配,对读请求来说很合适,因为从库通常有多台,轮流分发可以实现负载均衡。但如果你的从库性能差异很大,一台是高性能物理机、一台是低配虚拟机,那么round-robin可能导致快的那台空闲、慢的那台堆积,这时候可以考虑round-robin-with-fallback或者加权策略。
6.3 业务连接数特别多的时候,注意观察 Router 自身压力
MySQL Router 本身是一个轻量级转发层,它不像 ProxySQL 那样有复杂的结果集处理能力,只做基于端口的路由转发,所以性能损耗相对较小。但要注意的是,它对每条连接会有一定的内存占用。如果你后端的连接池开得很大,连接数达到几百上千,Router 所在机器的内存和文件描述符上限就成了瓶颈。
建议提前调整操作系统的文件描述符限制:
ulimit -n 65535同时在/etc/security/limits.conf里加上:
mysqlrouter soft nofile 65535 mysqlrouter hard nofile 65535否则一旦连接数冲上来,Router 会因为文件句柄耗尽而拒绝新的连接,表现就是应用端偶尔报"Connection refused"或"Too many open files"。
7. 这次安装中踩过的坑与排查过程
最后这部分,我把实际安装和调试过程中遇到的问题整理出来。这些问题看起来都不复杂,但每一步都有可能浪费你半小时起步。
7.1 坑一:配置文件权限不对导致启动失败
安装完 RPM 包后,默认配置文件的属主是mysqlrouter用户。我在手动编辑配置文件之后,没有注意保持属主不变,导致文件属主变成了root。重启服务的时候,systemd 以mysqlrouter用户的身份去读取配置文件时提示权限不足,服务起不来。
排查过程是先看日志:
journalctl -u mysqlrouter --no-pager日志里明确写了Permission denied,指向配置文件。解决方法很简单:
chown mysqlrouter:mysqlrouter /etc/mysqlrouter/mysqlrouter.conf7.2 坑二:bind_address 配置成固定 IP,导致本机无法连接
我一开始图省事,把bind_address写成了127.0.0.1,结果从应用服务器连接 Router 的时候,网络层就直接超时了。因为 Router 只监听了回环地址,外部流量根本进不来。
排查思路是先用netstat -tlnp确认监听地址,看到127.0.0.1:6446就明白问题在哪了。修改成0.0.0.0后重启服务才恢复正常。这个坑很多刚接触 Router 的人都会踩到,特别是从 MySQL 单库迁移过来、习惯了本地连接的人。
7.3 坑三:路由规则配置了多个端口,但只有一个端口生效
这是我配置文件中比较隐蔽的一个问题。当时加了一条新的[routing:read_only]规则,配置检查也通过了,但 6447 端口就是没有监听。
后来反复对比两条规则才发现,新加的规则里写了bind_port=6447,但是destinations拼写错误,写成了destination。MySQL Router 对配置节里的未知参数是静默忽略的,所以destinations没被加载,规则不完整,Router 启动的时候就默认跳过了这条规则。
解决办法是把拼写改对,重新加载配置。这提醒了我:配置检查命令只能验证语法,不能验证语义,关键参数写错了它不一定报错,所以写完配置一定要看端口监听和实测路由结果。
7.4 坑四:忘记验证配置文件里的密码字段
如果你的配置里用到了后端实例的账号密码(比如 Bootstrap 元数据缓存方式),那么密码字段也需要被加密存储,不能明文写在配置文件里。RPM 安装后默认会有mysqlrouter_passwd工具,可以用来加密密码:
mysqlrouter_passwd set它会让你输入密码并生成加密后的字符串,把生成的字符串填入配置文件里的相关字段即可。这个操作在手动配置模式下不是必须的,但在 InnoDB Cluster 模式下是强烈建议的,因为配置文件一旦泄露,明文密码就跟着暴露了。
MySQL Router 安装本身不难,难的是理解它在这套架构里扮演的角色,以及把路由规则配得符合实际业务场景。这次装完用了两周,读写分离的流量一直很稳,中间经历了一次主从切换演练,我在 Router 配置里把destinations调整了一下,应用端完全无感知,这大概是这台 Router 存在的最大意义了。