01 前言
哈嘍,好久沒更新啦。因為最近在面試。用了兩周時間準備,在 3 天之內拿了 5 個 offer,最后選擇了廣州某互聯網行業獨角獸 offer,昨天剛入職。這幾天剛好整理下在面試中被問到有意思的問題,也借此機會跟大家分享下。
這家企業的面試官有點意思,一面是個同齡小哥,一起聊了兩個小時(聊到我嘴都干了)。他問了我一個有意(keng)思(b)問題:
數據庫中的自增 ID 用完了該怎么辦?
這個問題其實可以分為有主鍵 & 無主鍵兩種情況回答。
國際慣例,先上張腦圖:
1.1 往期精彩
MySQL 查詢語句是怎么執行的?
MySQL 索引
MySQL 日志
MySQL 事務與 MVCC
MySQL 的鎖機制
MySQL 字符串怎么設計索引?
02 有主鍵
如果你的表有主鍵,并且把主鍵設置為自增。
在 MySQL 中,一般會把主鍵設置成 int 型。而 MySQL 中 int 型占用 4 個字節,作為有符號位的話范圍就是 [-2^31,2^31-1],也就是 [-2147483648,2147483647];無符號位的話最大值就是 2^32-1,也就是 4294967295。
下面以有符號位創建一張表:
CREATE TABLE IF NOT EXISTS `t`(
`id` INT(11) NOT NULL AUTO_INCREMENT,
`url` VARCHAR(64) NOT NULL,
PRIMARY KEY ( `id` )
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
插入一個 id 為最大值 2147483647 的值,如下圖所示:
如果此時繼續下面的插入語句:
INSERT INTO t (url) VALUES ('wwww.JAVAfish.top/article/erwt/spring')
結果就會造成主鍵沖突:
2.1 解決方案
雖說 int 4 個字節,最大數據量能存儲 21 億。你可能會覺得這么大的容量,應該不至于用完。但是互聯網時代,每天都產生大量的數據,這是很有可能達到的。
所以,我們的解決方案是:把主鍵類型改為 bigint,也就是 8 個字節。這樣能存儲的最大數據量就是 2^64-1,我也數不清有多少了。反正在你有生之年應該是夠用的。
PS:單表 21 億的數據量顯然不現實,一般來說數據量達到 500 萬就該分表了。
03 沒主鍵
另一種情況就是建表時沒設置主鍵。這種情況,InnoDB 會自動幫你創建一個不可見的、長度為 6 字節的 row_id,默認是無符號的,所以最大長度是 2^48-1。
實際上 InnoDB 維護了一個全局的 dictsys.row_id,所以未定義主鍵的表都共享該 row_id,并不是單表獨享。每次插入一條數據,都把全局 row_id 當成主鍵 id,然后全局 row_id 加 1。
這種情況的數據庫自增 ID 用完會發生什么呢?
1、創建一張無顯示設置主鍵的表 t:
CREATE TABLE IF NOT EXISTS `t`(
`age` int(4) NOT NULL
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
2、通過 ps -ef|grep mysql 命令獲取 mysql 的進程 ID,然后執行命令,通過 gdb 先把 row_id 修改為 1。PS:沒有 gdb 的,百度安裝下
sudo gdb -p 16111 -ex 'p dict_sys->row_id=1' -batch
出現下圖就是沒錯的:
3、插入三條數據:
insert into t(age) values(1);
insert into t(age) values(2);
insert into t(age) values(3);
此時的數據庫數據:
4、gdb 把 row_id 修改為最大值:281474976710656
sudo gdb -p 16111 -ex 'p dict_sys->row_id=281474976710656' -batch
5、再插入三條數據:
insert into t(age) values(4);
insert into t(age) values(5);
insert into t(age) values(6);
此事的數據庫數據:
分析:
- 剛開始設置 row_id 為 1,插入三條數據 1、2、3 的 row_id 也理應是 1、2、3;這是沒問題的。
- 接著設置 row_id 為最大值,緊跟著插入三條數據。這時的數據庫結果是:4、5、6、3;你會發現 1、2 被覆蓋了。
- row_id 達到后最大值后插入的值 4、5、6 的 row_id 分別是 0、1、2;由于 row_id 為 1、2 的值已存在,所以后者的值 5、6 會覆蓋掉 row_id 為 1、2 的值。
結論:row_id 達到最大值后會從 0 重新開始算;前面插入的數據就會被后插入的數據覆蓋,且不會報錯。
04 總結
數據庫自增主鍵用完后分兩種情況:
- 有主鍵,報主鍵沖突
- 無主鍵,InnDB 會自動生成一個全局的 row_id。它到達最大值后會從 0 開始算,遇到 row_id 一樣時,新數據覆蓋舊數據。所以,我們還是盡量給表設置主鍵。
為什么我說這是個有意(keng)思(b)問題?
我的回答除了以上解決方法外,還提到在業務開發中,我們不會等到主鍵用完那天就已經分庫分表了,基本不會遇到這種情況。
這時,面試官可能會問你分庫分表咋處理,如果你不會就不要主動提了,點到即止。
05 參考文章
- blog.csdn.net/weixin_39640090/article/details/113227742
- blog.csdn.net/qq_35393693/article/details/100059966
- time.geekbang.org/column/article/69862
原文鏈接:
https://mp.weixin.qq.com/s/1gmpJqBmCBsNPlLbrN0Q7Q