關於自增id 你可能還不知道

導讀:在使用MySQL建表時,咱們一般會建立一個自增字段(AUTO_INCREMENT),並以此字段做爲主鍵。本篇文章將以問答的形式講述關於自增id的一切。mysql

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

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 重啓前的值」,具體狀況是:code

在 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 恢復重啓以前的值。rem

形成自增id不連續的狀況可能有:

  • 1.惟一鍵衝突
  • 2.事務回滾
  • 3.insert ... select語句批量申請自增id

3.自增id有上限嗎?

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

下表列舉下 intbigint 字段類型的範圍:

類型 大小 範圍(有符號) 範圍(無符號)
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);
// 成功插入一行 4294967295
show 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.多關注大表的自增值,防止發生主鍵溢出狀況。
相關文章
相關標籤/搜索