1. 程式人生 > >深入解析 SQL Server 高可用映象實現原理

深入解析 SQL Server 高可用映象實現原理

SQL Server 是 windows 平臺 .NET 架構下標配資料庫解決方案,與 Oracle、MySQL 共同構成了 DB-Engines Ranking 的第一陣營,在國內外企業市場中有著廣泛的應用。

Mirroring 是 SQL Server 最常用的高可用解決方案,具有自動故障轉移,高安全模式下具有資料“零”丟失,對客戶端透明等優勢,目前多家大的雲端計算廠商均採用該技術實現雲端 SQL Server 高可用部署。今天,我們就來聊聊 SQL Server 高可用映象實現原理。

資料副本

映象技術實現了位於不同物理伺服器上的兩個 SQL Server 例項資料同步,在映象叢集中, SQL Server 例項具有三種角色;

Principal:具有完整的資料副本,對外提供資料庫讀寫服務;

Mirror:具有完整的資料副本,本身不提供讀寫服務,通過接收來自 Principal 的更新日誌實現資料同步,允許建立快照實現報表;

Witness:本身不儲存資料,只負責在高安全執行模式下提供自動故障切換的能力,確保兩個 SQL Server 例項只有一個對外提供服務,避免腦裂情況出現;

在映象叢集中,principal 和 mirror 的資料同步是依靠事務日誌來實現的,與 Oracle 和 MySQL 不同,SQL Server 的事務日誌是 Database 級別的,不是例項級別的,每個 Database 都單獨的事務日誌,這也就使得 SQL Server 的映象是可以基於 Database 層面實現。

一個 SQL Server 例項中的兩個 Database,一個可以作為 principal,一個可以作為 mirror,分別與其他 SQL Server 例項組建映象關係;另外,SQL Server 一個 Database 只能有一個 mirror 節點,一個 mirror 的 database 不可以再有 mirror 節點,這點與 MySQL 級聯複製不同;

SQL Server 的事務日誌是物理記錄級別的,記錄了對資料庫某個頁的某行記錄(slot)的操作,principal 建立映象後,會啟動一個單獨的事務日誌傳送執行緒,維護一個虛擬的傳送佇列,然後讀取事務日誌,將其進行壓縮,根據官方公佈的測試資料,壓縮比不低於12.5%,然後傳送給 mirror 節點,mirror 節點接收到以後,會將其寫入本地在磁碟上的一個重做佇列檔案中,然後再通過另外的一個執行緒非同步的方式,從重做佇列中獲取事務日誌,然後分發給應用執行緒(process unit)進行回放。

不同於 MySQL Binlog,由於 SQL Server 的事務日誌在 principal 上事務執行過程中就已經源源不斷的寫入(MySQL Binlog 僅在事務提交階段生成),在 principal 事務執行的同時事務日誌就已經傳遞給了 mirror,所以基於事務日誌的 SQL Server 的複製效能會更理想。同時,在 mirror 回放的過程中,可以基於頁(page)級別進行併發更新(redo parallel),在 SQL Server 的標準版中,僅提供一個執行緒進行事務日誌回放(roll forward),在企業版中,如果當前伺服器 CPU 大於5核,則每4個核,增加一個並行執行緒,如果低於5個核,則仍然使用單執行緒回放。同時,基於頁的事務日誌的併發執行,還有一個好處就是對於同一個頁面的更新可以合併重新整理,減少髒頁重新整理數量。

執行模式

映象叢集提供了三種執行模式:

高效能模式:principal 與 mirror 之間資料非同步傳輸,principal 上的事務提交無需等待 mirror 的響應,principal 宕機後,存在資料更新丟失的可能,不支援自動故障轉移,可以通過強制服務的方式使得 mirror 提供服務。適合對資料可靠性要求不高,效能要求較高的業務場景,與 MySQL 的非同步複製模式,Oracle DataGuard 最大效能模式相近;

不帶故障轉移的高安全模式:principal 上所有的事務提交,都必須要確認該事務涉及的事務日誌均已經傳送到的 mirror 上,並寫入 mirror 的重做佇列中,持久化(是否持久化到外存裝置還與 windows 作業系統寫入快取策略相關),mirror 返回確認後,才可提交,可以實現 principal 宕機下資料“零”丟失,不支援自動故障轉移,可以通過手動轉移或者強制服務方式使得 mirror 提供服務。與 MySQL 5.7 Loss-less replication、Oracle DataGuard 最大可用模式相近;

帶故障轉移的高安全模式:與不帶故障轉移的高安全模式相比,增加了 witness (見證伺服器),可以實現自動的故障轉移,通過 witness,可以確保只有一個節點成為 principal,對外提供服務,實際上 witness 最重要的一個作用就是選主;

故障轉移

映象叢集故障轉移最複雜場景就是帶見證伺服器的支援自動故障轉移的高安全模式,所以我們重點討論該模式下的故障處理流程。

初始狀態下,witness、principal 和 mirror 三個節點兩兩之間均保持長連線會話,現在討論其中一方連線中斷的情況:

Principal 與 witness 連線中斷:

此時 witness 與 mirror 連線正常,觸發自動故障恢復流程,principal 丟失 witness 連線會話,如果 principal 仍在執行狀態,則將狀態標記為 disconnected,表示失去與 mirror 連線,切斷所有客戶端連線,停止讀寫服務,等待故障切換。為了防止網路抖動引起不必要的切換,會話超時預設時間為 10秒;witness 和 mirror 將 principal 標記為不可用,等待 mirror 上的重做佇列中的事務日誌回放(roll forward)完成後,mirror 成為新的 principal,開始對外提供讀寫服務。

