您好,登錄后才能下訂單哦!
本篇內容主要講解“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進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。