色婷婷狠狠18禁久久YY,CHINESE性内射高清国产,国产女人18毛片水真多1,国产AV在线观看

mysql查詢延遲,如何實現分布式系統的高可用性

張吉惟2年前24瀏覽0評論
mysql查詢延遲,如何實現分布式系統的高可用性?

職場新兵的指南:關于高可用系統建設的一些思考

在參與公司幾個多數據中心項目的容災架構設計后,積累了一些高可用和多數據中心容災的一些思考,希望總結和分享出來和大家一起學習。

可用性衡量指標我們做軟件系統核心是服務于業務,構建高可用系統本質也是為了讓業務的服務質量提供,因為在構建高可用系統之前,我們需要根據業務特性確認我們系統需要怎么樣的高可用級別,也就是需要一個指標度量我們系統的可用性。

可用性度量的指標有以下幾個:

MTBF(Mean Time Between Failure),平均故障間隔,代表兩次故障的間隔時間,也就是系統正常運轉的平均時間。這個時間越長,系統穩定性越高。MTTR(Mean Time To Repair),故障的平均恢復時間,也叫平均故障時間。這個值越小,故障對于用戶的影響越小。但 MTBF 和 MTTR 這兩個指標中的故障不僅僅是IT系統宕機故障,也包括了性能問題和人為的錯誤。甚至USITS一項關于大型互聯網服務的研究發現,運維配置錯誤是導致服務中斷的首要原因,而硬件故障(服務器或網絡)僅導致了 10-25% 的服務中斷,但如何避免人為錯誤并不在今天的討論當中。

所以,在針對數據中心的容災,我們可能用到更多地是RTO和RPO這兩個指標:

RTO(Recovery Time Object),恢復時間目標,RTO是反映數據中心業務恢復的及時性指標。RPO(Recovery Point Objective),復原點目標,指數據中心能容忍的最大數據丟失量,是指當業務恢復后,恢復得來的數據所對應時間點,RPO取決于數據中心數據恢復到怎樣的更新程度,反映數據中心恢復數據完整性的指標。

一般我們對現有系統做可用性改造時,可以先看看現有系統的基準值是多少,然后根據業務目標,確定要提升到多少來改善。

高可用改造層級在和業務確定好可用指標后,接下來就需要對系統做高可用改造,從容災級別可以分為三個層面:

基于Region(地理區域)的高可用架構同城多Zone的高可用架構單Zone多實例的的高可用架構注:這里Region表示地區,一般是指行政單元比如地級市,Zone表示可用區,指電力和網絡相互獨立的物理區域

從軟件架構的角度來看,針對可用性改造可以分為四個部分:

DNS服務負載均衡(LB)應用服務數據庫/中間件其中,DNS服務和負載均衡都是無狀態的,數據庫和中間件則是有狀態的,而應用服務根據業務邏輯不同可能是無狀態也可能是有狀態的。

單機房多實例的的高可用架構這種架構是最簡單的,一般這種架構只有一個LB網關,通過LB轉發到下游的應用服務,應用服務可以通過服務發現的方式做成多副本從而實現多活,比如可以用k8s部署應用服務,配合存活探針檢查和k8s的service服務發現,可以輕松實現無狀態應用的多活。如果你的服務不基于k8s部署,也可以基于微服務框架部署在多臺主機上實現應用多活,只要實現服務發現、存活探針檢查和自動的流量切換就可以了。

但對于有狀態的,比如數據庫和中間件,僅僅依賴服務發現和流量切換并不能解決問題,因為數據在多副本之間需要做同步。因此針對數據庫或中間件的高可用方案基本都是需要專門設計,因為除了流量切換還要解決數據同步問題。這部分在后面數據庫和中間件高可用解決方案在單獨展開。

同城多機房的高可用架構同城多機房(多AZ)容災一般通過 BGP 實現單IP多線網絡,然后機房之間通過專線相互打通物理網絡,當出口流量出現故障時,可以通過 BGP 在路由層切換報文轉發表來實現線路切換。架構圖如下:

在數據鏈路正常的情況下,機房1,2會分別向路由宣告自己的路由表:機房1:地域AS -> 運營商AS -> 機房1 AS機房2:地域AS -> 運營商AS -> 機房2 AS

