胡宏娟
中國電信股份有限公司江蘇分公司
(1)異地多活,是指在多個不同物理地域之間同時提供業務流量和商業服務,各個地域之間不是主備關系,各個地域有自己獨立的基礎設施以及數據中心,業務流量可以不均等地分配在某幾個地域之間。與異地冷備相比,其不僅具有更高的成本優勢,而且更具有可擴展性。異地多活實際上就是讓各個地域同時承擔部分業務流量,緩解單一地區的業務壓力。與冷備相比,異地多活提供了更快速的業務恢復能力,業務流量可以隨時在不同地域相互切換,達到了提供商業可持續性的同時進行流量動態分配的目的。
(2)CAP理論。異地多活涉及分布式系統和數據庫,就涉及著名的一致性、可用性、分區容錯性(Consistency-Availability-Partition tolerance,CAP)理論。一般CA是無法兼得的,P是無法放棄的,所以所有分布式系統的設計要么是保AP、要么保是CP,如圖1所示。

圖1 CAP理論說明
多數互聯網公司都經歷了從單機房、同城雙活、異地災備,再到異地多活、單元化的過渡。本文著重從架構和存儲兩個方面來講述。
目前的應用一般都部署在公有云平臺上,硬件方面(包括網絡、電力、散熱)都是冗余的,服務方面則是通過集群模式來減少機器、機架故障造成的影響,對于SLA要求不高的應用,基本能滿足要求。
單機房部署在整體組網架構中存在單點隱患,一旦機房出現故障,服務只能暫停并等待恢復,災害性事故導致服務長時間中斷所引發的資產損失以及社會效益損失,都是企業無法承擔的。所以,BAT等大型互聯網公司都會考慮服務的冗余,如“同城雙活”或“同城災備”。
同城雙活和同城災備的差別在于兩個機房是否都跑流量。(1)部署方面:當單機房因斷網、斷電等故障而停止服務,同城機房可及時彌補;而同城機房的物理距離足夠近,機房之間通過光纖專線連接,跨機房調度的時延(<3 ms)一般可以忽略不計,在部署方面和單機房相比并未提高復雜性。(2)分層方面:第一,接入層。CDN、4層代理、7層代理、網關,可以選擇路由的方式,如隨機、輪詢、權重、一致性哈希等,也可以選擇有目的性的路由策略,如主機房優先、同機房優先、自定義路由規則等。第二,服務層。設計為無狀態的,應用之間用SOA框架連通,如Dubbo、Spring Cloud、Istio等,內部可以橫向擴展,具備auto scaling能力。第三,存儲層(以一般數據庫為例,高端存儲設備不作描述)。一般互聯網公司使用MySQL,數據拓撲架構為Master-Slave,采用主從復制(async或semi-sync),在數據中心切換之前,僅在主數據中心進行寫操作,同步到非主數據中心的數據庫,便于主庫故障時可以手工或自動切換(failover),減少恢復時間。這種架構問題主要是偽雙活,當主數據中心故障時,數據的一致性無法得到保障,解決方式是使用分布式數據庫,利用Paxos、Raft分布式一致性協議保證強一致。
(1)同城雙活+異地災備架構:同城雙活能解決架構上的單點問題,但依然無法擺脫地域性的災難,如地震、洪水等自然災害,跨地域部署鏡像災備機房和服務便成為進階考慮。但大量的跨地域調度,時延往往比較可觀,如果在網絡設計和帶寬方面高可用設計或資源冗余不足,網絡抖動往往容易影響業務的穩定性,所以,一般異地備份機房只做災備,采用非對等獨立部署(更小規模、各層獨立部署)不承載業務流量,存儲基于數據庫同步機制進行復制。這種同城雙活+異地災備的架構,一般稱為“兩地三中心”架構,即2個地域、3個IDC,如圖2所示。這種架構適用于中小型應用,由于容災機房不跑流量,對于數據一致性要求很高的場景有一定風險。同時,擴容和集群伸縮性、計算、存儲、網絡等資源受限于單地域的容量。

圖2 兩地三中心架構示意
(2)異地多活架構:地域之間不是主備關系,不同物理地域之間同時提供業務服務,不涉及故障切換failover的工作(流量會有策略調度)。failover的災備策略往往不是自動化的,而且一般比較復雜,需要測試演練,所以維護成本、切換風險很高。異地多活架構如圖3所示。異地多活下,各個地域機房獨立運行,流量可以按策略調度到各個地域和可用區,與異地災備相比,其優勢在于:(1)成本更低,因為兩個機房都跑流量。(2)復雜度更高,因為需要涉及流量的調度策略。(3)若不同機房的流量產生切換,則會帶來更復雜的數據同步問題。(4)伸縮性好,服務和數據可以相對平衡負載在各個區域,不受單地域資源限制。需要說明的是,并不是所有應用都需要做異地多活,一般對主鏈路業務、SLA要求較高、擴容頻繁的業務優先做,否則方案會比較復雜,工作量也比較大。

