10分鐘實驗,PG鎖機制更容易理解

2019-08-05     波波說運維

概述

前面已經介紹了PG鎖機制的一些概念性內容,這裡主要做一些相關實驗,幫助大家理解下PG的鎖機制。

以下基於PG10.9進行測試。


1、創建測試表

CREATE TABLE parent (
id serial NOT NULL PRIMARY KEY,
name text NOT NULL);

CREATE TABLE child (
id serial NOT NULL PRIMARY KEY,
parent_id int4 NOT NULL,
name text NOT NULL, CONSTRAINT child_parent_fk FOREIGN KEY (parent_id) REFERENCES parent(id)
);


2、簡單事務測試

嘗試一些簡單的事務,看看鎖是什麼樣的:

BEGIN;
SELECT * FROM active_locks; -- There are no active locks yet
INSERT INTO parent (name) VALUES ('Parent 1');
SELECT * FROM active_locks;
COMMIT;
SELECT * FROM active_locks;

可以看到在 parent 表里插入一行後,獲得了 parent 表上的行獨占鎖。parent_id_seq 是 parent 表的主鍵序列。由於這種關係被選中(如表),獲得了訪問共享鎖。


3、插入數據

咱們試著往 child 表里插點東西;

BEGIN;
INSERT INTO child (parent_id, name) VALUES (1, 'Child 1 Parent 1');
COMMIT;

可以看到 parent 表上增加的行共享鎖。我們看不到的是,這個插入同樣獲得了 parent 表上引用行的行級共享鎖。並行執行兩個事務我們就可以看到它了:

-- Transaction 1
BEGIN;
INSERT INTO child (parent_id, name) VALUES (1, 'Child 2 Parent 1');
-- Transaction 2
BEGIN;
DELETE FROM parent WHERE id = 1;

現在開始第三個會話,看看鎖怎麼樣了:

SELECT * FROM active_locks;

DELETE 查詢被阻塞了,等待事務 1 完成。我們可以看到它在元組 1 上獲得了一個鎖。但是如果我們看到所有的鎖都是准許的(granted=t),為什麼 DELETE 查詢被阻塞了?這兩個事務在任何關係上的鎖都沒有同步過。事實上,如果一個事務在某些行上持有一個鎖,第二個事務請求這個鎖,第二個事務會嘗試獲取持有此鎖的事務上的共享鎖。當第一個事務完成時,第二個事務將繼續。這是可能的,因為每個事務都持有它自身的排他鎖。我們可以看看 pg_locks 視圖,這是輸出(只有部分是重要的):

可以看到事務 563(pid 5690)和事務564(pid 5790)擁有它們事務標識上的排他鎖,事務564 獲得了事務563 上的共享鎖。


4、更新子表

可以玩玩更新子表但不實際改變任何父表與相關的東西(在這個案例中是parent_id列)。

BEGIN;
UPDATE child SET name = 'My new name' WHERE id = 1;
SELECT * FROM active_locks;
UPDATE child SET name = 'My new name' WHERE id = 1;
SELECT * FROM active_locks;
COMMIT;

可以看到正在執行的 UPDATE 查詢不會觸及任何與父表相關的東西。第一次執行後,我們可以看到,只有 child 表包含表級鎖。行級鎖也是如此。只有 child 表的行有更新鎖。這是 Postgres 中的優化。

如果鎖管理器可以從第一個查詢中發現外鍵沒有改變(沒有被更新查詢提及或被設置為相同的值),它不會鎖定父表。但在第二個查詢它會像文檔描述的那樣處理(它將鎖定 parent 表為行共享鎖定模式和涉及的行為分享模式)。這是非常危險的,因為它會導致最危險的和最難找到的死鎖。不過可以在事務開頭使用顯式鎖定以避免它。

防止死鎖的最好方式,是當我們意識到它們可能在兩個事務之間發生時,去按一定順序獲取行級鎖(例如主鍵排序)和首先獲取最嚴格的鎖。一旦在 Postgres 發生死鎖,可以通過中止一個參與死鎖的事務來消除。


總結

對 Postgres 中鎖的工作機制銘記於心非常重要。在高並發環境中死鎖可能無法避免,但重要的是要知道如何發現、監控並解決它們。即使所有事情都照書而行也不一定能解決所有潛在的鎖問題,但會減少它們並使其易於解決。表級鎖可以通過 pg_locks 系統視圖查看,但行級鎖不行,所以這讓調試鎖更為困難,不過Postgres 的未來版本中還是有可能實現的。後面會分享更多devops和DBA方面的內容,感興趣的朋友可以關注一下~

文章來源: https://twgreatdaily.com/zh-cn/thLrYWwBUcHTFCnfkIeq.html