今天有個技術(shù)群里面,突然有人冒出來說,他被MySQL坑了。據(jù)說是有個包含了auto_increment的表,放在事務(wù)里面操作,雖然事務(wù)回滾了,但是這個自增列自增后的值并沒有回滾。
我掐指一算,多半是他自己建錯了表引擎,然后還把鍋扔給了mysql。于是一貫樂(xi)于(huan)助(da)人(lian)的我馬上切到mysql,按照他的說法,做了下面的操作:
先建了一個表,引擎innodb,同時還有一個自增id。
然后,隨便插入幾條數(shù)據(jù),再看看插入結(jié)果:
好了,打臉開始。
開啟事務(wù),插入,查看,回滾,查看。
再插入數(shù)據(jù),看。
我勒個去,玩砸了,我臉好痛。
這一定是打開方式不對,是不是哪錯了?可是,這不就是一個正常得不能再正常的操作嗎,也就是說,這還真是個坑咯?
事出反常必有妖。通過各種找資料,我才明白其實自增列下一個自增的值是放在內(nèi)存的,跟事務(wù)并沒有關(guān)系,當(dāng)然也不會回滾。這個值在服務(wù)器啟動后會初始化一次,然后就只會增加不會減少了。
這么說的話,可以在一次事務(wù)回滾之后,看一下show create table,然后重啟下mysql,然后再show create table,應(yīng)該就能看出問題來。
首先,show一下
然后,重啟mysql
接著,再回來show一下
果然,下一個auto_increment變回去了。說明查到的資料是靠譜的。
其實冷靜下來想想,這個feature也不難理解,事務(wù)的回滾,回滾的其實是數(shù)據(jù)庫寫入的值,而對于下一個自增id的值這種東西,其實不算數(shù)據(jù)庫寫入值的一部分。我們只是想當(dāng)然的認(rèn)為它該回滾而已,但其實它不回退回去也不是一個違反了定義的事。
其實如果知道了這個實現(xiàn)方式之后,很多關(guān)于自增列的特性也就不難理解了。比如先插入再刪除,自增id是不會回去的,但重啟了服務(wù)端之后就可以了。
雖然整篇都在說自增id,但是實際在實踐中,我更傾向于自己生成主鍵,而不是使用數(shù)據(jù)庫的自增。使用數(shù)據(jù)庫的自增id,其實就是偷了一個懶,想著防止主鍵重復(fù),而且還能按順序增加。但實際上,如果使用了自增id,所有插入數(shù)據(jù)的地方都必須要放在一個入口,很容易成為瓶頸。另一方面,使用自增id的數(shù)據(jù),往往小id都是一些特殊的數(shù)據(jù),可能是臟數(shù)據(jù),也可能是很久遠(yuǎn)的數(shù)據(jù)。這些數(shù)據(jù)在現(xiàn)在的代碼規(guī)則下會有什么表現(xiàn),并沒有人能說得清楚,搞不好就成為黑客攻擊站點的突破口。最關(guān)鍵的是,這些id其實算是業(yè)務(wù)數(shù)據(jù)的一部分,但是卻由數(shù)據(jù)服務(wù)來生成維護(hù),增加了耦合。
不過技術(shù)沒有絕對的優(yōu)點或者缺點,只有相對的適合和不適合。也許這個feature在現(xiàn)在是個意料之外的麻煩,自增在大業(yè)務(wù)量下面是個可能的瓶頸,但它們也會有更合適的地方。我們也沒必要帶著感情來吐槽這個特性,熟記特性于胸,說不定能在某個時刻發(fā)現(xiàn)它們牛逼閃閃的用途。