避免 PostgreSQL 中的多客戶端死鎖
我正在開發啟動多個並發客戶端以連接到 PostgreSQL (12) 數據庫的軟體。當每個客戶端啟動時,它在連接到 PostgreSQL 時做的第一件事就是執行模式創建腳本。
這個腳本是冪等的——至少在原則上——這樣多個客戶端不應該絆倒自己。總的來說,這很好用。但是,PostgreSQL 有時會檢測到死鎖和受影響的客戶端崩潰。查看日誌記錄,我相信這些是按照這樣的順序發生的:
- 客戶端 A:開始架構創建事務
- 客戶端 A:完成模式創建事務
- 客戶端 B:開始架構創建事務
- 客戶端 A:使用模式的新事務(從視圖中選擇)
- 客戶端 A 和 B 現在處於死鎖狀態
日誌不是 100% 清晰的,我無法確定地重現這一點,但這似乎是正在發生的事情:客戶端 A 正在嘗試
SELECT
從架構定義的視圖中進行操作,但由於客戶端 B 正在嘗試重新創建該視圖,因此出現了死鎖模式腳本中的視圖 (CREATE OR REPLACE VIEW
)。是否可以確保模式創建腳本獨占執行?或者,是否有其他解決方案(例如,而不是
CREATE OR REPLACE VIEW
,我只有CREATE VIEW
在我確定它不存在之後)?
根據@DanielVérité,從對我的問題的評論中,可以將問題概括為“並發DDL 很糟糕;並發DDL 和DML 真的很糟糕”。我沒有意識到這一點——即使我小心翼翼地使 DDL 冪等——但知道這是一件好事。所以,這給我留下了兩個廣泛的解決方案:
- 不要同時執行 DDL(例如,讓單個客戶端實例化模式)。
從應用程序開發的角度來看,這可能是“最簡單”的解決方案——我將其視為我的備份計劃——但它會對應用程序的架構產生影響。如果必須,我只想走這條路。 2. 根據@a_horse_with_no_name 的建議使用諮詢鎖。
這只需要對我的應用程序的數據庫庫進行少量修改,因此不需要對架構進行大的架構更改或更改。它的缺點是我必須獲取和釋放每個事務的鎖(以確保它沒有被程式碼的 DDL 部分鎖定),這必然會跨客戶端序列化所有事務。
我正在嘗試選項 2,它似乎正在工作。我需要再執行幾次以說服自己死鎖已經消失以及序列化對性能的影響(有趣的是,到目前為止,它可以忽略不計)。當我有結果時,我會報告結果……