讓我們假裝有一個tags
表,其中有一個名爲name
的獨特字段。如何使這兩個同時事務在任何時候都成功,而不會導致違反唯一性?
我有我進行選擇,以查看是否有特定名稱的標籤存在一個事務,如果不是我創建它:
START TRANSACTION;
SELECT * FROM TAGS WHERE NAME = "FOO";
-- IF A TAG NAMED "FOO" DIDN'T EXIST THEN
INSERT INTO TAGS VALUES("FOO");
COMMIT;
當兩個客戶端在運行此交易默認隔離級別(可重複讀),該交錯會導致他們中的一個失敗,唯一性衝突:
START TRANSACTION;
START TRANSACTION;
SELECT * FROM TAGS WHERE NAME = "FOO";
SELECT * FROM TAGS WHERE NAME = "FOO";
-- IF A TAG NAMED "FOO" DIDN'T EXIST THEN
INSERT INTO TAGS VALUES("FOO");
-- IF A TAG NAMED "FOO" DIDN'T EXIST THEN
INSERT INTO TAGS VALUES("FOO");
COMMIT;
COMMIT;
我想如果我設置的隔離級別設置爲序列化,我能避免這種情況,但我注意到,相同的交織將導致死鎖。
我該如何修改事務,以避免由於唯一性約束違規而失敗?
根據記錄,這是Ruby on Rails的(ActiveRecord的)代碼對應於這樣的場景:
class Tag < ActiveRecord::Base
def self.create_tag(name)
transaction do
# setting isolation level to serializable leads to a deadlock
# Tag.connection.execute("SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE")
gets
tag = Tag.find_by_name(name)
if tag.nil?
gets
Tag.create!(:name => name)
end
end
end
end
一種offtopic,但更好的使用從名稱='foo'的標籤中選擇名稱;這將是索引覆蓋的查詢,它比訪問表的速度更快 – 2012-01-04 21:35:42
您可以執行插入...選擇嗎?這[使用互斥表](http://www.xaprb.com/blog/2005/09/25/insert-if-not-exists-queries-in-mysql/) – 2012-01-04 21:50:51