在機房1出現故障和運營商邊緣(接入)路由器斷開的時候,機房2會向運營商AS宣告連接機房1的最短路徑從而讓流量轉發給自己:地域AS -> 運營商AS -> 機房2 AS -> 機房1 AS

如果對路由器BGP協議不是很了解的可以參考BGP的wiki

基于地理區域的高可用架構如果我們要做基于地理區域級別的高可用,那么我們需要DNS智能路由和跨地域的云連接。

DNS智能路由DNS服務結合存活探測可以實現跨地域流量切換。當存活探測發現后端 LB 不可用的時候,可以直接修改DNS解析,使其失效。但需要注意 DNS 協議切換一些延遲,生效時間在15分鐘~30分鐘(RTO),因此 DNS 一般只用于跨地域的 LB 的高可用,只有當整個地區的 LB 不可用的時候才會被啟用。

我們知道跨地域的故障可以根據智能DNS協議來切換流量到不同的LB中,那么DNS服務本身是如何做高可用的呢?DNS 服務做高可用一般是基于 Anycast 和路由協議來實現,比如 BGP 或 OSPF。和同城多機房的高可用架構一樣,通過路由協議實現單IP多線的網絡架構來實現容災切換。Anycast 網絡允許網絡上的多臺服務器使用相同的 IP 地址或一組 IP 地址, 通過 BGP 的路徑選擇算法改變路由的選擇,從而使失效的DNS服務器節點下線,或者實現智能路由。不過 DNS 服務做了高可用并不一定就萬無一失了,2021年10月4日的 facebook 全球死機事件就是 BGP 配置錯誤導致了 facebooK 的 DNS 全部失效了。所以,像開頭說的,高可用系統架構只是解決了系統硬件故障,但人為配置錯誤并不能避免。

云連接跨地域的云連接可以是基于骨干網的專線,也可以是VPN。通過云連接將處在不同地域的子網連接起來構造一個互聯互通的企業網。專線相比VPN網絡傳輸會更穩定些,安全性更高,但價格也會更貴,成本更高。

數據庫及中間件的高可用方案數據庫和中間件的容災是一種典型的有狀態服務應用的場景,其核心是數據復制和同步。前面說到的兩個指標 RTO 和 RPO 就是圍繞數據容災來描述的,如果我們是雙活架構那么RTO就是0,如果是主備那么RTO就是主備切換所需的時間;如何我們的數據復制采用完全同步的方式,RPO就是0,如果采用異步復制,那么RPO就是數據復制之間的時間差,如果是快照,那么RPO就是快照產生備份數據和。

數據復制數據復制按照leader可以分為三種:單領導者(single leader,單主),多領導者(multi leader,多主) 和 無領導者(leaderless,無主)。單主和多主都屬于主從架構,從節點通過復制主節點的日志或變更流(change stream)來同步數據,但在使用場景上兩者存在比較大的不同,多主架構一般被用于地理位置上的多數據中心的容災和就近服務,而單主架構一般用于單數據中心,因為多主架構給整個系統帶來的復雜度是很高的,我們需要處理數據沖突,這會給整個系統帶來復雜性并降低性能。架構設計核心是要解決問題,因此本質上是一種取舍和balance,在架構設計的時候要視業務和場景而定。

單主架構單主架構就是我們常說的主從架構,是分布式架構中最簡單的架構,只在 leader 節點做讀寫操作,follower 節點提供讀操作。從庫復制主庫通常通過變更日志實現,這種變更日志既可以是預寫日志(WAL),也可以是復制邏輯日志。常用的數據庫的數據復制:

Kafka的kafaka MirrorMarkerMySQL的 binlog 主從復制PostgreSQL的 WAL 主從復制多主架構多主架構在主從數據同步邏輯上和單主架構是一樣的,區別核心在多個主節點寫入數據的時候如何進行數據同步。一般引入多主架構其中一個原因是解決跨地域數據同步問題,比如在單主架構下,一個在廣州的用戶在寫入數據需要需要傳輸到北京的主節點上,那么性能就會比較差了。另一個原因則是多主的故障容忍要大于主從,比如在兩個主節點的情況下,其中一個節點出故障的時候另一個節點并不會受到影響,只會影響一半的用戶,而主從架構在從節點切換完成之前是全用戶故障的。我們可以先從最簡單的,兩個主節點來討論。在雙主的結構下,核心要解決的就是寫入的時候的數據沖突問題,如下圖所示:

