数据库高可用与容灾实战(5):代理层路由选型:ProxySQL、中间件与直连的取舍

发布时间:2026/9/30 11:22:51

数据库高可用与容灾实战(5):代理层路由选型:ProxySQL、中间件与直连的取舍 切换完成后谁来通知应用前一篇的自动化切换把数据库内部谁当主管到了底但整个链条还缺一环切换成功的那一刻全公司几百个服务实例、几万个长连接还握着旧主不放。连接与路由这一层有四种活法客户端直连加 DNS、客户端自带多主机插件如 Connector/J 的 failover 模式、地址层VIP/LVS漂移、独立代理层ProxySQL、MySQL Router或数据库中间件。四种方案在高可用里的差别不是能不能连而是改口的速度、读写分离的位置、以及故障瞬间流量的去向。本篇用两个模拟把代理层最值钱也最容易翻车的两件事量化带延迟阈值的读路由和连接复用带来的会话状态问题。三种接法的机制账直连加 DNS/VIP数据库切换 把 VIP 或 DNS 记录指向新主。优点是零额外组件、零额外跳数缺点是改口速度受 TTL 和连接池心跳控制——DNS 缓存 300 秒就意味着 300 秒的错乱窗口且客户端手里的长连接根本不看 DNS必须等到 TCP 断开重连实际窗口比 TTL 还长。VIP 漂移快于 DNS但跨机房就无能为力。客户端插件把拓扑写进连接串jdbc:mysql://primary,secondary1,secondary2/app?failOverReadOnlyfalsesecondsBeforeRetrySource60驱动层完成故障转移与回切。它本质是每人一个迷你代理拓扑知识散落在所有应用里改拓扑要发布应用读扩散read after write之类一致性策略只有驱动作者懂。适合单库主备、连接规模不大的场景。代理层/中间件ProxySQL 这类独立进程持有全部路由知识hostgroup定义写组读组mysql_query_rules按用户名、schema、语句指纹分流mysql_replication_hostgroups用read_only标志自动搬移节点max_replication_lag给从库设延迟熔断线监控模块每秒探活节点失联直接出组。切换系统上一篇的 MHA/Orchestrator只管改节点的read_only和 GTID 身份路由表跟着自动收敛——这就是两者衔接的接口。中间件ShardingSphere、老 MyCat 路线则是库内路由 分库分表的超集功能更多、链路更深。代价同样明确多一跳同机约 100 微秒、跨机亚毫秒代理自身要有集群与 VIP路由规则成为新的分布式配置中心要人养。实验一读路由的延迟熔断是保护伞也是主库的隐形放大器600 条查询按三读一写排列两个从库各有一段延迟窗口R1 撞上 5 秒大报表、R2 有 3 秒补数窗口且与 R1 重叠。对比max_replication_lag设与不设的路由结果。N600# 600 条查询WRITE_EVERY4# 每第 4 条是写: 25% 写deflag_of(node,i):ifnodeR1and200i260:return5.0# R1 撞上定时大报表, 延迟 5sifnodeR2and210i230:return3.0# R2 的批量补数与 R1 窗口重叠 20 条return0.1defroute(threshold):dist{M:0,R1:0,R2:0}stale_reads0fallback_reads0foriinrange(N):if(i1)%WRITE_EVERY0:dist[M]1# 写永远进主库continueok[nfornin(R1,R2)iflag_of(n,i)threshold]ifok:nodemin(ok,keylambdan:lag_of(n,i))# 最健康优先dist[node]1iflag_of(node,i)1.0:stale_reads1else:dist[M]1# 全落后 - 回主库fallback_reads1returndist,stale_reads,fallback_readsforthin(999.0,1.0):dist,stale,fbroute(th)print(max_replication_lag%.0fs: 主库 %d 条 | R1 %d 条 | R2 %d 条%(th,dist[M],dist[R1],dist[R2]))print( 读到 1s 陈旧数据的查询: %d 条; 因全员落后回主的读: %d 条%(stale,fb))dist0,_,_route(999.0)dist1,_,_route(1.0)print(\n阈值收紧的代价: 主库承担读 %d 条(写基线 %d 条 - 现 %d 条, 主库负载 %.0f%%)%(dist1[M]-dist0[M],150,dist1[M],100.0*(dist1[M]-dist0[M])/150))print(重叠窗口 [210,230) 内两条读规则同时失效是风暴源头: 保护读一致性的阈值, 就是主库的隐性流量放大器)运行输出max_replication_lag999s: 主库 150 条 | R1 405 条 | R2 45 条 读到 1s 陈旧数据的查询: 15 条; 因全员落后回主的读: 0 条 max_replication_lag1s: 主库 165 条 | R1 405 条 | R2 30 条 读到 1s 陈旧数据的查询: 0 条; 因全员落后回主的读: 15 条 阈值收紧的代价: 主库承担读 15 条(写基线 150 条 - 现 165 条, 主库负载 10%) 重叠窗口 [210,230) 内两条读规则同时失效是风暴源头: 保护读一致性的阈值, 就是主库的隐性流量放大器熔断阈值本身没有对错它是一台陈旧读→主库过载的汇率转换器。真正的工程要点藏在数字之外延迟窗口的源头往往是从库上跑的定时报表/备份扫描第 1 篇 MTS 栅栏的账所以要么给报表库单独建组、设更高的熔断线要么把批任务调度错开而全员落后回主触发时主库正在承担写洪峰10% 的读可能就是压垮它的最后一根稻草——容量规划时要给这个回退路径留出至少 20% 的主库读余量并给它配独立告警。实验二连接复用的账省下的都是脏会话没占的ProxySQL 的招牌特性之一是多路复用multiplexing应用侧连接与后端服务器连接解耦事务间隙归还后端连接给别的会话用。省连接是真的会话状态污染也是真的——用户变量、SQL_CALC_FOUND_ROWS、临时表、LOCK TABLES都长在后端连接上。用一组交错事件看三种策略的差别。events[(S1,SET,A),# S1: SET flagA(S1,GET,None),# S1: SELECT flag(S1,DONE,None),# S1 结束, 连接归还池(S2,SET,B),# S2: SET flagB(S2,GET,None),# S2: SELECT flag(S2,DONE,None),(S3,GET,None),# S3 新会话, 没 SET 过, 只查(S3,DONE,None),]defsimulate(pin,reset):free,held,made[],{},0dirty_leave0wrong[]forsid,op,valinevents:ifopDONE:cheld.pop(sid,None)ifcisnotNoneandc[state]:ifpin:dirty_leave1# 带会话状态的连接不回池, 等真空闲再回收else:ifreset:c[state]None# COM_RESET_CONNECTION 清用户变量free.append(c)elifcisnotNone:free.append(c)continueifsidnotinheld:cfree.pop(0)iffreeelseNoneifcisNone:made1c{name:sc%d%made,state:None}held[sid]c cheld[sid]ifopSET:c[state]valelse:seenc[state]ifsidS3andseenisnotNone:wrong.append(S3 SELECT flag 读到 %s(上一个会话的残留)%seen)elifsidS2andseen!B:wrong.append(S2 的 SET 之前先读到 %s%seen)returnwrong,dirty_leave,made w0,_,m0simulate(pinFalse,resetFalse)w1,_,m1simulate(pinFalse,resetTrue)w2,dl,m2simulate(pinTrue,resetFalse)print(策略一 裸复用(ProxySQL 关闭 multiplexing 语义错误用法): %s%(w0or无异常))print(策略二 复用 借出前 COM_RESET_CONNECTION: %s%(w1or无异常))print(策略三 复用 脏连接钉住不归还: %s (钉住回收 %d 次, 新建连接 %d)%(w2or无异常,dl,m2))sessions500sticky_ratio0.6print(\n规模账(%d 并发会话, %.0f%% 会话携带用户变量/临时表/显式事务):%(sessions,sticky_ratio*100))print( 直连 1:1: 后端连接 %d, 无污染%sessions)print( 全复用 10:1: 后端连接 %d, 但上面策略一的重演(会话串数据)%(sessions//10))print( 复用 脏会话钉住: 后端连接 %d(钉住) %d(复用) %d, 无污染%(int(sessions*sticky_ratio),sessions//10,int(sessions*sticky_ratio)sessions//10))print(结论: multiplexing 省的是干净会话的连接, 脏会话的连接数省不掉——代理层的连接收益要打折扣)运行输出策略一 裸复用(ProxySQL 关闭 multiplexing 语义错误用法): [S3 SELECT flag 读到 B(上一个会话的残留)] 策略二 复用 借出前 COM_RESET_CONNECTION: 无异常 策略三 复用 脏连接钉住不归还: 无异常 (钉住回收 2 次, 新建连接 3) 规模账(500 并发会话, 60% 会话携带用户变量/临时表/显式事务): 直连 1:1: 后端连接 500, 无污染 全复用 10:1: 后端连接 50, 但上面策略一的重演(会话串数据) 复用 脏会话钉住: 后端连接 300(钉住) 50(复用) 350, 无污染 结论: multiplexing 省的是干净会话的连接, 脏会话的连接数省不掉——代理层的连接收益要打折扣策略一就是真实事故脚本的抽象ORM 里一句SET tenant ?被下一个借用这条后端连接的会话读到轻则数据查串、重则跨租户越权。ProxySQL 的处理是自动失效 显式钉住双轨检测到会话级状态就把该连接踢出复用等价于策略三SET var、GET_LOCK、部分隔离级别语句默认禁复用策略二的COM_RESET_CONNECTION则要求后端支持且状态可枚举临时表、 prepared statement 各有归宿。规模账提醒另一件事如果你的应用会话普遍带粘性大量用户变量或长事务代理层宣称的连接数收益会缩水到和直连同一量级此时上代理层的理由只剩路由与故障摘除。常见陷阱与落地清单代理层只配了一个实例路由层单点故障时数据库健康也没人给流量指路至少两实例 VIP/负载均衡并把代理自身纳入监控告警。读写分离规则用是否包含 select 关键字判断SELECT ... FOR UPDATE、INSERT ... SELECT、存储过程里的读全都被错放去从库按语句指纹加事务上下文判定。写后立读走从库熔断阈值挡不住落后 0.8 秒的窗口订单详情页照样读到旧状态会话内在主库写过N 秒内强制回主ProxySQL 可用事务粘性 自定义规则实现。故障切换没联动代理新主提了、read_only改了但 ProxySQL 写组还指着旧主 IP 探测失败反复进出组震荡切换系统的on_master_failover钩子里要有主动LOAD MYSQL SERVERS TO RUNTIME的一步。DNS TTL 一刀切 300 秒演练时切换 5 分钟才改口全是它贡献的数据库域名用短 TTL30~60 秒并把客户端连接池的 maxLifetime 配得比 DNS TTL 还短。压测只走直连、上线才过代理代理的跳数、复用、规则匹配开销全被藏进生产首周 P99 抖动作废。到这里“写谁读谁、故障时流量怎么改口已经闭环。但从第 1 篇开始就埋着一句话复制只保证传播”不保证可回溯——误删、坏发布、勒索加密代理层一律帮不上。下一篇《数据库高可用与容灾实战6备份体系设计mysqldump、XtraBackup 与快照的边界》开始讲回到过去的能力。参考来源ProxySQL Documentationhttps://proxysql.com/documentation/GitHubsysown/proxysqlhttps://github.com/sysown/proxysqlMySQL Connector/J 8.0 Developer Guidehttps://dev.mysql.com/doc/connector-j/8.0/en/Apache ShardingSphere 文档站https://shardingsphere.apache.org/document/current/en/overview/WikipediaLoad balancing (computing)https://en.wikipedia.org/wiki/Load_balancing_(computing)本系列已结集为免费专栏数据库高可用与容灾实战从主从复制到机房级演练进阶推荐付费专栏Python 自动化接单实战从脚本到第一单限时 ¥9.9首篇免费试读
延伸阅读