圖3 異地多活架構示意
實現了異地多活,也并不代表到達了終點。異地多活主要的問題在于跨地域延時,業界目前的解決方案主要集中在網絡層面(如搭建BGP網絡、CDN加速等)。在架構層面,阿里2016年提出了 “單元化”概念,讓特定user的請求和數據收斂、封閉在同一個IDC,單元高內聚,盡量減少跨區域訪問,即“單元封閉”。一些流量較小的長尾應用,仍然只在中心部署,配套容災方案。
單元化的策略是按照某個業務維度拆分用戶,例如,線上商城和支付App一般是按照會員ID(userid)取模劃分單元的,物流一般是按照user所處的地域劃分。每個IDC根據URL設定路由策略調度流量,并封閉服務和存儲,只承載本單元的請求。單元化架構如圖4所示。

圖4 單元化架構示意
對于傳統數據庫,需要額外配套HA策略來切換Master和Slave。MySQL在本區域或者跨區域的高可用,可以通過主從復制(replication)加多副本(replica)實現,一個實例做Master,其他實例做Standby,主故障即可切換,其他可以做本區域或者跨區域的復制,僅只讀,擴展讀能力。數據庫集群高示意如圖5所示。

圖5 數據庫集群高可用示意
假設有5 000對MySQL主備實例,又有一套用來切換MySQL主備實例的HA工具,要做到任意一臺主庫宕機,HA都能夠在極短的時間內切換主備,需要實現以下功能:(1)部署監控代理。為了監控各個主庫的狀態,需要在各臺機器上部署Agent以采集實例健康狀態。Agent以一定時間不斷向HA匯報實例的健康狀態(Heartbeat)。(2)Heartbeat與仲裁節點。這些實例的健康信息如果存放于一個節點上,也存在單點風險。因此,需要把風險分散到多個節點上,被稱為仲裁節點。(3)數據一致性。
到此,HA已經是一個分布式系統,與ZK息息相關,因此,對于分布式集群的管理,最終還是要通過Paxos數據一致性協議。基于這種思路,業界涌現了很多分布式數據庫的解決方案。
對于傳統關系型數據庫,無論是Oracle還是MySQL,最通用的做法是通過存儲共享或者日志同步來保證。
(1)Oracle的存儲共享。比較著名的是Oracle的真正應用集群(Real Application Cluster,RAC),基于share everything架構,比如,3個節點的RAC系統在CPU、內存上是做了擴展,但是為了保證數據強一致性,存儲做了共享。這種架構最大的弊端是各個節點爭用熱點塊而引起的“GC”(Global Cache)等待事件。引起“GC”事件的原因是同一時間只能有一個節點在更新同一個塊。Oracle RAC在Fusion Cache層做了內存鎖,用以協調各個節點對同一個Block的Update操作。Block有資源Master概念,只有得到Master的授權,才能對塊進行更改。因此,在架構設計上,Oracle引入了動態資源管理(Dynamic Resource Management,DRM)。另外,為了解決集群的腦裂(Brain Split),Oracle引入了基于共享存儲的Voting Disk,來對Brain Split進行仲裁。這種架構顯而易見的弊端是對硬件依賴非常嚴重,任何存儲介質的異常都必須進行容災切換,并且無法跨機房部署,做不到IDC間的容災。而基于Paxos一致性協議的分布式數據庫系統很容易做到,只需將其中一個副本存放于其他IDC中即可。
(2)日志同步。對于性能的損耗較大。無論是Oracle的Data Guard還是MySQL的Binlog同步,對于Master主庫的TPS有明顯的損耗。每次事務commit,必須要等到從庫ACK并成功應用后,主庫再返回給用戶事務提交信息。業界有非常多的優化方案,如日志緩沖區鎖優化、異步化、減少不必要的context switch等,但網絡開銷上即使采用萬兆網、調優網卡軟中斷模式,也很難得到改善。
以MySQL為例,如果采用異步復制(Replication),由于其異步特性,若數據未完成同步即切換,會導致數據丟失,PRO>0。此時,被提升為新主庫的數據自然與之前的不一致,如果在同步時停止服務,可用性就會受到影響,業務是無法接受的。
為改善Replication帶來的數據不一致問題,MySQL提供了半同步復制(semi-sync),即主庫需要接受從庫的ACK(代表同步到Binlog),才成功返回客戶端。對于幻讀,在MySQL 5.7版本之后,又可以通過設置AFTER_SYNC或AFTER_COMMIT模式來解決,需要注意,開啟semi-sync會影響寫性能。MySQL半同步復制AFTER_SYNC和AFTER_COMMIT兩種模式如圖6所示。

圖6 MySQL半同步復制兩種模式示意
開啟semi-sync,也不一定PRO=0,非高負載select的場景下,MySQL通常開啟InnoDB引擎,采用2PC進行提交,在事務Prepare階段寫完Redo log,事務不是commit狀態,需要走到Server層面,Binlog寫成功才進行解鎖,清理Undo log的commit工作,再返回客戶端。
MySQL支持用戶自定義在commit時,解決如何將log buffer中的日志刷到log files中的問題,主要方法是通過設置變量innodb_flush_log_at_trx_commit的值來控制Redo Log的刷盤策略,如圖7所示。其中,“0”:commit時,寫入log buffer中,每秒寫入os buffer并同步fsync刷盤,若進程crash,丟失1 s數據。“1”:默認模式,commit時,寫入os buffer并同步fsync刷盤,安全,但需要等待磁盤IO,性能差。“2”:commit時,僅寫入os buffer,每秒fsync()將os buffer中日志寫入到log file disk中,比“0”安全,比“2”快,若宕機,丟失1 s數據。

