2014-04-08 77 views
0

讓我們在SQL窗口1假設我做的:鎖和交易中的Postgres應該阻止查詢

-- query 1 
BEGIN TRANSACTION; 
UPDATE post SET title = 'edited' WHERE id = 1; 
-- note that there is no explicit commit 
從另一個窗口

然後(窗口2)我做的:

-- query 2 
SELECT * FROM post WHERE id = 1; 

我得到:

1 | original title 

由於默認隔離級別是READ COMMITTED,並且因爲查詢1從不提交,因此更改它pe rforms無法讀取,直到我明確地從窗口1.

其實犯,如果我在窗口1,做:

COMMIT TRANSACTION; 

然後我可以看到的變化,如果我重新運行查詢2.

1 | edited 

我的問題是:

爲什麼查詢返回2罰款,我第一次運行呢?由於窗口1中的事務尚未提交,因此我預計它將被阻止,並且與id = 1一起放在行上的鎖是(應該是)未發佈的專用應用程序,應該阻止像在窗口2中執行的讀取操作。其餘所有其他對我意義重大,但我期待SELECT卡住,直到窗口1中的顯式提交被執行。

+1

PostgreSQL使用MVCC(多版本併發控制)而不是鎖。你會獲得較舊的值而不是阻塞。正如你所看到的,這大大提高了併發性,但可能有點難以繞開你的頭。 –

+0

@DanielLyons:說「而不是鎖」將會走向遠...鎖定好了,寫作通常不會阻止閱讀,反之亦然。 –

+0

@ErwinBrandstetter我認爲你讓你的知識深度(這是巨大的)不必要地基於一種常見的誤解來着色一個直接的問題。由於MVCC的原因,OP的預期鎖定方式並非如此。確實有些操作會鎖定,而且無疑某些內部使用鎖定,問題中的任何內容都不會導致預期的鎖定行爲。 –

回答

0

PostgreSQL's MVCC的實現中,原理是讀取不會阻止寫入,反之亦然。 Per documentation:

利用併發控制MVCC模型 而不是鎖定的主要優點是,查詢收購MVCC鎖 (讀)數據不寫入數據, 獲取的鎖衝突,所以讀永不寫作和寫作從不阻止閱讀。 即使在通過使用創新的可串行化快照隔離(SSI)級別提供最嚴格的事務隔離級別時,PostgreSQL仍能保證這一保證。

每筆交易只能看到(主要)交易開始前已經提交的內容。

這樣做不是表示不會鎖定。一點也不。對於許多操作,獲得各種鎖。並採用各種策略來解決可能的衝突。

1

您描述的行爲在任何事務關係數據庫中都是正常的和預期的。

如果PostgreSQL的表現你第SELECTedited它會是錯誤的,這樣做的 - 這就是所謂的「髒讀」,並在數據庫中的壞消息。

PostgreSQL將被允許在SELECT處等待,直到您提交或回退爲止,但SQL標準並不要求這麼做,您還沒有告訴它您想等待,並且它不必等待任何技術原因,因此它會立即返回您要求的數據。畢竟,直到它承諾,那update只存在 - 它仍然可能或可能不會發生。

如果PostgreSQL一直在這裏等待,那麼你很快就會遇到一次只有一個連接可以對數據庫做任何事情的情況。表現不佳,在絕大多數時間完全沒有必要。

如果您想等待併發UPDATE(或DELETE),那麼您需要使用SELECT ... FOR SHARE。 (但請注意,這不適用於INSERT)。


詳情:

SELECT沒有FOR UPDATEFOR SHARE條款不採取任何行級鎖。因此它可以看到當前提交的行是什麼,並且不受可能正在修改該行的任何正在進行的事務的影響。這些概念在MVCC section of the docs中解釋。一般的想法是PostgreSQL是copy-on-write,它具有版本控制功能,允許它根據事務或語句在啓動時「看到」什麼時返回正確的副本 - PostgreSQL稱之爲「快照」。

在默認READ COMMITTED隔離創建快照在語句級別,因此,如果您SELECT一排,COMMIT從另一個事務它的變化,SELECT一遍,你會甚至在一個TRANSATION看到不同的值。如果您不希望在事務開始後看到提交的更改,或者隔離度爲SERIALIZABLE以針對某些類型的事務相關性添加進一步的保護,則可以使用SNAPSHOT隔離。

參見the transaction isolation chapter in the documentation

如果您希望SELECT等待正在進行的事務提交或回滾對所選行的更改,則必須使用SELECT ... FOR SHARE。這將阻止UPDATEDELETE所採用的鎖定,直到鎖定回滾或提交的交易完成。

INSERT是不同的,雖然 - 直到提交之前,這些元組才存在於其他事務中。等待併發INSERT的唯一方法是取EXCLUSIVE表級鎖,以便在讀取它時知道沒有人正在更改表。通常需要這樣做意味着您在應用程序中存在設計問題 - 但如果未提交insert仍在運行中,您的應用程序不應該使用保護

請參閱the explicit locking chapter of the documentation