更多相关文章

2026/9/30 11:22:51

第027篇 ArrayList 源码与扩容机制——1.5 倍增长的细节

摘要:本篇是《Android软件开发面试从入门到精通》第 27 篇,主题为「ArrayList 源码与扩容机制——1.5 倍增长的细节」。「ArrayList 源码与扩容机制——1.5 倍增长的细节」属于Java 核心基础阶段的必修内容。概念不难,难的是把原理、代价与边界讲清楚,这正是面试的区分度所…

2026/9/30 12:13:03

GO [ 泛型 ]

泛型 前面我们已经学习了 Go 的变量、常量、数据类型、输入输出、条件控制、切片、字符串、映射表、指针、结构体、函数、方法、接口、类型、错误、文件和反射。接下来学习 Go 语言中一个很重要、也最容易被“写复杂”的能力:泛型(Generics)…

2026/9/30 12:13:03

STM32上电启动全流程解析:从复位向量到RTOS第一个任务

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/30 12:13:03

国产车规MCU首次量产主动悬架:从工程视角拆解核心技术

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/30 12:13:03

DeepSeek-VL2在证券结算对账中的多模态核验实战

简介:本资源是一份面向金融科技从业者、量化系统开发工程师及AI模型落地工程师的深度技术方案文档,聚焦证券交易结算对账这一高精度、强合规场景,系统性提出基于DeepSeek-VL2多模态大模型的自动化核验与差异归因框架。全文610页、61章&#x…

