基於PostgreSQL 9.4
9.3中文文檔:http://58.58.27.50:8079/doc/html/9.3.1_zh/explicit-locking.html#LOCKING-TABLES
9.4英文文檔:http://www.postgresql.org/docs/9.4/static/explicit-locking.html#LOCKING-TABLES
MVCC、事務、事務隔離級別:http://my.oschina.net/liuyuanyuangogo/blog/497929
四、顯式鎖定
PostgreSQL提供了多種鎖模式用於控制對表中數據的並發訪問。這些模式可以用於在MVCC無法給出期望行為的場合。同樣,大多數PostgreSQL命令自動施加恰當的鎖以保證被引用的表在命令的執行過程中不會以一種不兼容的方式被刪除或者修改。 比如,在存在其它並發操作的時候,TRUNCATE是不能在同一個表上面執行的。
要檢查數據庫服務器里所有當前正在被持有的鎖,可以使用pg_locks系統視圖。有關監控鎖管理器子系統狀態的更多信息,請參考章Chapter 27。
3.1表級鎖:
下面的列表顯示了可用的鎖模式和它們被PostgreSQL自動使用的場合。你也可以用LOCK命令明確獲取這些鎖。兩個事務在同一時刻不能在同一個表上持有相互沖突的鎖。不過,一個事務決不會和自身沖突。比如,它可以在一個表上請求ACCESS EXCLUSIVE然后接着請求 ACCESS SHARE。 非沖突鎖模式可以被許多事務同時持有。請特別注意有些鎖模式是自沖突的(比如,在任意時刻ACCESS EXCLUSIVE模式就不能夠被多個事務擁有), 但其它鎖模式都不是自沖突的(比如,ACCESS SHARE可以被多個事務持有)。
表級鎖LOCK TABLE命令語法:
(中文文檔:http://58.58.27.50:8079/doc/html/9.3.1_zh/sql-lock.html)
LOCK [ TABLE ] [ ONLY ] name [ * ] [, ...] [ IN lockmode MODE ] [ NOWAIT ] 這里的lockmode可以是下列之一: ACCESS SHARE | ROW SHARE | ROW EXCLUSIVE | SHARE UPDATE EXCLUSIVE | SHARE | SHARE ROW EXCLUSIVE | EXCLUSIVE | ACCESS EXCLUSIVE
下面是可用的鎖模式和它們被PostgreSQL自動使用的場合。你也可以用LOCK命令明確獲取這些鎖。請注意所有這些鎖模式都是表級鎖,即使它們的名字包含"row"單詞(這些名稱是歷史遺產)。從某種角度而言,這些名字反應了每種鎖模式的典型用法—但是語意卻都是一樣的。兩種鎖模式之間真正的區別是它們有着不同的沖突鎖集合(參見Table 13-2)。 兩個事務在同一時刻不能在同一個表上持有相互沖突的鎖。不過,一個事務決不會和自身沖突。比如,它可以在一個表上請求ACCESS EXCLUSIVE然后接着請求 ACCESS SHARE。非沖突鎖模式可以被許多事務同時持有。請特別注意有些鎖模式是自沖突的(比如,在任意時刻ACCESS EXCLUSIVE模式就不能夠被多個事務擁有),但其它鎖模式都不是自沖突的(比如,ACCESS SHARE可以被多個事務持有)。
8大表級鎖模式:
ACCESS SHARE:只與ACCESS EXCLUSIVE沖突。 SELECT命令在引用的表上請求這個鎖。通常,任何只讀取表而不修改它的命令都請求這種鎖模式。
--事務1 BEGIN; --給表lyy上access share鎖 LOCK TABLE lyy IN ACCESS SHAER MODE; --與SELECT * FROM lyy;產生相同類型的鎖 --事務2: BGEIN; --給表lyy上access exclusive鎖. LOCK TABLE lyy IN ACCESS EXCLUSIVE MODE; --與DROP TABLE lyy或者其他DDL操作產生相同類型的鎖。 --以上操作被阻塞,因為產生了鎖沖突
ROW SHARE:與EXCLUSIVE和ACCESS EXCLUSIVE鎖模式沖突。SELECT FOR UPDATE和SELECT FOR SHARE命令在目標表上需要一個這樣模式的鎖 (加上在所有被引用但沒有ACCESS SHARE的表上的FOR UPDATE/FOR 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選項),ANALYZE,CREATE INDEX CONCURRENTLY和ALTER TABLE請求這樣的鎖。
SHARE:與ROW EXCLUSIVE,SHARE UPDATE EXCLUSIVE,SHARE ROW EXCLUSIVE,EXCLUSIVE和ACCESS EXCLUSIVE鎖模式沖突。這個模式避免表的並發數據修改。CREATE INDEX(不帶CONCURRENTLY選項)語句要求這樣的鎖模式。
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:與所有模式沖突(8大模式,也包括自身類型)。這個模式保證其所有者(事務)是可以訪問該表的唯一事務。 ALTER TABLE,DROP TABLE,TRUNCATE,REINDEX,CLUSTER和VACUUM FULL命令要求這樣的鎖。多種形式的ALTER TABLE也要求一個該級別的鎖 (參見 ALTER TABLE). 在LOCK TABLE命令沒有明確聲明需要的鎖模式時,ACCESS EXCLUSIVE是缺省表鎖模式。
注意: 只有ACCESS EXCLUSIVE阻塞SELECT(不包含FOR UPDATE/SHARE語句)。
--事務1 BEGIN; LOCK TABLE lyy in access exclusive mode; --事務2 BEGIN; SELECT * FROM lyy; --與LOCK TABLE lyy IN ACCESS SHAER MODE;都產生ACCESS SHARE鎖 --SELECT操作阻塞,因為與access exclusive產生了鎖沖突
一旦請求已獲得某種鎖,那么該鎖模式將持續到事務結束。但是如果在建立保存點之后才獲得鎖,那么在回滾到這個保存點的時候將立即釋放所有該保存點之后獲得的鎖。這與ROLLBACK取消所有保存點之后對表的影響的原則一致。同樣的原則也適用於PL/pgSQL異常塊中獲得的鎖:一個跳出塊的錯誤將釋放在塊中獲得的鎖。
--事務1 postgres=# begin; BEGIN postgres=# LOCK TABLE lyy IN ACCESS SHARE MODE; LOCK TABLE postgres=# savepoint svp1; SAVEPOINT postgres=# LOCK TABLE lyy IN ACCESS exclusive MODE; LOCK TABLE --事務2 postgres=# begin; BEGIN postgres=# select * from lyy;--此時操作被阻塞,因為此時是ACCESS exclusive鎖 --事務1 postgres=# rollback to savepoint svp1;//此時是access share鎖 ROLLBACK --事務2 此時select * from lyy返回查詢結果.//access share鎖不阻塞SELECT操作。
沖突鎖模式對比表
Requested Lock Mode | Current Lock Mode | |||||||
---|---|---|---|---|---|---|---|---|
ACCESS SHARE | ROW SHARE | ROW EXCLUSIVE | SHARE UPDATE EXCLUSIVE | SHARE | SHARE ROW EXCLUSIVE | EXCLUSIVE | ACCESS EXCLUSIVE | |
ACCESS SHARE | X | |||||||
ROW SHARE | X | X | ||||||
ROW EXCLUSIVE | X | X | X | X | ||||
SHARE UPDATE EXCLUSIVE | X | X | X | X | X | |||
SHARE | X | X | X | X | X | |||
SHARE ROW EXCLUSIVE | X | X | X | X | X | X | ||
EXCLUSIVE | X | X | X | X | X | X | X | |
ACCESS EXCLUSIVE | X | X | X | X | X | X | X | X |
備注: 上圖是Postgresql 表級鎖的各種沖突模式對照表,‘X’表示沖突項。
3.2行級鎖:
行級鎖可以是排他的或者是共享的。特定行上的排他行級鎖是在行被更新的時候自動請求的。 該鎖一直保持到事務提交或者回滾。行級鎖不影響對數據的查詢,它們只阻塞對同一行的寫入。
行級鎖(SELECT .. FOR ..)命令語法
(中文文檔:http://58.58.27.50:8079/doc/html/9.3.1_zh/sql-select.html)
FOR UPDATE,FOR NO KEY UPDATE,FOR SHARE和FOR KEY SHARE是鎖定子句;他們影響SELECT如何從表中鎖定行作為獲得的行。鎖定子句的一般形式:
FOR lock_strength [ OF table_name [, ...] ] [ NOWAIT ]
這里的lock_strength可以是下列之一:
UPDATE NO KEY UPDATE SHARE KEY SHARE
4個行級鎖強度
FOR UPDATE:令那些被SELECT檢索出來的行被鎖住,就像在更新一樣。這樣就避免它們在當前事務結束前被其它事務修改或者刪除;也就是說, 其它企圖UPDATE,DELETE,SELECT FOR UPDATE,SELECT FOR NO KEY UPDATE,SELECT FOR SHARE或SELECT FOR KEY SHARE這些行的事務將被阻塞,直到當前事務結束。同樣, 如果一個來自其它事務的UPDATE,DELETE,SELECT FOR UPDATE已經鎖住了某個或某些選定的行,SELECT FOR UPDATE將等到那些事務結束,並且將隨后鎖住並返回更新的行(或者不返回行,如果行已經被刪除)。但是,在REPEATABLE READ或SERIALIZABLE事務內部,如果在事務開始時要被鎖定的行已經改變了,那么將拋出一個錯誤。更多的討論參閱Chapter 13。
FOR NO KEY UPDATE的行為類似於FOR UPDATE,只是獲得的鎖比較弱:該鎖不阻塞嘗試在相同的行上獲得鎖的SELECT FOR KEY SHARE命令。該鎖模式也可以通過任何不爭取FOR UPDATE鎖的UPDATE獲得。
FOR SHARE的行為類似於FOR NO KEY UPDATE,只是它在每個檢索出來的行上獲得一個共享鎖,而不是一個排它鎖。一個共享鎖阻塞其它事務在這些行上執行UPDATE,DELETE,SELECT FOR UPDATE或SELECT FOR NO KEY UPDATE,卻不阻止他們執行SELECT FOR SHARE或SELECT FOR KEY SHARE。
FOR KEY SHARE的行為類似於FOR SHARE,只是獲得的鎖比較弱: 阻塞SELECT FOR UPDATE但不阻塞SELECT FOR NO KEY UPDATE。一個共享鎖阻塞其他事務執行DELETE或任意改變鍵值的UPDATE, 但是不阻塞其他UPDATE,也不阻止SELECT FOR NO KEY UPDATE, SELECT FOR SHARE 或SELECT FOR KEY SHARE。
為了避免操作等待其它事務提交,使用NOWAIT選項。如果被選擇的行不能立即被鎖住, 那么語句將會立即匯報一個錯誤,而不是等待。請注意,NOWAIT只適用於行級別的鎖, 要求的表級鎖ROW SHARE仍然以通常的方法進行(參閱Chapter 13)。 如果需要申請表級別的鎖同時又不等待,那么你可以使用LOCK的 NOWAIT選項。
舉例說明:
准備表和數據:
CREATE TABLE lyy(id int primary key, name varchar); INSERT INTO lyy values(1,'a1'),(2,'a2'),(3,'a3'),(4,'a4'); --for update 事務1: BEGIN; SELECT * FROM lyy WHERE id<3 FOR UPDATE; 事務2: BEGIN; UPDATE lyy SET name='aa' WHERE id=2; --此時id=2的行已被FOR UPDATE鎖定,所以update操作阻塞,直到事務1,提交之后,才能執行。 --for no key update 事務1: BEGIN; SELECT * FROM lyy WHERE id=1 FOR NO KEY UPDATE; --在對行1的鎖定效果上,等價於 update lyy set id=2 where id=2; 事務1: BEGIN; SELECT * FROM lyy WHERE id=1 FOR KEY SHARE; --該行可以執行,因為雖然id=1的行已被鎖定,但是FOR NO KEY UPDATE不阻塞SELECT ... FOR KEY SHARE; --for share 事務1: BGEIN; SELECT * FROM lyy WHERE id=1 FOR SHARE; 事務2: BEGIN; SELECT * FROM lyy WHERE id=1 FOR [KEY] SHARE;--可以執行,因為該行的FOR SHARE鎖,不阻塞FOR [KEY] SHARE; UPDATE lyy set name='sss' WHERE id=1; --阻塞, --for key share 事務1: BGEIN; SELECT * FROM lyy WHERE id=1 FOR KEY SHARE; 事務2: BEGIN; UPDATE lyy SET id=222 WHERE id=2; --阻塞,for key share 阻塞鍵值字段的update操作; UPDATE lyy SET name='coco' WHERE id=2; --正常執行,for update share不阻塞非鍵值字段的update操作。
3.3頁級鎖
除了表級鎖和行級鎖,頁級別的共享/排他鎖用來控制中對表頁的讀/寫訪問(在共享緩存池)。一個行被抓去或者更新后,這些鎖會迅速被釋放。應用開發者通常不需要關心頁級鎖,在此處提及頁級鎖是為了鎖機制介紹的完整性。
3.4死鎖:
顯式鎖定的使用可能會增加死鎖的可能性。
死鎖是指兩個(或兩個以上)事務相互持有對方期待的鎖,如果沒有其他機制,這些事務都將無法進行下去。
避免死鎖的方法
防止死鎖的最好方法通常是保持所有使用同一個數據庫的應用都能與相同的順序在多個對象上請求排它鎖。
由於數據庫可以自動檢測出死鎖,所以應用也可以通過補貨死鎖異常來處理思索。但這不是一個很好的方法,因為數據庫檢測死鎖需要一些代價,可能會導致應用程序過久持有排它鎖,從而導致系統的並發處理能力下降。
排它鎖持有的時間越長,就越容易導致死鎖,所以在程序設計時,要盡量短的持有排它鎖。
阻塞與死鎖的區別
參考:http://blog.163.com/caisq_eva/blog/static/12748817120104751552974/
數據庫阻塞的現象: 第一個連接占有資源沒有釋放,而第二個連接需要獲取這個資源。如果第一個連接沒有提交或者回滾,第二個連接會一直等待下去,直到第一個連接釋放該資源為止。對於阻塞,數據庫無法處理,所以對數據庫操作要及時地提交或者回滾。
數據庫死鎖的現象:第一個連接占有資源沒有釋放,准備獲取第二個連接所占用的資源,而第二個連接占有資源沒有釋放,准備獲取第一個連接所占用的資源。這種互相占有對方需要獲取的資源的現象叫做死鎖。對於死鎖,數據庫處理方法:犧牲一個連接,保證另外一個連接成功執行。
3.5咨詢鎖:
PostgreSQL允許創建由應用定義其含義的鎖。這種鎖被稱為咨詢鎖, 因為系統並不強迫其使用— 而是由應用來保證其被恰當的使用。 咨詢鎖可用於 MVCC 難以實現的鎖定策略。 比如,咨詢鎖一般用於模擬常見於"平面文件"數據管理系統的悲觀鎖策略。 雖然可以用存儲在表中的一個特定標志達到同樣的目的,但是使用咨詢鎖更快,還可以避免表臃腫, 更可以在會話結束的時候由系統自動執行清理工作。