GaussDB(DWS)中的分散式死鎖問題實踐

来源:https://www.cnblogs.com/huaweiyun/archive/2023/12/26/17927571.html
-Advertisement-
Play Games

出現分散式死鎖現象後,如果沒有外部干預,通常是一方等待鎖超時報錯後,事務回滾清理持有鎖資源,另一方可繼續執行。 ...


本文分享自華為雲社區《GaussDB(DWS)中的分散式死鎖問題實踐》,作者: 他強由他強 。

1、什麼是分散式死鎖

分散式死鎖是相對於單機死鎖而言,一個事務塊中的語句,可能會分散在集群里多個節點(CN/DN)執行,在不同節點上可能都會持有鎖,當併發事務進行時可能會導致分散式(全局)死鎖,如下圖所示,會話SESSION1持有了DN1上的lock1資源後再去請求DN2上的lock2,會話SESSION2持有了DN2上的lock2資源後再去請求DN1上的lock1,兩個會話形成互相等待。出現分散式死鎖現象後,如果沒有外部干預,通常是一方等待鎖超時報錯後,事務回滾清理持有鎖資源,另一方可繼續執行。

2、常見的分散式死鎖場景

一般來說,分散式死鎖的產生與在不同節點上的併發時序或持鎖順序有關,所以現網實際發生概率較低,分散式死鎖通常都是RegularLock類型,下麵是幾種常見的分散式死鎖場景,舉例說明兩個併發事務產生的分散式死鎖:

1)鎖升級

# 集群兩個CN,兩個DN
create table mytable(a int, b int);
insert into mytable values(1,1),(2,2);

其中sessionA與sessionB由不同CN發起(sessionA:CN1,session2:CN2),執行時序如下:

session A(CN1)

session B(CN2)

begin;

begin;

select * from mytable;

// CN1上拿1級表鎖

 
 

select * from mytable;

// CN2上拿1級表鎖

truncate table mytable;

// CN1上拿8級表鎖

// CN2上拿8級表鎖,waiting

 
 

truncate table mytable;

// CN1上拿8級表鎖,waiting

可以看到sessionA里select會持有本地1級鎖,truncate會持有8級鎖,出現鎖升級現象,導致sessionA在CN2上等鎖,sessionB在CN1上等鎖,形成相互等待。

2)行更新衝突

# 集群兩個CN,兩個DN
create table mytable(a int, b int);
insert into mytable values(1,1),(2,2); 

行存表發生行更新衝突是比較常見的分散式死鎖場景。因為表是round robin分佈,所以行a = 1 與 a = 2數據可以保證分別分佈在DN1和DN2節點。

一個事務在更新數據時需要在對應DN節點持有本xid事務鎖的Exclusive鎖,當發生行更新衝突時(寫寫衝突),一個事務需要阻塞等待另一個事務提交(等待獲取對方事務鎖ShareLock),形成相互等待時造成分散式死鎖。

其中sessionA與sessionB可由相同或者不同CN發起,執行時序如下:

session A(xid1)

session B(xid2)

begin;

begin;

update mytable set b = 1 where a = 1;

// DN1上拿xid1的事務鎖

 
 

update mytable set b = 2 where a = 2;

// DN2上拿xid2的事務鎖

update mytable set b = 1 where a = 2;

// DN2上拿xid2的事務鎖,waiting

 
 

update mytable set b = 2 where a = 1;

// DN1上拿xid1的事務鎖,waiting

3)CU更新衝突

# 集群兩個CN,兩個DN
create table mytable(a int, b int) with (orientation = column);
insert into mytable values(1,1),(2,2),(3,3),(4,4);

其中sessionA與sessionB可由相同或者不同CN發起,執行時序如下:

session A(xid1)

session B(xid2)

begin;

begin;

update mytable set b = 1 where a = 1;

// DN1上拿xid1的事務鎖

 
 

update mytable set b = 2 where a = 2;

// DN2上拿xid2的事務鎖

update mytable set b = 1 where a = 3;

// DN2上拿xid2的事務鎖,waiting

 
 

update mytable set b = 2 where a = 4;

// DN1上拿xid1的事務鎖,waiting

當出現更新衝突時,對於行存表來說是對一行數據加鎖(如場景2所述),但對於列存表來說是對一個CU加鎖。所以一個事務里的更新語句如果涉及到不同的CU,也會拿事務鎖,可能就會產生分散式死鎖。

