资讯动态

数据库中间件Atlas实战:读写分离、分库分表与连接管理全解析

发布时间:2026/10/19 16:18:43 来源:尧图企业网站定制
最近在整理一套老系统的数据库访问链路项目代号就叫 Atlas。这套东西本质是一个放在应用和 MySQL 之间的访问中间层用来统一解决读写分离、分库分表、连接管理这些烦心事。你可能也遇到过类似情况业务量上来之后单库单表扛不住应用层到处写死数据库地址一到大促就要临时拆库代码改到想骂人。Atlas 要做的就是让应用侧只认一个入口真正落在哪台库、哪张表由中间层去决策。这篇东西我打算从项目整体设计讲到核心原理再到部署实操和排障经验全程用我实际踩过的坑来铺垫。适合正在做数据库拆分、读写分离或者被连接数打满折磨过的后端开发、DBA 和架构师参考。即便你是第一次接触中间件只要跟着走一遍也能理解它到底怎么工作、能解决什么问题。1. 项目定位与方案拆解1.1 Atlas 在整个数据链路中扮演什么角色先打个比方。以前应用直连数据库就像每个员工都直接跑到仓库去拿货仓库窗口就那几个人一多就排队偶尔有人拿错货还没人管。Atlas 就好比在仓库门口加了一个分拨台所有取货请求先到这里登记分拨台根据货单内容决定你去哪个窗口顺带还能帮你把一些零散的小请求合并成大订单。放到技术链路上看Atlas 的位置非常清晰应用层通过标准的 MySQL 协议连上 Atlas 的端口Atlas 解析 SQL 之后再往后端真实的 MySQL 实例转发。应用侧根本感知不到后端有几台机器、哪些是主库、哪些是从库。这个方式的优势在于数据库拓扑变更不用再通知所有业务方只需要改中间层配置。我在做这套项目时最直观的感受是连接数和流量可控了。以前几百个服务实例每个都保持几十个长连接数据库光握手就要耗尽 CPU。Atlas 收口之后后端连接数变得非常平稳应用扩容也不用担心把数据库连接池打爆。说白了它让数据库从“被很多人直接操作”变成“被一个有脑子的人统一调度”。1.2 核心功能清单与方案选型对比Atlas 的核心功能我整理成了一张表和直连方案对比大家看得更清楚功能维度直连数据库Atlas 中间件后端地址管理每个应用配置自己的连接串统一入口后端变更对应用透明读写分离应用自己实现代码侵入大中间层按 SQL 类型自动路由分库分表应用层判断并拼接表名按分片键路由应用无需关心连接池复用每个服务维护一堆连接中间层统一复用降低连接数故障摘除依赖应用侧重试机制健康检查后自动剔除故障节点监控审计分散难汇总统一日志与统计便于排查为什么不直接选应用层改造或者引入其他重量级方案而是用 Atlas 这类中间件我当时的考虑有几点。第一应用层改造成本太高几十个服务都要动一遍光排期就要跨两个迭代。第二很多轻量级方案对分库分表的支持很弱尤其是一致性事务场景自己写容易挖坑。第三Atlas 这类基于 MySQL 协议的代理方案对业务代码基本透明切换成本低上线失败也容易回滚。当然它也有短板比如跨库 JOIN 支持不好分布式事务需要业务配合。但这不是 Atlas 的问题而是所有分库分表都要付出的代价。你选的不是完美方案而是在当前阶段收益最大、代价最小的方案。1.3 整体架构拓扑与数据流向Atlas 的部署形态不复杂核心就三层。最上层是接入层也就是应用服务器它们只需要配置一个虚拟 IP 或者域名指向 Atlas 所在的主机端口可以是常见的 3306也可以换成别的。中间是 Atlas 节点建议至少部署两个一台为主、一台为备配合 Keepalived 之类的工具做高可用切换。下面是真实的 MySQL 实例集群分为主库和若干从库主库负责写入从库负责读取。数据流向大概是这样的应用发出 SQL 请求Atlas 先解析这条 SQL 的类型和涉及的表然后根据配置的路由规则以及当前各节点的健康状态决定把请求转发给哪个后端实例。如果是SELECT默认会挑一个负载较低的从库如果是INSERT、UPDATE、DELETE或者会话开启了事务就走主库。如果配置了分库分表还需要根据分片键算出具体落到哪个库、哪张表。这里有个容易被忽略的点Atlas 自身是无状态的也就是说它不存业务数据只是做转发和路由。这样设计的好处非常明显一旦某台 Atlas 宕机另一台可以直接顶上数据不会丢。坏处是配置要同步好不然两个节点的规则不一致会引发路由错乱。我后续会专门讲怎么用脚本同步配置和管理节点。2. 核心机制与关键原理2.1 读写分离的实现细节与路由策略读写分离是 Atlas 最常用的能力但很多人只知道“SELECT 走从库其他走主库”实际做起来没有那么简单。路由的判断不仅是看 SQL 的第一个关键词。比如SELECT ... FOR UPDATE虽然以 SELECT 开头但它带了行锁必须走主库否则你在从库加锁主库的数据还在被别人改业务就出大问题。再比如事务内的所有语句只要你在一个显式事务里哪怕全是 SELECT也应该全走主库。原因是事务的隔离级别和快照读在从库上可能拿到过期数据尤其主从延迟明显的时候容易引发脏读。所以 Atlas 在做路由决策时至少要参考这三个维度语句是否为SELECT、是否在事务中、语句是否包含锁语义或函数调用。我见过不少业务事故都是因为没有处理事务内路由比如先开启事务插入数据再查询刚插入的记录结果查到了旧值因为查询走了从库而主从还没同步。这类问题非常隐蔽线上复现难用户又催得急排查起来特别费劲。路由策略还需要考虑主从延迟。正常情况下读流量全部打到从库无可厚非但一旦从库延迟超过阈值比如 relay log 应用积压超过几秒Atlas 最好把部分读请求临时切回主库。我在配置里就预留了这样的开关不是说一定要用而是关键时刻能救命。2.2 分库分表的规则设计与扩容思路讲完读写分离再聊分库分表。Atlas 支持按某个字段做分片常见的就是哈希取模和时间范围分片。哈希取模是最简单也最常用的方案。假设订单表要拆成 16 张物理表分片键是订单号路由算法就是hash(order_no) % 16。关键是选择一个分布足够均匀的分片键订单号、用户 ID 都算不错但如果你按地区分片就会出现头部地区数据量巨大、尾部地区几乎没数据的倾斜问题。这里我要提一个规划层面的教训分片数一开始就要预留余量最好设置成 2 的幂次方比如 8、16、32。为什么因为后面扩容时如果分片数不能按倍数扩数据迁移的范围会非常大。比如从 16 扩到 24几乎所有数据都要重新分布但从 16 扩到 32则只有一半的数据需要迁移迁移逻辑也更简单。我做 Atlas 项目规划时直接定成了 32 个分片初期只用 8 个分片剩下的暂时空着后续加机器直接把新分片启用就行。分库分表的代价也要提前想清楚。跨分片 JOIN 基本别想了IN 查询需要拆成多个查询再在内存里合并分布式事务需要业务方配合做最终一致性方案。我一般建议团队把分片边界设计在业务层面比如同一个用户的所有数据始终落在一个分片内这样大部分查询都能限定在单分片内执行复杂度会小很多。2.3 连接池复用与高可用保障再说连接管理。数据库连接很贵从创建连接到认证再到 MySQL 内部分配线程、初始化会话开销都不小。应用侧如果每个请求都新建连接数据库很快就会被拖垮。Atlas 在中间层做了一件很巧妙的事它对外暴露的连接和后端实际建立的连接并不是一比一对应的。应用连上 Atlas 之后Atlas 从后端的连接池里取一条空闲连接出来复用请求处理完再把连接还回去。这样一来后端只需要维持比应用连接数少一个数量级的连接就能支撑大量并发请求。我实测过一个场景一个有 50 个节点的应用集群原来每个节点 30 个连接直连时 MySQL 要维持 1500 个连接接入 Atlas 后后端只保留了 200 个连接负载直接降下来一大截。高可用方面Atlas 会周期性对后端每个实例做健康检查。检查方式可以是很简单的SELECT 1也可以是自定义的探活语句。如果某个实例连续几次失败它会被标记为不可用读流量自动转移到其他可用节点。等到实例恢复健康会重新放回池子里。这套机制在从库宕机、主从切换两种场景下都非常有用。主从切换的场景尤其要小心。Atlas 必须具备配置热更新的能力不然你手动把主库切换成了新机器中间件还傻傻地往旧地址发请求服务就全挂了。我的做法是在 Atlas 管理接口上预留动态调整后端的命令切换完成后立刻更新后端节点列表不需要重启进程。3. 部署与实操全过程3.1 环境规划与安装前的准备先看机器规划这是我实际部署时用的一个标准方案大家可以参考角色机器配置数量说明Atlas 节点4C8GSSD2一主一备Keepalived 做 VIP 漂移MySQL 主库8C16GSSD1承载写入流量MySQL 从库8C16GSSD2承载读流量延迟监控监控/日志机2C4G1收集 Atlas 日志和 MySQL 慢查询安装前有几项系统设置必须处理否则 Atlas 跑起来就会出现各种莫名其妙的连接问题。打开文件描述符上限修改/etc/security/limits.conf把nofile调高到 65535 以上不然高并发下会报 “Too many open files”。关闭透明大页数据库链路对延迟敏感透明大页会导致内存分配不稳定建议在/etc/rc.local里执行echo never /sys/kernel/mm/transparent_hugepage/enabled。同步时间主从和中间件节点都要配置 NTP时间不一致会导致日志分析困难甚至认证异常。Atlas 的运行依赖 MySQL 客户端库和 glibc安装前确认系统里有libmysqlclient.so没有的话用系统的包管理器装上即可。我用的是 CentOS 7.9直接安装编译好的二进制包解压到/usr/local/atlas没有遇到依赖地狱的问题。3.2 配置文件逐项解读Atlas 的配置是我觉得整个项目里最值得花时间研究的部分。配置文件结构不复杂但每个参数都暗藏玄机。我贴一份我实际使用的精简配置并逐个讲解。[mysql] # Atlas 对外监听地址和端口 proxy-address 0.0.0.0:3306 # 后端 MySQL 实例列表主库在前从库在后 backend-address 10.0.0.11:3306 backend-address 10.0.0.12:3306 backend-address 10.0.0.13:3306 # 主库标识用于区分读写角色 master-address 10.0.0.11:3306 # 客户端连接 Atlas 所需的账号 pwds admin:xxxxx # 管理接口地址和账号 admin-address 0.0.0.0:2345 admin-username admin admin-password xxxxx # 分库分表规则文件 tables /usr/local/atlas/conf/table.rule # 日志配置 log-path /usr/local/atlas/logs log-level INFO # 主从延迟阈值超过则延迟读切主 max-delay 5先看backend-address注意主库要写在前面还是后面其实不重要重要的是列表里必须包含主库。master-address是单独指定的Atlas 用这个字段来区分哪个实例承担写流量。从库在列表里通常排在主库后面。pwds这个字段很多人会踩坑它存的是加密后的密码不是明文。我第一次配置时直接写了明文结果客户端连接一直认证失败。后来查了文档才知道要先通过工具生成加密串再写进去。admin-address是中间件的管理口和业务端口分开防止外部误连。日常运维、查看状态、动态调整后端地址都靠这个端口。max-delay是主从延迟阈值单位是秒。前面提到过延迟超过阈值就要把读流量切回主库这个参数就是那套机制的触发条件。设得太小会导致读流量频繁抖动设得太大又会出现数据新鲜度问题我一般从 5 秒开始调根据业务容忍度再动态改。3.3 启动验证与压测观察配置完成之后启动过程非常简单执行启动脚本就行。但启动成功不代表路由已经符合预期必须做一套完整的验证。第一步用 MySQL 客户端连接 Atlasmysql -h 127.0.0.1 -P 3306 -u admin -p能正常连上说明基本链路通畅。第二步执行SHOW DATABASES;能列出后端 MySQL 的真实库说明 Atlas 成功代理到后端实例。第三步验证读写分离是否真的生效。我在从库上执行下面的 SQL 来查看当前连接来源SHOW PROCESSLIST;然后在应用侧不断发起SELECT请求观察从库的Info字段里是否持续出现来自 Atlas 转发的查询。如果从库一点动静都没有大概率是路由配置有问题所有流量都打到了主库。紧接着做一轮简单压测。我用 sysbench 造了大概 100 万条数据分别测试直连 MySQL 和通过 Atlas 访问的吞吐差异。结果如下压测场景连接数QPS 峰值平均延迟(ms)直连主库2001200012走 Atlas 读从库2001050015走 Atlas 读写混合200830018看这个结果走 Atlas 后单次请求延迟确实有微幅上升大概 2 到 3 毫秒这是代理层的正常开销。但换来的是整体连接数大幅下降后端单实例负载压力小了很多系统的扩展性和稳定性才是更大的收益。3.4 业务接入与快速回滚策略验证通过后就是接业务。Atlas 最让我喜欢的一点是业务接入成本几乎为零只需要把应用里的数据库连接地址从原来的 MySQL 实例 IP 改成 Atlas 的地址即可。端口如果没有特殊要求可以继续用 3306 端口这样连驱动里的 URL 都不用改。不过接入顺序还是有讲究的。我建议先挑一个读多写少、不影响核心交易链路的服务做灰度跑一两天观察日志里有没有路由异常、连接中断、主从延迟导致的脏读报警。确认稳定之后再逐步扩大到所有服务。回滚方案我一般提前准备好。所有服务在变更配置时保留旧连接串一旦发现 Atlas 这边有问题直接用发布系统回滚配置应用连接重新指向原 MySQL 实例。整个回滚过程不会超过五分钟中间件的无状态特性在这里发挥了巨大作用因为 Atlas 本身不落业务数据回滚不会造成数据不一致。我还特意写了一个一键回滚脚本核心就是把应用配置里的atlas.host改回mysql.host然后重启服务。这种看似很笨的方案在线上紧急故障时往往最管用。4. 常见问题与排查技巧实录4.1 高频故障速查表我把这段时间遇到的问题整理成了一张速查表按“现象 - 可能原因 - 解决思路”的方式排列方便大家直接对照。现象可能原因解决思路客户端连接 Atlas 报认证失败pwds里写的是明文而非加密串用工具生成加密密码后重写配置应用启动后查询偶发超时后端连接池耗尽调大 Atlas 后端连接上限同时检查 MySQL max_connections从库 CPU 飙高但主库很低读流量全部打到主库检查master-address配置以及路由规则主从切换后新主库不接收写请求Atlas 后端列表未更新通过管理接口动态更新不需要重启SELECT 查询看到旧数据主从延迟超过容忍阈值调低max-delay或部分读请求强制走主日志里大量连接被断开后端 MySQLwait_timeout过短调整 MySQL 会话超时参数Atlas 进程内存持续增长连接泄漏或慢查询堆积排查慢 SQL定期释放空闲连接每一个问题我都在自己环境里踩过一两遍尤其是主从切换后的写请求丢失这个坑影响最大。当时我以为 Atlas 会把新主库自动识别出来结果没有因为它不知道你外部已经把主库切换了。后来我在切换脚本里加入了通知 Atlas 更新后端的步骤才彻底解决。4.2 排查命令与日志分析实战排查问题不能瞎猜我分享几个最常用的命令和方法遇到异常时可以快速定位。第一招连接 Atlas 管理端口看全局状态。管理端口不是给业务用的是给运维和管理员用的。登录后直接输入show status可以查看当前的前端连接数、后端连接池使用情况、路由次数统计等。如果没有这些指标问题的定位会非常困难。第二招看日志里的关键字。Atlas 的日志路径在配置文件里可以指定正常运行时输出到/usr/local/atlas/logs。排查时重点看三类关键词ERROR表示有请求执行失败TIMEOUT表示连接或查询超时BACKEND表示后端实例的状态变化。比如下面这行日志[ERROR] cannot get connection from backend pool, timeout这行日志一出现基本可以断定是后端连接池满了或者 MySQL 真的无法建立新连接。优先检查 MySQL 的max_connections和threads_connected再看 Atlas 到 MySQL 之间的网络是否正常。第三招用tcpdump抓包看 SQL 流向。在执行查询的同时抓包能非常直观地看到 SQL 到底发给了哪个后端 IP。这招在处理路由怀疑的争论时最有效不靠猜直接看包。tcpdump -i eth0 -nn port 3306 -w atlas.pcap抓完之后用 Wireshark 分析可以看到每个连接的握手、查询语句、响应耗时。如果某个 SQL 应该走从库但抓包发现反向了那路由配置肯定有问题。4.3 独家避坑心得最后分享几个我在项目中总结的独家经验别的地方不一定能看到。第一分片键的选择要有防御性。不要在项目初期图方便选一个业务主键做分片业务主键往往带有自增特性和局部性很容易出现热点。我见过一个支付业务按照门店 ID 分片结果头部门店数据量是尾部门店的几百倍查询严重倾斜。分片键要选访问频率均匀、区分度高的字段。第二中间件节点不要跨机房部署。刚开始我图省事把两台 Atlas 分别放在两个可用区想着提升容灾能力。结果发现跨机房的网络延迟让每次 SQL 转发都增加了十几毫秒应用层受不了最后还是改回了同机房双节点。想跨机房容灾应该从数据库层面做双向同步而不是让中间件跨机房转发。第三升级和变更尽量在低峰期进行。Atlas 的配置热更新虽然是动态的但涉及表规则变更这类重大调整时最好还是先灰度再全量。我在一次规则更新时因为新旧规则并行产生了歧义导致部分请求被路由到错误的表虽然很快恢复了但那次教训让我学会了中间件的变更要走和业务发布一样的流程不能因为它是一层代理就掉以轻心。5. 写在最后的一点体会做完 Atlas 这套中间层之后我对“数据库治理”这件事的理解深了不少。以前总觉得数据库不够用就加缓存、加队列、加机器实际上如果没有一个统一的流量入口加再多资源也只是把问题往后拖延。我个人的建议是如果你的系统已经出现连接数打满、读写混合导致锁竞争激烈、或者表数据量增长速度明显超过预期Atlas 这类中间件是一个非常值得考虑的分层方案。它不能解决所有问题但它能让数据库拓扑的变化不再牵动整个应用侧这一点在业务扩张期尤其重要。最后再送一个小技巧投入运行之后定期写一个检查脚本去从库看真实流量分布。别只依赖 Atlas 自己的统计真实 SQL 的执行情况会告诉你很多中间件日志里看不到的细节比如哪些表才是真正的热点、哪些查询该被缓存兜住。掌握了这些一手数据你才能把 Atlas 的路由规则调得越来越聪明。

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

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

免费获取报价 →
↑