天天看點

TDSQL XA 全局死鎖處理

本文是我最初于2017年發表在我的個人微信公衆号裡面,現釋出在這裡。

1.概述

TDSQL XA的全局事務(global transaction)就是使用者用戶端連接配接到TDSQL XA分布式資料庫系統後發起和執行的事務,也就是TDSQL XA處理的分布式事務。一個全局事務可能會寫入資料到多個後端mysql 資料庫執行個體,每個執行個體上面的本地事務都是這個全局事務的事務分支(transaction branch)。用戶端發起全局事務送出時,運作在TDSQL XA的網關子產品中的全局事務管理器會控制該事務通路的所有後端mysql資料庫執行個體完成兩階段送出。

全局事務執行過程中可能發生兩類死鎖,第一類就是每個mysql的innodb(本文隻讨論MySQL使用innodb存儲引擎的情形,下同)執行個體内部産生的死鎖,innodb現有的死鎖檢測和處理機制就可以處理這樣的死鎖;第二類就是全局死鎖,這是單一的innodb執行個體無法解除的死鎖,也就是本文要讨論的主要内容。

2.全局死鎖示例

GT1和GT2分别在set1上更新資料行R1,在set2上更新資料行R2,事務分支:

GT1:

{

T11: update t1 set t1.age=11 where pk=1,

T12: update t1 set t1.age=22 where pk=2;

}

GT2:

T21: update t1 set t1.age=22 where pk=2,

T22: update t1 set t1.age=11 where pk=1

執行流程是GT1.T11在set1上執行,同時GT2.T21在set2上面執行,然後GT1.T12在set2上面執行被GT2.T21阻塞,GT2.T22在set1上面執行也被GT1.T11阻塞,此時的等待關系:

T11鎖住R1, T22等待R1行鎖:T22->T11,即GT2->GT1

T21鎖住R2, T12等待R2行鎖:T12->T21,即GT1->GT2

對于set1或者set2上面的innodb來說,它們都認為沒有死鎖發生,因為set1上面T22在等待T11的行鎖,沒有環路;set2上面T21在等待T22的行鎖,也沒有環路。但是全局來看,GT1和GT2确實産生了環路等待,并且每個事務分支在結束前都不會釋放其已經持有的行鎖,因而構成了

QQ号賣号

全局死鎖。這種全局死鎖是任何一個TDSQL XA的MySQL執行個體無法解除的,必須在TDSQL XA的TM(事務管理器)也就是網關中來解除。

3.全局死鎖的檢測和解除

與innodb或者其他事務存儲引擎的全局死鎖檢測機制都類似,在TDSQL XA的網關中,我們使用定期檢測結合語句逾時觸發檢測。在網關中觸發全局死鎖檢測後,全局死鎖檢測的具體做法是:

  1. 從每個後端set上查詢 innodb_trx(加列xid) 和innodb_lock_waits表,得到每個set上面的事務分支的等待關系圖。由于這些事務分支各自所屬的全局事務也有相同的等待關系,是以可以得到每個set上的全局事務等待關系圖。
  2. 合并全局事務的等待關系圖得到GTG。
  3. 在GTG中尋找環路,kill掉環路當中某個連接配接,給它的用戶端傳回死鎖錯誤。

在原始的MySQL 中, information_schema.innodb_trx表并沒有XA事務ID列,但是為了第#1步從每個set的事務分支等待關系推出全局事務等待關系,就必須修改mysql和innodb的代碼給這個表加上這一列。另外還需要對該表做其他一些改造方可正确完成全局死鎖檢測。

4.結論

全局死鎖檢測機制是TDSQL XA的重要組成部分:實際測試中可以看出沒有全局死鎖檢測的話TDSQL XA在大量并發負載的情況下很容易産生很多因為全局死鎖而阻塞的事務。由于TDSQL XA并不會非常頻繁地執行死鎖檢測,其運作開銷可以忽略不計。