2010-05-07 84 views
32

一位同事剛剛讓我意識到一個非常奇怪的MySQL行爲。爲什麼在失敗的插入時MySQL自動增量會增加?

假設您有一個帶有auto_increment字段和另一個字段設置爲唯一的表(例如用戶名字段)。當試圖插入一個已經在表中的用戶名的行時,插入失敗,如預期的那樣。然而,auto_increment值會增加,這是因爲在多次失敗嘗試後插入有效的新條目時可以看到。

例如,當我們的最後一個條目看起來像這樣...

ID: 10 
Username: myname 

......我們試圖用我們將創建一個新的行就像我們的下一個插入相同的用戶名值五個新項目所以:

ID: 16 
Username: mynewname 

雖然這不是它本身似乎是一個非常愚蠢的攻擊向量與失敗的插入請求淹沒它殺了表的一個大問題,因爲MySQL參考手冊的狀態:

「如果該值大於可以存儲在指定整數類型中的最大整數,則自動遞增機制的行爲未定義。」

這是預期的行爲嗎?

+6

你的攻擊向量似乎非的問題。如果你可以用失敗的插入請求來洪水它,你不能同樣用非失敗的請求來洪水嗎? – 2010-05-07 10:52:22

+0

您可以攜帶重現**代碼**而不是手動解釋嗎? – 2010-05-07 10:55:59

+4

@martin smith:雖然這是真的,但我認爲突然涌現的新用戶會比auto_increment的沉默增加更明顯,如果沒有檢查的話,它可能會很好地落在旁邊。 – Sorcy 2010-05-07 12:40:40

回答

23

InnoDB是一個事務引擎。

這意味着,在以下情形:

  1. Session A插入記錄1
  2. Session B插入記錄2
  3. Session A回滾

,有任一間隙的可能性或session B將鎖定,直到session A承諾或回滾。設計師(與大多數其他交易引擎設計師一樣)選擇允許差距。

documentation

當訪問自動增長計數器,InnoDB採用了特殊的表級AUTO-INC鎖,它保持對當前SQL語句的結束,而不是事務的結束。特殊的鎖釋放策略被引入以提高併發插入到含有AUTO_INCREMENT

表...只要

InnoDB使用內存自動增長計數器作爲服務器運行。如上所述,當服務器停止並重新啓動時,InnoDB將第一個INSERT的每個表的計數器重新初始化爲該表。

如果你害怕id列環繞,請將其設置爲BIGINT(8字節長)。

+1

您的回答幫助我解決了很多自己的問題。謝謝。有沒有辦法繞過InnoDB有關自動增量的行爲? – Aufwind 2011-08-08 22:23:46

+0

與Aufwind相同的問題。 – 2012-03-28 06:23:36

+1

@Ankit:請將它作爲另一個問題發佈,並在此處放置一個鏈接。 – Quassnoi 2012-03-28 09:06:06

4

不知道確切的內部,我會說是的,自動增量應該允許跳過的值做失敗插入。比方說,你正在做一個銀行交易,或者其他整個交易和多筆記錄都是一筆一筆或一筆交易。如果您嘗試插入,獲取ID,然後使用該交易ID標記所有後續詳細信息並插入詳細記錄,則需要確保您的合格唯一性。如果你有多人關閉數據庫,他們也需要確保他們獲得自己的事務ID,以便在他們的事務被提交時不與你的衝突。如果第一筆交易失敗了,沒有任何傷害,並且沒有下游的懸掛成分。

+0

其使用skkiping性質的自動增量,但我想知道的是,這是任何方式來阻止這個skkiping – 2012-03-28 09:08:43

+2

@Ankit,不,你不能停止跳過。跟蹤分配的最後一個ID的文件頭始終增加。如果出現問題,並且有10人正在輸入交易並且3人中止,那麼您永遠不會希望僅僅使用ID來回填此類中止的交易。 – DRapp 2012-03-28 10:11:33

+0

thanx結算dout – 2012-03-28 10:39:08

-1

我知道這是一篇舊文章,但由於我也找不到正確的答案,我實際上找到了一種方法來做到這一點。你必須在if語句中包裝你的查詢。它通常插入查詢或插入和重複querys是搞亂了有組織的自動遞增順序,以便定期插入使用:

和替代insert和DUPLICATE使用UPDATE SET WHERE QUERY或外部的if語句並不重要,一個REPLACE INTO查詢也似乎工作

+2

我downvoting這個答案,因爲它根本不適用。是的,您可以在嘗試插入之前檢查記錄是否存在,但這完全在旁邊。 – Mave 2016-03-23 09:53:08