MySQL:數(shù)據(jù)庫(kù)自增 ID 用完了會(huì)咋樣?

01 前言

數(shù)據(jù)庫(kù)中的自增 ID 用完了該怎么辦?

這個(gè)問(wèn)題其實(shí)可以分為有主鍵 & 無(wú)主鍵兩種情況回答。

先上張腦圖:

02 有主鍵

如果你的表有主鍵,并且把主鍵設(shè)置為自增。

在 MySQL 中,一般會(huì)把主鍵設(shè)置成 int 型。而 MySQL 中 int 型占用 4 個(gè)字節(jié),作為有符號(hào)位的話范圍就是 [-231,231-1],也就是[-2147483648,2147483647];無(wú)符號(hào)位的話最大值就是 2^32-1,也就是 4294967295。

下面以有符號(hào)位創(chuàng)建一張表:

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;
復(fù)制代碼

插入一個(gè) id 為最大值 2147483647 的值,如下圖所示:

如果此時(shí)繼續(xù)下面的插入語(yǔ)句:

INSERT INTO t (url) VALUES ('wwww.javafish.top/article/erwt/spring')
復(fù)制代碼

結(jié)果就會(huì)造成主鍵沖突:


2.1 解決方案

雖說(shuō) int 4 個(gè)字節(jié),最大數(shù)據(jù)量能存儲(chǔ) 21 億。你可能會(huì)覺(jué)得這么大的容量,應(yīng)該不至于用完。但是互聯(lián)網(wǎng)時(shí)代,每天都產(chǎn)生大量的數(shù)據(jù),這是很有可能達(dá)到的。

所以,我們的解決方案是:把主鍵類型改為 bigint,也就是 8 個(gè)字節(jié)。這樣能存儲(chǔ)的最大數(shù)據(jù)量就是 2^64-1,我也數(shù)不清有多少了。反正在你有生之年應(yīng)該是夠用的。

PS:單表 21 億的數(shù)據(jù)量顯然不現(xiàn)實(shí),一般來(lái)說(shuō)數(shù)據(jù)量達(dá)到 500 萬(wàn)就該分表了。

03 沒(méi)主鍵

另一種情況就是建表時(shí)沒(méi)設(shè)置主鍵。這種情況,InnoDB 會(huì)自動(dòng)幫你創(chuàng)建一個(gè)不可見(jiàn)的、長(zhǎng)度為 6 字節(jié)的 row_id,默認(rèn)是無(wú)符號(hào)的,所以最大長(zhǎng)度是 2^48-1。

實(shí)際上 InnoDB 維護(hù)了一個(gè)全局的 dictsys.row_id,所以未定義主鍵的表都共享該 row_id,并不是單表獨(dú)享。每次插入一條數(shù)據(jù),都把全局 row_id 當(dāng)成主鍵 id,然后全局 row_id 加 1。

這種情況的數(shù)據(jù)庫(kù)自增 ID 用完會(huì)發(fā)生什么呢?

1、創(chuàng)建一張無(wú)顯示設(shè)置主鍵的表 t:

CREATE TABLE IF NOT EXISTS `t`(
   `age` int(4) NOT NULL
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
復(fù)制代碼

2、通過(guò) ps -ef|grep mysql 命令獲取 mysql 的進(jìn)程 ID,然后執(zhí)行命令,通過(guò) gdb 先把 row_id 修改為 1。PS:沒(méi)有 gdb 的,百度安裝下

sudo gdb -p 16111 -ex 'p dict_sys->row_id=1' -batch
復(fù)制代碼

出現(xiàn)下圖就是沒(méi)錯(cuò)的:

3、插入三條數(shù)據(jù):

insert into t(age) values(1);
insert into t(age) values(2);
insert into t(age) values(3);

此時(shí)的數(shù)據(jù)庫(kù)數(shù)據(jù):

4、gdb 把 row_id 修改為最大值:281474976710656

sudo gdb -p 16111 -ex 'p dict_sys->row_id=281474976710656' -batch

5、再插入三條數(shù)據(jù):

insert into t(age) values(4);
insert into t(age) values(5);
insert into t(age) values(6);

此事的數(shù)據(jù)庫(kù)數(shù)據(jù):

分析:

  • 剛開(kāi)始設(shè)置 row_id 為 1,插入三條數(shù)據(jù) 1、2、3 的 row_id 也理應(yīng)是 1、2、3;這是沒(méi)問(wèn)題的。

  • 接著設(shè)置 row_id 為最大值,緊跟著插入三條數(shù)據(jù)。這時(shí)的數(shù)據(jù)庫(kù)結(jié)果是:4、5、6、3;你會(huì)發(fā)現(xiàn) 1、2 被覆蓋了。

  • row_id 達(dá)到后最大值后插入的值 4、5、6 的 row_id 分別是 0、1、2;由于 row_id 為 1、2 的值已存在,所以后者的值 5、6 會(huì)覆蓋掉 row_id 為 1、2 的值。

結(jié)論:row_id 達(dá)到最大值后會(huì)從 0 重新開(kāi)始算;前面插入的數(shù)據(jù)就會(huì)被后插入的數(shù)據(jù)覆蓋,且不會(huì)報(bào)錯(cuò)。

04 總結(jié)

數(shù)據(jù)庫(kù)自增主鍵用完后分兩種情況:

  • 有主鍵,報(bào)主鍵沖突
  • 無(wú)主鍵,InnDB 會(huì)自動(dòng)生成一個(gè)全局的row_id。它到達(dá)最大值后會(huì)從 0 開(kāi)始算,遇到 row_id 一樣時(shí),新數(shù)據(jù)覆蓋舊數(shù)據(jù)。所以,我們還是盡量給表設(shè)置主鍵。

我的回答除了以上解決方法外,還提到在業(yè)務(wù)開(kāi)發(fā)中,我們不會(huì)等到主鍵用完那天就已經(jīng)分庫(kù)分表了,基本不會(huì)遇到這種情況。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容