表級鎖
下面的列表顯示了可用的鎖模式和它們被 PostgreSQL 自動使用的環境。
你也可以用命令 LOCK 明確獲取這些鎖。
請注意所有這些鎖模式都是表級鎖,即使它們的名字包含單詞 “row”;這些鎖模式的名稱是歷史造成的。
從某種角度而言,這些名字反應了每種鎖模式的典型用法 — 但是語意都是一樣的。
兩種鎖模式之間真正的區別是它們有着不同的沖突鎖集合。
兩個事務在同一時刻不能在同一個表上持有相互沖突的鎖。
(不過,一個事務決不會和自身沖突。比如,它可以在一個表上請求
ACCESS EXCLUSIVE 然后稍后的時候請求 ACCESS SHARE。)
非沖突鎖模式可以由許多事務並發地持有。
請特別注意有些鎖模式是自沖突的(比如,在任意時刻 ACCESS EXCLUSIVE 模式就不能夠被多個事務擁有)
而其它地都不是自沖突的(比如,ACCESS SHARE 可以被多個事務持有)。
一旦請求到了某種鎖,那么該鎖模式將持續到事務結束。
表級鎖模式
- ACCESS SHARE
-
只與
ACCESS EXCLUSIVE 沖突。
SELECT 和 ANALYZE 命令在被引用的表上請求一個這種鎖。
通常,任何只讀取表而不修改它的命令都請求這種鎖模式。 - ROW SHARE
-
與
EXCLUSIVE和
ACCESS EXCLUSIVE模式沖突。
SELECT FOR UPDATE 和 SELECT FOR SHARE
命令在目標表上需要一個這樣模式的鎖(加上在所有被引用但沒有
FOR UPDATE/FOR SHARE 的表上的 ACCESS SHARE 鎖)。 - ROW EXCLUSIVE
-
與
SHARE,
SHARE ROW EXCLUSIVE,
EXCLUSIVE 和
ACCESS EXCLUSIVE 模式沖突。命令 UPDATE,DELETE,
和 INSERT 自動請求這個鎖模式。
(加上所有其它被引用的表上的 ACCESS SHARE 鎖)。
通常,這種鎖將被任何修改表中數據的查詢請求。 - SHARE UPDATE EXCLUSIVE
-
和
SHARE UPDATE EXCLUSIVE,
SHARE,
SHARE ROW EXCLUSIVE, EXCLUSIVE,
和 ACCESS EXCLUSIVE 模式沖突。
這個模式保護一個表不被並發模式改變和 VACUUM。VACUUM(不帶 FULL 選項)請求這樣的鎖。
- SHARE
-
與
ROW EXCLUSIVE,
SHARE UPDATE EXCLUSIVE,
SHARE ROW EXCLUSIVE, EXCLUSIVE
和 ACCESS EXCLUSIVE 模式沖突。
這個模式避免表的並發數據修改。CREATE INDEX
語句要求這樣的鎖模式。 - SHARE ROW EXCLUSIVE
-
與
ROW EXCLUSIVE,
SHARE UPDATE EXCLUSIVE, SHARE,
SHARE ROW EXCLUSIVE, EXCLUSIVE,
和 ACCESS EXCLUSIVE 模式沖突。任何 PostgreSQL 命令都不會自動請求這樣的鎖模式。
- EXCLUSIVE
-
與
ROW SHARE,
ROW EXCLUSIVE,
SHARE UPDATE EXCLUSIVE,
SHARE, SHARE ROW EXCLUSIVE,
EXCLUSIVE 和 ACCESS EXCLUSIVE
模式沖突。
這個模式只允許並發 ACCESS SHARE 鎖,也就是說,
只有對表的讀動作可以和持有這個鎖模式的事務並行執行。任何 PostgreSQL 命令都不會在用戶表上自動請求這樣的鎖模式.
不過,在有些操作的時候,會在某些系統表上請求它。 - ACCESS EXCLUSIVE
-
與所有模式沖突。
( ACCESS
SHARE, ROW SHARE, ROW
EXCLUSIVE, SHARE UPDATE
EXCLUSIVE, SHARE, SHARE
ROW EXCLUSIVE, EXCLUSIVE, 和
ACCESS EXCLUSIVE).
這個模式保證其所有者(事務)是可以用任意方式訪問該表的唯一事務。ALTER TABLE,
DROP TABLE,REINDEX,CLUSTER和
VACUUM FULL 命令要求這樣的鎖。
在 LOCK TABLE 命令沒有明確聲明需要的鎖模式時,它也是缺省鎖模式。
提示: 只有 ACCESS EXCLUSIVE 阻塞
SELECT (沒有
FOR UPDATE/FOR SHARE語句)。
行級鎖
除了表級鎖以外,還有行級鎖,他們可以是排他的或者是共享的。
特定行上的排他行級鎖是在行被更新的時候自動請求的。
該鎖一直保持到事務提交或者回滾。
行級鎖不影響對數據的查詢;
它們只阻塞對同一行的寫入。
要在不修改某行的前提下請求在該行的一個排他行級鎖,用 SELECT FOR UPDATE 選取該行。
請注意一旦我們請求了特定的行級鎖,
那么該事務就可以多次對該行進行更新而不用擔心沖突。
要在一行上請求一個共享的行級鎖,用 SELECT FOR SHARE 選取該行。
一個共享鎖並不阻止其它事務請求同一個共享的鎖。不過,其它事務不允許更新,刪除,
或者排他鎖住一個其它事務持有共享鎖的行。任何這么做的企圖都將被阻塞住,等待共享鎖釋放。
PostgreSQL
不會在內存里保存任何關於已修改行的信息,
因此對一次鎖定的行數沒有限制。
不過,鎖住一行會導致一次磁盤寫;因此,象 SELECT FOR UPDATE 將修改選中的行以標記它們被鎖住了,
因此會導致磁盤寫。
除了表級別的和行級別的鎖以外,
頁面級別的共享/排他銷也用於控制對共享緩沖池中表頁面的讀/寫訪問。
這些鎖在抓取或者更新一行后馬上被釋放。
應用程序員通常不需要關心頁級鎖,我們在這里提到它們只是為了完整。
死鎖
明確鎖定的使用可能會增加死鎖的可能性,
死鎖是是指兩個(或多個)事務相互持有對方期待的鎖。比如,
如果事務 1 在表 A上持有一個排他鎖,
同時試圖請求一個在表 B 上的排他鎖,
而事務 2 已經持有表B的排他鎖,而卻正在請求在表 A上的一個排他鎖,那么兩個事務就都不能執行。
PostgreSQL 自動偵測到死鎖條件並且會通過退出一個當事的事務來解決這個問題,
以此來允許其它事務完成。(具體哪個事務會被退出是很難預計的,而且也不應該依靠這樣的預計。)
要注意的是死鎖也可能會因為行級鎖而發生(因此,即使是沒有使用明確的鎖定,也可能發生)。
考慮這樣一種情況,兩個並發事務在修改一個表。第一個事務執行了:
UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 11111;
這樣就在指定帳號的行上請求了一個行級鎖。然后,第二個事務執行:
UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 22222; UPDATE accounts SET balance = balance - 100.00 WHERE acctnum = 11111;
第一個 UPDATE 語句成功地在指定行上請求到了一個行級鎖,因此它成功更新了該行。
但是第二個 UPDATE 語句發現它試圖更新地行已經被鎖住了,
因此它等待持有該鎖的事務結束。事務二現在就在等待事務一結束,然后再繼續執行。
現在,事務一執行:
UPDATE accounts SET balance = balance - 100.00 WHERE acctnum = 22222;
事務一企圖在指定行上請求一個行級鎖,但是它得不到:事務二已經持有這樣的鎖了。
所以它等待事務二完成。因此,事務一被事務二阻塞住了,而事務二也被事務一阻塞住了:這就是一個死鎖條件。
PostgreSQL 將偵測這樣的條件並退出其中一個事務。
防止死鎖的最好方法通常是保證所有使用一個數據庫的應用都以一致的順序在多個對象上請求鎖定。
在上面的例子里,如果兩個事務以同樣的順序更新那些行,那么就不會發生死鎖。
我們也要保證在一個對象上請求的第一個鎖是該對象需要的最高的鎖模式。
如果我們無法提前核實這些問題,那么我們可以通過在現場重新嘗試因死鎖而退出的事務的方法來處理。
只要沒有檢測到死鎖條件,一個等待表級鎖或者行級鎖的事務將等待沖突鎖的釋放不確定的時間。
這就意味着一個應用持有打開的事務時間太長可不是什么好事情(比如鎖,等待用戶輸入)。