☰
用Atlas实现MySQL读写分离:部署、配置、调优与排障实录
2026/10/21 18:01:35 网站建设 项目流程

最近有朋友问我,现在的业务量不大不小,想给MySQL做读写分离又不想在应用层引入太重的东西,到底怎么选。我第一反应还是那个老牌方案:Atlas代理。虽然它已经好几年没有大的版本更新了,但它在MySQL协议解析、读写分类、从库负载均衡这些方面做得非常直接,配置相对简单,很多团队至今还在拿它当数据库前端的访问入口。这篇就按我在生产环境里折腾Atlas的经过,把读写分离的原理、部署、配置、调优和排障一次讲清楚,希望给正在纠结方案的朋友一个参考。

Atlas不是数据库本身,也不是ORM框架,它更像一个架在应用和MySQL中间的“透明转发层”。应用连接它的时候,它就是数据库;它连后端MySQL的时候,它又是一个标准客户端。读写分离、多从库负载均衡、IP白名单这些能力,我们在应用层不用写任何代码,直接把数据库地址指向Atlas就行。

1. 为什么选Atlas做读写分离入口

1.1 客户端视角的“假MySQL”

Atlas本质上是个MySQL反向代理。它监听一个端口,应用层用普通的MySQL驱动连上去,执行的SQL会被Atlas解析,然后根据规则转发到后端的真实数据库节点。

这个“假MySQL”的设计有很多好处:应用不需要换驱动,JDBC、mysql-client、各种连接池都能直接连;不需要改应用代码,原来怎么连MySQL,现在就怎么连Atlas;也不需要在每个应用里单独实现读写分离逻辑,Atlas统一处理掉了。

我在实际项目中体会最深的一点是,Atlas对底层架构的侵入非常小。数据库扩容、从库上线、从库摘除,这些操作都集中在一个代理配置文件里搞定,应用侧完全无感知。对于团队规模不大、又不想专门维护一套数据库中间件平台的情况,这种“轻代理”反而比很多重框架更省心。

1.2 为什么它在读多写少的场景里很实用

大多数互联网业务的SQL占比都是读远大于写,查多改少。读写分离的核心思路很简单:把只读流量压到从库上,主库只承担写流量和少量敏感读,从而降低主库负载。

Atlas天然就是为这个场景设计的。它提供了明确的“主库地址池”和“只读地址池”配置,从库可以挂多个,代理层内部做轮询或带权重的负载均衡。在我经手的项目里,一个Atlas实例后面挂一台主库、两三台从库是很常见的组合。

相比自己写脚本基于HAProxy做流量分发,Atlas的调度粒度要细得多,它不是盲目转发网络包,而是能识别SQL类型,把SELECT分到从库、把写操作卡在主库。这一点非常关键,因为很多网关节点在TCP层面根本看不到SQL语句,也就做不了这么细的路由决策。

另外,Atlas还带了管理接口。通过一个普通MySQL客户端连上去,就能查看各个后端节点的状态,还能把某个故障从库临时设置为离线状态,不需要重启进程就把坏节点摘掉。这个能力在出故障时特别好用。

2. 读写分离的判定逻辑与实际配置

2.1 主从库流量是怎么被分开的

Atlas判断一条SQL该走主库还是走从库,内部有一套规则。理解了这套规则,很多疑难问题就能迎刃而解。

先说结论:普通的SELECT语句默认走从库;INSERT、UPDATE、DELETE、DDL等写语句一律走主库;在事务中执行的SELECT也会被路由到主库,因为事务一旦开启,状态就绑定在特定连接上了,中间禁止再切换后端节点。

还有几种特殊情况。比如SELECT语句带FOR UPDATE或LOCK IN SHARE MODE,这属于带锁的读,必须走主库,否则在主从复制延迟下会读到旧数据,锁也失去了意义。再比如通过/*master*/注释强制走主库的SQL,这是Atlas给我的一个非常实用的逃生通道,后面我会细说实际用法。

