您好,登录后才能下订单哦!
本篇内容主要讲解“GitHub的MySQL高可用怎么解决”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“GitHub的MySQL高可用怎么解决”吧!
GitHub在所有非Git的地方使用MySQL存储数据,其可用性对GitHub的运行至关重要。网站,API,鉴权等等,都需要数据库访问。GitHub运行多个MySQL集群来满足其不同的服务和任务。这些集群使用典型的主复
结构,在集群中只有唯一节点(主)能写入,其他节点(复制点)异步地复制主节点的变化(重播)并提供【读】服务。
主节点的可用性至关重要。没有了主节点,整个集群都不能写入了(数据保存不下来)。任何数据变化操作,如提交、Bug、注册、新建库等等,都将失败。要能写入显然需要有一个可获得的写入节点,即主节点。其中关键是,我们能定位或者发现这个节点。
在主节点崩溃的情形,必须保证一个新的主节点出现,并且快速地广播出去。检测到崩溃,主节点切换和集群广播告知,一起合计的时间就是总宕机时间。当然越小越好!
本文展示了GitHub的MySQL高可用和集群选主解决方案,该方案让GitHub可靠地运行跨数据中心操作,容忍数据中心隔离(不可用),实现更短的宕机时间。
本文描述的解决方案是之前GitHub实现的高可用的迭代优化。当扩容的时候,MySQL的高可用能够适应变化。我们也期望在GitHub有类似MySQL的策略用于其他服务。
当考虑高可用和服务发现时,一些问题会引导你走向一个合适解决方案,不完全的问题清单如下:
能忍受多久的宕机时间?
崩溃检测是否可靠?能否忍受错误(过早切换)?
失效切换是否可靠?那里失效了?
方案在跨中心时是否也工作良好?在高/低延迟的网络里呢?
方案能否容忍整个数据中心崩溃或网络隔离?
是否有机制组织或减缓脑裂
出现(集群中两个节点都声称自己是主,两者独立互,不知道对方,并都接受写入)?
能否承受数据丢失?丢多少能忍?
为了说明这些问题,让我们看看以前的高可用解决方案,并且为何要优化它!
在以前的迭代中,我们使用:
用orchestrator做崩溃检测和失效切换,并
用VIP和DNS做主节点发现 在该策略下,客户端用名字来发现写节点,例如mysql-writer-1.github.net,该名字被解析到一个虚拟IP地址(VIP),对应的主节点主机就能访问到。因此,正常情况下,客户端会解释这个名字,连到获得的IP,找到主节点。
考虑这样的复制拓扑结构,跨了3个数据中心: <img> 在主节点失效时,复制节点里的一个 ,必须被提名替代其【主】位。
orchestrator将检测失效,提名新主,接着重新定义名字或VIP。客户端并不知道新主怎么定位,因为他们只知道名字,而名字必须解释到新主上。情况是这样的:
VIP是协同的:他们被数据库服务器声称并拥有。为了获得或释放一个VIP,服务器必须发送一个ARP请求。占用该VIP的服务器必须先释放才能让新主获得它。这隐含的效果是:
一个有序的失效切换操作先得联系失效旧主并请求它释放VIP,接着联系新主请求其抓住该VIP。假如旧主无法访问了或者拒绝释放VIP咋办?在旧主失效情景里,它不能按时响应或完全不响应,都是可能的。
容许脑裂可以解决该问题:两个主机都生成拥有同一个VIP。不同的客户端可能连到其中之一上,看在网络上谁离的近。
其中根源在于两个独立的服务器需要协作,而这个结构是不可靠的。
就算旧主做出协作了,整个流程也浪费了宝贵的时间:切换到新主必须等到能联系到旧主。
而且当VIP改变了,已存在的客户端连接也不能保证从旧主哪里断开,仍然会出现事实上的脑裂情景。
VIP通常是以物理位置边界来设计,由路由器或开关拥有。因而,我们只能给相关位置的服务器重设VIP。在一些特殊情况下,我们无法给另一个数据中心的新主重设VIP,必须修改DNS。
DNS的变化扩散出去需要更长时间。客户端缓存了DNS(特定时间后才刷新)。跨数据中心的失效通常导致更长的宕机时间:需要更多时间让客户端意识到主节点变了。
这些限制促使我们寻求更好的解决方案,更多地考虑:
主节点通过pt-heartbeat心跳服务来自主地注入自己,基于延迟度量和流量控制
。该服务必须在新提名的主节点上被启动。如果可能,在旧主上的该服务将被关停。
类似的,Pseudo-GTID注入由主节点自主管理。它应该在新主启动,并最好在旧主关停。
新主被置为可写入,旧主被置为只读(如果可能)。
这些额外的步骤会增加更多的总宕机时间,并且他们自身也会遇到失败和冲突。这个解决方案是有效的,GitHub也做过成功的失效切换,在雷达监控下运行良好。但我们希望高可用在这些方面做得更好:
跨数据中心无感。
能忍受数据中心失效。
去除不可靠的协作流程。
减少总宕机时间。
尽可能做到无损失效切换。
我们的新策略,伴随着附带的优化,上述关注问题的解决或减轻。当前的HA结构中包括:
orchestrator负责失效探测和切换(用的是跨数据中心的orchestrator/raft)。
Hashicorp的Consul用于服务发现。
GLB/HAProxy作为代理层处于客户端和写节点之间。
anycast用于网络路由。
<img> 新的结构去掉了VIP和DNS修改。虽然我们引入了更多的组件,但能够解耦组件并简化任务,也能利用上坚固稳定的解决方案。接下来详细的说明下。
正常情况下,应用通过GLB/HAProxy连接到写节点,应用永远感受不到主节点身份。在以前,应用需要使用名字。例如,cluster1集群的主节点用mysql-writer-1.github.net。在当前的结构下,这个名字会被解释到一个anycast地址(IP)。
用anycast,这个名字解释到任何地方的同一个IP,但流量会基于客户端位置被路由到不同地方。尤其是,我们的每一个数据中心都有GLB,我们的高可用负载均衡器,部署在多个机柜里。去往mysql-writer-1.github.net的流量总是路由到本地数据中心的GLB集群。
因而,所有的客户端都由本地的代理服务。GLB运行于HAProxy之上。HXProxy有写节点池:每个MySQL集群有这么个池,每个池仅只一个后端服务器 — 集群主节点。所有数据中心的GLB/HAProxy机柜用同一个池,亦即用池里的同一个后端服务器。因而,应用如果想写入到mysql-writer-1.github.net,和其所连接到的GLB服务器无关,它总是被路由到cluster1集群的主节点。
应用只需了解,发现终结于GLB,不存在重新发现的必要。剩下的就由GLB负责把流量路由到正确的目的地。那么GLB怎么知道服务于那些后台服务器,并且怎么扩散对GLB的修改呢?
Consul是众所周知的服务发现解决方案,也提供DNS服务。在我们的方案里,使用它作为高可用的KV存储。
在Consul的KV存储里存放集群主节点的标识。对每个集群,有一组KV条目标识集群主节点的fqdn、port、ipv4和ipv6。
每个GLB/HAProxy节点运行着consul-template:一个监听Consul数据变化的服务(对我们来说就是对集群描述数据的变化),该服务产生一个合法配置文件能用于重载HAProxy(当配置变化时)。
因而,Consul里一个主节点标识的变化被每个GLB/HAProxy机柜跟踪着,并对自己进行重新配置,将新主设为集群后台服务器池里唯一实体,并重载以反映这些变化。
在GitHub我们每个数据中心都有一个Consul,都是高可用结构。但这些结构是互相独立的,不互联复制也不共享任何数据。
那么Consul怎么获知变化?以及信息怎么在跨数据中心间传递的?
我们运行着一个orchestrator/raft结构:orchestrator节点间通过raft互联交流。每个数据中心有1-2个orchestrator节点。
orchestrator负责失效检测,MySQL失效切换,以及Consul里主节点信息变化的传递。失效切换由单一的orchestrator/raft领头节点操作,但变化 — 集群有个新主的消息,被扩展到所有orchestrator节点,通过raft机制。
当orchestrator节点接收到新主变化的消息,他们与本地Consul交互:触发一个KV写操作。有多个orchestrator节点的数据中心可能触发多次对Consul的写入。
当主节点崩溃时:
orchestrator检测到失效。
orchestrator/raft领头节点触发一个恢复。一个新主被提名。
orchestrator/raft广播主节点变化给所有raft集群节点。
每个orchestrator/raft节点收到领头节点变化通知,更新本地Consul的KV存储(新主的标识)。
每个GLB/HAProxy的consul-template监听到Consul里KV值变化,随即重新配置并重载HAProxy。
客户端流量转向新主。
流程中每个组件都有明确的功能,整个设计解耦得很好也相当简单。orchestrator不知道负载均衡,Consul不知道信息从哪儿来,HAProxy只关注Consul,而客户端只关注Proxies。而且:
没有DNS变化需要扩展。
没有TTL。
流程不需要死旧主协作。
为了让流程更加安全,我们还做了这些工作:
HAProxy配置很短的hard-stop-after。当其重载新的后台服务器到写节点池,它自动地终止所有对旧主的连接。
用hard-stop-after参数我们不必寻求客户端的协作,从而减轻脑裂影响。当然这明显没有完全消除,我们断掉所有旧连接需要时间。但总算有个时间点,在那之后让我们感觉舒服并且不会有令人不爽的意外。
我们不严格要求Consul在任何时候都可用。事实上,我们只需要它在失效切换时能用即可。如果Consul恰好不可用,GLB会接着用最后已知的信息完成操作,不会造成什么极端行为。
GLB被设置为校验新提名主节点的标识,类似我们的context-aware MySQL pools,会对后台服务器进行检查,确认其确实是个写入节点。如果我们不慎删除了Consul中的主节点标识,也没问题,空项会被忽略。如果我们不慎将非主服务器写到到Consul里,也没问题,GLB将拒绝更新它仍按旧状态继续运行。
我们继续追寻高可用的目标和关注。
orchestrator用holistic approach来检测失效,这是很可靠的。我们没有观察到错误的失效切换,也就没有承担不必要的宕机时间。
orchestrator/raft更进一步深究完整的数据中心网络隔离的情形。该情形下会出现误导:数据中心里服务器能互相对话。但此时,是自己被其他数据中心隔离了,还是其他数据中心被隔离了?
在orchestrator/raft结构里,raft领头节点负责失效切换。领头节点是个被组里多数支持的节点(类似民主投票)。我们orchestrator节点部署单中心选主,而是任何n-1个中心来做。
在完整的数据中心网络隔离事件里,数据中心的orchestrator节点从其他节点(在其他数据中心的)断开连接。这样,隔离数据中心的orchestrator节点就不能成为raft集群的领头。如果任何这类节点不巧成为领头,它会宕掉。一个新的领头会从其他数据中心赋予,这个领头有其他数据中心的支持(投票),它有能力相互之间通信。
因而,这个orchestrator节点被叫做【枪击】,它是网络隔离数据中心外面来的。假设在隔离的数据中心里有个主节点,orchestrator会触发失效切换来替换它,用其他数据中心获得的服务器。我们缓解了数据中心隔离,通过委托其他非隔离的数据中心进行选主。
总宕机时间能显著地降低,如果能更快地广播主节点变化。怎么做到呢?
当orchestrator开始失效切换,它观察可能被提名的众多服务器。通过提示或限制,理解复制规则和曾经记忆,它会依据合理程序做出科学的选择。
它认为可被提名的服务器是个理想候选者,依据:
没有阻止该服务器被提名的任何障碍(或者用户潜在地提示该服务器是适合被提名),并且
该服务器能被期待可以让它的兄弟节点作为复制节点。
满足条件下,orchestrator将其先设置为可写入,并立即广播该服务器提名(写入到Consul库里),同时异步地开始修复复制节点树,该操作通常需要花几秒钟时间。
等到我们的GLB服务器都完成重载,复制节点树也基本完成重整了,当然这并不是必须的。服务器恢复到可写入就算OK。
在MySQL里,半同步复制是主节点不确认一个事务的提交,直到数据变化已被传递到1个或多个复制从节点。该行为提供了一种达到无损失效切换的方法:任何主节点的变化都已在复制从节点上应用或正在应用。
一致性是有成本的:对可获得性造成风险。假设复制节点没给收到变化的确认,主节点会阻塞,写操作堆积。幸运的是,可以设置超时,超过时间就让主节点切回到异步复制模式,让写操作可以继续。
我们设置该超时时间在一个合理低值(500ms),这相对于将变化传递到本地DC的复制点的时间足够久,甚至够传递到远程DC的复制点。在这个超时时间内,我们观察到完美的半同步复制行为(没有跌落到异步复制),以及满意的短暂阻塞(当确认失败时)。
在本地DC复制点我们用半同步复制,在遇到主节点死亡,我们期待(不是绝对要求)无损的失效切换。无损的失效切换在一个整个DC失效中代价过于高昂,不是我们的诉求。
在实验半同步复制超时时,我们也观察到对我们有利的现象:在主节点失效时我们能够影响理想候选者的标识。通过在指定的服务器设置半同步复制,标识其作为候选者,我们能减少总宕机时间(通过影响失效结果)。在我们的实验中,我们观察到能更快完成候选者筛选,并因此加快新主广播。
相对于让pt-heartbeat服务在当选_落选的新主上启_停,我们选择让其在任何时间任何地点都运行着。这需要对pt-heartbeat打个补丁让其能够适应服务的读写状态变换,甚至完全宕机。
在当前配置中pt-heartbeat服务运行在主节点和复制点上。在主节点上,它产生心跳,在复制点上,它标识服务器为只读并定时循环地检查其状态。一旦某个服务器被提名为新主,其上的pt-heartbeat就标识其为可写入并且开始产生心跳。
orchestrator承担了更多的工作:
Pseudo-GTID生成器。
设置新主可写入状态,清除复制点状态。
设置旧主只读状态(如果可能)。
这是为了减少新主各工作的冲突。新主被选出显然是期望它活着并可访问,否则我们就没必要提名它。当它能感知了,就让orchestrator将变化直接应用到它身上。
代理层让应用无法感知到主节点的标识,但他也让应用的标识被屏蔽在主节点之外。所有主节点看起来连接都都来自代理层,丢失了连接的真实来源信息。随着分布式系统前行,我们仍有些未处理到的情形。
尤其,在数据中心网络隔离情形,假设主节点是在被隔离DC里,而同DC的应用还能写入到主节点,而此时网络恢复了,这就可能导致状态不一致。我们正在探索降低脑裂后患的方法,通过实现一个可靠的STONITH,在正好隔离的DC里。像之前一样,将主节点关停需要点时间,这仍将存在短暂的脑裂期。完全避免脑裂的操作成本是相当高昂的。
更多的情形包括:Consul在失效切换时宕了;部分DC隔离,等等。我们明白在分布式系统里,堵上所有的漏洞是不可能的,所以我们专注于最重要的情形。
我们的orchestrator_GLB_Consul架构实现了:
可靠的失效检测;
不可知数据中心失效切换;
典型的无损失效切换;
数据中心网络隔离支持;
降低脑裂损害;
没有协作延迟;
大多数10-13秒左右的总宕机时间,少数要20秒,极端要25秒;
到此,相信大家对“GitHub的MySQL高可用怎么解决”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。