上一篇聊了NDB Cluster这种特种武器,有读者说:"前面几篇把开源方案讲遍了,能不能讲讲大厂自己玩的那一套?"
这个话题我确实想聊很久了。我研究过不少大厂的高可用方案,淘宝的TMHA(Taobao Master High Availability)是我觉得设计最精巧的一个。它出现在MHA还没普及的年代,双十一交易主库都敢在当天做切换,这份底气背后是一整套严密的设计。
今天这篇,我把TMHA的整体架构、异常切换、数据一致性保障、自动切换机制拆开来聊。就算你不打算自研,这些设计思路也能帮你把开源方案用得更明白。

一、先搞清楚:大厂面临的高可用难题和我们不一样
我们在小公司碰到的主库故障,一年没几次。淘宝那个体量,服务器以万台计,按硬件厂商给的MTBF数据推算,机器故障是天天有的事。
所以淘宝要解决的问题,不是"主库挂了怎么办"这种偶发事件,而是"主库挂是常态,怎么让切换像呼吸一样自然"。
具体拆解下来,难题有这么几个:
主库挂了,从哪个从库选新主?数据还没同步完怎么办?切换动作怎么让应用知道?老主库修好了,怎么把它塞回集群?一套库好办,几百套库同时切怎么办?
开源方案里,MHA解决了前两个问题的一半,应用感知和批量切换基本没管。这就是淘宝自己造轮子的原因。
二、TMHA的整体架构:决策和执行分离
TMHA的架构里有四个关键角色,我把它们的分工讲清楚,你就明白这套设计的精妙之处了。

