MySQL作為最常用的關系型數據庫,無論是在應用還是在面試中都是必須掌握的技能。
一、MySQL自增主鍵會用完嗎
我們在使用MySQL設置的自增主鍵的時候,一般都是定義初始值和定義步長,我們知道自然數是沒有上限的,但是MySQL的自增主鍵是會設置字節長度的,但凡有字節長度那么就會有上限。
二、MySQL自增主鍵用完會怎樣
不管我們設置字節長度為多大,如果假設MySQL運行時間足夠長,那么就一定會用完,對于MySQL的情況會分為兩種:
1.程序員自己設置的自增主鍵。
毫無疑問,當數值達到最大時候,再去獲取自增主鍵得到的依然是最大值,插入的時候就會報主鍵沖突。這個是在server層實現的。
2.程序員沒有設置自增主鍵,mysql自動創建row_id。
這里需要注意,MySQL中的row_id是在引擎層實現的,InnoDB代碼中會創建一個不可見的長度為8的自增字段row_id,步長為1,但是InnoDB在實現的時候卻只給此字段分配6個字節的空間長度,因此在保存數據的時候只能取row_id字段的最后6字節進行保存,我們知道6字節數值最大為2的248次方,如果已經達到這個值后,再次插入數據時候,row_id就是2的248次方加1,從這個數值中取最后6字節正好是0,而在InnoDB的實現邏輯中如果row_id重復,不會報主鍵沖突,而是會覆蓋原數據。
現在你應該清楚mysql的自增主鍵是有上限的,達到上限后就會出現上面說的現象。
三、mysql中還有哪些自增id,達到最大又會如何呢
1.max_trx_id
我們知道MySQL中,沒創建一個事物就會去申請一個事物id(trx_id),申請的方式就是從獲取全局變量max_trx_id當前值,然后將max_trx_id+1,max_trx_id是InnoDB內部維護的,并且是持久化保存的,也就是說即便MySQL重啟也不會重置這個值。
一般的select語句是不會申請事物id的,除非語句后面加上for update。
max_trx_id也是8個字節的長度,雖然數字足夠大,但是假設mysql運行時間足夠長,早晚也會達到最大值的,max_trx_id達到最大值后會重置為0,重新開始。
這種情況下就會有個問題,看下圖:
我們知道在可重復讀隔離級別下數據的可見性是通過事物的一致性視圖來判斷的。這種情況下就會出現臟讀的bug。
解釋:
假設在上面sql執行前系統的max_trx_id已經是最大值999(假設這是最大值),所以在session A啟動的事務的低水位就是999。
在T2時刻,session B執行第一條update語句的事務id就是999,而第二條update語句的事務id就是0了,這條update語句執行后生成的數據版本上的trx_id就是0。
在T3時刻,session A執行select語句的時候,判斷可見性發現,c=3這個數據版本的trx_id,小于sessionA的事務低水位,因此認為這個數據可見。
但實際sessionA不應該看到c=3這條數據,因此出現這個是臟讀。
這是MySQL必現的一個bug。
2.thread_id
thread_id是MySQL中常見的一種自增id,長度為4個字節,當達到最大值時就會重置為0,重新開始,但是我們在日常的維護中用show processlist查看的時候從來都不會看到重復的id,這是因為MySQL在實現的時候做了一些操作,代碼如下:
do {
new_id= thread_id_counter++;
} while (!thread_ids.insert_unique(new_id).second);
因此MySQL中的thread_id不會出現重復。
mysql中還有一些其他的自增id,比如mysql中還有redo log和binlog相關的xid,binlog文件序號,還有table_id等。但是我們最應該知道就是上面這幾個,其他的感興趣可以隨時來找我探討。