天天看點

insert into select 主鍵自增_關于自增id 你可能還不知道

insert into select 主鍵自增_關于自增id 你可能還不知道
導讀:在使用MySQL建表時,我們通常會建立一個自增字段(AUTO_INCREMENT),并以此字段作為主鍵。本篇文章将以問答的形式講述關于自增id的一切。

注: 本文所講的都是基于Innodb存儲引擎。

1.MySQL為什麼建議将自增列id設為主鍵?
  • 如果我們定義了主鍵(PRIMARY KEY),那麼InnoDB會選擇主鍵作為聚集索引、如果沒有顯式定義主鍵,則InnoDB會選擇第一個不包含有NULL值的唯一索引作為主鍵索引、如果也沒有這樣的唯一索引,則InnoDB會選擇内置6位元組長的ROWID作為隐含的聚集索引(ROWID随着行記錄的寫入而主鍵遞增,這個ROWID不像ORACLE的ROWID那樣可引用,是隐含的)。
  • 資料記錄本身被存于主索引(一顆B+Tree)的葉子節點上。這就要求同一個葉子節點内(大小為一個記憶體頁或磁盤頁)的各條資料記錄按主鍵順序存放,是以每當有一條新的記錄插入時,MySQL會根據其主鍵将其插入适當的節點和位置,如果頁面達到裝載因子(InnoDB預設為15/16),則開辟一個新的頁(節點)
  • 如果表使用自增主鍵,那麼每次插入新的記錄,記錄就會順序添加到目前索引節點的後續位置,當一頁寫滿,就會自動開辟一個新的頁
  • 如果使用非自增主鍵(如果身份證号或學号等),由于每次插入主鍵的值近似于随機,是以每次新紀錄都要被插到現有索引頁得中間某個位置,此時MySQL不得不為了将新記錄插到合适位置而移動資料,甚至目标頁面可能已經被回寫到磁盤上而從緩存中清掉,此時又要從磁盤上讀回來,這增加了很多開銷,同時頻繁的移動、分頁操作造成了大量的碎片,得到了不夠緊湊的索引結構,後續不得不通過OPTIMIZE TABLE來重建表并優化填充頁面。

綜上而言:當我們使用自增列作為主鍵時,存取效率是最高的。

2.自增列id一定是連續的嗎?

自增id是增長的 不一定連續。

我們先來看下MySQL 對自增值的儲存政策:

InnoDB 引擎的自增值,其實是儲存在了記憶體裡,并且到了 MySQL 8.0 版本後,才有了“自增值持久化”的能力,也就是才實作了“如果發生重新開機,表的自增值可以恢複為 MySQL 重新開機前的值”,具體情況是:

在 MySQL 5.7 及之前的版本,自增值儲存在記憶體裡,并沒有持久化。每次重新開機後,第一次打開表的時候,都會去找自增值的最大值 max(id),然後将 max(id)+1 作為這個表目前的自增值。

舉例來說,如果一個表目前資料行裡最大的 id 是 10,AUTO_INCREMENT=11。這時候,我們删除 id=10 的行,AUTO_INCREMENT 還是 11。但如果馬上重新開機執行個體,重新開機後這個表的 AUTO_INCREMENT 就會變成 10。

也就是說,MySQL 重新開機可能會修改一個表的 AUTO_INCREMENT 的值。

在 MySQL 8.0 版本,将自增值的變更記錄在了 redo log 中,重新開機的時候依靠 redo log 恢複重新開機之前的值。

造成自增id不連續的情況可能有:

  • 1.唯一鍵沖突
  • 2.事務復原
  • 3.insert ... select語句批量申請自增id
3.自增id有上限嗎?

自增id是整型字段,我們常用int類型來定義增長id,而int類型有上限 即增長id也是有上限的。

下表列舉下 

int

 與 

bigint

 字段類型的範圍:

類型 大小 範圍(有符号) 範圍(無符号)
int 4位元組 (-2147483648,2147483647) (0,4294967295)
bigint 8位元組 (-9223372036854775808,9223372036854775807) (0,18446744073709551615)

從上表可以看出:當自增字段使用int有符号類型時,最大可達2147483647即21億多;使用int無符号類型時,最大可達4294967295即42億多。當然bigint能表示的範圍更大。

下面我們測試下當自增id達到最大時再次插入資料會怎麼樣:

create table t(id int unsigned auto_increment primary key) auto_increment=4294967295;insert into t values(null);// 成功插入一行 4294967295show create table t;/* CREATE TABLE `t` (`id` int(10) unsigned NOT NULL AUTO_INCREMENT,PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=4294967295;*/insert into t values(null);//Duplicate entry '4294967295' for key 'PRIMARY'
           

從實驗可以看出,當自增id達到最大時将無法擴充,第一個 insert 語句插入資料成功後,這個表的AUTO_INCREMENT 沒有改變(還是 4294967295),就導緻了第二個 insert 語句又拿到相同的自增 id 值,再試圖執行插入語句,報主鍵沖突錯誤。

4.關于自增列 我們該怎麼維護?

維護方面主要提供以下2點建議:

  • 1.字段類型選擇方面:推薦使用int無符号類型,若可預測該表資料量将非常大 可改用bigint無符号類型。
  • 2.多關注大表的自增值,防止發生主鍵溢出情況。

— END —

如果你喜歡我的文章

請在文末右下角點一下在看❤

insert into select 主鍵自增_關于自增id 你可能還不知道

繼續閱讀