InnoDB的自增鍵和row_id用完了會發生什麼?

標籤: 公衆號文章mysql


自增鍵用完了會發生什麼?

咱們在建表的時候爲某個索引列(注意:必須是索引列)添加AUTO_INCREMENT屬性,就像這樣:sql

CREATE TABLE t (
    c1 TINYINT AUTO_INCREMENT,
    c2 TINYINT,
    KEY idx_c1 (c1)
) ENGINE=InnoDB;
複製代碼

t中包含一個索引列c1,該列被添加了AUTO_INCREMENT屬性。咱們先向該表中插入一條記錄:bash

mysql> INSERT INTO t(c1, c2) VALUES(126, 1);
Query OK, 1 row affected (0.02 sec)
複製代碼

以後咱們再也不在INSERT語句中顯式地插入c1列的值,它的默認值就將是從當前插入的最大的那個值以後自增,好比這樣:函數

mysql> INSERT INTO t(c2) VALUES(1);
Query OK, 1 row affected (0.01 sec)
複製代碼

咱們看一下此時表t中的數據:ui

mysql> SELECT * FROM t;
+-----+------+
| c1  | c2   |
+-----+------+
| 126 |    1 |
| 127 |    1 |
+-----+------+
2 rows in set (0.02 sec)
複製代碼

由於c1列是TINYINT類型的,使用1個字節存儲數據,它能存儲最大的值就是127,若是當該列的值到達127以後,咱們繼續向表中插入數據,自增列c1的值將會變成什麼呢?spa

mysql> INSERT INTO t(c2) VALUES(1);
Query OK, 1 row affected (0.01 sec)
複製代碼

插入成功以後咱們再看一下表中的數據:設計

mysql> SELECT * FROM t;
+-----+------+
| c1  | c2   |
+-----+------+
| 126 |    1 |
| 127 |    1 |
| 127 |    1 |
+-----+------+
3 rows in set (0.01 sec)
複製代碼

很顯然,自增列c1的值將再也不繼續增加,而是取的TINYINT所能存儲的最大值。code

這裏須要注意的是,在當前舉的例子中,咱們只是在自增列c1上邊創建了一個普通的二級索引idx_c1,因此鍵值重複也沒啥問題。不過咱們通常將AUTO_INCREMENT屬性應用在表的主鍵上,此時若是自增列值達到了主鍵對應數據類型所能存儲的最大值時,就會報錯(由於主鍵值重複),你們必定注意!cdn

row_id用完了會發生什麼?

在咱們使用InnoDB存儲引擎來建表時,若是咱們本身沒有顯式地建立主鍵時,存儲引擎會默認找一個具備NOT NULL屬性的惟一二級索引列來充當主鍵,若是咱們在建表語句中也沒有寫具備NOT NULL屬性的惟一二級索引列,那很抱歉,存儲引擎默認會爲咱們添加一個稱之爲row_id的主鍵列。blog

這個row_id列默認是6個字節大小,值得注意的是,設計InnoDB的大叔並非爲每個用戶未顯式建立主鍵的表的row_id列都單獨維護一個計數器,而是全部的表都共享一個全局的計數器。比方說咱們沒有對錶t1t2顯式建立主鍵,存儲引擎爲它們都建立了一個row_id列,若是咱們向表t1中插入了一條記錄,那麼就從全局計數器裏分配一個值作該表row_id列的值,而後將全局計數器加1;接着咱們再向表t2中插入一條記錄,那麼就再從全局計數器裏分配一個值作該表row_id列的值,而後將全局計數器加1。

有不少同窗有疑惑,若是這個全局計數器的值超過了6個字節所能表示的最大值時,會發生什麼,全局計數器從0從新開始技術,一切從頭再來麼?

哈哈😄,並不會這樣。雖然row_id由6個字節組成,可是設計InnoDB的大叔倒是使用8個字節存儲全局計數器的值,他們將這8個字節分兩次寫入row_id,第一次寫入右邊四個字節到row_id的右邊4個字節,接着將左邊四個字節再寫入row_id的左邊的2個字節,就像這樣:

image_1dtsbdgl250r7ue10c3f8qmd9.png-25.4kB

在將全局計數器左邊四個字節再寫入row_id的左邊的2個字節 時採用以下函數:

UNIV_INLINE void mach_write_to_2( /*============*/ byte* b, /*!< in: pointer to two bytes where to store 也就是row_id值前2個字節所在的內存地址*/ ulint n) /*!< in: ulint integer to be stored 也就是全局計數器的左4個字節值*/ {
	ut_ad(b);
	ut_ad((n | 0xFFFFUL) <= 0xFFFFUL);

	b[0] = (byte)(n >> 8);
	b[1] = (byte)(n);
}
複製代碼

能夠看到代碼中有這樣一行:

ut_ad((n | 0xFFFFUL) <= 0xFFFFUL);
複製代碼

這是一個斷言函數,這行代碼的意思就是全局計數器的左邊4個字節值n必須不大於2個字節所能存儲的最大值,不然的話斷言就失敗了,而後MySQL就掛掉了,就掛掉了,就掛掉了~

也就是說若是row_id用完了以後MySQL就會掛掉,那種程序直接退出的掛掉~ 不過6個字節已經足夠大了,你們能夠算算若是想讓MySQL掛掉須要插入多少條記錄呢?

更多文章連接

超全面MySQL語句加鎖分析(上篇)

超全面MySQL語句加鎖分析(中篇)

超全面MySQL語句加鎖分析(下篇)

題外話

寫文章挺累的,有時候你以爲閱讀挺流暢的,那實際上是背後無數次修改的結果。若是你以爲不錯請幫忙轉發一下,萬分感謝~ 這裏是個人公衆號「咱們都是小青蛙」,裏邊有更多技術乾貨,時不時扯一下犢子,歡迎關注:

相關文章
相關標籤/搜索