從文檔find_or_create
:如何在使用DBIx :: Class :: ResultSet的find_or_create方法時避免競爭條件?
注意:由於find_or_create()從數據庫中讀取信息,然後基於該結果 可能插入,該方法是受a種族 條件。在 查找已完成且在創建開始之前,另一個進程可以在表中創建一條記錄。爲避免 這個問題,請在事務中使用find_or_create()。
在PostgreSQL的事務中使用find_or_create()
足夠嗎?
從文檔find_or_create
:如何在使用DBIx :: Class :: ResultSet的find_or_create方法時避免競爭條件?
注意:由於find_or_create()從數據庫中讀取信息,然後基於該結果 可能插入,該方法是受a種族 條件。在 查找已完成且在創建開始之前,另一個進程可以在表中創建一條記錄。爲避免 這個問題,請在事務中使用find_or_create()。
在PostgreSQL的事務中使用find_or_create()
足夠嗎?
不,文檔不正確。單獨使用交易而不是避免此問題。它只能保證如果發生異常時整個事務會回滾 - 這樣就不會有任何不一致的狀態持久存在數據庫中。
到避免這個問題你必須鎖定表 - 在一個事務中,因爲所有的鎖都在事務結束時被釋放。類似於:
BEGIN;
LOCK TABLE mytbl IN SHARE MODE;
-- do your find_or_create here
COMMIT;
但是,這並不是萬能的萬能藥。它可能會成爲一個性能問題,並且可能有死鎖(併發事務相互試圖鎖定另一個已經鎖定的資源)。 PostgreSQL將檢測到這種情況並取消除競爭事務之外的所有事務。您必須準備好在失敗時重試操作。
The PostgreSQL manual about locks.
如果你沒有很多併發的也可能只是忽略的問題。時隙非常小,所以它實際上很少發生。如果您捕獲重複的密鑰違規錯誤,這將不會造成任何損害,那麼您也已經介紹了這一點。
這個實現的find_or_create
應防止競爭條件,在OP描述:
eval {
$row = $self->model->create({ ... });
}
if([email protected] && [email protected] =~ /duplicate/i) {
$row = $self->model->find({ ... });
}
它還減少find_or_create()
在最好的情況下,單一的查詢。
這反轉了邏輯。但是現在你有一個很小的時間段,其中的條目可以被刪除 - 在這種情況下,邏輯將失敗。試圖寫第一個比嘗試閱讀更昂貴。所以這只是一個改進,如果重複是非常罕見的。不管怎樣,衝突應該是非常罕見的,因爲時間間隔很小。 – 2012-04-22 13:35:50
其他有用的頁面。 Docs:http://www.postgresql.org/docs/current/interactive/mvcc.html PostgreSQL 9.1或更高版本的可串行化實現:http://wiki.postgresql.org/wiki/SSI其他隔離級別或PostgreSQL版本:http ://www.postgresql.org/files/developer/concurrency.pdf – kgrittn 2012-04-22 00:27:17
但是,在DBIC中捕捉「重複密鑰違規錯誤」的正確方法是什麼? – 2012-10-15 21:37:21
@eugeney:我想你會爲此提出一個新問題,而不是評論。你總是可以鏈接到這一個,以節省自己的一些打字。 – 2012-10-15 22:09:19