MySQL中间件Atlas详解:读写分离、部署配置与运维排障实战

发布时间:2026/10/19 13:05:47
MySQL中间件Atlas详解:读写分离、部署配置与运维排障实战
这两年只要聊到MySQL的高可用和读写分离圈子里总绕不开一个老牌中间件——Atlas。很多刚接触数据库架构的同学一听到“Atlas”第一反应是地图册再一问才知道这其实是奇虎360开源的一个MySQL数据库中间件项目。我最初接手的那套主从集群里就跑着Atlas几年下来踩过不少坑也积累了一些实际经验这次就围绕这个项目好好聊一聊。这个Atlas能做什么简单说它处在应用和MySQL实例之间把开发者的读写请求自动分发到不同的数据库节点上主库负责写入从库分摊读流量。非常适合那些“读多写少”、希望快速拆分数据库压力、又不想大改业务代码的团队。这篇文章不是官方文档的复读而是结合我实际部署和维护的经验把原理、搭建、配置、排障这些东西一次讲透适合DBA、后端开发以及正在选型中间件的架构师参考。1. 项目背景与整体设计思路1.1 为什么会出现Atlas这样的中间件先聊点背景。MySQL的主从复制能力出来之后“一主多从”成了很多团队缓解数据库压力的首选方案主库扛写入从库分担读流量。但很快大家就发现一个现实问题——业务代码不会自动区分读写。你需要自己实现一套路由逻辑要么在应用层写多个数据源要么让ORM框架去做读写分离这就有几个绕不开的麻烦业务代码侵入严重每个新项目都得重写一套数据源逻辑非常痛苦。主从切换时连接管理复杂一旦主库宕机应用连接池需要感知并切换处理起来很繁琐。从库负载不均衡多个从库之间的读流量没法均匀分配某台机器容易成为热点。在这种背景下数据库中间件就出现了。它的核心价值就是把“读写路由”这件事从业务代码里剥离出来放在一个独立的代理层统一处理。Atlas就是这类产品里很有代表性的一员。1.2 Atlas在整个架构里的角色画一条简单的链路业务应用 → Atlas中间件 → MySQL主库/从库。应用连接Atlas就像连接一个普通的MySQL实例端口、协议都兼容。Atlas收到SQL语句后判断它是SELECT还是写操作INSERT/UPDATE/DELETE等再结合配置里的读写权重把请求转发到后端的真实数据库上。对于应用来说它并不知道背后有几台MySQL也不需要关心哪台是主、哪台是从连接串只需要指向Atlas就行。这个设计其实是借鉴了LVSLinux虚拟服务器的思路对外暴露一个虚拟VIP背后挂载多台真实服务器。对调用方来说整个数据库集群被“抽象”成了一个地址这是中间件能够显著降低业务改造量的关键。我当时选择Atlas而不是自己写数据源路由核心原因就是它的部署成本低、协议兼容度高。Java可以用JDBC直连PHP有mysqli扩展Go有go-sql-driver都不用额外装驱动只要把连接地址改一下就能接入。对于那种“一堆老项目都要从单库升级到主从集群”的场景Atlas几乎是成本最低的过渡方案。1.3 Atlas基于MySQL Proxy的传承与改动Atlas这个项目不是从零写的。它基于MySQL官方早期推出的MySQL Proxy 0.8.2版本做了深度二次开发。MySQL Proxy本身是一个基于Lua脚本的代理框架理论上可以用Lua写各种自定义逻辑但性能和稳定性一直不太理想在生产环境落地不多。360团队把这个框架C语言内核拿过来改了协议解析和线程模型砍掉了很多用不上的Lua扩展能力换成了更纯粹、更高效的读写分离和负载均衡实现。这也是为什么你在Atlas的安装包里还能看到一些MySQL Proxy的影子——比如配置文件和启动脚本都比较像。但它的实际使用体验和性能要比裸的MySQL Proxy好不少。简单理解Atlas就是“站在官方代理这个半成品肩膀上做了一款真正面向生产环境的中间件”。1.4 Atlas解决了哪些具体问题归纳来说在我的使用场景里Atlas主要帮我解决了几个实际问题读写自动分离不用在业务代码里做任何判断写请求自动走主库读请求自动走从库。从库负载均衡多个从库可以配置不同的权重请求按权重分发避免某台从库负载过高。连接池复用Atlas本身维护了一套到后端MySQL的连接池避免大量前端连接直接压到MySQL上。后端实例可动态调整通过管理接口可以在不重启服务的情况下上下线后端的MySQL实例。基础的高可用辅助虽然Atlas本身不是专职的高可用组件但配合脚本可以实现主从切换后快速调整读写指向。这些功能现在看来可能不算新奇但放在它开源的年代确实解决了很多团队的燃眉之急。即便今天再看它的设计思路依然有很强的参考价值。2. 核心原理与配置细节拆解2.1 Atlas的路由与负载均衡原理Atlas的路由逻辑相对简单但简单不等于粗糙。它内部维护了两个后端节点列表一个Master组用于写、一个Slave组用于读。当收到SQL语句时它会先做SQL解析判断语句类型SELECT语句默认走从库除非事务内或者使用注释强制走主库。INSERT、UPDATE、DELETE、DDL等语句一律走主库。事务中的读操作也会被路由到主库避免复制延迟导致数据不一致。这里有一个容易踩坑的点默认情况下即使是SELECT如果前面开启了事务BEGIN也必须在主库执行否则读到自己还没提交的数据或者读到从库的旧数据都会出问题。Atlas在内部会跟踪会话的“事务状态”一旦发现处于事务中后续SQL全部送主库。这个设计非常实用也避免了业务把“读主库”的标记写在SQL里。在负载均衡上Atlas 支持给每个从库配置不同的权重。比如两个从库一个机器配置好、带宽大可以配weight2另一个普通机器配weight1那么读请求就会按2:1的比例分发。它内部使用的是平滑加权轮询算法不会出现某个连接被连续打到一个节点的情况。2.2 关键配置项逐项解读Atlas的默认配置文件是/usr/local/mysql-proxy/conf/atlas.cnf。我挑几个最核心的配置项来聊这些都是日常运维中碰得最多的。mysql-proxy-backend-addresses这个配置项用来指定后端写节点也就是主库。格式是ip:port多个地址用逗号分隔。注意写节点虽然可以配置多个但同一个时刻只有第一个可用的节点会被选为主写节点。这里如果你做主从自动切换的脚本需要同步修改这个字段并重启Atlas或者用管理接口动态调整。示例mysql-proxy-backend-addresses 192.168.1.10:3306mysql-proxy-read-only-backend-addresses这个配置项指定只读节点即从库列表。格式同样是ip:port可以配置多个每个后面还可以跟weight来指定权重。示例mysql-proxy-read-only-backend-addresses 192.168.1.11:33061,192.168.1.12:33062上面这个例子里192.168.1.11的读权重是1192.168.1.12的读权重是2。我建议给配置高的机器分配更大的权重给配置低的机器分配更小的权重让流量匹配机器能力。mysql-proxy-address该配置项是Atlas对外提供服务的监听地址和端口也就是应用要连接的入口。我的习惯是绑内网IP不要绑0.0.0.0这样更安全。示例mysql-proxy-address 192.168.1.20:1234mysql-proxy-charset数据库字符集这个非常关键如果配置错了中文写入和读取会出现乱码问题。建议和MySQL实例的字符集保持一致比如utf8mb4。admin-username / admin-password管理接口的用户名和密码用于通过MySQL客户端连接到Atlas的管理端口执行管理操作比如动态上下线后端节点。这个密码不要用弱密码管理接口虽然只允许本地或者指定IP访问但生产环境最好还是强密码。pwds业务账号的加密列表格式是用户名:加密后的密码。Atlas不会直接把明文密码写在配置里而是用一个加密工具生成密码串再填到配置文件里。加密工具在Atlas安装目录下的bin/encrypt。daemon / keepalivedaemon配置控制Atlas是否以守护进程方式运行keepalive则控制进程异常退出时是否自动拉起。这两个我都是开着的保证服务崩溃后能自动恢复。2.3 读写分离的强制路由与注释透传有些场景比较特殊比如某个报表查询耗时很长你不希望它读从库担心把从库拖垮又或者刚写完一条数据立刻就要读到自己写入的内容而此时从库复制延迟还没追上。这时候Atlas提供了一种“注释携带路由”的玩法只要在SQL前面加上/master/这样的注释无论SQL是不是SELECT都会强制走主库。示例SQL/*master*/ SELECT * FROM order_info WHERE user_id 10086;应用里只要在关键SQL上加上这个注释就能打破默认路由规则。这个特性非常实用我经常用来处理“写后立即读”的场景避免因为复制延迟造成数据不一致。但要注意Atlas对注释的解析有要求强制走主库的注释必须放在SQL语句的最前面如果前面有空格或者其他注释可能导致解析失败。踩过这个坑所以建议在代码里做成常量统一拼接。2.4 分库分表能力的边界Atlas还支持一定的分库分表能力可以在配置里定义规则把不同的表分散到不同的后端实例上。不过说实话Atlas的分库分表能力比较初级适合简单场景比如按数字范围取模。一旦涉及复杂的分片键、跨库JOIN它就力不从心了。我的建议是这样的如果项目刚起步数据量在百万级、千万级读写分离加索引优化就够用了完全没必要上分库分表如果数据量到了亿级又需要透明分片那就应该考虑MyCat、ShardingSphere这类更专业的分片中间件。Atlas的分片功能只能作为一个轻量辅助不要把它当主力。3. 环境准备与实操部署全过程3.1 安装前的环境规划我在生产环境里部署Atlas之前通常会先画一张表把节点信息理清楚。一个比较典型的规划如下角色IP地址端口说明MySQL主库192.168.1.103306写节点MySQL从库1192.168.1.113306只读节点权重1MySQL从库2192.168.1.123306只读节点权重2Atlas节点192.168.1.201234应用接入入口管理端口192.168.1.202345运维管理入口这里有个经验之谈Atlas和MySQL最好分开部署不要混在一台机器上。Atlas本身虽然轻量但它要同时维护前端连接和后端连接系统资源占用并不低。如果和数据库抢CPU和内存两边性能都会受到影响。3.2 安装步骤与版本选择Atlas的安装包已经有几年没更新了但这并不妨碍使用很多生产系统跑得都非常稳。我用的版本是2.2.1这是个比较成熟的Release版本。安装包一般提供RPM和源码编译两种方式我习惯用RPM省事且方便卸载。安装命令示例这里用RPM包的方式rpm -ivh Atlas-2.2.1.el6.x86_64.rpm安装完成后默认安装路径通常是/usr/local/mysql-proxy/目录结构大致如下/usr/local/mysql-proxy/ ├── bin/ │ ├── mysql-proxy │ ├── encrypt │ └── ... ├── conf/ │ └── atlas.cnf ├── lib/ │ └── ... ├── log/ │ └── ... └── test/ └── ...如果你的系统是较新的Linux发行版安装旧版RPM可能会遇到依赖缺失的问题比如libmysqlclient版本不兼容。这时候可以尝试用源码方式编译安装或者找一下社区里适配新系统的补丁包。我在CentOS 7上装过一次需要手动安装兼容库才能跑起来。3.3 配置文件的编写与加密密码生成安装完毕后最重要的一步就是配置。我先把配置文件的完整示例贴出来然后一行一行说。[mysql-proxy] # Atlas后端主库地址 mysql-proxy-backend-addresses 192.168.1.10:3306 # Atlas后端从库地址及其权重 mysql-proxy-read-only-backend-addresses 192.168.1.11:33061,192.168.1.12:33062 # Atlas对外服务端口 mysql-proxy-address 192.168.1.20:1234 # Atlas管理端口 admin-address 192.168.1.20:2345 # 管理接口用户名和密码 admin-username admin admin-password admin123 # 业务账号加密后的密码 pwds user1:加密后的密码串 # 守护进程和自动拉起 daemon true keepalive true # 日志路径 log-path /usr/local/mysql-proxy/log # SQL日志开关建议开启便于排查问题 sql-log ON # 实例名称 instance atlas_1 # 字符集 mysql-proxy-charset utf8mb4生成业务密码的方法是进入bin目录执行encryptcd /usr/local/mysql-proxy/bin ./encrypt your_password执行后会输出一串编码过的字符串把它填到pwds字段里格式是用户名:加密后的字符串。这个加密是单向的配置文件里不会出现明文这是一个很好的安全习惯。注意配置文件修改后需要重启Atlas才会生效。重启前最好先执行mysql-proxy --defaults-file/usr/local/mysql-proxy/conf/atlas.cnf check做语法检查确认配置没问题再重启。3.4 启动Atlas并验证基本功能配置完成后启动命令如下/usr/local/mysql-proxy/bin/mysql-proxy --defaults-file/usr/local/mysql-proxy/conf/atlas.cnf启动后检查进程是否存活ps -ef | grep mysql-proxy看到进程存在后再用MySQL客户端连接Atlas的对外端口测试读写分离是否生效mysql -h 192.168.1.20 -P 1234 -u user1 -p登录后执行一条查询如果一切正常你会看到结果正常返回。接着在MySQL从库上开启通用日志或者用SHOW PROCESSLIST观察确认连接是从Atlas转发过来的。我用过一个小技巧在从库执行SHOW PROCESSLIST如果能看到来自Atlas IP的连接说明读流量确实走到了从库。3.5 管理接口的常用操作Atlas的管理接口非常实用日常上下线后端节点不用改配置重启。我总结了几条最常用的操作-- 登录管理接口 mysql -h 192.168.1.20 -P 2345 -u admin -padmin123 -- 查看后端节点状态 SELECT * FROM backends; -- 将某个从库下线不再接收新读请求 SET OFFLINE 2; -- 将某个从库重新上线 SET ONLINE 2; -- 动态添加从库 ADD SLAVE 192.168.1.13:33061; -- 动态移除从库 REMOVE BACKEND 3;这里的数字是后端节点的ID可以用第一条SQL查看到。这个管理接口是我最喜欢Atlas的地方配合自愈脚本可以在从库故障时自动把它踢出集群恢复后再加回来整个过程对业务完全透明。3.6 与业务系统的对接要点应用接入Atlas本质就是换一个连接地址和端口。但有几个细节一定要处理妥当不然上线容易出问题。第一连接池的initialSize和minIdle别设太大因为Atlas本身也维护连接池业务侧再搞几十个空闲连接资源就浪费了。我一般建议最小空闲连接控制在2~4个。第二JDBC连接串里不要启用自动提交优化。有些框架会默认把autoReconnect设为true这会让连接断开再重连时对业务无感知。但Atlas对后端MySQL的连接可能有状态上下文重连后事务状态并不稳定最好还是靠连接池的“借出前校验”来做健康检查。第三超时时间要调短。业务连接Atlas的connectTimeout和socketTimeout建议不要超过30秒因为中间件层出问题时如果应用侧一直傻等很容易引发雪崩。我踩过一次坑就是业务侧超时设置太长Atlas因为后端MySQL主库切换卡住连接结果应用线程全卡在获取连接上核心链路直接瘫痪。第四保留SQL前部的强制读主注释。如果业务里本来就用了/master/这种注释确认应用没有在中间截断或者转义掉。Atlas对注释的识别是硬解析注释被破坏等于路由失效。4. 常见问题与排障实战4.1 配置无误却连接报错检查字符集和密码加密遇到“Access denied”或者连接直接超时先别慌。我遇到最多的情况其实有两种一是pwds里的密码没有用encrypt工具加密直接填了明文导致认证不通过二是charset配置与后端MySQL不一致握手阶段就失败。排查方法很简单先看Atlas日志通常会有认证失败的记录。tail -f /usr/local/mysql-proxy/log/sql_log.log如果日志中提示字符集不对就把mysql-proxy-charset改成和后端MySQL一样的字符集重启后再试。如果是密码问题重新生成加密串注意每次生成的密文可能不同这是正常的直接把新密文替换进配置即可。4.2 读写分离没有生效查询全走了主库这是一个出现频率很高的问题。表现是主库负载很高但从库闲置。原因通常有这么几个SQL语句虽然以SELECT开头但在前面有显式事务比如Java代码里使用了TransactionalAtlas会把整个事务期间的SQL都路由到主库。业务代码里走了存储过程或者带有不确定结果的语句Atlas的SQL解析器无法识别可能默认路由到主库。配置文件里read-only-backend-addresses没有配置正确或者从库地址不可达Atlas自动放弃了读负载均衡。排查思路是先在管理接口执行SELECT * FROM backends;确认从库节点的状态为UP。如果节点正常再通过从库通用日志确认是否有查询到达。如果从库侧没日志那大概率就是事务问题或SQL解析问题。针对事务问题解决方案有两条一是业务代码检查是否真的需要事务二是对只读操作去掉事务注解。事务内读主库是合理的安全设计不建议为了性能强行把事务内查询踢到从库。4.3 主从切换后Atlas仍指向旧主库MySQL主从切换在生产环境是大事Atlas这边如果处理不好会导致写入失败。Atlas本身不感知主从角色变化所以你需要在切换后手动更新配置。我的处理流程是这样的通过管理接口把所有写流量暂时切到新主库上。修改atlas.cnf里的mysql-proxy-backend-addresses把新主库IP写进去。如果旧主库变成从库就把旧主库IP加入mysql-proxy-read-only-backend-addresses。重启Atlas使配置生效。更有经验的团队会写一个切换脚本把上述操作自动化。脚本逻辑大致是监控MySQL主从状态发现主库故障后自动触发切换然后调用Atlas管理接口动态调整后端节点列表最后校验Atlas是否成功感知到新节点。这类脚本不复杂但能省下很多半夜救火的功夫。4.4 性能瓶颈Atlas所在机器成了短板运行久了你会发现Atlas的CPU、内存占用会逐步上升。这背后通常是下面几个原因开启了sql-log日志高流量下日志写入消耗大量IO。连接数太多Atlas需要维护的后端连接池膨胀。后端慢SQL多所有等待都积压在Atlas连接池里。我的经验是给Atlas所在的中间件机器预留足够的CPU和内存资源通常至少2核4G起步。同时sql-log可以在生产环境关了或者按天轮转。慢SQL的治理应该在数据库侧做而不是依赖Atlas扛住所有压力。还有一个易被忽视的点TCP内核参数。Atlas作为代理会大量产生网络连接请求。我通常会在服务器上调整以下参数net.ipv4.tcp_tw_reuse 1 net.ipv4.ip_local_port_range 1024 65535这些调整能让TIME_WAIT的连接更快复用减少端口耗尽的风险。4.5 连接池报警Too many connections有时候业务方会反馈数据库连接数告警但你去MySQL看连接数并没有爆。这种情况往往是因为Atlas把后端连接池撑得太满。Atlas的后端连接池参数是默认的在高并发场景下可能会和后端MySQL的max_connections起冲突。我的建议是调小Atlas的并发连接控制同时在后端MySQL上把max_connections放宽。但不要无脑放宽连接数太多反而会拖垮MySQL。最理想的状态是定量分析一个应用实例50个线程部署了10个实例那Atlas到后端的连接总量控制在200~300之间就够了完全不用到1000以上。4.6 常见问题速查表问题表现可能原因快速处理办法应用连接Atlas超时Atlas未启动/网络不通/防火墙拦截检查进程telnet 端口测试认证失败pwds密码未加密或密码错误重新生成加密密文更新配置中文乱码charset配置不一致统一设为utf8mb4重启Atlas查询全部走主库事务内执行SQL/从库地址错误查看事务、检查backends节点状态主库切换后写失败Atlas仍指向旧主库修改配置文件或动态调整backend内存持续上涨sql-log开启/连接数过多关闭sql-log优化连接池参数5. 从Atlas迁移到其他中间件的一些体会人到后期总会考虑是不是要换掉Atlas。原因不是它不好用而是技术栈演进后需求变了。比如用了云数据库自带的代理或者引入了Kubernetes后希望有更灵活的Service Mesh方案又或者团队希望使用ShardingSphere这样能嵌入应用端的中间件。我在迁移过程中的体会是不要一次性替换最好灰度。因为Atlas对业务代码的侵入几乎为零你只是换了个连接入口。具体操作上可以先让一部分服务连新中间件观察一段时间确认没问题再全量切换。同时连接池里的URL要预留好回滚方案一旦新中间件出问题马上切回Atlas影响面要控制在最小。如果你问我什么时候该换、什么时候不该换我的标准是这样只要Atlas还能满足负载均衡和读写分离的需求且没有频繁的故障继续用完全没问题。但如果团队开始做分库分表、分布式事务那确实该换更专业的中间件了。工具应该是为业务服务的不是为“用新工具”而换。6. 运维心得与作业化建议6.1 状态监控和日志体系不要等出故障才去看Atlas日志。我建议从一开始就把监控和日志接入一体化运维平台至少要盯这几个指标Atlas进程是否存活。Atlas CPU使用率和内存占用。后端节点状态UP/OFFLINE/DOWN。读写流量比例如果发现写流量长期占比较高说明读写分离效果不佳。连接数曲线提前预警连接数逼近上限。Atlas的管理接口其实已经暴露了节点状态用脚本定期采集SELECT * FROM backends的结果就可以做到基础监控。日志方面如果开启了sql-log可以用它来做慢查询分析但注意控制日志量我建议至少按天切割保留7天就够。6.2 灾备与高可用方案的组合Atlas是个中间件不是高可用组件。一个成熟的方案是让前端的keepalived负责Atlas本身的故障切换两个Atlas节点一主一备共享同一个VIP。当主Atlas宕机备用Atlas自动接管VIP应用几乎无感知。然后再配合MySQL主从的MHA或者Orchestrator方案构建出一套全链路的数据库高可用体系。这套组合拳的关键在于运维脚本要提前写好预案要定期演练。不要等到真出事了半夜爬起来现查文档那体验我深有体会真的很酸爽。6.3 团队协作与知识沉淀Atlas作为一个开源中间件网络上的资料并不算多很多问题要靠自己踩坑总结。所以我特别建议团队内部建立一个“Atlas运维手册”把常用的管理命令、切换脚本、排障案例沉淀下来。新人来了不用从零摸索遇到问题先查手册再不行才去翻代码。我自己的习惯是每次踩坑都会顺手记到wiki里哪怕只有几行字例如“配置字符集不一致导致中文乱码”这种下次遇到直接搜索关键词就能定位。这种知识沉淀的价值在关键时刻比任何架构设计文档都管用。7. 写在最后的一个小经验如果只让我分享一条关于Atlas的实操心得我会说生产环境里一定要把sql-log和实时日志分开或者干脆默认关闭sql-log。很多人在排查问题时第一反应就是开sql-log结果高流量下直接把磁盘打满引发二次故障。日志是辅助手段不是排查的唯一途径优先用管理接口看节点状态用MySQL的general_log在从库侧抽样这样既不影响性能又能快速定位问题。Atlas这个项目也许不再是当今数据库中间件舞台上的主角但它的设计思路、运维理念放在今天依然值得借鉴。希望我这篇文章能帮你少走一些弯路如果你正在部署或者维护Atlas欢迎在评论区一起讨论踩过的坑和解决方案。