2010-05-26 71 views
0

我們非常沮喪地在MySQL中導致死鎖。這不是因爲超過了鎖定超時,因爲它們確實發生時會立即發生死鎖。下面是是在2個獨立的線程(從連接池2個獨立的連接)執行時產生死鎖的SQL代碼:在MySQL中獲取死鎖

UPDATE Sequences SET Counter = LAST_INSERT_ID(Counter + 1) WHERE Sequence IS NULL 

序列表中有2列:序列和反

的LAST_INSERT_ID讓我們到retrieve this updated counter value as per MySQL's recommendation。這對我們來說非常合適,但是我們遇到了這些僵局!我們爲什麼得到它們,我們如何避免它們?

非常感謝您的幫助。

編輯:這是所有事務(因爲我使用Hibernate而需要)和AUTO_INCREMENT在這裏沒有意義。我應該更清楚。序列表包含許多序列(在我們的案例中約有1億個序列)。我需要增加一個計數器並檢索該值。 AUTO_INCREMENT在所有這一切中都沒有任何作用,這與Ids或PRIMARY KEY無關。

+0

請問您可以發佈'SHOW CREATE TABLE Sequences'的輸出嗎? – Quassnoi 2010-05-26 23:47:21

+0

CREATE TABLE'sequences'( 'Id' BIGINT(20)NOT NULL AUTO_INCREMENT, 'Sequence' VARBINARY(1005)DEFAULT NULL, 'Counter' INT(11)DEFAULT '0', PRIMARY KEY('Id' ), UNIQUE KEY'Sequences_Constraint'('Sequence'(104)) )ENGINE = InnoDB AUTO_INCREMENT = 134 DEFAULT CHARSET = utf8 – 2010-05-27 00:59:59

回答

2

將你的sql語句包裝在一個事務中。如果您沒有使用事務,您將在LAST_INSERT_ID上獲得競爭條件。

但是真的,你應該有計數器字段auto_increment,所以你讓mysql處理這個。

您的第三個解決方案是使用LOCK_TABLES來鎖定序列表,以便其他進程不能同時訪問它。除非您使用INNODB,否則這可能是最慢的解決方案。

+0

這是在一個事務中。我在上面編輯了我的問題,解釋AUTO_INCREMENT在這裏沒有關係,抱歉不清楚。鎖定序列表對我們來說可能是一個嚴重的性能問題。 – 2010-05-27 01:02:02

+1

事件導致*更多*死鎖...他們不是解決死鎖的解決方案。 – zombat 2010-05-27 04:42:51

1

死鎖是任何事務數據庫的正常組成部分,並且可以隨時發生。一般來說,你應該編寫你的應用程序代碼來處理它們,因爲沒有確定的辦法可以保證你永遠不會陷入僵局。也就是說,有些情況會增加發生死鎖的可能性,例如使用大型事務,還有一些事情可以減輕它們的發生。

首先,您應該閱讀this manual page以更好地瞭解如何避免它們。其次,如果你所做的只是更新一個計數器,那麼你確實應該使用一個AUTO_INCREMENT列來代替Counter,而不是依賴於「select then update」過程,正如你所看到的那樣,它是一個可能導致僵局的競爭條件。實質上,表列的AUTO_INCREMENT屬性將作爲您的計數器。

最後,我將假設你在事務中有這個更新語句,因爲這會產生頻繁的死鎖。如果你想看到它的行動,請嘗試experiment listed here。這正是您的代碼所發生的情況......兩個線程正在嘗試在提交其中一個之前同時更新相同的記錄。即時死鎖。

你最好的解決方案是弄清楚如何在沒有事務的情況下完成它,AUTO_INCREMENT會讓你這樣做。

+0

有趣的實驗鏈接到,謝謝。但我不確定這是怎麼回事。我只更新了1行。我會閱讀手冊頁。這當然是在交易中。我在上面編輯了我的問題,解釋AUTO_INCREMENT在這裏沒有關係,抱歉不清楚。 – 2010-05-27 01:06:23

+0

@at - 你只更新一行,但它是兩個事務中的同一行,因此是死鎖。如果兩個事務同時觸發,則兩者都會嘗試更新同一行,因爲在提交之前,每個事務的LAST_INSERT_ID()將相同。 – zombat 2010-05-27 04:40:24

+1

爲什麼不更新更新同一行的語句只是一個接一個地執行?爲什麼會陷入僵局? – 2010-05-27 05:40:15

0

沒有涉及其他SQL?似乎對我來說不太可能。

'where sequence is null'可能會導致全表掃描,導致在每行/每頁/ ...上讀取鎖。

如果(您的特定引擎不使用MVCC並且)在同一事務中更新之前有INSERT,則會成爲問題。該INSERT將獲得對某些資源(行/頁/ ...)的排他鎖,這將導致任何其他線程獲取讀取鎖等待。因此,兩個連接可以先插入它們,導致它們中的每一個都在表的一小部分上擁有排它鎖,然後它們都會嘗試更新,要求它們中的每一個都能夠獲得對整個桌子。

+0

死鎖中沒有涉及與該表有關的任何其他SQL。死鎖都發生在上面的UPDATE語句上。有趣的是你對NULL問題的看法。還有一個UPDATE語句,序列名有一個值,但死鎖不會在那裏發生。我認爲是因爲我們的應用程序的性質,我們需要更頻繁的空序列。如果NULL導致全表掃描,我可以很容易地改變它來尋找其他的東西,比如空字符串。我確實在同一個事務中插入,導致死鎖? – 2010-05-27 09:25:39

+0

「沒有涉及其他SQL」和「我在同一個事務中插入了」?來吧。事務之間發生死鎖,而不是在語句級別。 爲什麼你首先寫「WHERE ... IS NULL」呢?你不想限制你的更新只是由同一個事務插入的行嗎? – 2010-05-27 09:32:23

+0

您問我是否沒有涉及其他SQL,然後提示問題可能是同一事務中的插入語句。這就是爲什麼我用那些引用的短語回答。 「WHERE Sequence IS NULL」實際上是將我的更新限制在Sequence爲null的那一行。我最後一個問題有點困惑。只有插入纔會發生,因爲序列名稱在序列表中尚不存在。 – 2010-05-27 16:32:44