天天看點

​阿裡雲RDS深度定制-XA Crash Safe

一、阿裡雲RDS MySQL(AliSQL)

AliSQL是MySQL的分支,阿裡在這個分支上做了很多深度的定制,以充分挖掘MySQL的潛力。AliSQL支撐了阿裡集團電商業務十餘年,其穩定性、安全性和高性能是經過了極其嚴苛實踐檢驗的。除此之外,AliSQL做了很多實用性的功能,以提高MySQL的易用性和使用效率。下圖是AliSQL上重要的功能的清單。

​阿裡雲RDS深度定制-XA Crash Safe

二、RDS定制化功能介紹

1. 實用性:SQL Outline 線上固化SQL執行計劃

我們使用MySQL的時候時常會碰到一種情況,即業務跑着的時候有個SQL執行會變慢,分析之後發現是這個SQL執行計劃發生了變化。這種變化有很多原因,比如bug或是版本更新了等等。

MySQL就提供了hint功能,它可以使我們在SQL語句裡增加一些提示,以保證SQL在生成執行計劃的時候是按照提示來工作的。但這隻是比較理想化的狀态,實際情況中這樣是很不友善的,因為業務已經線上上運作了,這個時候即使能夠改變業務的SQL,也需要一個很漫長的時間和過程。

為了解決這個問題,就有了SQL Outline的功能。這個功能不需要改變應用的SQL語句,隻需要在server端告訴RDS碰到哪種類型的特定的SQL,可以給它定制一個hint,然後按照使用者指定的方式執行。

2. 實用性:Performance Agent可診斷、可度量

我們在執行個體的監控上也做了大量的工作,進而可以很容易的分析資料庫中的一些問題。首先就是執行個體級别的統計資訊,它包括了作業系統層面、server層和InnoDB等共計55個名額。然後把它放到一個Performance表中,每秒鐘進行一次統計。通過這些統計的資訊可以分析系統出現問題的原因。

3. 實用性:Performance Insight可診斷、可度量

這個是對象級别性能度量的名額,包括表和索引。這些統計可以支援業務資料模型的優化和變更。

​阿裡雲RDS深度定制-XA Crash Safe

還有語句級别的統計資訊。MySQL本身具有語句級别統計資訊,但是它的統計資訊不夠豐富,是以在這個基礎上我們又增加了更多實用性的統計資訊,比如CPU的使用時間,加鎖消耗的時間等。

​阿裡雲RDS深度定制-XA Crash Safe

MySQL裡用了大量的Mutex來保證多線程之間的資料通路,我們加了對于Mutex加鎖時消耗時間的統計,友善對資料庫熱點的分析。

這些統計資訊提供了更充分的資料依據,幫助我們做快速的問題定位。

4. 穩定性:Buffer Pool優化

在穩定性方面,我們也做了大量的工作。首先是Buffer Pool的優化。

雲上使用者會有臨時變配的需求。舉個例子,在業務高峰時,若希望執行個體臨時規格變大,過了高峰之後再把規格降下來。MySQL是支援線上的resize,但是它的穩定性不夠好。在做Online resize對性能的影響還是比較大的。AliSQL針對這個做了優化後,可以看到下圖藍色線條是動态變配的波動曲線,穩定性好很多。

​阿裡雲RDS深度定制-XA Crash Safe

5. 穩定性:Concurrency Control 并發控制

使用者常常會碰到幾個SQL過來一下子就把執行個體的CPU打滿的情況,或者是記憶體耗光等類似的情況。Concurrency Control并發控制這個功能,可以讓使用者根據實際使用情況,對SQL限制執行個數,以提高執行個體整體運作的穩定性。

​阿裡雲RDS深度定制-XA Crash Safe

6. 安全性:TDE支援國密SM4

企業級使用者對于安全性的要求越來越高,對于各種各樣的加密、密碼強度、生命周期等要求也越來越多。AliSQL在安全這塊做了更全面的支援,比如對于加密來講,除了支援TDE這種AES的加密算法,還支援國密加密算法SM4,對于有涉密要求的使用者可以用SM4的加密算法來保證資料的安全性。

7. 安全性:Recycle Bin防止誤删除

當使用者在做删除表或是Truncate表的時候,使用這個資源回收筒功能,并不會把資料檔案直接删除掉,而是會把這些表放到一個資源回收筒裡,這樣就避免了使用者在誤删之後資料丢失的風險,誤删除後還能通過資源回收筒把删除的資料快速找回。

8. 安全性:Flashback Query