读流量会自动在从库之间做负载均衡。多个从库一起挂在Atlas后面,它会按权重轮询分配,权重在配置里用@数字表示,数字越大分配到的连接越多。默认情况下权重相同,就是普通轮询。写操作则始终落在主库上,不存在跨库写的问题。

2.2 配置项别只看字面意思

Atlas的配置文件是一个INI风格的文本文件,以[mysql_proxy]作为节开头。初次上手时,最核心的几个配置项要搞清楚:

  • proxy-address:Atlas对外提供服务的地址和端口,应用连的就是这个。
  • proxy-backend-addresses:主库后端地址,写操作全部转发到这里。
  • proxy-read-only-backend-addresses:从库后端地址,多个地址用逗号分隔,可带权重。
  • admin-address / admin-username / admin-password:管理接口的地址、账号、密码,用MySQL客户端登录后执行管理命令。
  • pwds:客户端账号和加密后密码的映射关系,格式是用户名:密文。
  • event-threads:代理的工作线程数,一般设置为CPU核数的2倍左右。
  • log-level / log-path:日志级别和日志目录。
  • sql-log:是否开启SQL日志,开全量日志很快会占满磁盘,生产环境建议只开慢日志。

这里有一个容易踩的坑:pwds里的密码不是明文。Atlas安装包里会带一个加密工具,你要先用它对密码加密,再把密文填到配置里。直接用明文会连不上,而且这个问题报错时不太直观。

2.3 一份能直接抄作业的配置文件

以我常用的模板为例,假设主库在192.168.1.101:3306,从库两台分别是192.168.1.102和192.168.1.103,对外提供服务端口是1234,管理端口是2345:

[mysql_proxy] proxy-address = 0.0.0.0:1234 proxy-backend-addresses = 192.168.1.101:3306 proxy-read-only-backend-addresses = 192.168.1.102:3306@1,192.168.1.103:3306@2 pwds = reader:加密后的密文串 admin-address = 0.0.0.0:2345 admin-username = admin admin-password = admin daemon = true keepalive = true event-threads = 8 log-level = message log-path = /data/mysql-proxy/log sql-log = OFF sql-log-slow = ON sql-log-slow-ms = 200

注意我把从库权重配成了1:2,也就是第二台从库分到的读流量大约是另外一台的两倍。如果你两台从库配置一样,就把权重去掉或保持一样,否则流量会不均匀。

daemon和keepalive两个参数建议同时打开。daemon=true表示后台运行,keepalive=true表示主进程异常退出后自动拉起。这两个参数在生产环境里非常重要,别省。

3. 部署实录:从安装到第一条SELECT落地

3.1 环境准备与安装

Atlas的安装其实不复杂,我建议直接下载官方社区编译好的二进制包,放在/usr/local/mysql-proxy/目录,比从源码自己编译省事得多。二进制包解压后的结构大概是:bin/放启动程序,conf/放配置文件,lib/放加密和辅助脚本。

依赖方面,它不需要额外装什么特殊环节,运行在普通Linux服务器上,要求系统能跑常见的glibc程序即可。不过配置文件的编码格式要留意,建议统一用UTF-8保存,避免中间注释里的中文变成乱码导致解析异常。

安装完成后,先建日志目录并授权:

mkdir -p /data/mysql-proxy/log mkdir -p /data/mysql-proxy/conf cp /usr/local/mysql-proxy/conf/proxy.cnf /data/mysql-proxy/conf/

我习惯把配置和日志放到独立的数据盘路径,避免和系统盘混在一起。这样一旦系统盘出问题,代理日志和配置还在。

3.2 密码加密这一步别省

前面说过,pwds里不能直接写明文。Atlas自带的加密工具一般放在安装目录的lib/或bin/下面,比如有一个可执行文件叫encrypt。执行方式类似:

/usr/local/mysql-proxy/lib/encrypt MyPlainPassword

执行后会输出一串密文,还有一个类型标识。你只需要把密文部分填到配置文件的pwds里,例如:

pwds = reader:XXXXXXXXXX

之所以强调这一步,是因为我见过不止一次有人把明文写在配置里然后反复排查为什么连不上。这个加密是有意设计的,不是为了操作复杂度而复杂。数据库密码这类敏感信息如果以明文存在配置文件里,一不小心被并发出去或者被日志收集走,风险很大。