最後 mirror 會通過後臺執行緒,將未提交的事務回滾(基於 binlog 的 MySQL 複製,由於 binlog 是在事務提交階段生成的,所以不存在事務回滾的階段)。

從整個故障切換的流程來看,故障切換時間主要包括三個部分:檢測到 principal 宕機的時間、mirror 上重做佇列中事務日誌的回放時間以及回滾未提交事務的時間,其中前面兩段時間服務不可用,尤其是重做佇列的回放時間,直接決定了服務不可用時間。

mirror 與 witness 連線中斷:

此時,principal 與 witness 連線正常,principal 狀態變為 Disconected,表示終止與 mirror 連線,mirror 狀態變為 suspend,principal 不再向mirror 傳送事務日誌,等待 mirror 重新建立到 witness 連線後,principal 才會恢復與 mirror 進行資料同步。

Principal 與 mirror 連線中斷:

principal 與 mirror 同時保持 witness 的連線會話,但是 principal 與 mirror 之間會話中斷,witness 會通知 mirror,principal 依然保持連線狀態,不會觸發故障切換;此時 principal 由於保持有 witness 的連線會話,服務正常。

下面來考慮三方會話兩個會話同時中斷情況:

principal 與所有節點會話中斷:

只要 mirror 與 witness 會話正常,即可完成正常的故障轉移;如果 mirror 與 witness 連線也中斷,則無法完成,即便是後來 mirror 與 witness 的會話優先恢復,則也無法故障切換,因為已然不確定 mirror 是否擁有全部 principal 的資料,此時即便 principal 處於執行狀態,也無法提供服務,等待 principal 與任意節點會話恢復正常,即可恢復讀寫服務;

mirror 與 所有節點會話中斷:

不會觸發故障切換,principal 切入公開執行模式(非同步),即不會再向 mirror 傳送事務日誌,也不再需要等待 mirror 的響應,直到 mirror 重新恢復會話。

witness 與所有節點會話中斷:

不會觸發故障切換,principal 繼續提供讀寫服務,與 mirror 資料繼續同步,映象叢集喪失自動故障轉移能力,退化為不帶故障轉移的高安全模式;

如果三方會話同時連線中斷,則 principal 無法提供服務,直到 principal 與任意節點通訊恢復正常。

場景1中,初始狀態例項 A、B 和 witness 保持會話連線,其後,A 例項宕機,失去與其他成員的會話,例項 B 與 witness 保持會話,觸發故障切換,例項 B 升級為 principal。其後例項 B 也宕機,服務停止。然後例項 A 恢復,但是由於此時例項 A 已經是 mirror,不能確定例項 A 擁有例項 B 的所有更新資料,所以無法故障切換,最後例項 B 恢復,服務恢復正常。

場景2 中,例項 A 和 B 同時失去了與 witness 的會話,但是服務已然正常,A 與 B 之間資料繼續同步,其後 A 與 B 同時宕機,服務停止。最後 A 恢復服務,與 witness 恢復會話,A 繼續提供服務。

透明切換

一個完整的高可用機制除了後端節點的切換,還包括髮生故障轉移後,客戶端如何能夠快速地連線到冗餘節點,繼續業務讀寫。常用的實現,包括 Driver 層解決方案,例如 mongoDB replication set,也有通過二層內的廣播方式實現 vip,例如 keepalived,當然還包括 proxy,以及三層 DNS 的實現。

SQL Server 的實現採用了 Driver 層處理的方式,開發者要實現自動的故障轉移,在連線資料庫時,除了必須要指定初始節點的 IP 和埠,還要指定故障轉移的節點的 IP 和埠。客戶端首先嚐試使用初始節點建立連線,如果初始節點指向的例項當前為 principal,則連線會建立成功,可以正常的讀寫。當發生故障切換時,principal 會切斷所有已有客戶端連線,然後客戶端建立到初始節點連線也會失敗,通過一定的重試策略失敗後,會嘗試連線之前指定的故障轉移節點,從而實現服務入口的切換。

SQL Server 常用的訪問介面:OLE DB、ODBC、ADO 均支援指定故障轉移節點,格式如下:

Server=250.65.43.21,4734; Failover_Partner=250.65.43.22,4734;

叢集監控

映象叢集的監控可以通過 SQL Server Management stdio 啟動映象監視器,或者系統內建的儲存過程來實現,監控的主要指標包括:

未傳送日誌:principal 上未傳送的日誌超過指定的閾值,會在 principal 上生成一個警告,在高效能模式下,強制服務時可以作為評估 principal 上事務丟失數量的依據,同樣也適用於在高安全模式切換成非同步模式狀態下(mirror 失去連線)

未還原日誌:重做佇列中的未被應用的事務日誌數量(KB),超過閾值,會在 mirror 上生成一個警告,該值可以作為評估故障轉移時間的主要因素。

最早未被髮送的事務:principal 傳送佇列中,最早未被髮送的事務距離現在的時間,單位時分鐘,超過閾值,會在 principal 上生成警告,與未傳送日誌量一起,從時間維度,衡量高效能模式下和高安全非同步模式下,資料丟失數量;

映象提交開銷:高安全模式下,principal 上事務從提交到等到 mirror 響應的時間開銷的平均值,如果超過閾值,則在 principal 上生成一個警告,在同步模式下,該值可以衡量同步開銷。

參考文件:

https://msdn.microsoft.com/en-us/library/ms189852.aspx