2026/9/30 12:08:02

整车性能目标书(QR)完全指南:模板、分解与版本管理

我在主机厂调试了十来年整车性能,接手新项目的第一件事永远是同一件:翻开上一代车型的性能目标书,和动力总成、底盘、NVH、总布置几个team的人一起过一遍。很多新同事不理解,项目启动阶段最要紧的是节点计划,是零件定点…

2026/9/29 11:07:23

东莞市品牌网站建设报价常见报错与解决

东莞品牌网站建设报价单背后:一份保姆级建站教程避坑实录 网站做好了没人访问,这大概是很多老板最头疼的事。花了大几万做的品牌站,上线后流量惨淡,比路边摊还冷清。别急着骂外包公司,很多“东莞品牌网站建设报价”里藏着不少猫腻,比如用模板站冒充定制…

2026/9/29 21:48:03

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/9/29 7:00:49

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/9/30 0:01:22

MATLAB+Yalmip+CPLEX实战:综合能源系统优化调度全流程解析

做综合能源系统优化调度这活儿,最痛苦的不是建模本身,而是模型写完之后不知道该怎么求解。看论文里轻飘飘一句“采用Yalmip调用CPLEX求解”,自己上手时却往往卡在环境配置、变量声明、约束写法和求解状态判读上,一耗就是两三天。这…

2026/9/30 0:01:22

I3C比I2C快10倍?RK3576实战:速率、DTS配置与混合总线避坑指南

I3C 比 I2C 快 10 倍?这句话在嵌入式群里传了很久,每次都能吵出一堆截图。前段时间我正好在 RK3576 上调板级 I3C 接口,从控制器寄存器一路摸到 Linux DTS 配置,踩了不少坑,也把这笔速度账彻底算明白了。本文就用 RK35…

2026/9/30 0:01:22

字符串转对象:JSON.parse、new Function与URLSearchParams

“字符串转对象”这几个字,我在技术群里见过的问法至少有十几种:有人拿着一串{a:1,b:2}说 JSON.parse 直接报错,有人要从 URL 里抠出参数,还有人只是想把abc变成能挂属性的东西。js 这门语言里,字符串和对象之间的转换…

2026/9/29 3:53:39

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/29 9:46:12

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/30 10:28:53

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