MySQL Router 8安装配置教程:实现读写分离与高可用路由

MySQL Router 8安装配置教程:实现读写分离与高可用路由 做MySQL架构的同学应该都有过这种经历业务侧需要高可用主从切换了应用却还连在旧主库上或者想让读写分离又不想在每个应用里写一堆数据源切换逻辑。与其在代码层反复造轮子不如在前端挂一个统一的“入口”。MySQL Router 8就是干这个事的——它是MySQL官方出品的轻量级路由中间件可以理解成数据库前面的“智能门卫”负责转发连接、负载均衡还能感知集群拓扑变化读写分离和故障转移都能在中间件层面直接解决。这篇笔记我直接基于在 Linux 环境里从零安装 MySQL Router 8 的完整过程来写重点讲清楚每一步的来龙去脉以及在生产环境实操中容易踩的坑。适合刚接触 MySQL 高可用架构的 DBA也适合在应用层被数据库切换搞得头疼的开发同学。1. 为什么需要单独装一个MySQL Router1.1 架构演进的必然选择在没有 Router 之前应用连数据库的模式很直接JDBC 连接串里写主库 IP备份库或者从库只能靠应用层自己管理。一旦发生主从切换DBA 得改 DNS或者应用发布一次配置才能连到新主库读写分离稍微一复杂就得引入中间件团队自研还得维护一堆哪吒闹海版本。这其实不是技术能力的问题而是架构灵活性太差——数据库拓扑变了应用也跟着变两者耦合很深。MySQL Router 8 要解决的就是这个耦合问题。它把应用和真实 MySQL 实例隔开Router 维护了一张路由表应用只感知一个虚拟地址比如127.0.0.1:6446。后面挂了几台 MySQL、谁是主谁是备、谁故障了需要摘掉都由 Router 自己根据元数据动态处理。这个思路和 Nginx 做反向代理有点类似——数据库变了应用无感知架构弹性一下就上来了。1.2 Router 8的核心价值不只是转发很多同学以为 Router 就是个简单的 TCP 端口转发工具装了发现它还会连接 MySQL 实例查询状态。其实 Router 8 的核心机制是“元数据感知”它通过持久化连接到 MySQL 的元数据库实时感知 InnoDB Cluster 或主从复制拓扑的变化。官方文档里管这条路叫 metadata cache也就是说 Router 不是瞎转发的它知道哪台机器健康、哪台机器权重高。读写分离场景下这个能力特别值钱。Router 8 支持把读流量分散到多个从库从库负载不一样还能配置不同权重应用端完全无感。主库故障时只要集群完成了自动提升Router 通过元数据感知到新主库地址就会自动把写流量切过去应用只需要在连接失败那一下做一次重连。这个特性在官方架构图里写得很清楚实际跑通了你才能体会“数据库拓扑对应用透明”这句话的分量。2. 安装前要把这几件事想清楚2.1 版本选择和下载渠道MySQL Router 8 的版本号跟着 MySQL 8.0 走比如 8.0.33、8.0.37。版本选择没那么多玄学我个人的建议是能用新不用旧但别追最新选当前 8.0 系列里成熟的小版本即可。因为它要配合 MySQL Shell、MySQL Server 8.0 做 InnoDB Cluster版本差异太大会出现元数据协议不兼容的问题所以如果服务器上已经建好了 ClusterRouter 版本尽量和 MySQL Server 大版本保持一致都是 8.0.x 就没问题。下载渠道有两条官方 Yum/Apt 源和二进制通用包。如果服务器能访问官方源用包管理器装最省事依赖自动解决如果内网环境没外网就去官方下载页面先把二进制 tar 包拖到内网。我这次实操用的就是通用二进制包解压即用方便控制安装目录也能看清楚 Router 内部有哪些组件。2.2 先确认拓扑方式再决定引导命令Router 8 的配置方式有两种一种是--bootstrap自动引导直接对接 InnoDB Cluster把整个 Router 配置文件自动生成另一种是纯手工写配置文件适合非 Cluster 的普通主从复制架构。这一步非常关键因为很多人安装 Router 时会直接执行引导命令结果发现自己的 MySQL 压根没有启用 MySQL Shell 的 AdminAPImetadata 还没初始化引导必然失败。简单说后端架构推荐配置方式前提条件InnoDB Clustermysqlrouter --bootstrapMySQL Server 8.0 MySQL Shell 已创建 Cluster普通主从复制手工编写 router.conf有主库、从库地址明确读写分离策略单机故障转移手工配置或用--bootstrap加--directory想用 Router 隐藏真实端口如果你只是想在测试环境快速跑一把我建议先搭一个单机实例再用 bootstrap 引导 Router 熟悉流程别一上来就怼生产。2.3 提前规划端口、账号和目录安装之前端口规划很重要。Router 默认的读写端口是6446只读端口是6447但生产环境容易和别的进程冲突建议提前用ss -lntp | grep 6446查一下。另外 Router 在引导模式下还会在本地监听8440之类的管理端口这些都需要在防火墙规则里提前放行。账号方面Router 需要一个能登录 MySQL 的用户来读取元数据生产环境别直接用 root建议单独建一个最小权限账号。官方推荐的姿势是从 MySQL Shell 里创建 Cluster 时自动生成的mysql_router1_user但手工配置场景下我们可以自己创建只授权SELECT和UPDATE权限即可。目录上二进制包安装的 Router 默认会写到/opt/mysql-router但它的配置、日志和运行状态文件分别在/etc/、/var/log/、/var/lib/下后续排查问题要清楚这些路径别到时候找不到日志干着急。3. 完整安装与配置实操记录3.1 二进制方式安装详解我用 MySQL Router 8.0.37 在 CentOS 7.9 上走了一遍完整流程这里把每一步都记录下来。先去官方下载页面拿到 Linux Generic 二进制包的下载地址然后服务器上执行wget -c https://cdn.mysql.com/Downloads/MySQLRouter/mysql-router-8.0.37-linux-glibc2.17-x86_64.tar.xz tar -xvf mysql-router-8.0.37-linux-glibc2.17-x86_64.tar.xz mv mysql-router-8.0.37-linux-glibc2.17-x86_64 /opt/mysql-router解压完成后先看一下目录结构我习惯用tree -L 2 /opt/mysql-router确认有bin、lib、share几个关键目录。Router 的二进制主体就是/opt/mysql-router/bin/mysqlrouter你可以执行mysqlrouter --version验证一下是否可用能看到版本号说明依赖库没问题。按官方建议Router 不应该以 root 身份直接跑最好创建一个专用系统用户useradd -r -s /bin/false mysqlrouter然后把安装目录属主改一下chown -R mysqlrouter:mysqlrouter /opt/mysql-router mkdir -p /var/log/mysqlrouter /var/lib/mysqlrouter chown -R mysqlrouter:mysqlrouter /var/log/mysqlrouter /var/lib/mysqlrouter这一步看着繁琐但对生产装态很有必要。Router 进程如果被入侵至少不是 root 权限能少很多麻烦。3.2 用 bootstrap 引导配置快速对接 InnoDB Cluster接下来是重头戏。如果你的后端已经有一套 InnoDB Cluster那么执行引导命令即可/opt/mysql-router/bin/mysqlrouter --bootstrap root192.168.1.100:3306 --usermysqlrouter --directory/opt/mysql-router这条命令会连接192.168.1.100:3306上的 MySQL 实例检测是否在这个节点上启用了 Cluster 元数据。如果一切正常Router 会自动生成mysqlrouter.conf、mysqlrouter.key等文件还会打印出各个路由端口的配置概要。--directory把配置和日志都合并到一个目录里方便测试环境一键清理。需要特别注意账号权限。如果你直接拿 root 用户去 bootstrap大概率会碰到提示说账号没有 admin 权限或者元数据不是最新版本。官方推荐的姿势是先在 MySQL Shell 里用cluster.addRouter()注册 Router 账号或者 bootstrap 时用 Cluster 管理员账号Router 会自动创建后续连元数据库的专用账号。测试时可以图省事用 root但生产环境务必在 MySQL Shell 里预先创建账号CREATE USER router_user% IDENTIFIED BY StrongPass123; GRANT SELECT, UPDATE ON mysql_innodb_cluster_metadata.* TO router_user%; GRANT SELECT ON performance_schema.* TO router_user%;bootstrap 完成之后Router 配置文件里已经自动写好了 metadata_cache 和 routing 两段配置读写端口和只读端口也按默认规则生成了。此时可以直接后台启动测试/opt/mysql-router/bin/mysqlrouter --config/opt/mysql-router/mysqlrouter.conf 启动后看端口监听状态ss -lntp | grep -E 6446|6447如果能看到两个端口都在监听说明核心路由功能已经上线了。3.3 手工配置方案不靠 Cluster 也能跑如果说你的环境压根没建 Cluster只是普通的 Master-Slave 主从复制那 bootstrap 这一套就有点不匹配了。Router 8 也支持手工配置我直接写了一份最基础的mysqlrouter.conf这里做个示例[DEFAULT] logging_folder /var/log/mysqlrouter runtime_folder /var/lib/mysqlrouter config_folder /opt/mysql-router [logger] level INFO [routing:primary] bind_address 0.0.0.0 bind_port 6446 mode read-write destinations 192.168.1.100:3306,192.168.1.101:3306 routing_strategy first-available [routing:secondary] bind_address 0.0.0.0 bind_port 6447 mode read-only destinations 192.168.1.102:3306,192.168.1.103:3306 routing_strategy round-robin注意看几个关键点[routing:primary]和[routing:secondary]是配置块的名字可以任意命名但建议读写分离语义用 primary/secondary 区分。mode read-write表示这条路由接受写流量read-only只接受读流量。Router 8 的read-only模式并不是简单的禁止写操作如果你往只读端口发写 SQLRouter 会在 MySQL 协议层直接拦截并返回错误。destinations可以写多个地址Router 会根据routing_strategy做负载均衡。first-available倾向于把流量导给第一个健康实例适合主从切换场景round-robin适合把读流量分散到多个只读实例。手工配置的好处是灵活坏处是 Router 不会自动感知后端拓扑变化主库从192.168.1.100切换到了192.168.1.101你得手工改配置或者依赖外部脚本调用mysqlrouter --reload重新加载。所以能用 Cluster 的场景还是建议优先 ClusterRouter 才能发挥出动态感知的优势。3.4 用配置文件快速验证路由是否生效配置写好后先用以下命令验证配置语法/opt/mysql-router/bin/mysqlrouter --config/opt/mysql-router/mysqlrouter.conf --validate如果没有任何输出说明配置是合法的。然后前台启动看日志/opt/mysql-router/bin/mysqlrouter --config/opt/mysql-router/mysqlrouter.conf看到日志里有Starting MySQL Router和Listening on相关的行就可以用 MySQL 客户端连一下试试mysql -h127.0.0.1 -P6446 -uroot -p登录后执行SELECT port;如果返回的是后端某个实例的实际端口比如 3306说明连接已经被正确路由到后端 MySQL 了。多连几次SELECT server_uuid;会看到不同的值说明负载均衡生效。4. 作为系统服务托管避免进程守护的坑4.1 注册 systemd 服务实例二进制方式启动 Router 非常灵活但生产环境不可能靠命令行后续台跑进程守护、开机自启都是必要诉求。CentOS 7 基本都是 systemd单位文件可以这样写[Unit] DescriptionMySQL Router Afternetwork.target [Service] Usermysqlrouter Groupmysqlrouter ExecStart/opt/mysql-router/bin/mysqlrouter --config/opt/mysql-router/mysqlrouter.conf Restarton-failure RestartSec5 [Install] WantedBymulti-user.target把这个文件保存为/etc/systemd/system/mysqlrouter.service然后systemctl daemon-reload systemctl enable mysqlrouter systemctl start mysqlrouter systemctl status mysqlrouter需要注意一点如果使用 bootstrap 生成的文件没有mysqlrouter.key属主为 root 的情况系统服务跑在 mysqlrouter 用户下会因为访问权限不足启动失败。安装时我已经chown过了但手工配置时很容易漏掉/var/lib/mysqlrouter目录的属主启动前记得检查一遍ls -ld /var/lib/mysqlrouter /var/log/mysqlrouter /opt/mysql-router/mysqlrouter.conf4.2 配置热加载与平滑重启Router 8 支持配置热加载修改mysqlrouter.conf之后不需要停止进程只需要给进程发送一个 SIGHUP 信号。实际操作kill -HUP $(cat /var/lib/mysqlrouter/mysqlrouter.pid)但要注意并不是所有配置项都支持热加载像监听端口、bind_address 这种核心网络配置改了还是需要完整重启。为了安全起见大改配置后我一般先kill -HUP看一眼日志有没有报错再决定是否重启。这个习惯能减少很多不必要的连接中断因为热加载失败 Router 会回滚配置不会把后端连接全部断掉但重启一定会打断所有活跃连接业务侧的感觉非常明显。4.3 多实例部署时的端口和日志隔离如果你的环境需要多个 Router 实例比如一组给业务A一组给业务B或者读写和只读流量要完全隔离尽量不要在一个进程里塞多个[routing]块而是用 systemd 的实例化服务来管理。配置文件按实例分开服务名用模板[Unit] DescriptionMySQL Router instance %i Afternetwork.target [Service] Usermysqlrouter Groupmysqlrouter ExecStart/opt/mysql-router/bin/mysqlrouter --config/etc/mysqlrouter/%i.conf Restarton-failure [Install] WantedBymulti-user.target然后分别存放/etc/mysqlrouter/businessA.conf、/etc/mysqlrouter/businessB.conf启动时systemctl start mysqlrouterbusinessA systemctl start mysqlrouterbusinessB每个实例监听不同端口、写不同日志排障时互不干扰。端口分配上我习惯在一个 excel 表里统一登记防止以后加实例时端口冲突别笑生产事故不少就是在这种小细节上翻车的。5. 常遇到的问题和排障思路5.1 bootstrap 报错元数据不可用最常见的问题出现在--bootstrap的时候错误信息一般是ERROR: Error connecting to MySQL server at X.X.X.X:3306: Access denied for user rootX.X.X.X (using password: YES)或者ERROR: The MySQL metadata is not available for the account used for bootstrap.前一个本质是账号密码或权限问题检查连接串有没有写对账号能不能远程登录后一个说明当前实例压根没有初始化 InnoDB Cluster 元数据或者元数据版本太老。要解决先用 MySQL Shell 连上实例执行var cluster dba.getCluster(); console.log(cluster.status());如果这一步都报错说明你还没建 Cluster那就老老实实用手工配置路线或者先用 MySQL Shell 创建 Cluster 再回来 bootstrap。5.2 配置没问题端口却一直不监听如果--validate通过、进程也在跑但ss -lntp看不到端口大概率是防火墙把端口拦了或者 Router 只绑定了 IPv6 的某个地址。前者用firewall-cmd --add-port6446/tcp --permanent放行后者需要检查bind_address配置写成0.0.0.0听所有地址写成::听所有 IPv6 地址。在纯 IPv4 环境下bind_address 配置成::会让你客户端根本连不上这是个非常隐蔽的坑。5.3 连接被拒绝但日志没报明显错误有一次我帮同事排查Router 进程活着、端口也监听但客户端连接总是超时。最后发现是destinations里写的主机名无法解析。Router 启动时不会立刻去解析 destinations 里的域名只有客户端的连接进来后它才会去连接后端如果 DNS 解析失败连接就一直在重试。所以在配置里我建议一律写 IP或者确保/etc/hosts里有对应解析别过度依赖 DNS否则生产上会莫名多出很多疑难杂症。5.4 读写分离后只读端口出现“写操作被拦截”之前有同事反馈应用通过只读端口执行INSERT居然不报错数据还真的写进去了。原因是他把mode写成了read-write导致只读端口实际上也是读写端口。这个不是 Router 的 bug而是配置语义理解不到位。手工配置时[routing:secondary]的mode必须明确写成read-onlyRouter 才会在协议层拦截非读操作。用集群bootstrap自动生成时一般不会犯这个错但手工配置就特别容易翻车。5.5 日志文件刷得太快磁盘告警Router 默认日志级别是 INFO如果后端 MySQL 实例频繁重启Router 会反复重新连接日志瞬间膨胀。生产环境我一般把级别调成 WARNING只在需要排查问题时临时改成 INFO/DEBUG排查完再调回来。日志轮转方面Router 自身没有内置 logrotate 配置建议在/etc/logrotate.d/mysqlrouter加一个/var/log/mysqlrouter/*.log { daily rotate 7 compress delaycompress missingok notifempty copytruncate }这样能防止日志把磁盘吃满避免一个中间件进程拖垮整个数据库服务器。6. 安装配置之后的几点真实体会整个过程走完我最大的感受是 MySQL Router 8 的安装并不难难的是把它放到整个架构里理解清楚。它不是一个孤立的软件而是 InnoDB Cluster 高可用方案里的连接层少了它Cluster 的故障转移只能做到数据库层面应用侧依然会连接中断。有了它应用只认端口数据库再怎么折腾都与应用无关。我实际操作中还有两个小技巧值得分享。第一个是连接测试时别用root建一个专用应用账号这样通过 Router 的端口连接后端实例时权限模型和生产完全一致能提前暴露授权问题。第二个是安装完成后一定要把 Router 的数据目录、日志目录纳入备份和监控体系它虽然不存业务数据但它的配置文件变了意味着整个高可用拓扑的入口变了这种变更不给监控出问题只能靠事后翻记录。另外Router 8 默认的routing_strategy在不同模式下行为差异很大测试环境建议把round-robin、first-available、round-robin-with-fallback都试一遍用mysql -e select port;循环压一下观察流量分布是否符合预期。这个验证动作最多十分钟但它能让你对 Router 的调度策略有非常直观的感知后面调优心里就有底了。这篇文章里的配置路径和命令大方向对绝大多数 Linux 发行版都适用细节上Yum 安装和二进制安装会有些不同但底层原理一致。如果你正在搞 InnoDB Cluster或者刚接手一套用 Router 做读写分离的业务把上面几个场景吃透基本就能应对日常的安装和运维需求了。