2013-10-21 54 views
4

我有一個表有序號。這個序號會改變,引用自動號碼將不起作用。我擔心觸發器的值會發生碰撞。如果兩個事務同時讀取。基於查詢原子的觸發器?

我已經在3個連接上運行了模擬測試,每個模擬測試有100萬條記錄,沒有碰撞。

CREATE TABLE `aut` (
    `au_id` int(10) NOT NULL AUTO_INCREMENT, 
    `au_control` int(10) DEFAULT NULL, 
    `au_name` varchar(50) DEFAULT NULL, 
    `did` int(10) DEFAULT NULL, 
    PRIMARY KEY (`au_id`), 
    KEY `Did` (`did`) 
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1 

TRIGGER `binc_control` BEFORE INSERT ON `aut` 
FOR EACH ROW BEGIN 
SET NEW.AU_CONTROL = (SELECT COUNT(did)+1 FROM aut WHERE did = NEW.did); 
END; 
+1

'SELECT COUNT(*)+ 1' ---這真是個壞主意。 'au_control'應該指的是什麼? – zerkms

+0

如果碰撞可能是一個問題,那麼創建一個'UNIQUE(did,au_control)'似乎是明智的。我不確定在觸發器中的選擇是否是原子的,我會認爲它們不是_not_。 – Wrikken

+0

雖然你可以定義你的'PRIMARY KEY(did,au_control)',第二個是自動增量字段,但是它有一些缺點(1)你失去了'au_id'列,而是一個真正的主鍵而不是代理可能會更好,(2)該表將被認爲是MyISAM,因爲InnoDB不支持它,這具有嚴重的缺點。 [看到這個小提琴](http://sqlfiddle.com/#!2/31788/1) – Wrikken

回答

3

是的,這是受競爭情況,如果兩會運行在同一時間動了扳機。你不應該使用這個解決方案。

在測試過程中可能不會發生,但您可以假定它在生產過程中會發生。 :-)

有句古話:One in a million is next Tuesday

0

技術上,是的。它是原子的,最初的聲明加上所有的副作用都是作爲同一事務處理的。

但是,然後,根據隔離級別的不同,您的查詢可能會出錯。如果內存服務,MySQL在目錄中有一個緩存的值,但如果你在併發事務中使用不可序列化的隔離讀取它,那麼這個緩存可能會過時。

0

即使在明確的隔離事務中,仍然存在競爭條件,最終可能會出現重複的au_control值。計數(*)對於性能來說會很糟糕。如果那是你想要做的,有更好的方法來獲得一個單調遞增的guid。

0

MySQL UUID_SHORT()函數可能對您有用。它是原子的,每次調用時都會產生一個永不增加的值,但要求您不要停止服務器,並且要及時向後設置時鐘到上次啓動之前或接近上次啓動的時間(取決於多久你調用它)並再次啓動服務器,或將@@server_id的值更改爲LSB較低的值。

http://dev.mysql.com/doc/refman/5.6/en/miscellaneous-functions.html#function_uuid-short