Agent:每台MySQL服务器上部署一个,负责干两件事:盯着本机的MySQL状态,跟ZooKeeper保持心跳。
ZooKeeper:分布式协调服务,存集群状态,靠Watcher机制给关注者推送事件。
SwitchManager:切换决策中心,多机房部署,无状态,支持幂等操作。它接收ZooKeeper的事件,确认故障后执行切换。
TDDL:淘宝的分布式数据中间层,应用不直连数据库,全部走TDDL。切换时TDDL拿到新配置,自动把流量导过去。
这套架构最妙的地方是决策和执行分开了。MHA的Manager又当裁判又当运动员,它自己就是单点。TMHA里,Agent只负责上报状态,SwitchManager只负责决策,TDDL只负责执行路由,每个角色都可以水平扩展,没有单点。
三、异常检测:所有故障都抽象成"节点消失"
这是我觉得TMHA设计里最有智慧的部分。
主库故障千奇百怪:MySQL进程崩了、机器死机了、网卡坏了、机房断电了。要是每种故障都写一套检测逻辑,系统会复杂到没法维护。
TMHA的做法是:把所有故障抽象成一个信号,ZooKeeper上的Agent节点消失。
你看这个映射关系多干净:
MySQL进程崩了,Agent检测到后主动删除自己在ZK上的节点。机器死机了,Agent跟着死,ZK上的临时节点超时后自动消失。网络断了,Agent和ZK的心跳断了,同样是节点超时消失。整个机房断电,还是节点消失。
SwitchManager不用关心到底是什么故障,它只需要处理"节点消失"这一种事件。这就是抽象的力量。
还有个细节值得学:二次确认。Agent2收到Agent1消失的事件后,不会立刻切换,而是创建异常节点记录下来,SwitchManager拿到异常节点后再确认一次,真的异常才推切换。网络抖动造成的误判,在这一层就被过滤掉了。
主库选举用的也是ZK的经典玩法:每个客户端在/lock下创建带序号的临时节点,序号最小的当主。主库挂了节点消失,剩下的节点重新抢锁,抢到锁的就是新主。这套写锁机制保证了任何时刻只有一个主库。
四、数据一致性:rSQL回滚机制
切换最怕的不是服务中断,是数据错乱。TMHA把可能丢的数据分成两类,分别给出了解法。
Δ2:从库的Relay Log损坏。这个好办,比对Exec和Read的位置,不相等说明有丢失,reset slave重新拉取就行。
Δ1:挂掉的主库上还有Binlog没传到从库。这个麻烦。主库都挂了,数据拿不回来,直接切换就会和从库产生差异。
TMHA的处理策略分两步走:
如果老主库能抢救回来,就让Δ1继续同步,不切换,等数据追平再说。
如果决定切换,老主库后来起来了,就要把Δ1回滚掉。怎么回滚?这里用了一个很骚的操作:把Binlog倒序解析,生成逆向SQL。主库执行了INSERT,回滚就执行对应的DELETE。这个机制叫rSQL,前提是Binlog必须是ROW模式,因为只有ROW模式才能精确还原每一行的变化。
配套的还有个Replay机制:回滚之后,把回滚掉的SQL在新主库上重放一遍,业务数据能救回来多少算多少,中间靠冲突检测机制兜底。
我第一次看到这个设计的时候挺震撼的。大部分人想到的都是"怎么把数据补全",淘宝的思路是"补不全就回滚到一致状态,再想办法重放"。方向反过来了,但工程上更可行。
五、自动切换:白名单加人工兜底
TMHA的切换分好几种类型,对应不同场景。
正常切换:机器维修、扩容这类计划内操作,慢慢来,数据追平再切。
强制切换:主库load飙高扛不住了,比如双十一流量洪峰,不等了,直接切。
自动切换:主库真挂了,走前面说的检测流程,但只对白名单里的库开放。白名单这个设计很务实,核心库才敢开自动切换,边缘库宁可人工介入。
批量切换:机房级别的操作,几十套库一起切。这个场景开源工具根本不支持,只能自研。
有一点我很认同:故障自动切换和人工强制切换是兼容的,两条腿走路。全自动听起来美好,真出了事你就知道,能把决策权收回到人手里有多重要。
六、主备延迟的解法:数据复制中心DRC
主备延迟是异步复制的娘胎病,TMHA的解法是搞了个数据复制中心(DRC)。
DRC的思路是把复制这件事从MySQL手里拿出来,由专门的系统负责。它多线程往目的端写,支持事务,还能顺便把数据dump给搜索之类的下游系统用。
这个设计后来演化成了很多大厂的通用数据同步平台,思路都是一样的:MySQL原生复制不够用,就自己造一条更快、更可控的管道。
七、TMHA给我们什么启发?
TMHA我们抄不走,它和TDDL、ZK体系、淘宝的基础设施深度绑定的。但它的设计思路,每一条都能用到我们自己的系统里。
故障检测要抽象。别给每种故障写一套逻辑,找到它们的共同表现形式,统一处理。
决策要留余地。二次确认、白名单、人工兜底,这些机制让系统在面对异常时更谨慎。
数据一致性要想清楚兜底方案。补不齐就回滚,回滚完再重放,保证任何时刻集群状态是可解释的。
切换不是数据库自己的事。数据库切完了,应用没切过去,等于没切。TMHA的总结里有句话说得很到位:APP切换才意味着整体的切换完毕。
八、面试题拆解
题1:TMHA和MHA有什么区别?
MHA是单Manager架构,Manager既做检测又做决策,自己是单点。TMHA把检测、决策、执行拆开了:Agent负责上报,ZooKeeper负责协调,SwitchManager负责决策,TDDL负责应用层切换。每个组件都能水平扩展。
数据一致性上,MHA靠抢救Binlog补数据,TMHA除了补数据还有rSQL回滚机制,能把老主库回滚到和新主库一致的状态。
题2:TMHA怎么防止误切换?
两道防线。第一道是故障抽象,所有故障统一表现为ZooKeeper节点消失,不用关心具体原因。第二道是二次确认,SwitchManager收到异常事件后再确认一次,网络抖动在这个环节就被过滤了。
题3:rSQL回滚是什么原理?
把宕机主库上没同步出去的Binlog倒序解析,生成逆向SQL,在老主库上执行回滚,让它和新主库数据一致。比如主库执行了INSERT,回滚就执行对应的DELETE。前提是Binlog必须是ROW模式,只有ROW模式能精确还原行级变化。
题4:大厂方案能直接搬到中小公司吗?
不能照搬,能抄的是思路。故障抽象、二次确认、决策与执行分离、切换要覆盖到应用层,这些设计理念用MHA加自研脚本也能实现个七八成。但TDDL那种中间层、DRC那种数据管道,没有相应的团队规模别碰。
九、总结
TMHA是特定时代的产物,它解决的是超大规模实例下的高可用自动化问题。今天云数据库普及了,很多能力RDS开箱即用,自研一套TMHA的必要性没那么大了。
但它留下来的设计思想没过时:故障抽象、决策分层、数据兜底、应用联动。你去看现在各种数据库高可用方案,骨子里都是这些思路。
这个系列到这里就真的结束了。从主从复制的底层原理,到MHA、PXC、MGR这些开源方案,再到InnoDB Cluster官方套件、NDB内存集群,最后到TMHA这种大厂自研,MySQL高可用的完整图景都在这了。
面试也好,选型也好,记住一句话:方案没有高低贵贱,只有合不合适。
这个系列完结撒花。你还想听我聊什么数据库话题?评论区告诉我。
干货已送达,点赞是签收,转发是好评,留言是售后!
760



被折叠的 条评论
为什么被折叠?