不僅是表級的誤删除,當我們對某一些資料更新的範圍錯誤了之後,使用Flashback機制,可以恢複到更新前的曆史版本。同時,使用者還可以自定義查詢某個時間戳的某個資料。

是以Flashback Query對誤操作删除恢複或是回檔需求是很有效的方法。

9. 高性能:Binlog In Redo

對比原生MySQL,AliSQL的性能提升很多。首先介紹的是Binlog In Redo功能。衆所周知,在MySQL裡面,事務送出的時候需要持久化兩次,因為要執行兩階段的事務送出過程。這裡面做了一個改進,即可以把Binlog寫到Redo裡面,這樣的話就隻需要持久化一次Redolog,Binlog可以異步刷盤。

通過這種方式,使用者事務在送出的時候就隻需要一次刷盤動作,是以時延會降低,吞吐量會增大。

​阿裡雲RDS深度定制-XA Crash Safe

上圖是基于兩個Binlog in Redo版本的測試結果。通過左側第二個版本的資料,我們發現性能會提高很多。對于Update non index來講,可以有大概38%的性能提升。對于write only來講,也可以達到25%的性能提升。

10. 高性能:Fast Query Cache

AliSQL針對Query Cache在并發控制、記憶體管理和緩存機制等等做了大量的優化。

優化之後,它的性能提升非常明顯。在point select的場景下,性能提升甚至可以達到100%以上。通過測試發現,在rewrite模式命中率比較低的情況下,幾乎沒有任何性能損失。是以使用者在讀比較多的場景就可以把Query Cache打開,可以保持穩定高效的狀态。

11. 高性能:DDL Optimization

圍繞DDL我們做了大量優化。

使用者在使用DDL的時候會發現,如果表特别大需要做rebuild或更新資料等操作的時候,效率非常低。主要是因為DDL利用Buffer Pool的模型是效率低下的模型。優化之後,對于rebuild表的這種操作效率會高很多,對于其他SQL語句的影響也會降低很多。

下圖是針對Create Index和Optimize Table的測試,可以看到優化之後,操作都會有10倍以上的性能提升。

​阿裡雲RDS深度定制-XA Crash Safe

三、XA Crash Safe 介紹

1. XA Crash Safe背景

抛開XA Crash Safe本身,MySQL本身也有Crash Safe機制。為什麼會需要這樣的機制呢,因為在MySQL裡,同時包括Binlog和資料兩個部分,可以了解為存了兩份一模一樣的資料。為了保證這兩份資料的一緻性,MySQL Crash Safe實作了兩階段送出機制。為了保證Binlog和資料的一緻性,任何使用者的事務都會被轉化成為兩階段的事務,首先就是進行prepare,然後再寫Binlog并持久化,最後做事務的送出。是以在這兩個階段的送出過程中,Prepare、刷Redo、寫Binlog和刷Binlog的執行順序是保持不變的。

如何保證Crash Safe呢?這要看它恢複的處理過程。在實際的事務執行過程中,隻要是Binlog有這個事務,一定是Prepare的狀态。那麼利用這個原則,在MySQL Crash重新開機的時候,它會取出所有已經prepare的事務,把它們的XID取出來,掃描最後一個Binlog檔案,然後确認XID所對應的事務是否已經存儲到Binlog裡了。如果已經存儲過了,就直接送出即可;如果還沒有存儲,就復原掉。通過這個Crash Recovery機制後,Binlog裡的資料就和引擎裡面的資料保持一緻了。

對于普通使用者事務可以用兩階段來保證Crash Safe,那麼對于使用者的XA事務怎麼處理呢?

在Binlog裡會把這個事務分為獨立的兩部分,當使用者執行XA Prepare的時候,會寫Binlog檔案,然後把這個Prepare狀态執行到引擎裡,這是完全獨立的。使用者在XA Commit裡,可以在任何時間執行,當使用者在執行XA Commit之後會被再記錄一次Binlog,是以這兩者是完全脫離的。

對于普通的使用者事務,執行過程隻記錄一次Binlog,而且整個事務是一個基本的單元存在Binlog中的;對于XA事務,這就是分開的。而且它整個過程是先寫Binlog,然後才去把狀态持久化到引擎中,做引擎的prepare。對于MySQL來講,不能保證外部XA事務資料和Binlog的一緻性。

也就是說當寫完Binlog,還沒有在引擎中執行prepare和commit,這個時候就Crash了,那麼它起來之後就變成,Binlog裡有事務記錄,而在引擎裡卻丢失了,也許隻以prepare的狀态停留在那裡,這就沒有被送出。

