#高可用與高性能mysql
首先要明晰兩個概念:高可用和高性能,高可用和高性能在某些「方法論」或者實現上有相似的地方,可是其解決的問題和側重點有所不一樣:linux
高可用關注的是「持續可用問題」,我知道這是廢話,那麼何爲「可用」,簡單的的說「部分服務器掛了依然還能爲用戶提供服務」。衡量可用度有一個簡單的指標:可用性,他是一個百分百比,具體爲:在一段時間內服務器(組)正常提供服務的時間/總的時間100。若是你的服務器(組)在93%的狀況下是正常的,咱們能夠認爲系統可用性爲93%,固然對於單臺服務器或者組還有其餘指標參數,好比宕機時間(平均故障間隔時間),故障率等。 如下是github的狀態頁面:https://status.github.com 上面顯示了系統可用性和網站的其餘指標,能夠做爲用戶的參考。nginx
高性能側重於「規模」問題,當你的計算量很是大的時候,好比須要處理的數據量(請求量)很是大,或者解題步驟很是多或者繁雜的時候的時候咱們爲了節省時間,提供更優質的服務須要考慮高性能問題;git
咱們假設程序和系統已經到最大優化,此時高性能依賴於服務器或者CPU的「堆疊(集羣)」,一臺服務器能夠服務100人,兩臺服務器能夠200人,固然這是理想狀況。github
而高可用依賴於容錯機制,好比失敗以後採起什麼措施,部分服務不可用以後的熔斷措施等,固然備份也是高可用的一個具體措施(包括數據備份),哪怕是冷備:有的銀行在採購的時候一樣的設備會買兩套,一套放在倉庫裏晾着,生怕天災人禍而供貨商貨或者生產商不在繼續生產該型號的設備,都是保證系統高可用的一週手段。web
##高性能和高可用的交叉點: 1,增長或者加強設備是相同的手段 咱們須要增長設備來實現其中一套設備掛了還能使用另外一套的方式來實現高可用(我的以爲備機,不管是熱備仍是冷備,是高可用的必要條件);一樣增長設備,若是你能保證兩套設備同時工做分擔任務,他便能增長系統的性能(我以爲吞吐量或者處理能力這些詞更適合這個場景) 2,機制或者方法的交叉點 綜合而言高可用通常有兩種機制 a,保險絲熔斷機制,這種機制在系統出現問題的時候將問題系統切出去,一面問題擴大化,航天飛機電路設計的時候大概會採用這種機制,當某一電路出現問題,爲了不問題蔓延須要斷開問題系統;軟件種也須要這種機制,1,避免數據污染,2.避免請求堆積;這些問題之後能夠討論 b,線路(備份)切換機制 當系統出現異常,將備機自動或者手動切換過來的機制保證高可用的一種手段,切換的速度和精確度是系統可用性的一個標準。一樣,在高性能集羣,尤爲是負載均衡集羣種,將不一樣請求「路由」到不一樣的服務器也是一個必要的手段。redis
#高性能集羣 高性能集羣的方法取決於解決問題的性質,第一,問題(數據規模)是否是能夠分解;第二,問題是否是能夠分步 第一點關注數據或者規模的水平切分,就是將一塊大數據或者一個大問題分解成不一樣的小問題而後分佈到不一樣的節點處理,以後或許要彙總(這是mapReduce的核心思想),第二點傾向於將問題分紅不一樣的步驟,好比我本來須要先洗菜,而後再炒菜的,可是我得想想我能不能將洗菜和炒菜一塊兒放在不一樣的地方作(固然這個例子必然是不行,對吧),這是分佈式計算的核心。算法
高性能集羣分負載均衡集羣和分佈式計算集羣;負載均衡集羣爲了解決規模分解問題,也就是水平切分; 分佈式計算主要解決問題分步解決問題(從業務層面上講);sql
舉個例子:咱們假設完成一筆交易須要作一下幾件事情:1,收到,並記錄用戶請求 2,將請求抽象成一個「交易訂單」記錄到數據庫3,交易系統調用外部系統(能夠是一個抽象了的金融基礎服務,也能夠是一個第三方,好比銀行的外部服務)完成交易。3,銀行或者外部系統通知交易系統,交易系統更新交易狀態。4,交易系統同步返回用戶,告知交易結果。5,交易系統調用短信服務,通知用戶交易結果。數據庫
作這些事情的服務器能夠是不一樣的,他們各自完成交易環節的一小部分,組合在一塊兒完成了整個交易的流程。這些服務器實現不一樣的功能,組合在一塊兒是一個簡單的分佈式計算系統。分佈式計算側重於業務的垂直切分,側重於業務層面的抽象和複用。
假設咱們發現交易系統單機沒法應付目前的交易量,咱們須要對相同的交易系統部署兩份(或者多份),讓這個兩份系統協同完成規模龐大的交易請求,此時他們將組成一組功能或者業務相同的交易負載均衡系統集羣。
#正文 以上是準備工做,本文主要討論「高性能集羣」中的「負載均衡集羣」,以及常規場景
#何爲負載均衡以及爲什麼須要負載均衡 若是看了上面的準備概述,我以爲就沒有必要概念性的解釋負載均衡了,咱們舉個例子; 一家網站,爲用戶提供新聞資訊服務,剛起步的時候只有幾百用戶,咱們從成本出發準備一臺虛擬機就能解決問題;可是隨着網站訪問量的增長,服務器資源耗今,用戶反饋訪問緩慢;須要兩臺或者更多的機器才能提供相同質量的服務,此時最節省成本的作法是再買2臺機器(這相比於優化程序優化系統的成本要小的多,獲得的收益也可能會好得多);3臺服務器分擔請求,理論上速度能夠比原來快2倍。 那麼問題來了:如何作?如何讓一部分用戶使用服務器A,另外一部分用戶使用服務器B或者
##負載均衡集羣的難題(如下其實都是一個問題): ###均衡問題 咱們有兩臺如出一轍的服務器,咱們更多的是但願於兩臺服務器分擔相同量的工做,而不是一臺服務器很忙,而另兩臺臺服務器很空閒。這每每很難,均衡問題不只僅取決於使用的方法或者策略,更取決於你對問題的預判,還有對現實問題的妥協。惟一的作法是不斷的調整,積累經驗逐步調優。 最基本也是最簡單的均衡策略是:隨機,固然還有輪詢,基於後端最少使用或者人爲權重; a.隨機(Random): 不考慮實際狀況,將問題或者請求隨機的拋給兩臺服務器,就像跑硬幣實驗同樣,訪問量越高,就越均衡。 b.輪詢(Round Robin): 不考慮後端服務器的實際狀況,ABCABCABCABCABC。。。的方式依次給A,B,C三臺服務器。 c.最少使用(LeastUse/LeastActive) 服務器選擇相對空閒的機器最爲下一個請求的處理者。這相對要複雜一些,由於實現這個策略須要負載設備和ABC三臺臺服務器通訊,獲得AB和C的資源使用狀況,固然對每一個後端的服務維護一個簡單的計數器,調度最小活躍的機器也是一個很好的辦法。 d.人爲權重 本質上和隨機沒什麼區別只是加入了人爲因子(咱們能夠理解隨機策略每每得不到理想中均衡,須要人爲設置一些因子做爲調整,從而獲得一個相對均衡) e.Hash、 使用hash或者一致性hash也是一種很好策略。 f.等。
###路由問題 路由或者線路切換問題是負載均衡的核心;(如何根據業務(這很重要)來實現路由策略是一個很大的難題;這須要考慮上面的「均衡問題」以及下面的「高可用當機均衡問題」,總而言之理由要作的不只僅是簡單的線路上的問題。) 固然這裏但講線路切換問題: 線路切換每每依賴於網絡
a.基於解析服務 採用中間人模式,好比: 請求每每不會直接到達前置服務器,更多的時候咱們使用DNS,他是一箇中間人的角色,經過解析一個域名得到前置服務器的具體IP,隨後再直接基於解析出來的IP地址;再好比dubbo的負載咱們能夠認爲是藉助一箇中間人(註冊中心)
b.物理線路切換 簡單粗暴的將網線拔下來,插在另外一臺相同服務器上是簡單粗暴解決可用性的一個好方法;然而你不可能作到一秒鐘內插拔200次甚至更多;固然你能夠說我在光纖種使用不一樣長度的波,或者無線網絡,我基於不一樣的頻段或者頻道來作(實際上這種方式並不能很好的用於此種場景);就目前而言物理層線路切換的代價過高了。
c.三層、四層的轉發 多數狀況下基於IP層的轉發是效率相對最好也是最廣泛,基本硬件負載或者基於IPVS(LVS)的負載 只需解析從新封裝三到四層的協議,他不會產生產生流量,也不須要維護兩份鏈接句柄(七層轉發種會提到),若是基於LVS軟負載,基於linux net filter實現的轉發,只需在linux內核層工做,避免了內核層和用戶層的內存拷貝所帶來的開銷,甚至一臺廉價的戴爾PC服務器就能穩定的到達10W的connection,沒有足夠的資金購買硬件負載,而訪問量極高的系統建議採用此種方式。
d.七層轉發 所謂的七層協議,就是應用層協議(有的書上會說五層 what ever/),開發經常使用的HTTP協議就是第七層的協議,mysql服務器或者redis本身實現的協議也是第七層協議;使用NGX或者HTTPD解析七層協議,而後rewrite是目前主流的解決辦法;理由很簡單:設置簡單,維護方便,可自定義程度高。相比於LVS我相信絕大多數系統工程師對NGX會更加熟悉一些,這就帶來了設置接單維護成本低,出了問題好解決;相比第三四層的協議,七層能夠獲得最大靈活度,解析HTTP協議得到URL得到用戶傳遞過來某一個參數(好比sessionid等),簡單的腳本或者正則就能定義個性化的轉發策略。一樣MYSQL proxy,AMIBA這種代理中間件經過解析MYSQL協議得到發送的SQL,經過簡單的腳本語言(好比mysql 使用lua...)能夠很靈活的更具SQL的內容來肯定具體的後方服務器,好比讀寫分離; 七層轉發會產生流量的,同時七層代理服務器須要維護先後兩個鏈接句柄(收到用戶請求產生並維持一個服務鏈接,同時做爲客戶端向後端服務發起請求是另一個鏈接),這種轉發模式通常在用戶層實現,系統須要將內核層的數據拷貝到用戶空間,完成解析和處理,再將用戶空間的數據拷貝到內核空間,由linux的網絡子系統轉發,因此咱們說他會產生流量,相比三四層的轉發其代價就要高不少。
e.基於客戶端的路由 咱們在客戶端實現請求具體去哪個服務器,經過配置一個服務器集羣列表或者從「名字服務器」查找和下載這個列表,而後由客戶端決定具體選擇哪條線路的路由。redis官方的「哨兵」方案就是這種模式。
綜述:DNS輪詢屬於中間人模式,dubbo一樣採用中間人方式;三四七層轉發屬於代理模式(proxy);基於用戶選擇其實是講路由的功能放到客戶端實現,好比淘寶的數據中間件等採用了這種模式(dubbo是向註冊中心獲取服務提供者列表而且存儲的,策略在註冊中心實現,一樣服務器提供者的負載策略也不徹底取決於服務的消費者);選擇四層轉發(交換)仍是七層不只僅取決於性能需求,更多的仍是取決於功能需求,好比你的系統要求經過URL的某個關鍵字或者參數來決定具體線路的,你可能就必須得選擇「七層設備」來作代理了。
###代理模式的單點問題 上面簡單的講負載均衡的方案或者方式概括了一下;其中的代理轉發模式會存在「單點故障問題」,好比最簡單前置服務器爲NGX,後面有兩臺TOMCAT服務器,當前置NGX當機或者出現問題的時候,服務必然會中斷。那麼咱們應該怎麼解決這個問題呢?
其中的一個答案是VIP(虛IP),虛擬IP的方案是基於VRRP協議(虛擬路由冗餘協議)的,它將局域網中的多臺設備(路由)虛擬成一個設備,以一主多從的形式工做。經常使用的開源方案是KeepAlived,從名字上大概能夠看出keepAliveD接管一個對外的虛擬IP,並在多臺設備之間維持心跳。 如下是網上的圖片很簡單的說明了這個方案:
另外一個答案是:中間人模式,好比基於DNS的切換
###負載均衡的高可用以及當機均衡問題 理想狀況下使用均衡策略慢慢優化,能夠獲得一個相對滿意的結果,可世事難料,忽然有一天B服務器掛了,若是不作任何事情你可能會損失1/3的頁面請求,實際上此時咱們須要將這部分的請求從新定位的好的A和C服務器,選擇一個好的策略或者算法避免將請求過於集中的切換到單臺服務器(這可能會一下拖垮那臺服務器)是一個重要的問題;我將其稱爲「當機再均衡問題」;假設B掛了,咱們經過合理的算法將請求定位到AC服務器,系統勉強能夠支撐,此時咱們的工程師將B服務器修復,從新加入集羣,2臺服務器又回到了原來的三臺服務器的狀態,這依然須要「再次均衡」不是嗎?
#通用的負載均衡方案
通常而言經常使用四層協議相對單一(注意基於四層交換設備不能理解四層以上的協議) ##LVS+KeepAlived 其中LVS負責四層轉發並維持會話(經過檢索會話表和標記來維持會話鏈接,避免發往相同地方的數據包走不一樣的線路),而KeepAliveD則負責接管虛擬IP和維持心跳;
##HAProxy+KeepAlived 其中HAProxy負責四層或者七層的轉發,而keepAlived負責接管虛擬IP和維持心跳;HAProxy是經常使用負載均衡方案,相比於LVS而言HaProxy工做在用戶層,提供更多更豐富的功能;
而七層協議相對就豐富了,各個軟件根據本身的功能需求能夠實現本身的七層協議; 經常使用的七層協議包括郵件協議,HTTP協議,FTP,對於各個關係數據庫或者非關係數據庫通常也都有本身的協議。要理解和解析這些協議才能完成七層轉發。
經常使用的HTTP代理包括市場上多數的硬件負載,NGX等web服務器;
#負載均衡經常使用場景 ##前置WEB服務的負載均衡 通常指接入服務器,好比靜態頁面服務器,接入代理服務器等的負載均衡 一般對NGX這種接入服務器作集羣須要在其前面加入一個四層或者七層代理,使用上面所說的通用方案是比較好的選擇,固然你也能夠將NGX做爲NGX的七層代理(在NGX前面再放一個NGX),造成一個樹形的結構(固然我沒試過那麼作,印象當中NGX+NGX的方案只在爲了數據轉發問題的狀況出現過,大概是機房A的服務器做爲接入,轉發到B的接入服務器,我的以爲不是一個負載均衡的常規手段)。
##業務服務器的負載均衡 這裏的業務服務器通常指完成必定業務功能和邏輯服務組件,好比tomcat、jetty等,咱們須要在這些業務服務器前面加一個Proxy來做把控全局: ###NGX+業務服務器 好比nginx/apache httpd+tomcat/jetty等。ngx做爲前置接入,基於七層的NGX或者HTTPD是一個很好的選擇。 ##Redis的負載均衡 常規的基於四層的 ###單機Redis沒法知足業務需求的兩種狀況 1,性能沒法知足(這裏一般指響應速度),通常採用複製+負載均衡的方式實現這種業務需求場景 2,容量沒法知足,咱們知道Redis將數據放在內存當中,而實際上內存大小是有限的,好比一臺服務器64G的內存,而實際上咱們的數據量達到了128G左右,咱們忽略系統和軟件運行所佔的內存開銷,128G的數據沒法放到64G的單機上去,必須分佈到2臺甚至3臺服務器上去,這些服務器上的數據是不同的,並不能簡單的經過複製來實現,這個時候咱們須要對數據「分塊」,將部分數據放到A,其餘數據放到B。 基本的由第一種狀況致使的問題咱們可使用複製+通用七層,七層,客戶端實現等方式來實現負載均衡;而第二種狀況相對要複雜一些,咱們也能夠經過七層或者客戶端實現路由算法的方式來實現數據的分佈存儲問題。 a,haproxy+KeepAliveD b,twemproxy:推特開發的代理服務器,基於七層轉發,同時支持redis和memcached協議。可以使用KeepAlived來解決代理的點單問題。
##MYSQL負載均衡 MYSQ,其實任何數據存儲方案都面臨上面Redis一節種提到的兩個問題,對於數據庫來講,更多被提到的是分表分庫,這是基於業務討論的,通常簡單只是爲了知足訪問性能的需求實現的「同構」的方案先對簡單,通常的叫法是讀寫分離,該方案的核心思想是講讀數據和寫數據分離,以免鎖帶來的開銷,其實現的基本思路是複製(主從複製); 除了通用的基於四層的負載均衡方案(HAProxy/LVS),咱們還可使用一些中間件來完成MySQL的負載均衡,一般更業務掛鉤的狀況下咱們都會選擇七層轉發做爲,咱們知道MYSQL有本身的協議,對於
a,關於MYSQL集羣和分佈式問題 mysql提供ndb引擎,這是一個分佈式的存儲引擎,相比於簡單的同構集羣(好比讀寫分離等)
b,分庫分表問題 隨着業務規模的增長
c,關於複製: 數據庫的複製多數採用二進制日誌的方式,也有按照快照的複製方法,相比而言各有各的優點,好比你經過updage語句(update xxx where id < 10000)咱們假設這條語句更新了10000條數據,若是經過順序的日誌的方式來實現,咱們在同步從服務器的時候只須要發送和處理一條一句,而經過快照的方式咱們則須要將這10000條數據(至少是diff部分)發送到
複製的通訊通常採用推(push)或者拉(pull)的方式,二者的區別在於誰是主動一方,以及實時性問題。採用推的方式能夠簡單的理解爲主服務器上數據發生變化主動推送到從服務器上。拉的方式則是每一個一段時間完成一次同步。
##應用程序的負載均衡 每種語言有本身的實現,這裏講一下dubbo的負載均衡。 dubbo的核心是registery,這至關於一個目錄查找服務,提供服務的組件向註冊中心註冊本身的服務,消費服務的組件向註冊中心詢問服務提供者的位置。 dubbo是分層設計的,每一層解決不一樣的問題,詳細的設計圖能夠再官方文檔上找到,dubbo實現負載均衡的層是cluster這一層實現,該層的主要目標是爲上層提供一個通用的統一的界面,實現負載均衡在上層的透明。對於用戶來講相同的服務能夠在不一樣的機器上部署多分,同時用戶能夠經過簡單配置來實現負載策略,咱們也能夠經過實現LoadBalance相關接口來定製本身的負載策略,而ZK的一致性hash算法能夠解決服務的單點問題