在解決數據沖突的時候通常會采用以下的一些辦法:

最簡單的方式就是采用同步的方式寫入數據,即數據寫入成功需要等待其他主庫解決沖突之后,這樣就將多個主庫寫入變成串行執行了,也就失去了多主庫的核心。通過分庫的方式來避免沖突,比如請求通過統一的路由讓a用戶數據都讀寫在A庫中,而b類數據讀寫在B庫,其實這樣本質類似于將數據庫以多分區的形式存放在多個地區,只是a庫會通過數據復制在同步到b庫。只保證所有副本最終一致性,通過全局唯一ID或全局時鐘確定最后更新的數據才寫入。但是這種方法在異步的情況下會導致一部分中間數據丟失。除了要解決數據沖突,多主在數據復制的時候還需要解決節點復制傳播的順序,也就是復制拓撲(replication topology)。對于多主復制,常見的復制拓撲主要有三種:

環型拓撲架構(circular topology),即一個數據節點將數據復制給相鄰的節點,依次循環一周。星型拓撲架構(star topology),所有節點圍繞著一個中心節點進行數據復制和交互。全拓撲架構(all-to-all topology),即所有節點相互之間都會進行數據的復制和交互,常見的比如 gossip 協議。

在多個主節點進行數據復制和傳播的時候,由于會經過多個節點,節點之間需要識別攜帶其他節點的變更信息,比如每個節點添加有一個唯一ID標識其已經過的節點,這樣才不會造成無休止的死循環無休止的傳播。

無主架構在無主架構中每個節點都可以對外提供服務,從設計理念上可以看出無主架構天生就是為可用性而生,不過知道CAP理論的都知道,可用性和一致性不能兼得,無主架構是個典型的AP模型,其犧牲了強一致性用最終一致性代替。無主架構中最出名的是 AWS 的 Dynamo,像 Cassandra 這種采用和他類似的無主架構的都被成為類Dynamo。Dynamo 采用Gossip協議來做復制數據,任何一個節點收到數據后會向其他節點異步地復制數據。那么 Dynamo 是怎么保證數據最終一致性的呢?Dynamo 使用 W + R > N 這個公式保證,R代表最少讀取的節點個數,W代表最少寫入的節點個數,N為數據副本數,這里的副本數并不是實際的物理節點,因為 Dynamo 使用的一致性 hash。

比如N有3個節點,R是2,W也是2,那么客戶端向集群寫入數據的時候只有在2個節點寫入成功后才會返回給客戶端,這個過程是同步的,剩下的兩個節點則是異步的,在讀數據的時候,必須讀到2個節點,并取2個中最新的數據,可以看出這樣肯定可以讀到最新的數據。

當出現數據沖突的時候 Dynamo 通過引入向量時鐘解決數據沖突:

向量時鐘通過帶上其他節點的向量時鐘來確定偏序關系,按圖上例子三個節點P0,P1,P2,初始三個節點都是(0,0,0)

首先,P0寫入在 a 寫入數據,P0 的向量時鐘為(1,0,0)當P0把時鐘信息傳播到 P1 的時候,對應的是時間點 b,P1 的向量時鐘是(1,1,0),這時候 P1 節點如果有個c寫入,那么他的向量時鐘是(1,2,0),c的向量時鐘里面的元素均大于等于 a,所以我們可以知道 c 時刻大于 a。這時候 P1 把時鐘信息傳播 d,向量時鐘是(1,2,1),寫入數據 e 的數據時鐘是(1,2,2),這個信息傳回 P0 后變成 g 時刻,向量時鐘是(3,2,2)在這里我們可以看到,a -> b -> c -> d -> e -> g 這個邏輯順序是成立的,而在向量時鐘上表現就是后一個所有元素都大于或等于前一個時間點。但 f 和 c, e 的先后順序關系是不確定的,在沒有全局時鐘的情況下你并不能知道誰先誰后,而在向量時鐘上表現就是 f 不是所有元素都大于等于 c 或 e。因此,其實向量時鐘表示為:

if V(a) > V(b) then a -> b

