在面試中,大家應該經歷過如下場景
面試官:"用過mysql吧,你們是用自增主鍵還是UUID?"
你:"用的是自增主鍵" 面試官:"為什么是自增主鍵?"
你:"因為采用自增主鍵,數據在物理結構上是順序存儲,性能最好,blabla..."
面試官:"那自增主鍵達到最大值了,用完了怎么辦?"
你:"what,沒復習啊!!" (然后,你就可以回去等通知了!)
今天我們就來談一談,這個自增主鍵用完了該怎么辦!
一、關於自增ID
https://mp.weixin.qq.com/s/hkYcXap1FmtuEFWsxwEDDg,這篇文章介紹了好幾種自增id,每種自增 id 有各自的應用場景,在達到上限后的表現也不同:
1、 表的自增 id 達到上限后,再申請時它的值就不會改變,進而導致繼續插入數據時報主鍵沖突的錯誤
2、 row_id 達到上限后,則會歸 0 再重新遞增,如果出現相同的 row_id,后寫的數據會覆蓋之前的數據
3、 Xid 只需要不在同一個 binlog 文件中出現重復值即可。雖然理論上會出現重復值,但是概率極小,可以忽略不計
4、 InnoDB 的 max_trx_id 遞增值每次 MySQL 重啟都會被保存起來,所以我們文章中提到的臟讀的例子就是一個必現的 bug,好在留給我們的時間還很充裕
5、 thread_id 是我們使用中最常見的,而且也是處理得最好的一個自增 id 邏輯了
6、 redis外部自增,毫秒級別,理論上會出現重復值,但是概率極小,可以忽略不計
7、 其實,每種自增id都有各自的適用場景,大家在平時使用中可以根據具體場景再選擇。但是要未雨綢繆,因為系統的運行時間和數據的存儲,這些都是要考慮在內的,綜合考慮,選擇一個在系統運行期間一定不會出現重復即刻。你學會了嗎?
二、自增 ID 用完怎么辦
1、簡單回答版 —— int 改為 bigint
我們先明白一點,在mysql中,Int整型的范圍如下:
我們以無符號整型為例,存儲范圍為0~4294967295,約43億!我們先說一下,一旦自增id達到最大值,此時數據繼續插入是會報一個主鍵沖突異常如下所示
//Duplicate entry '4294967295' for key 'PRIMARY'
那解決方法也是很簡單的,將Int類型改為BigInt類型,BigInt的范圍如下
就算你每秒10000條數據,跑100年,單表的數據也才 10000*24*3600*365*100=31536000000000,
這數字距離BigInt的上限還差的遠,因此你將自增ID設為BigInt類型,你是不用考慮自增ID達到最大值這個問題! 然而,如果你在面試中的回答如果是
你:"簡單啊,把自增主鍵的類型改為BigInt類型就好了!"
接下來,面試官可以問你一個更坑的問題!
面試官:"你在線上怎么修改列的數據類型的?" 你:"what!我還是回等通知吧!"
2、如何在生產環境修改列的數據類型
目前業內在線修改表結構的方案,據我了解,一般有如下三種
(1)方式一:使用mysql5.6+提供的在線修改功能
所謂的mysql自己提供的功能也就是mysql自己原生的語句,例如我們要修改原字段名稱及類型。
mysql> ALTER TABLE table_name CHANGE old_field_name new_field_name field_type;
那么,在mysql5.5這個版本之前,這是通過臨時表拷貝的方式實現的。執行ALTER
語句后,會新建一個帶有新結構的臨時表,將原表數據全部拷貝到臨 時表,然后Rename,完成創建操作。這個方式過程中,原表是可讀的,不可寫。但是會消耗一倍的存儲空間。 在5.6+開始,mysql支持在線修改數據庫表,在修改表的過程中,對絕大部分操作,原表可讀,也可以寫。
那么,對於修改列的數據類型這種操作,原表還能寫么?官網mysql8.0版本的一張圖
我們可以看到:如圖所示,change the column data type 對於修改數據類型這種操作,是不支持並發的DML操作!也就是說,如果你直接使用ALTER
這樣的語句在線修改表數據結構,會導致這張表無法進行更新類操作(DELETE
、UPDATE
、DELETE
)。 因此,直接ALTER
是不行滴!
那我們只能用方式二或者方式三。
(2)方式二:借助第三方工具
業內有一些第三方工具可以支持在線修改表結構,使用這些第三發工具,能夠讓你在執行ALTER
操作的時候,表不會阻塞!
比較出名的有兩個:1、pt-online-schema-change
,簡稱 pt-osc;
2、GitHub正式宣布以開源的方式發布的工具,名為 gh-ost
以pt-osc
為例,它的原理如下:
- 1、創建一個新的表,表結構為修改后的數據表,用於從源數據表向新表中導入數據。
- 2、創建觸發器,用於記錄從拷貝數據開始之后,對源數據表繼續進行數據修改的操作記錄下來,用於數據拷貝結束后,執行這些操作,保證數據不會丟失。
- 3、拷貝數據,從源數據表中拷貝數據到新表中。
- 4、rename源數據表為old表,把新表rename為源表名,並將old表刪除。
- 5、刪除觸發器。
然而這兩個有意(KENG)思(B)
的工具,居然也有坑!如果你的表里有觸發器和外鍵,這兩個工具是不行滴! 如果真碰上了數據庫里有觸發器和外鍵,只能硬杠了,請看方式三。
(3)方式三:改從庫表結構,然后主從切換
此法極其麻煩,需要專業水平的選手進行操作。因為我們的mysql架構一般是讀寫分離架構,從機是用來讀的。我們直接在從庫上進行表結構修改,不會阻塞從庫的讀操作。改完之后,進行主從切換即可。唯一需要注意的是,主從切換過程中可能會有數據丟失的情況!
3、高深版
其實答完上面的問題后,這篇文章差不多完了。但是,還記得我在開頭說的么。這是一個很有意(KENG)思(B)
的問題,為什么呢?
假設啊,你的表里的自增字段為有符號的Int類型的,也就是說,你的字段范圍為-2147483648到2147483648。 一切又那么剛好,你的自增ID是從0開始的,也就是說,現在你的可以用的范圍為0~2147483648。 我們明確一點,表中真實的數據ID,肯定會出現一些意外,ID不一定是連續的。
因此,表中的真實id必然會出現斷續的情況。 好,那這會你的自增主鍵id的數據范圍為0~2147483648,也就是單表21億條數據!考慮id會出現斷續,真實數據頂多18億條吧。 老哥,都單表18億條了,還不分庫分表?
你一旦分庫分表了,就不能依賴於每個表的自增ID來全局唯一標識這些數據了。此時,我們就需要提供一 個全局唯一的ID號生成策略來支持分庫分表的環境。
所以,你需要關注的文章是《分庫分表后如何上線部署》
因此在實際中,你根本等不到自增主鍵用完到情形! 所以,專業版回答如下
面試官:"那自增主鍵達到最大值了,用完了怎么辦?"
你:"這問題沒遇到過,因為自增主鍵一般用int類型,一般達不到最大值,我們就分庫分表了,所以不曾遇見過!"
原文鏈接:https://zhuanlan.zhihu.com/p/61690701