阿里云RDS深度定制-XA Crash Safe
簡介:?近幾年,隨著分布式數據庫系統的興起,特別是基于MySQL分布式數據庫系統,會用到XA來保證全局事務的一致性。眾所周知,MySQL對XA事務的支持是比較弱的,存在很多問題。為了滿足分布式數據庫系統對XA事務的要求,阿里做了大量的工作。 本篇內容將從兩部分介紹,第一部分介紹在AliSQL分支上做的一些實用功能;第二部分介紹XA Crash Safe問題的根源和在5.7和8.0版本上的實現原理。
一、阿里云RDS MySQL(AliSQL)
AliSQL是MySQL的分支,阿里在這個分支上做了很多深度的定制,以充分挖掘MySQL的潛力。AliSQL支撐了阿里集團電商業務十余年,其穩定性、安全性和高性能是經過了極其嚴苛實踐檢驗的。除此之外,AliSQL做了很多實用性的功能,以提高MySQL的易用性和使用效率。下圖是AliSQL上重要的功能的列表。
二、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可診斷、可度量
這個是對象級別性能度量的指標,包括表和索引。這些統計可以支持業務數據模型的優化和變更。
還有語句級別的統計信息。MySQL本身具有語句級別統計信息,但是它的統計信息不夠豐富,因此在這個基礎上我們又增加了更多實用性的統計信息,比如CPU的使用時間,加鎖消耗的時間等。
MySQL里用了大量的Mutex來保證多線程之間的數據訪問,我們加了對于Mutex加鎖時消耗時間的統計,方便對數據庫熱點的分析。
這些統計信息提供了更充分的數據依據,幫助我們做快速的問題定位。
4. 穩定性:Buffer Pool優化
在穩定性方面,我們也做了大量的工作。首先是Buffer Pool的優化。
云上用戶會有臨時變配的需求。舉個例子,在業務高峰時,若希望實例臨時規格變大,過了高峰之后再把規格降下來。MySQL是支持線上的resize,但是它的穩定性不夠好。在做Online resize對性能的影響還是比較大的。AliSQL針對這個做了優化后,可以看到下圖藍色線條是動態變配的波動曲線,穩定性好很多。
5. 穩定性:Concurrency Control 并發控制
用戶常常會碰到幾個SQL過來一下子就把實例的CPU打滿的情況,或者是內存耗光等類似的情況。Concurrency Control并發控制這個功能,可以讓用戶根據實際使用情況,對SQL限制執行個數,以提高實例整體運行的穩定性。
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可以異步刷盤。
通過這種方式,用戶事務在提交的時候就只需要一次刷盤動作,因此時延會降低,吞吐量會增大。
上圖是基于兩個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倍以上的性能提升。
三、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集合。
對于外部XA事務會記錄兩個Gtid,一個是Prepare的時候,一個是Commit的時候。最初實現這個功能的時候,只會記錄一個Gtid,也就是說在commit的時候會把prepare的給覆蓋了。這就要求,在Commit之前需要把Prepare的Gtid持久化到Gtid executed表里,效率非常低。后來官方優化成將兩個Gtid分別存在兩個位置,這就大大提高了效率。
經過以上的優化之后,我們可以看到Gtid和事務數據之間的關系,是Gtid Executed表中的Gtid代表了InnoDB中已經執行了的事務的Gtid。這兩者之間是原子對應的關系。
通過這樣的對應關系,我們可以在系統啟動的時候做Apply Binlog這樣的操作,來保證外部XA事務的Crash Safe。
根據Gtid Executed表中的Gtid就可以知道哪些事務在引擎里存在,然后通重放Binlog里的事務將引擎里的數據補齊。通過這樣的方式,不需要修改過添加Binlog Event,原來的recover過程也不需要變化。只需要在recover之后,檢查Gtid Executed的表,然后對比Binlog。將缺少的事務重新Apply一下就可以了,這樣就可以保證XA事務的Crash Safe。
原文鏈接
本文為阿里云原創內容,未經允許不得轉載。?
總結
以上是生活随笔為你收集整理的阿里云RDS深度定制-XA Crash Safe的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用AirFlow调度MaxComput
- 下一篇: 交付铁三角的故事之兵戎相见