V(a) > V(b), 表示a向量的所有坐標元素大于b向量的所有對應坐標的元素,a->b 表示 a 到 b 存在事件順序。

向量時鐘只能解決最終一致性(收斂)問題,如果數據在達成最終一致性之前產生版本沖突,Dynamo 會將沖突版本返回給客戶端,由業務自行判斷。除了交由客戶端判斷,我們也可以采用“最后寫入勝利(LWW, last write wins)”的策略,在數據生成的時候通過帶上時間戳,最后比較兩個版本的時間戳誰新以誰為準。

Dynamo 檢測數據不一致用的 MerkleTree,MerkleTree 是通過一個 hash 樹來計算每部分的數據,父節點是子節點數據的 hash,只要有一塊數據變動了,最上層的根節點的hash就會改變,然后可以通過逐層遞歸的方式找到目標節點,查詢時間復雜度是o(log(n)),如下圖:

更多詳細的 Dynamo 可以參考原論文,這是國外老哥的一篇論文筆記:

數據復制的幾種方式前面說了數據復制的幾種架構,那么具體數據復制的形式有哪些呢?這里根據數據庫復制數據的主體不同分為四類:

基于語句(statement)的數據復制基于邏輯日志(行)的數據復制基于預寫日志(WAL)的數據復制基于觸發器的復制基于語句復制直接基于數據庫的語句進行復制,MySQL 5.1 版本前都是基于語句進行復制,基于語句的主從復制下 MySQL 會將 SQL 變更語句寫入 binlog 中,然后同步給從節點讓其更新,基于語句的復制主要簡單,而且傳輸數據量少,但其可能會存在不安全語句,而且每次更新都只能串行,特別是某些語句比如 INSERT ... SELECT 會因為鎖比行復制慢更多。PostgreSQL 的 pgpool-II 也是一種基于語句復制工具,但其本身相當于數據庫的 Proxy,而不是數據庫自身提供的CDC。

復制邏輯日志(行)邏輯日志是針對語句復制提出來的,因為基于statement的復制存在諸多問題,比如事務沒辦法并行復制,只有等待一個commit才能復制另一個,性能差。因此另一種,是以行為顆粒度基于邏輯日志的數據方式,經典代表就是 MySQL的binlog(row)格式。其對數據庫表的寫入記錄:

對于插入的行,日志包含所有列的新值。對于刪除的行,日志包含足夠的信息來唯一標識被刪除的行,這通常是主鍵,但如果表上沒有主鍵,則需要記錄所有列的舊值。對于更新的行,日志包含足夠的信息來唯一標識被更新的行,以及所有列的新值(或至少所有已更改的列的新值)。復制預寫日志(WAL)很多數據庫在寫數據的時候為了磁盤順序讀寫優化和事務性會引入預寫日志(write ahead logs,WAL),因此一些數據同步方案會嘗試利用 wal 特性來做數據復制和同步。比如 PostgreSQL 9.0之后的 PITR(Point in Time Recovery) 就是基于 WAL 做主從復制。 PostgreSQL 的預寫日志復制傳輸提供兩種:存檔式(archive)和流式(streaming)。存檔式就是等一個WAL文件寫完后,再拷貝從節點;流式則是寫完一條WAL記錄就通過TCP直接傳給從節點。存檔式存在數據延遲較大,流式則再主節點崩潰時從節點存在丟失數據的可能。

PostgreSQL Archive Replication

PostgreSQL Streaming Replication

基于觸發器的復制

上面講的那些復制方式都是數據庫系統提供的,比如基于語句和邏輯日志的復制是在數據庫的 server 計算層來做,預寫日志(WAL)則是在存儲層做,而觸發器是數據庫系統系統的將自定義的程序注冊進數據庫讓其在數據變更時自動觸發。由于是由外部程序對變更進行捕捉,因此他的靈活性是最高的,像多主復制的沖突解決方案大部分都是基于觸發器實現,比如 PostgreSQL 的 bucardo 就是基于 pg 的觸發器來做多主的數據復制。

小結整篇小作文洋洋灑灑寫了近一萬字,從系統架構、各層級組件的高可用到數據復制拓撲,希望可以給大家一個相對比較完整的視角去認識高可用系統的建設。

參考文獻BGP協議2021年Facebook死機事件Designing Data-Intensive Application