您好,登錄后才能下訂單哦!
在數據庫表設計中會糾結于”自然鍵”和”代理鍵”的選擇,自然鍵在實現數據“軟刪除”時實現比較復雜,部分自然鍵因為鍵值過長或多列組合導致不適合作為表主鍵,而比較常見兩種代理鍵為自增列(auto incremnet)和全局唯一標識列(GUID)。
使用自增列作為主鍵的優缺點:
1、 主鍵鍵值長度短,INT列需要4個字節,BIGINT列需要8個字節;
2、 自增主鍵順序遞增,在INSERT操作時”順序”寫入表;
3、 由于數據集中插入到表尾部,在高并發情況下容易造成”數據頁熱點”,影響插入效率;
4、 自增主鍵只能保證在表內數據唯一,對于分庫分表場景,可能因錯誤操作產生相同的“唯一值”。
自增鍵用完了會發生什么?
我們在建表的時候為某個索引列( 注意:必須是索引列 )添加 AUTO_INCREMENT 屬性,就像這樣:
CREATE TABLE t ( c1 TINYINT AUTO_INCREMENT, c2 TINYINT, KEY idx_c1 (c1)) ENGINE=InnoDB;
表 t 中包含一個索引列 c1 ,該列被添加了 AUTO_INCREMENT 屬性。我們先向該表中插入一條記錄:
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 中的數據:
mysql> SELECT * FROM t;+-----+------+| c1 | c2 |+-----+------+| 126 | 1 || 127 | 1 |+-----+------+2 rows in set (0.02 sec)
因為 c1 列是 TINYINT 類型的,使用1個字節存儲數據,它能存儲最大的值就是 127 ,如果當該列的值到達 127 之后,我們繼續向表中插入數據,自增列 c1 的值將會變成什么呢?
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 所能存儲的最大值。
這里需要注意的是,在當前舉的例子中,我們只是在自增列 c1 上邊建立了一個普通的二級索引 idx_c1 ,所以鍵值重復也沒啥問題。不過我們一般將 AUTO_INCREMENT 屬性應用在表的主鍵上,此時如果自增列值達到了主鍵對應數據類型所能存儲的最大值時,就會報錯(因為主鍵值重復),大家一定注意!
row_id用完了會發生什么?
在我們使用 InnoDB 存儲引擎來建表時,如果我們自己沒有顯式地創建主鍵時,存儲引擎會默認找一個具有 NOT NULL 屬性的唯一二級索引列來充當主鍵,如果我們在建表語句中也沒有寫具有 NOT NULL 屬性的唯一二級索引列,那很抱歉,存儲引擎默認會為我們添加一個稱之為 row_id 的主鍵列。
這個 row_id 列默認是6個字節大小,值得注意的是,設計 InnoDB 的大叔并不是為每一個用戶未顯式創建主鍵的表的 row_id 列都單獨維護一個計數器,而是所有的表都共享一個全局的計數器。比方說我們沒有對表 t1 和 t2 顯式創建主鍵,存儲引擎為它們都創建了一個 row_id 列,如果我們向表 t1 中插入了一條記錄,那么就從全局計數器里分配一個值做該表 row_id 列的值,然后將全局計數器加1;接著我們再向表 t2 中插入一條記錄,那么就再從全局計數器里分配一個值做該表 row_id 列的值,然后將全局計數器加1。
有很多同學有疑惑,如果這個全局計數器的值超過了6個字節所能表示的最大值時,會發生什么,全局計數器從0重新開始技術,一切從頭再來么?
哈哈:smile:,并不會這樣。雖然 row_id 由6個字節組成,但是設計 InnoDB 的大叔卻是使用8個字節存儲全局計數器的值,他們將這8個字節分兩次寫入 row_id ,第一次寫入右邊四個字節到 row_id 的右邊4個字節,接著將左邊四個字節再寫入 row_id 的左邊的2個字節,就像這樣:
在將全局計數器左邊四個字節再寫入 row_id 的左邊的2個字節 時采用如下函數:
UNIV_INLINEvoidmach_write_to_2(/*============*/byte*b,/*!< in: pointer to two bytes where to store 也就是row_id值前2個字節所在的內存地址*/ulintn)/*!< 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掛掉需要插入多少條記錄呢?
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。