MySQL读写分离实战:Atlas中间件部署、配置与故障排查全解析

发布时间:2026/10/18 4:03:18
MySQL读写分离实战:Atlas中间件部署、配置与故障排查全解析 如果你维护过一套 MySQL 主从架构大概率遇到过这个场景主从复制跑得稳稳当当从库的负载却上不去主库反而被一波一波的读请求压得喘不过气。我最早处理这个问题时选择在应用代码里手动区分读库和写库结果每个服务都要配两套数据源后期加一台从库还要改一堆连接配置想想都头大。后来我把 Atlas 引进架构才算把读写分离从“应用层负担”变成了“中间件能力”。这篇内容围绕 Atlas 这个 MySQL 代理中间件把部署、配置、故障切换、分表规则和排障过程完整讲一遍适合正在选型读写分离方案的 DBA、后端开发以及所有想了解 MySQL 代理层原理的人。1. 读写分离为什么需要 Atlas 这层代理1.1 主从复制解决不了“流量打在主库”的问题先聊一个很基础但经常被忽略的问题。MySQL 主从复制解决的是数据冗余、灾备、以及“读多写少”场景下的扩展问题但它本身不会帮你做流量调度。主库和从库都立在那里应用程序怎么知道哪条 SQL 该走哪台机器答案是不知道。大多数团队一开始的写法是在服务里配置一个主库连接串和一个从库连接串然后自己写一个读写分离的工具类根据 SQL 前缀判断是 SELECT 还是其他操作再选择不同的数据源。这方案不是不能跑而是后面维护起来极其痛苦。举个例子业务从 5 个服务涨到 20 个服务每个服务的连接池参数、从库地址都要独立维护哪次从库扩容漏改了一个配置那部分流量就继续压在主库上等于白扩了。我当时的判断是必须在应用和 MySQL 之间加一层统一的代理让所有数据库访问都走同一道门由这道门来分发读写流量。这也是引入 Atlas 最核心的原因不是因为它多花哨而是它能把“读写分离”从每个服务各自的私活收拢成整个架构层面的统一能力。1.2 Atlas 和其他中间件的定位差异Atlas 是 360 基于 MySQL-Proxy 0.8.2 二次开发的一个开源数据库中间件2013 年前后开始在 GitHub 上维护核心能力就是读写分离、负载均衡、单库分表和管理接口。这里得说清楚它跟现在更流行的 MyCat、ProxySQL 不是一个路子。我画个简单对照你感受下组件核心定位读写分离分库分表动态管理上手难度AtlasMySQL 代理偏读写分离强开箱即用只支持单库分表不支持分库通过管理接口在线切换后端状态低MyCat分布式数据库中间件支持支持分库分表能力强需要改配置重启为主中高ProxySQL高性能 SQL 代理支持且规则灵活主要通过路由规则 后端分片很强支持在线改配置中Atlas 的优势在于“简单”。部署结构简单、配置项少、路由逻辑纯粹。它不打算帮你做复杂的分布式事务也没想替你把跨节点的 join 全都接管它就是把读写分离和单库分表这两件事做好。如果你的需求很简单主库写、从库读、从库之间轮询分摊读流量那 Atlas 是个非常顺手的选择运维起来也不用养一个专门的中间件团队。1.3 Atlas 在架构里的典型位置在实际部署中Atlas 就是一个无状态的代理层放在应用和 MySQL 之间。应用只认识 Atlas 的地址和端口至于后端有几台从库、主库是谁都不需要关心。它默认会占用两个端口一个是代理端口默认 1234专门给业务连接用另一个是管理端口默认 2345给 DBA 或者运维系统查状态、做切换用。无状态这个特性很关键意味着你可以同时跑两个 Atlas 实例互相之间不需要同步任何会话信息。配合 keepalived 挂一个虚拟 IP谁挂了 VIP 自动漂到另一台应用那边根本感知不到。后面我会专门讲这个高可用方案。2. 部署 Atlas 时最容易忽略的两个细节版本与目录权限2.1 版本选择与安装包准备Atlas 的官方源码和预编译包都在 GitHub 上项目地址是 Qihoo360/Atlas。这里我想先强调一个经验能用预编译包就别自己编译源码。Atlas 本身依赖比较老自己编译会碰上一堆 glibc、libevent、luajit 的兼容问题折腾半天不如直接下载对应系统的编译产物。下载解压后整个目录会被平铺到/usr/local/mysql-proxy下这个路径是默认的后面所有配置、日志都基于它。如果你手动改安装目录那启动命令、脚本路径、PID 文件路径都要跟着改没必要。我把安装后的目录结构列一下心里有个数/usr/local/mysql-proxy/bin/mysql-proxy主程序/usr/local/mysql-proxy/conf/mysql-proxy.cnf核心配置文件/usr/local/mysql-proxy/lib/mysql-proxy/lua/Lua 脚本读写分离和分表的逻辑在这里/usr/local/mysql-proxy/log/日志目录SQL 日志和运行日志都在这里2.2 配置文件权限这个坑我替你们踩过了安装完第一件事不是直接改配置而是先处理配置文件权限。Atlas 对mysql-proxy.cnf的权限非常敏感如果权限大于 600启动时会在日志里报file not readable之类的问题然后拒绝启动。我第一次部署时没注意这茬从本地把配置文件传到服务器后还是默认的 644 权限来回检查配置语法半天最后才发现是权限问题。所以在改配置之前先执行chmod 600 /usr/local/mysql-proxy/conf/mysql-proxy.cnf这个操作还有个额外作用配置文件里有密码字段虽然是加密后的但把权限收紧到只有属主可读写总归符合安全习惯。2.3 启动、关闭与状态确认Atlas 启动很简单直接执行主程序就行/usr/local/mysql-proxy/bin/mysql-proxy前提是配置文件里的daemon true这样进程才会在后台常驻。启动后我习惯按顺序确认三件事进程是否在跑ps -ef | grep mysql-proxy端口是否监听netstat -an | grep 1234和grep 2345日志里有没有报错tail -f /usr/local/mysql-proxy/log/sql.log。关闭进程也别直接 kill -9Atlas 的 PID 文件默认在/usr/local/mysql-proxy/conf/下用脚本关闭最稳妥。如果发现 PID 文件缺失就需要手动查进程号再停但这种情况一般只出现在非正常退出之后。3. 读写分离配置逐项拆解每一行 conf 都不能瞎写3.1 从一份主从分离配置说起先给出一份我在测试环境里验证过的配置示例再用管理员口吻逐项解读。配置文件的格式是 INI 风格[mysql-proxy] admin-username atlasadmin admin-password 这里是加密后的密码 admin-address 0.0.0.0:2345 proxy-address 0.0.0.0:1234 proxy-backend-addresses 192.168.10.10:3306 proxy-read-only-backend-addresses 192.168.10.11:3306,192.168.10.12:3306 daemon true event-threads 4 max-connections 1024 log-level message log-file /usr/local/mysql-proxy/log/sql.log sql-log ON charset utf83.2 核心字段说明proxy-backend-addresses只有一个就是主库地址所有写操作都会转发到这里。proxy-read-only-backend-addresses可以写多个逗号分隔Atlas 会在这些只读后端之间做轮询负载均衡。这里有个很值得注意的点Atlas 默认把读请求按轮询方式分发到各个从库所以在配置从库之前你必须确认这些从库的数据是一致的。如果主从延迟很大轮询分发会让业务读到旧数据看起来就像“数据丢失”一样。我见过不止一次线上事故最后追踪到主从延迟已经有好几分钟而读写分离代理还在傻乎乎地轮询应用读到的全是过期数据。这个不是 Atlas 的锅而是引入读写分离前必须建立主从延迟监控。event-threads是工作线程数不是越大越好。我一般按 CPU 核数的 2 到 4 倍设置比如 4 核机器配 8 到 16。线程太多反而会在高并发下增加上下文切换开销。max-connections控制 Atlas 接受的最大客户端连接数。这个值要根据后面挂多少应用来估算不能拍脑袋。我通常把所有接入服务的连接池上限加起来再留 20% 到 30% 的余量。3.3 管理密码不是明文是加密串配置里的admin-password不能直接填明文需要先通过 Atlas 自带的加密工具生成密文再填进去。工具路径在bin目录下用法很简单/usr/local/mysql-proxy/bin/encrypt 123456执行完之后会输出一串加密后的字符串把它填到admin-password字段里。注意每次执行生成的结果大概率是不同的因为加密过程引入了随机因子所以只需要保证“当前配置文件里的密文是对应明文加密得来”的即可。3.4 配置完成后如何验证读写分离真的生效配置改完重启 Atlas很多同学的验证方式就是连上去跑一条 SELECT 一条 UPDATE看到没报错就认为 OK 了。这远远不够。我的验证流程是这样的。首先在 Atlas 日志里确认 SQL 确实经过代理tail -f /usr/local/mysql-proxy/log/sql.log然后开两个终端一个在从库机器上用通用查询日志监控实际进来的 SQL另一个在应用端执行操作。执行几条 SELECT去从库的日志里看语句有没有到执行一条 UPDATE再去主库看。两边都能对上才叫验证通过。另外还可以通过管理接口看后端状态。Atlas 管理端口支持 MySQL 协议直接拿 mysql 客户端登录mysql -h127.0.0.1 -P2345 -uatlasadmin -p登录后执行SELECT * FROM backends;这个命令会列出所有后端实例的地址、类型、状态等信息。type字段里rw代表主库ro代表只读从库看到状态是UP就说明 Atlas 的心跳检测已经通过节点可用。4. 从库掉线、主库切换把故障挂在 Atlas 管理接口上4.1 从库掉线时Atlas 会怎么做在 Atlas 的设计里每个后端节点都有健康状态它内部会定期检测后端可用性。检测方式就是向 MySQL 发起一个轻量的 ping 连接如果失败就标记节点不可用。当一个从库掉线时Atlas 不会把读流量继续发到故障节点而是自动在剩余可用从库之间重新分配流量。这个感知过程可能有一个短暂的时间窗口如果应用在那个瞬间发起查询会报一个连接错误但重试之后就能正常路由到其他从库。这里我要特别提醒不要把从库掉线完全交给 Atlas 的自动检测你仍然需要对后端做外部监控。原因有两个一是 Atlas 的状态感知有延迟生产环境里这个时间窗口可能造成个别请求失败二是如果所有从库都挂了读流量会全部打回主库这时候主库压力会陡增必须有告警通知到人。4.2 主库故障时的手动切换流程Atlas 不像 MHA 那样会自动提升新主库主库挂掉之后写流量会直接失败这时候需要人工介入做切换。我整理了一套比较稳妥的流程确认新主库数据已经补平。如果原来有几台从库通常选数据最新的一台接管。在 Atlas 管理接口里先把旧主库摘除让它不再接收流量。执行SET offline 1;这里的1是对应旧主库的 backend id具体看SELECT * FROM backends;返回结果的顺序。在 MySQL 层面把新主库的角色提升为主库并调整其他从库的主从复制关系。修改 Atlas 配置文件里的proxy-backend-addresses把地址改成新主库的 IP 和端口。重启 Atlas 进程重新在管理接口里确认新主库状态为UP。这套流程看起来不长但每一步都有坑。最典型的是第二步和第三步的顺序如果你先改了 MySQL 复制关系再处理 Atlas中间那段时间新主库可能还在 Atlas 的只读后端列表里读流量和新主库状态会暂时不一致。所以我的习惯是先摘掉 Atlas 侧的状态再改 MySQL 侧的角色最后再让 Atlas 重新加载配置。4.3 两个 Atlas 实例做高可用应用才不会断既然 Atlas 本身是无状态的那完全可以通过两实例冗余实现中间件层的高可用。我常用的做法是 keepalived 配虚拟 IP。两台服务器都装 Atlas配置完全一致唯一区别是 keepalived 角色不同。正常情况只有一台绑定了虚拟 IP所有应用都连这个虚拟 IP。如果这台机器宕机keepalived 会自动把虚拟 IP 漂到另一台应用重新建立连接后就走新的 Atlas 实例了。这个方案的关键点是应用连接中间件时不要用长连接永不断开最好在连接池里设置一个合理的连接存活时间否则当虚拟 IP 切换后旧连接会一直卡在超时状态反而影响恢复速度。我习惯把连接池的最大存活时间设成 30 到 60 分钟这样 IP 漂移最多影响一小部分正在使用的连接。5. 分表是最后一张牌Atlas 分表规则与业务约束5.1 分表规则到底怎么写Atlas 的分表能力与 MyCat 不同它只做“单库分表”也就是说所有子表都在同一个数据库实例里不会跨库。这对很多中型业务来说其实是更合理的选择因为跨库查询的复杂度会指数级上升。分表规则写在配置文件的末尾格式大致是tables testdb.user.id.4-0这段配置的含义是testdb库下的user表按照id字段分 4 张子表子表编号以0开始。也就是说业务想真正使用分表需要提前在数据库里把user_0、user_1、user_2、user_3这 4 张物理表都建好Atlas 只负责路由不负责建表。子表的表结构也要保持一致这是基本前提。至于某一行的数据具体落到哪张表Atlas 对分表键做哈希后取模。只要id相同同一行数据永远会落到同一张子表。这个特性对后续查询、更新很关键因为路由可以精确命中一张物理表而不是全表扫描。5.2 分表键选不好后面全是坑我在生产环境里见过最典型的问题就是分表键选了一个业务上无法稳定获取的字段或者选了一个查询频率不高的字段。比如按订单号分表但业务里大量查询都是根据用户 ID 查订单那这些查询就没法通过用户 ID 定位到具体某张子表只能广播到所有分表去搜一遍性能反而比不分表还差。选分表键要同时满足三个条件业务查询基本都会带上这个字段的等值条件这个字段的值几乎不会变更值的分布足够均匀不能集中在某几个值上。大多数场景我会优先推荐用户 ID、租户 ID、业务流水 ID 这类天然带有隔离性的字段。分表数量也别贪多4 到 16 张通常是大多数团队可以接受的复杂度。5.3 哪些查询能走分表哪些走不了Atlas 的分表路由不是万能的。带分表键等值条件的查询、插入、更新、删除都能精确命中分表但如果条件里没有分表键Atlas 会把 SQL 广播到所有分表上执行然后合并结果。小数据量看影响不大一旦单表数据量上去了广播查询足以拖垮整个实例。聚合类查询也要特别小心。例如不带分表键的COUNT(*)、SUM()、多表JOIN在分表场景下会变得非常复杂。虽然 Atlas 在简单场景下能做一些结果合并但我不建议在核心链路里依赖这个能力。我参与过的几个分表项目后面基本都演化出一条经验分表解决的是“单表写入压力”和“单表体积过大”的问题不是所有 SQL 问题都适合靠分表来解决。如果业务里有大量慢查询和复杂关联先把慢查询治理好再评估分表是否必要。分表一旦上了业务代码的改造量远超预期所以它真的只能是最后一张牌。6. 跑了半年才遇到的坑Atlas 生产环境排障实录6.1 连接数被打满应用全部超时上线几个月后第一次遇到大故障表现是应用侧数据库连接池的活跃连接数不断上涨错误日志里频繁出现Too many connections。刚开始怀疑 MySQL 的连接数满了但到 MySQL 上一查连接数远没有到上限。于是回头查 Atlas才发现是 Atlas 的max-connections配置太小。原因是新业务接进来后应用连接池的连接数估算做得过于乐观多个服务一起跑满后Atlas 先撑不住了。排查手段很简单在 Atlas 机器上执行netstat -an | grep 1234 | grep ESTABLISHED | wc -l看当前与代理端口建立的连接数再对比配置里的max-connections。调整建议是应用连接池总数上限加总的 1.3 倍左右同时要在 Atlas 前面做好连接数监控不能等爆了再去加。这次故障给我一个很深的教训中间件的连接数上限不是随便填一个差不多的值它应该是整个后端连接规划里的一等公民。6.2 应用连不上代理端口怎么一步步排查还有一个高频问题就是应用突然连不上 Atlas报连接超时或者连接被拒绝。排查要按链路一层一层来千万不要一上来就怀疑配置那会浪费大量时间。先在 Atlas 所在的机器上用 MySQL 客户端本地连一次mysql -h127.0.0.1 -P1234 -utest_user -p本地能连上说明代理进程本身没问题问题大概率在网络链路或权限上。再跨机器 telnet 一次telnet atlas_ip 1234不通的话检查防火墙、安全组、网络策略。如果通但应用还是报认证失败那就要检查用户名密码配置和授权 SQL。Atlas 会把后端的用户校验往 MySQL 上传所以也要同步确认 MySQL 里的用户权限和访问来源条件。这一套排查下来看着简单但在实际事故中最容易犯的错是跳步。比如直接在客户端验证不了就改动配置结果越改越乱。按顺序来每一步都有一个明确结论才能快速收敛问题。6.3 SQL 日志不开事后分析全靠猜我特别建议线上环境把 SQL 日志打开。Atlas 的 SQL 日志对于分析慢查询、排查分表路由、确认读写分离是否生效都很有用。配置里需要同时打开两个点log-level debug sql-log ONlog-level至少要debug级别sql-log控制是否记录 SQL 内容。如果只开message级别很多细节会丢失。日志写满的问题可以通过外部 logrotate 解决但排查问题的收益远大于那点磁盘消耗。后来踩过一次坑某天从库负载很高但谁都说不清是哪条 SQL 打过来的。打开 SQL 日志分析后发现是某个报表任务在凌晨批量扫表而且每条查询都没带分表键导致广播到所有分表执行。如果没有 SQL 日志这种问题基本无从下手。6.4 长事务和连接泄漏是慢查询之外的隐形杀手最后说一个比较隐蔽的问题。有段时间我观察到后端 MySQL 的 sleep 连接数持续增长而且集中在 Atlas 的代理连接上。一开始以为是正常现象但增长趋势一天比一天陡最后还是 OOM 之前被监控叫醒的。排查后发现是应用框架的连接池在异常情况下没有及时释放连接加上个别长事务一直不提交导致 Atlas 和后端之间的连接一直被占用。处理手段分两层应用侧给连接池配置空闲连接回收策略设置合适的——IDLE_TIMEOUT类参数MySQL 侧把wait_timeout和interactive_timeout调到合理范围让长时间空闲的连接被服务端主动回收。这类问题不能用单一手段解决必须应用和数据库两头同时治理。我是先收紧空闲连接超时再用夜里低峰期观察 sleep 连接曲线确认不再上涨之后才算告一段落。维护 Atlas 这大半年我最深的体会是中间件只负责路由它不会替你把 SQL 质量变好也不会自动消化设计上的不合理。读写分离让你少操主库的读压力但主从延迟监控、连接数规划、慢 SQL 治理这些基本功一样都不能省。如果你打算在项目里引入 Atlas我的建议很直白先从读写分离用起跑稳两三个月再考虑分表。分表的配置确实简单但它对业务的侵入比想象中大得多前期把 SQL 梳理清楚比自己闷头写一堆分表规则有用得多。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询