圖7 Redo Log的刷盤策略示意
通過調整參數innodb_flush_method的值來控制Redo Log的打開、刷寫模式。這個參數有4個值:(1) fdatasync:默認,調用fsync()去刷數據文件與redo log的buffer。(2)o_dsync:InnoDB會使用o_sync方式打開和刷寫redo log,使用fsync()刷寫數據文件。當write日志時,數據都write到磁盤,并且元數據也需要更新,才返回成功。(3)o_direct:InnoDB會使用o_direct打開數據文件,使用fsync()刷寫數據文件跟redo log,write操作是從MySQL InnoDB buffer里直接向磁盤上寫。(4)all_o_direct:與o_direct差別在于redo log也直接向磁盤上寫。這4個策略的選擇最終影響的是CPU、處理性能和響應時間,如圖8所示。

圖8 innodb_flush_method參數策略示意
Sync_Binlog控制Server層面Binlog的刷盤策略。在事務prepare階段,當MySQL進程Crash或者系統宕機、Binlog是否傳輸到從庫、相應的文件有沒有落盤,都會造成客戶端、主庫、從庫的數據不一致。MySQL的主從加上刷盤,是不能保證數據強一致的。如果采用semi-sync+強制刷盤的策略,可以做到不丟數據,但是MySQL實例之間的數據一致性無法保證。比如,redo log或者Binlog任意一方丟失,那么客戶端會感知失敗,主庫恢復時會rollback,如果Binlog同步到了從庫就生效了,從而出現主從不一致。
為了滿足可用性要求,減少數據不一致,可以優化策略:主庫宕機恢復后,先禁寫,避免新數據寫入主庫導致不一庫,等把gap diff apply到從庫上,如果沒有沖突,再恢復;否則,采用手工對帳訂正數據。
可用性不強求覆蓋100%的用戶,原則上滿足大部分用戶才是關鍵。面對跨地域的高時延和網絡抖動,遠距離同步Binlog有很多局限性。所以,存儲層面的數據同步在業界會采用DRC中間件解決,比較常用的是Otter和DRC(Data Replication Center)。對比Otter,DRC更關注業務的劃分,可以實現單元內封閉,而Otter只是在純數據庫層打通的技術。這類中間件的原理:從數據庫同步Binlog,異步的將數據進行解析、過濾、標準格式化(DRC Message)、復制遠程傳輸,中間件本身可以做持久化,便于實現一次訂閱,多點分發。可以在事務一致性的前提下提供秒級的DB同步,但其注重AP,犧牲了異地一致性。
在單元化異地多活的架構里,DRC可以承擔單元和中心之間的數據復制功能,如可以采用寫中心、讀單元的模式,中間數據廣播到各單元,單元通過中心做數據同步;也可以各單元部分寫,相互同步。請求可以做單元封閉,單元內的數據可以是全量的,支持區域故障時的機房切換。
比較熟悉的分布式數據庫有開源的TiDB、阿里云的RDS等,基于Paxos協議或者Paxos簡化變種Raft,來實現分布式系統的數據最終一致性。Paxos有兩種實現思路:(1)將Paxos與實際的數據節點獨立出來,如zk架構,比較簡單,但是增加了Leader和Follower的通信,帶來了更大的時延。(2)直接將Paxos實現到各數據節點的組件中,好處是時延低,但技術難度高,比如,螞蟻的OceanBase、Google的Spanner等。
分布式數據庫對數據的每次修改都看作一次增量log,再在存儲上套一層Raft協議。例如,3個節點的集群,Raft的Leader負責寫,Leader的事務日志同步到follower,超過多數派(majority quorum)落盤后,再commit。雖然延遲明顯高,但小幅延時卻可以保證數據一致性,而吞吐可以靠集群和拆分解決。以阿里云的RDS為例,通過Raft狀態機控制事務,會帶來明顯的延時問題,一般部署在同地域,依賴DRC(DTS)做異地復制。
從當前業界數據中心級的多活架構來看,私有云的部署一般以同城雙活或異地多活為主,更大規模的公司會嘗試采用單元化的方案來封閉數據和流量,減少異地流量的調度導致的網絡抖動,而造成業務的不穩定。但隨著公有云和混合云方案的發展,如今的技術架構又呈現了不同的發展趨勢,以同城雙機房的私有云配合多云的混合云方案,將成為另一種數據中心級容災的新趨勢。很多傳統企業或者較小的企業可以根據業務特性來選擇系統架構方案,由于異地多活的技術門檻高、投入成本大、運維要求高,并不適用于所有企業。就筆者所負責的系統而言,一般采用同城雙活,即可基本滿足業務要求。