2016-11-22 74 views
1

我有表tt_users其中有id作爲主鍵,列state(CHAR(1)),它可以是 「X」 或 「Y」,並state_position(INT)找到。這些列上沒有索引。存儲引擎是innodb。MySQL的 - 死鎖時,試圖獲得鎖定

state_positions必須是連續的,並有可能永遠不會成爲某種狀態,即重複的位置,如果我有5個用戶狀態「X」,他們state_positions必須是1,2,3,4,5

這是我在運行查詢導致死鎖:

insert into `tt_users` (`state`, `state_position`) 
values ('x', 
    (SELECT MAX(state_position) AS maxStatePosition 
    FROM tt_users AS u2 
    WHERE u2.state='x') + 1 
) 

爲了測試,我插入了大量用戶同時的,每一次我得到了僵局錯誤。

我看到這篇文章 - How to avoid mysql 'Deadlock found when trying to get lock; try restarting transaction',但我不明白,我應該用我的查詢,以防止死鎖做,如果這是在所有可能的,因爲這個問題的答案 - Working around MySQL error "Deadlock found when trying to get lock; try restarting transaction" - 說死鎖可能不管發生什麼。

我設法得到這個工作,始終工作的唯一辦法,就是這個(語言是PHP):

PDO::setAttribute(PDO::ATTR_EMULATE_PREPARES, true); 

然後手動鎖定表,並運行我的查詢:

SET autocommit=0; 

LOCK TABLES 
tt_users WRITE, 
tt_users AS u2 WRITE; 

insert into `tt_users` (`state`, `state_position`) 
values ('x', 
    (SELECT MAX(state_position) AS maxStatePosition 
    FROM tt_users AS u2 
    WHERE u2.state='x') + 1 
); 

COMMIT; 
UNLOCK TABLES; 

然後:

PDO::setAttribute(PDO::ATTR_EMULATE_PREPARES, false); 

根據該方法我同時4次運行的代碼,每個實例inser幫助2500名用戶,沒有問題。

是否只有這樣才能使其發揮作用,或者我可以100%確定地防止死鎖而不必手動鎖定表?

UPDATE:

每@ wallyk的回答,我試過如下:

1)把查詢在事務 - 仍然死鎖錯誤
2)WITH CONSISTENT SNAPSHOT開始交易,READ WRITEREAD ONLY。所有3個選項都需要設置PDO::setAttribute(PDO::ATTR_EMULATE_PREPARES, true);WITH CONSISTENT SNAPSHOTREAD WRITE仍然給我造成了死鎖錯誤,而READ ONLY自然也沒有讓我執行INSERT

所以現在看來​​手動鎖定表是唯一可行的。

回答

0

我不知道這是否會幫助,但有一個交易周圍的SQL語句可以提供內部正確的鎖定順序:

start transaction; 
insert into `tt_users` (`state`, `state_position`) 
values ('x', 
    (SELECT MAX(state_position) AS maxStatePosition 
    FROM tt_users AS u2 
    WHERE u2.state='x') + 1 
); 
commit; 

如果不工作,有幾個選項,這些選項可以添加到start transaction聲明中。請參閱here

+0

謝謝,我會嘗試所有這些,並得到結果。 – GTCrais

+0

已經嘗試了您的建議並更新了結果。可悲的是,它沒有奏效。 – GTCrais