2. XA Crash Safe:基于MySQL 5.7的修複

1)調整執行順序

為了更好的支援分布式資料庫系統,在MySQL 5.7的時候,阿裡就做過對于XA Crash Safe的修複。這個修複是把執行順序做了颠倒,即先執行引擎的Prepare,再寫Binlog,這樣它就跟普通事務的過程一樣了。在Prepare之後做Crash,如果使用者的XA事務做了Prepare之後沒有記Binlog,那麼資料就會被復原掉。但是由于XA事務跟普通事務不太一樣,需要更多處理來支援復原,而且它也不是一個完善的方案。因為使用者具有多樣性,比如使用者執行了一個事務但是并沒有記Binlog,這個時候就沒法區分是否記錄了Binlog。另外,XA事務的Prepare和Commit是分開的,當使用者将Prepare事務記錄到很久以前的Binlog裡了,也不能區分使用者是否記錄了Binlog。

這種情況下就需要一個解決方案,即在做Binlog rotation時候,要把所有Prepare事務的XID記錄到Binlog中。這樣我們就可以通過最後一個Binlog去了解所有Prepare狀态的XID,然後就可以通過Rollback的方式保持Binlog和資料的一緻性。

2)調整Recovery邏輯

Commit執行了另外一種邏輯,即先執行Binlog,在執行後發現事務是Prepare狀态,且在Binlog中記錄了Commit或Rollback,那麼執行Commit或Rollback。

這就是MySQL 5.7上XA Crash Safe的修複的過程。

3. XA Crash Safe:MySQL 8.0 Gtid和事務的一緻性

MySQL 8.0采用了另外一種方法。介紹這個方法之前,首先介紹下MySQL 8.0 Gtid和事務的一緻性。在MySQL8.0.17的時候,引入克隆功能,這個功能可以把Gtid記到事務的undo裡。記到Undo裡的過程也很簡單,在Prepare的時候還是先記Binlog,然後引擎裡做事務Prepare的時候,去監測這個事務是否有Gtid。如果有,在修改事務狀态時會連通Gtid一起寫到Undo裡。如果Crash之後,Prepare丢了,那麼Gtid一定也是不存在的,反之亦然。在Commit的時候也同理。這樣做,克隆的時候使用者使用起來就會非常簡單。

但是Rollback是個特例,尤其是對于XA事務來說。Rollback也會記錄一個Gtid。Rollback在引擎裡的做法是,把事務狀态改成active之後就會自然復原,是以在active的時候它也會記一個Gtid,是以XA Rollback會記錄Binlog也會産生Gtid。整個操作也是原子的。

Gtid最終會持久化到Gtid_executed表裡。之是以要做彙總是因為Undo本身需要Purge,否則會越來越大,而且檢索效率也非常低。是以雖然在Prepare後會放進Undo裡,但是實際上最後都會彙總到Gti_executed表中,然後會有一個單獨的Gtid Flush的線程,它會周期性的把Gtid list裡的Gtid寫到Gtid_executd表中。寫到表後,隻需要持續化整個表後,undo就可以丢掉了,這樣始終會有一個完整的Gtid集合。

​阿裡雲RDS深度定制-XA Crash Safe

對于外部XA事務會記錄兩個Gtid,一個是Prepare的時候,一個是Commit的時候。最初實作這個功能的時候,隻會記錄一個Gtid,也就是說在commit的時候會把prepare的給覆寫了。這就要求,在Commit之前需要把Prepare的Gtid持久化到Gtid executed表裡,效率非常低。後來官方優化成将兩個Gtid分别存在兩個位置,這就大大提高了效率。

經過以上的優化之後,我們可以看到Gtid和事務資料之間的關系,是Gtid Executed表中的Gtid代表了InnoDB中已經執行了的事務的Gtid。這兩者之間是原子對應的關系。

通過這樣的對應關系,我們可以在系統啟動的時候做Apply Binlog這樣的操作,來保證外部XA事務的Crash Safe。

​阿裡雲RDS深度定制-XA Crash Safe

根據Gtid Executed表中的Gtid就可以知道哪些事務在引擎裡存在,然後通重放Binlog裡的事務将引擎裡的資料補齊。通過這樣的方式,不需要修改過添加Binlog Event,原來的recover過程也不需要變化。隻需要在recover之後,檢查Gtid Executed的表,然後對比Binlog。将缺少的事務重新Apply一下就可以了,這樣就可以保證XA事務的Crash Safe。