我們可以通過如下語句觀察ctid信息判斷數據是否分佈在同一個CU上,如下圖:可以看到a = 1 與 a = 3分佈在DN1上,且在同一個CU;a = 2 與 a = 4分佈在DN2上,且在同一個CU。所以這也能解釋為什麼看上去列存表更新不同的“行數據”也會產生鎖阻塞和分散式死鎖現象。

4)單語句出現分散式死鎖

前面幾種場景都是事務塊里涉及到多條SQL語句,可能會到不同節點上去交錯拿鎖導致的分散式死鎖,但有時候某些單語句場景可能也會出現分散式死鎖,如下:

session A(xid1)

session B(xid2)

update/delete mytable set b = 1 where a = 1;

// waiting

update/delete mytable set b = 2 where a = 2;

// waiting

此類問題與數據分佈有關,如下場景都可能會導致這個現象:

1)若表是複製表,每個DN節點上都有數據,更新時會去所有DN併發執行

2)表是普通行存表或列存表,但有行數據(如a=1)同時分佈在了多個DN節點上,如round robin分佈下插入兩條相同a=1的數據

insert into mytable values(1,1);
insert into mytable values(1,2);

此場景需要具體去排查數據分佈是否會造成此情況。

3、規避分散式死鎖的方法

1)控制鎖級別,減少鎖升級

按照各類操作的鎖級別建議規則使用,開發時不要盲目提高鎖級別,造成可能發生的不必要的鎖等待

2)控制鎖粒度

合理控制鎖使用範圍,及時釋放

3)控制拿鎖順序

儘量控制對資源操作的順序,比如對各分區表的操作順序,避免亂序造成的死鎖。但全局各節點的拿鎖情況或順序一般無法提前預測,往往為了考慮提高性能,請求會在節點間併發執行,但我們可以在某個節點上控制併發互斥以規避分散式死鎖問題,如操作某個表時先去FirstCN上請求持鎖,持鎖成功後再對其他CN和DN並行拿鎖。GaussDB(DWS)內核的很多地方的設計中會有這種思想,如DDL語句,autoanalyze等。

4)主動設置較短的鎖超時時間

一般用在非關鍵的用戶路徑操作上,如用戶語句在runtime analyze子事務的流程里會主動設置鎖超時時間為2秒,發生阻塞後可及時放鎖,避免出現長時間鎖等待,也能規避潛在的分散式死鎖場景

4、如何排查系統是否產生了分散式死鎖

本質上是發現集群中是否有全局的死鎖環等待關係,內核中提供有許多視圖可以輔助觀察持鎖等待情況,但需要註意的是,因為查詢到的鎖等待關係可能只是暫時的瞬間狀態,只有持續存在的鎖等待才會造成分散式死鎖,需要判斷鎖是否稍後會主動釋放(事務提交前),還是只能等到事務提交後釋放。如何判斷系統是否產生了分散式死鎖,有以下方法:

1)查詢pgxc_deadlock視圖,會輸出全局死鎖環信息,如果信息為空,則代表無分散式死鎖,但需要註意在某些複雜的場景可能會出現誤報,即輸出有死鎖環信息,但可能並沒有形成分散式死鎖;

當有分散式死鎖時,直到等待鎖超時後,某一方事務會出現“Lock wait timeout...”,列印具體的鎖信息及鎖語句,報錯後釋放鎖,另一方解除阻塞。相關的鎖超時參數是lockwait_timeout或update_lockwait_timeout。

2)在GaussDB(DWS)的8.3.0版本及以後,內核已經支持了自動化地分散式死鎖檢測,當檢測到系統中存在分散式死鎖等待關係後,會自動報錯和挑選事務進行cancel,具體原理下一節中會詳細介紹。

如下圖所示,若用戶出現“cancelled by global deadlock detector”報錯,代表檢測到分散式死鎖並被查殺,此時可以去檢測CN上(FirstCN或者CCN)上去找相關日誌信息,會輸出具體死鎖和session查殺信息,需要註意用戶語句執行CN和檢測CN可能並不是一個,此時檢測CN會向執行CN發起事務cancel。

5、分散式死鎖檢測原理

分散式死鎖檢測的目標原則是做到不誤報,爭取不漏報,儘量及時報。

我們使用了中心化的收集檢測思想,如流程圖所示:首先挑選一個CN作為檢測CN(類似master角色),CN上新增後臺線程啟動GlobalDeadlockDetector模塊,周期性向集群所有節點收集鎖等待關係,計算等待者和持有者信息,然後構造全局有向圖(WFG),依賴定義的規則對圖的頂點和邊進行消除,判斷是否能消除完成。如果無法消除完成,則出現了死鎖環,併進行二次DoubleCheck,如果兩次的死鎖環信息有交集,則報告死鎖信息。當發現死鎖後,按照事務時間戳挑選最年輕的事務(youngest)進行中斷,並會對用戶報錯。

