资讯动态

MySQL Atlas中间件实战:读写分离与分库分表架构解析

发布时间:2026/10/19 9:27:12 来源:尧图企业网站定制
看到“atlas”这个标题我第一反应不是希腊神话里扛天的泰坦也不是波士顿动力那个会后空翻的机器人而是数据库圈子里一个相当经典的老伙计——MySQL Atlas。这是奇虎360在2012年开源的一个基于MySQL协议的数据库中间件核心做两件事读写分离和分库分表。可能年轻一点的开发都没听过它但在我早年做电商系统重构的时候Atlas真的是撑过不少大流量的场面。今天这篇就围绕Atlas聊聊它的设计思路、部署方式以及我实际运维中踩过的那些坑。如果你正在维护一个读写比很高、主从架构已经搭好但还没有中间层的MySQL集群或者你想搞清楚“应用层直连主从切换麻烦、读压力分散不均”这类问题该怎么解这篇文章应该能给你一些可直接落地的参考。哪怕是刚接触数据库中间件的新人把Atlas跑通一遍对理解整个数据库访问链路也会有很大帮助。1. Atlas是什么为什么数据库架构里需要它1.1 一句话认识AtlasAtlas是奇虎360基于MySQL Proxy源码二次开发出来的一个中间件位置处在应用和MySQL实例之间对应用层暴露一个标准的MySQL协议端口。应用连Atlas就跟连一个普通的MySQL实例一样不需要改代码不需要换驱动连接串改个端口就行。它最核心的职责有三个把写请求路由到主库把读请求按权重分发到多个从库以及把逻辑大表拆成多个物理小表来降低单表数据量带来的性能压力。用一句话总结就是让应用层无感知地访问一个“虚拟MySQL”实际背后站着一组真实的主从集群。1.2 没有中间件时读写分离是怎么做的先看一个典型场景。你的业务有1000个并发请求其中850个是查询150个是写操作。数据库有一主两从主库负责写入。没有中间件的时候读写分离通常有三种搞法第一种是应用层硬编码在代码里封装两个数据源读走从库、写走主库。这个方案最直接但问题也很明显哪天从库扩到5台你要改代码某台从库挂了你还要改代码做摘除。而且多个业务团队各搞一套规则散落各处后期维护非常痛苦。第二种是借助Spring的AbstractRoutingDataSource这类动态数据源路由通过AOP拦截方法名判断读还是写。这种方式比硬编码好一点但本质上还是应用层逻辑SQL一复杂就容易判断失误。第三种就是引入独立的数据库中间件由中间件来解析SQL、识别读写语义、完成后端节点的负载均衡和故障摘除。Atlas就是这个角色。应用层不需要知道背后到底有几台数据库也不需要关心哪台挂了中间件会把这一切都挡在外面。第三种方案的优势在于“分流逻辑下沉”。数据库架构的变更从此不需要跟着应用版本走DBA自己就能在中间件层面完成扩缩容。这对一个数据库实例几百上千套的中大型公司来说是刚需。1.3 Atlas在整个架构里的位置一个典型的Atlas部署架构大概是这个路子LVS或Keepalived在最前面做虚拟IP漂移后面挂两台Atlas节点一主一备或双活每台Atlas再通过配置连接后端的主库和若干从库。应用只连接LVS的虚拟IP虚拟IP后面的实际节点变化对应用完全透明。Atlas本身是单进程多线程模型不依赖第三方组件部署起来就是解压、改配置、启动三步。它的轻量是当时能火起来的一个重要原因。和同一个时代的Cobar、MyCat这类重一些的分布式数据库中间件相比Atlas更偏向于“解决主从读写分离和单库分表”而不是一开始就奔着“分布式事务、全局序列”那种大而全的方向去。选型的核心逻辑就是你的问题域是什么就选解决这个问题的工具。2. 核心设计解析读写分离、分库分表与连接管理2.1 读写分离SQL级智能路由Atlas的读写分离实现思路其实不复杂它解析客户端发过来的SQL语句判断语句类型然后决定把这条SQL发给主库还是从库。判断规则大致是这样的SELECT语句默认走从库但如果语句里带有FOR UPDATE或LOCK IN SHARE MODE这种显式锁语义就必须走主库因为从库是异步复制可能还没拿到最新的行锁状态。INSERT、UPDATE、DELETE、REPLACE、DDL等写操作一律走主库。事务内的读操作Atlas会确保在同一个后端连接上执行。也就是说你在一个事务里先UPDATE再SELECT那这条SELECT不会跑到从库去因为事务隔离级别要求读到本事务的修改走从库就会出现“自己写的数据自己读不到”的问题。这里面最关键、也最容易被忽略的是最后一点。我在早期使用Atlas的时候就遇到过业务方反馈“刚更新的数据刷新不出来”排查到最后发现是他们把多个查询塞进了同一个Spring声明式事务里而这些查询被Atlas路由到了从库。事务内读操作必须和后端写连接绑定这是中间件路由必须守住的底线。2.2 分库分表规则引擎怎么工作Atlas的分表能力是它区别于很多纯读写分离方案的亮点。它支持两种分表方式column分表和range分表。column分表的配置格式是表名.分表键.子表数量比如tables orders.id.0-9这个配置的含义是逻辑表orders按id字段分成10张物理子表orders_0到orders_9具体路由到哪张子表由分表键的值计算哈希后取模得出。子表数量的写法是0-9代表10张数量可以自定义。range分表则适合按时间或按区间数据的场景比如订单归档表按月分表配置里直接指定每个区间对应的子表名。这种方式的好处是查询条件只要带上分表键中间件就能精确地只去对应的子表拿数据不必全表扫描。这里有个很重要的点分表键必须出现在SQL的WHERE条件里否则Atlas无法确定路由目标。拿上面的orders表举例如果一条SQL是SELECT * FROM orders WHERE user_id 123id字段没出现在条件里Atlas就只能把这条SQL广播到所有10张子表再把结果合并返回。虽然功能上没问题但性能会非常难看。所以设计分表键的时候一定要想清楚业务上最核心的查询维度是什么然后拿那个维度做分表键。2.3 连接池与线程模型Atlas作为一个中间件本身必然要面对两个连接维度应用侧连接和后端数据库连接。应用侧连接就是客户端连上来的连接。Atlas默认配置里可以通过max-conn参数限制最大连接数超过的部分会排队或直接拒绝起到一个“连接闸门”的作用保护后端数据库不被突发的连接风暴打垮。这个参数非常有用我曾经见过因为应用连接池配置失误导致瞬间建立几千个连接直接把数据库打挂的案例。有了Atlas这层限制至少还有一个缓冲地带。后端连接池是Atlas自己维护到主库和从库的连接。它会在启动时预建一批连接执行SQL时从池里取执行完归还。需要注意event-threads这个参数它控制Atlas处理网络事件的线程数一般建议配置成CPU核心数的2倍。线程数太小会导致CPU利用率上不去太大则在线程切换上浪费过多时间这个参数需要结合压测结果来调。2.4 字符集、状态监控与管理接口Atlas还提供了一个独立的admin管理端口。默认情况下admin-address配置成0.0.0.0:2345用MySQL客户端连上去之后可以通过几条SQL命令管理整个集群SELECT * FROM backends;这条命令会列出所有后端节点的状态包括主库、从库的地址、type类型rw还是ro、state状态up还是down以及当前的连接数。生产环境做故障切换时我第一步永远是跑这条命令看后端状态。下线一台从库的指令是SET OFFLINE 192.168.1.11:3306;上线就是SET ONLINE 192.168.1.11:3306;这个“平滑上下线”能力很实用。比如从库要做硬件升级如果直接kill掉数据库进程正在通过Atlas执行查询的应用会立刻报错。但通过Atlas先把节点置为OFFLINEAtlas会停止向这个节点发新的请求等已有请求跑完后再去做底层维护就安全得多。字符集方面配置里的charset参数要和后端MySQL保持一致否则容易出现中文乱码这个后面实操部分细说。3. 从零搭建一套Atlas读写分离环境3.1 环境规划与主从同步前置动手部署之前先把环境规划清楚。我这边以一个典型的业务系统为例角色IP地址端口说明MySQL主库192.168.1.103306负责写入MySQL从库1192.168.1.113306负责读取权重1MySQL从库2192.168.1.123306负责读取权重1Atlas节点192.168.1.201234/23451234为客户端入口2345为管理入口前提条件是主从集群已经搭建好从库的数据和主库保持同步。Atlas本身不负责同步数据它只管把SQL路由到对的节点上。如果主从同步是坏的Atlas再怎么路由也没有意义。主从同步的搭建这里不展开太多只说一句务必确认同步线程是正常的。启动Atlas之前在从库上执行SHOW SLAVE STATUS\G看到Slave_IO_Running: Yes和Slave_SQL_Running: Yes两个都是Yes再往下走。3.2 安装与配置文件解析Atlas的安装非常简单从官方或镜像站下载对应操作系统的安装包解压到你想要的目录即可。假设解压到/usr/local/mysql-proxy核心的配置文件是/usr/local/mysql-proxy/conf/mysql-proxy.conf。一份典型的读写分离配置长这样[mysql-proxy] admin-username admin admin-password admin123 proxy-backend-addresses 192.168.1.10:3306 proxy-read-only-backend-addresses 192.168.1.11:33061,192.168.1.12:33061 pwencrypt true daemon true keepalive true event-threads 8 proxy-address 0.0.0.0:1234 admin-address 0.0.0.0:2345 charset utf8 tables orders.id.0-9 sql-log /usr/local/mysql-proxy/log/sql.log逐个说下关键参数proxy-backend-addresses是主库地址。proxy-read-only-backend-addresses是从库地址列表多个从库用逗号分隔符号后面跟的是权重值。权重默认是1如果你有一台从库配置特别高可以设成2或更高Atlas会按权重比例分配读请求。这个权重参数我在压测环境里验证过2的节点收到的读请求量大约是权重1节点的两倍计算逻辑是按权重做轮询。pwencrypt是密码加密开关。配置成true之后首次启动Atlas会把配置文件里明文的管理员密码和proxy-backend-addresses中涉及的用户密码自动加密写回配置。这是一个很好的安全习惯避免配置文件泄露后数据库密码直接裸奔。daemon是守护进程模式keepalive是崩溃自动拉起。这两个在生产环境必须开。sql-log会把经过Atlas的所有SQL记录到日志文件类似于MySQL的通用日志。注意这个是全量日志生产环境开启后日志增长非常快建议只在排查问题时临时开启平时保持关闭。3.3 密码加密、启动与验证启动之前需要先在后端MySQL上建一个专用账号让Atlas用它去连接主库和从库CREATE USER proxy192.168.1.20 IDENTIFIED BY proxy_pass; GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO proxy192.168.1.20;这里的权限只需要业务账号需要的增删改查权限即可不需要给超级管理员权限。配置里如果涉及账号密码可以通过Atlas自带的加密工具来生成密文。启动Atlas/usr/local/mysql-proxy/bin/mysql-proxy --defaults-file/usr/local/mysql-proxy/conf/mysql-proxy.conf启动之后先确认进程在不在ps -ef | grep mysql-proxy再确认端口监听netstat -tlnp | grep -E 1234|2345两个端口都监听上了基本就是起来了。接下来用MySQL客户端连一下Atlas的代理端口mysql -h192.168.1.20 -P1234 -uproxy -pproxy_pass能正常连上并执行SELECT 1说明客户端链路通了。再连管理端口看后端状态mysql -h192.168.1.20 -P2345 -uadmin -padmin123执行SELECT * FROM backends;如果看到主库的state是up两把从库的state也都是up整个读写分离链路就通了。3.4 客户端接入与应用改造应用接入非常简单只需要改数据库连接地址和端口其他全部不变。比如原来的JDBC连接串是jdbc:mysql://192.168.1.10:3306/business_db改成jdbc:mysql://192.168.1.20:1234/business_db一个比较容易被忽略的细节是应用侧最好把连接池的testOnBorrow或testWhileIdle中的校验SQL设为SELECT 1因为Atlas的路由逻辑是针对标准MySQL协议做的一些奇怪的连接校验语句有概率被错误路由虽然概率不大但碰到了很莫名其妙。业务代码完全不需要感知背后有几台数据库。写操作自动落在主库读操作自动分摊到从库这个过程中应用一个字节都不用改。4. 部署到生产前必须知道的事4.1 分库分表的边界与坑如果你的数据量已经大到单表扛不住那Atlas的分表功能可以帮你顶一阵子但有几个边界要提前想清楚。第一个是分表键的选择。前面说过分表键必须在WHERE条件里才能精确定位子表。一个常见的反面案例是订单表按订单ID分表但业务上最频繁的查询却是按用户ID查订单列表。那这条查询就只能广播到所有子表再合并体验非常差。正确做法是设计的时候就考虑“用户-订单”这个主查询路径按用户ID分表或者用关联表对分表键做映射。第二个是跨分表的JOIN和子查询。Atlas对跨子表的JOIN支持得并不好一旦SQL里出现了多张分表的关联操作它没法像真正的分布式数据库那样做复杂的执行计划。遇到这种情况通常的解法是冗余字段或者在上游服务里做数据拼装而不是指望中间件帮你智能处理。第三个是分表后的自增主键问题。如果一张表分成了10张子表每张子表的自增ID都是各自独立的0到9号子表都可能产生ID100的数据全局唯一性就没法保证了。Atlas不提供分布式ID方案生产上我们一般用雪花算法或者独立的ID发号器来生成业务主键再写入分表字段。4.2 主从延迟与读一致性读写分离最大的天敌是主从延迟。MySQL的主从复制是异步的主库刚写完从库可能立刻读不到这个时间窗口短则几十毫秒长则几秒甚至更久。Atlas本身没有智能的“延迟感知”能力。它不知道某个从库当前落后主库多少秒它只知道把这台从库标记为up。所以一旦业务对数据一致性要求比较高比如用户下单后马上要看到订单详情就得在架构层面做妥协。我在实际项目中用过两种解法。一种是在业务上做区分对实时性要求高的读操作在SQL里加注释标记强制走主库。Atlas支持通过注释来指定路由具体格式大概是在SQL前加上特定格式的注释中间件识别注释后会把这条SQL固定发到主库。这种做法简单粗暴但需要业务开发配合属于“人肉一致性保障”。另一种是把写后立即读的场景在代码里加一个短暂的延迟重试机制比如写完主库后先查一次主库查不到再走从库。这种方案实现起来稍微麻烦一点但对业务无侵入比改SQL注释更干净。4.3 高可用与故障切换单台Atlas本身也有单点风险。虽然Atlas进程很轻占用的系统资源不多但一旦进程挂了所有依赖它的数据库访问都会断掉。所以在生产环境Atlas前面必须再接一层高可用。最典型的做法是两台Atlas节点配合Keepalived对外提供一个虚拟IP。正常情况下虚拟IP绑定在节点A上A挂了之后Keepalived把虚拟IP漂移到节点B。前端应用只连虚拟IP完全不感知后端节点切换。需要注意Atlas本身不保存什么状态切换基本是无风险的。它的配置文件是静态的后端节点的上下线状态可以通过管理端口动态修改。这意味着节点B只要部署了相同的配置接管后就能直接服务不用做数据同步。如果还需要进一步降低风险可以考虑在Atlas前面再加一层DNS轮询或者负载均衡器把流量分摊到多个Atlas节点同时配合节点健康检查。不过大部分业务场景下两台Atlas加Keepalived已经完全够用。4.4 性能调优与监控Atlas的性能调优核心围着三个参数打转event-threads、max-conn和后端连接池相关的参数。event-threads前面提过建议从CPU核数的2倍起步。实践下来8核机器配置16是比较稳的起点然后压测观察响应时间和CPU利用率。如果CPU已经跑满但吞吐上不去可以把线程数再加4试一轮如果CPU还有余量但延迟已经很高则要考虑是不是后端数据库瓶颈而不是Atlas的问题。max-conn这个参数要结合应用侧的连接池规模来算。假设有20个应用实例每实例连接池最大配置5020×501000那Atlas的max-conn至少要设置成1200到1500留点余量。如果设小了高峰期会直接拒绝新连接应用侧表现为获取连接超时这种故障排查起来比后端数据库慢很多因为问题出在中间层。监控方面Atlas自带的管理端口就是最简单的监控入口。定期执行SELECT * FROM backends观察每个后端节点的连接数如果某个从库的连接数持续明显高于其他从库就要检查是不是权重配置不合理或者某台从库执行了特别慢的SQL被“卡住”导致连接不释放。还可以配合SELECT * FROM help看相关管理命令大部分运维操作都能通过管理口完成不需要重启Atlas进程。5. 常见问题与排查技巧实录问题现象可能原因排查思路与解法应用读写正常但某些查询结果明显过期主从延迟导致读到了旧数据在从库执行SHOW SLAVE STATUS看Seconds_Behind_Master必要时对该类查询强制走主库刚插入的数据查询不到SELECT被路由到了从库而从库复制还没完成确认该SQL是否在事务内事务内路由已绑定主库事务外需要业务配合或使用注释强制路由连接Atlas超时max-conn设置过小统计应用实例数乘连接池大小重新计算并调整max-conn某一子表数据特别多其他子表很少分表键值分布不均检查分表键的取值空间如果存在热点键考虑换分表键或加一层映射中文乱码Atlas和MySQL的字符集不一致确认Atlas配置里charset和后端MySQL实例的character_set_server一致同时JDBC连接串加上characterEncodingutf8从库被误下线后不会自动恢复下线操作是手动触发的不会自动回滚通过管理端口执行SET ONLINE重新上线同时确认从库本身状态正常我在实际运维里踩过最深的一个坑是有一次对从库做配置变更直接在数据库层面STOP SLAVE忘了先通过Atlas把节点置为OFFLINE。结果就是所有读到这台从库的业务瞬间报错排查了很久才定位到是Atlas还在往这台“假up”节点发请求。自那以后我定了一条规矩对后端数据库节点的任何维护操作第一步必须是先在Atlas管理端口把它置为OFFLINE等操作完成且验证通过后再置为ONLINE。这应该成为所有Atlas运维人员的肌肉记忆。还有一个我觉得特别值得分享的经验是SQL日志的“黑盒取证”用法。有一次业务方反馈某时段数据库压力飙升但业务量并没有明显增长。我们一开始怀疑是慢查询翻了一圈MySQL慢日志也没发现明显异常。后来想到Atlas有sql-log能力临时把这个参数打开跑了十分钟就抓到了真凶某个定时任务在循环里对一张大表执行没有分表键的模糊查询每次都要广播到所有子表等于一次查询放大了10倍压力。这就是中间件在你和数据库之间加了一层“可观测性”的价值——没有这层你连问题在哪都找不到。6. 一些关于选型的实在建议最后聊点我个人对Atlas在当前技术环境下的看法。Atlas毕竟是一个十多年前诞生的项目后期维护节奏放缓它在一些新特性上确实跟不上。比如对MySQL 8.0某些新认证插件和协议特性的支持不够完整对当前流行的容器化部署也不太友好。如果你的团队是全新的MySQL 8.0集群又没有历史包袱我可能会建议优先考虑官方MySQL Router或者ShardingSphere这类仍在活跃迭代的方案。但如果你遇到的场景和我当年一样一套MySQL 5.7主从集群需要快速解决读写分离和单库分表问题团队里DBA资源又紧张那Atlas依然是一个非常值得考虑的轻量级选择。它配置简单、文档多、社区里趟过坑的人也多出了问题搜一下基本都有答案。我的态度是工具没有绝对的新旧好坏关键看它和你的问题是否匹配。把Atlas用明白你对数据库访问链路的理解会比那些直接上手云数据库托管服务的人深得多。毕竟中间件替你解决的那一层问题终究是你系统架构里真实存在的一部分。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