3.3 启动、验证、管理接口

启动Atlas直接指定配置文件路径就好:

/usr/local/mysql-proxy/bin/mysql-proxy --defaults-file=/data/mysql-proxy/conf/proxy.cnf

启动之后先别急着让应用连过来,先用管理接口和普通SQL客户端各验证一遍。

管理接口本质上就是一个MySQL兼容端口。用mysql命令登录:

mysql -h127.0.0.1 -P2345 -uadmin -p

登录后执行:

SELECT * FROM backends;

这个命令能看到当前所有后端节点的地址、角色、状态。如果主库和从库都显示online,说明后端连通性正常,配置生效了。

接下来做一次读写分离验证。随便找一台能连Atlas端口1234的机器,创建一张临时表,插入几条数据,再执行一条SELECT。开了慢SQL日志或全量SQL日志的话,去日志里确认SELECT语句究竟落在哪台后端上。如果配置没问题,写操作的日志会显示连到主库,读操作会显示连到从库。

我强烈建议在正式交付给应用之前,把这套验证流程完整做一遍,不要图省事。代理层最大的风险是“配置看着没问题,但流量实际走错库”,这种问题一旦上线后再发现,排查成本远高于提前验证。

4. 调优与踩坑:连接数、日志、主从延迟

4.1 event-threads到底开多少

Atlas是事件驱动模型,event-threads这个参数类似于工作的线程数。开太少,高并发下请求排队,延迟上不去;开太多,上下文切换变频繁,性能反而下降。

我习惯的基准是:先按CPU核数的2倍设置,比如8核就设16,然后做一轮压测看P99延迟和Atlas进程的CPU占用率。如果CPU已经打满,而延迟还在可接受范围内,说明瓶颈在计算,可以适当提高线程数;如果CPU不高但延迟明显,可能不是Atlas本身的问题,需要检查后端MySQL的max_connections、慢查询和网络链路。

同时要注意,Atlas和后端MySQL之间的连接数也是有限的。应用端的连接池如果开得特别大,所有连接穿过Atlas到达后端,会成倍消耗MySQL的连接数。我的习惯是在应用连接池里把最大连接数限制在合理范围,比如单实例20到50个连接,比盲目加线程更管用。

4.2 SQL日志别随便全开

Atlas支持SQL日志,分为全量日志和慢日志两类。全量日志会把每一条经过代理的SQL都写下来,好处是方便排查问题,坏处是日志量增长非常恐怖。我见过一台读写流量并不算大的实例,开了全量日志之后,一天就写了好几个GB的日志。磁盘告警不说,日志写入本身也会拖慢进程。

生产环境我建议只开慢日志。把sql-log设为OFF,把sql-log-slow设为ON,再通过sql-log-slow-ms设定阈值。比如阈值设为200,表示超过200毫秒的SQL才会被记录下来。这样既能捕捉到性能异常,又不会让日志淹没磁盘。

慢日志出现大量某类SQL时,不要只看SQL本身,还要结合具体后端节点确认是主库慢还是从库慢。如果一条查询在从库上很快,但日志记录的总时间很长,问题可能出在Atlas到从库的网络或后端连接池复用上。

4.3 主从延迟怎么兜底

Atlas做的是读写分离,但它本身不负责主从复制,也不检查从库的数据是否已经追平主库。如果业务对“写入后立即读到”有强要求,而主从复制延迟又明显,直接走默认的读写分离就会读到旧数据。

我的处理手段有以下几种:

  • 关键业务SQL手动加/*master*/注释,强制走主库,比如读刚插入的订单详情、读取最新余额。
  • 把允许延迟的报表类查询留在从库,从库即使落后几十毫秒也无所谓。
  • 在从库上做复制状态监控,如果Seconds_Behind_Master超过设定阈值,通过监控脚本把对应从库在Atlas管理接口中置为离线,直到修复再上线。

这里特别说明一下/*master*/的用法。这条注释要放在SQL的最前面,比如:

/*master*/ SELECT * FROM orders WHERE user_id = 100

加了这条注释之后,即使是一条SELECT,Atlas也会强制把它转发到主库。我建议把这个用法写进团队的SQL规范,让研发在需要时主动标注。

Atlas本身不做主从切换,这也需要提前想清楚。主库宕机之后,代理不会自动把某一台从库提升为新主库,你需要配合数据库高可用方案来决定新的主库是谁,然后在配置里把proxy-backend-addresses改成新主库地址并重启或热更新。这个边界一定要和团队讲明白,不要误以为Atlas连高可用也一起解决了。

5. 常见问题排查实录

5.1 客户端报告登录被拒

大概率是pwds里的密码加密串和实际密码对不上。重新生成一遍密文,替换配置里的值,然后重启Atlas。另外检查一下proxy-address是否设置为0.0.0.0:1234,如果只写了127.0.0.1,远程客户端自然连不上。

5.2 所有SELECT都打到主库

这种情况多半是应用连接开启了事务或者自动提交被关闭。可以先看一下应用侧的连接池配置,确认每次请求后连接是否正常回滚或提交。还有可能是连接被某个长事务占着,事务没结束,Atlas看到的连接一直处于事务中,后续所有SQL都只能走主库。

另外一个容易被忽略的点是:有些SQL虽然是SELECT开头,但Atlas识别到它会访问系统变量或执行某些非确定函数时,路由策略可能更保守。遇到这种情况,我选择直接给SQL加/*master*/或/*slave*/策略相关注释做人工指定,不纠结于它内部怎么判断。

5.3 从库偶尔掉线或状态变为异常

从库掉线时,Atlas管理接口里对应节点的状态可能会变成异常。先确认MySQL进程是否存活,复制线程是否正常,然后在管理接口执行SET ONLINE 'IP:端口'把节点重新放回池子。如果某个从库频繁掉线,重点检查它的max_connections是否被打满,以及复制延迟是否导致SQL线程停止。

5.4 改了配置后不生效

配置文件修改之后,Atlas默认不会热加载大部分参数。改配置后一定要确认进程是否重启成功,可以看日志目录下的运行日志和进程列表。另外,配置文件名和路径要和启动命令里的参数一致,我见过有人改了别处的副本,进程里跑的还是老配置。

5.5 日志越来越大,磁盘告警

先确认是不是开了全量SQL日志。如果只是慢日志,再看看慢日志阈值是不是设得太低。sql-log-slow-ms如果是10甚至1,很多本来很正常的几十毫秒查询也会被记下来,长期积累也很可观。建议先设200,观察几天再按需调低。

现象可能原因处理办法
客户端连接被拒密码密文配置错误重新生成加密串并替换
全部读流量走主库连接处于事务中或autocommit被关闭检查应用连接池和事务边界
从库状态异常从库过载或复制中断排查MySQL状态后手动置为ONLINE
配置变更不生效进程未重启或配置文件路径不对核对启动参数并重启Atlas
磁盘日志暴涨开了全量SQL日志改为只开慢日志并调整阈值

一点实际操作后的体会

Atlas值得用的前提是你已经清楚它的边界:它能做读写分离、从库负载均衡、IP过滤,但它不做主从复制、不做自动主从切换、不做数据一致性校验。把这些边界研究明白之后,Atlas确实可以减轻不少工作量。

我个人建议在Atlas前端再加一个VIP或者域名,不要直接让应用记死IP地址。后续不管是Atlas节点替换,还是整体切换新机器,应用侧只要改DNS或者VIP即可,不用重新发布。还有一个小习惯:我每次改配置时都会先备份旧配置,并顺手记录本次改动的目的和时间。中间件这东西,出问题时最怕的不是改错,而是不知道上一次改了什么。

如果你也在做读写分离选型,不追求功能大而全,而想要一个简单、直接、能透明接入的MySQL协议代理,Atlas仍然值得试一把。先在一个低流量实例上把原理和配置跑通,再逐步放大到生产环境,它大概率不会让你失望。

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

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

立即咨询