我們在設計上主要參考了Greenplum的思路,由於與GaussDB(DWS)架構和應用場景上的差異性,也針對做了一些改造和優化,主要包括:

1、檢測節點的選擇:在FirstCN或CCN上啟動後臺檢測線程,依賴外部OM模塊做高可用切換;

2、等待關係圖節點的標識:由檢測CN構造全局唯一global_session下發,格式為:timestamp.pid.node_name(timestamp為事務開始的時間戳,pid為執行CN上的線程號,node_name為執行CN名稱);

3、虛實邊關係定義:支持定義線程級別虛實邊,過濾掉不必要的死鎖誤報;
  • 實邊:鎖等待關係的變化,需要等到持有者事務會話commit或abort
  • 虛邊:鎖等待關係的變化,不需要等到持有者事務會話commit或abort

4、死鎖結果的合法性檢查:增加DoubleCheck機制,提高檢測結果準確性,結果以連續兩次檢測到的死鎖環交集為準;

5、死鎖消除:執行CN與檢測CN可能不同,可能存在跨CN發起的事務中斷;

6、與單機死鎖檢測演算法互補:當分散式死鎖檢測演算法如果發現檢測到單機的死鎖環路等待關係後,則忽略,與單機死鎖檢測演算法處理不衝突; 

分散式死鎖檢測相關參數:

  • enable_global_deadlock_detector:分散式死鎖檢測功能是否開啟,預設off

  • global_deadlock_detector_period:分散式死鎖檢測周期,預設5秒

 

點擊關註,第一時間瞭解華為雲新鮮技術~

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言: 繼上篇:Taurus .Net Core 微服務開源框架:Admin 插件【4-4】 - 配置管理-Mvc【Plugin-CORS 跨域】 本篇繼續介紹下一個內容: 系統配置節點:Mvc - Plugin - Admin 後臺界面: 配置界面如下: 配置說明如下: 1、Admin.IsEna ...
  • 什麼是鍵控服務依賴註入? 在之前的依賴註入中,服務是根據其類型進行註冊和解析的。如果出現同一介面有多個實現怎麼辦呢?這時候就可以使用.NET 8的新功能“鍵控服務依賴註入”。它允許您註冊介面的多個實現,每個實現都與一個唯一鍵相關聯,然後基於該鍵解析所需的實現。 在.NET 8 中的實現 接下來介紹如 ...
  • 一:背景 1. 講故事 為什麼要提 宇宙射線, 太陽耀斑 導致的程式崩潰呢?主要是昨天在知乎上看了這篇文章:莫非我遇到了傳說中的bug? ,由於 rip 中的0x41變成了0x61出現了bit位翻轉導致程式崩潰,截圖如下: 下麵的評論大多是說由於 宇宙射線,這個太玄乎了,說實話看到這個 傳說bug ...
  • 在.NET Core中,UseStaticFiles、UseDefaultFiles、UseDirectoryBrowser和UseFileServer中間件用於處理靜態文件和目錄瀏覽。下麵我將為你提供一個簡單的例子,演示它們的用法。 首先,確保你的項目已經安裝了Microsoft.AspNetCo ...
  • 路是腳踏出來的,歷史是人寫出來的。人的每一步行動都在書寫自己的歷史。 Linux 基礎命令 open:打開文件操作,如環境配置文件。 open ~/.zshrc vi:vi (visual interface), linux 中最經典的文本編輯器 vim(vi improved)是 vi 發展出來的 ...
  • 在運行程式時有時候會需要查看資源占用,以方便部署在其他伺服器上時進行參考。以下是總結了我在linux上查找程式進程資源的兩種方法(cpu和gpu都有)。 ...
  • 在 Apache Flink 中實現高效的 Top N 數據處理,尤其是涉及時間視窗和多條件排序時,需要精細地控制數據流和狀態管理。 普通計算TopN: 1. 定義數據源(Source) 首先,我們需要定義數據源。這可能是 Kafka 流、文件、資料庫或任何其他支持的數據源。 val stream: ...
  • 在回答這個問題之前,首先我們看看 MySQL 中有哪些常用的 JDBC 連接池: c3p0 DBCP Druid Tomcat JDBC Pool HikariCP 這些連接池中,c3p0 是一個老牌的連接池,很多流行框架,在其老版本中,都將 c3p0 作為預設的連接池。 DBCP 和 Tomcat ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...