以下僅考慮 innodb 儲存引擎。
自增主鍵有兩個性質需要考慮:
單調性每次插入一條資料,其 id 都是比上一條插入的資料的 id 大,就算上一條資料被刪除。
連續性插入成功時,其資料的 id 和前一次插入成功時資料的 id 相鄰。
自增主鍵的單調性
為何會有單調性的問題?
這主要跟自增主鍵最大值的獲取方式,以及存放位置有關係。
如果最大值是通過計算獲取的,並且在某些情況下需要重新獲取時,會因為最新的資料被刪除而減小。
自增主鍵最大值怎麼取的?存放到**?
mysql 5.7 及之前的版本,自增主鍵最大值會在啟動(重啟)後從資料庫中取出放到記憶體:
select max(ai_col) from table_name for update;
這樣獲取是通過計算的,並且由於存放在記憶體而容易丟失。
如果刪除最新一條資料(假設 id 為 10),因故障或者其他必要重啟後再插入一條資料時會使用之前的 id (即 id 為 10)。
問題在於如果有其他表依賴了該 id,則其他表的資料關聯到的資料就符合要求了。除非設定了外來鍵。
比如我要向最大乙個 id 的賬號充了 100 萬。但是在充值之前,該賬號被刪除,然後伺服器故障重啟,重啟後有人新註冊了乙個賬號。結果我的 100 萬充到了他的新賬號上。註冊新賬號的人以為是新手福利,笑嘻嘻。
如何解決單調性的問題?
從 mysql 8.0 開始,自增主鍵最大值會在每次修改後寫入到 redo log,並且在每個檢查點寫入引擎私有的系統表。
如果是正常重啟,則讀取系統表裡的值。
如果是故障重啟,則先讀取系統表裡的值放到記憶體。接著掃瞄 redo log 裡儲存的值。如果掃瞄到的值大於記憶體的值,則將該值覆蓋到記憶體。
但由於資料庫可能在 redo log 刷入磁碟前就故障了,所以可能會用到之前申請的 id。
注:如果 redo log 都沒刷入,就更不用說將資料插入資料表了。
自增主鍵插入時的連續性
這裡不考慮由於刪除導致的連續性問題
為何會有連續性問題?
這主要是跟插入事務回滾有關係。
對於兩個插入事務,事務 a 先執行插入語句,之後事務 b 執行插入語句。在這之後,事務 a 回滾,導致 a 執行插入語句時占用的 id 被拋棄。
之所以事務 a 沒提交的情況下,事務 b 就能執行插入語句,跟 innodb 的自增長鎖(auto-inc locking)相關。該鎖是一種特殊的表鎖(table-level lock),但會在插入語句執行後立即釋放,不會等到事務結束。
如何解決連續性問題?
使用最高隔離級別 serializable (序列)。
由於效能上的考慮,通常不這樣做。
多事務批量插入的連續性
事務 a 和事務 b 都在執行 不確定數量 的批量插入(insert ... select):
保證事務 a 的資料的 id 連續: innodb_autoinc_lock_mode = 0 (auto-inc locking)
必須等待語句執行結束才釋放鎖。
保證事務 a 的資料的 id 連續: innodb_autoinc_lock_mode = 1 (auto-inc locking)
和上面的區別在於,當執行 確定數量 的批量插入時,使用輕量級互斥量(mutex)而不是特殊表鎖(auto-inc locking),從而提前向記憶體的計數器申請相應數量的 id。之後立即釋放,不用等語句執行結束。
會因為回滾而使得全域性 id 不連續。
不保證事務 a 的資料的 id 連續: innodb_autoinc_lock_mode = 2 (mutex)
三種插入定義:
簡單插入
能夠提前知道插入的行數
批量插入
不能提前知道插入的行數
混合插入
批量插入中的一部分的 id 是指定的(非 0 且非 null),另一部分未指定,使用資料庫生成的自增 id。
其他如果主動指定 id 為 0 或者 null 插入,則會使用資料庫生成的自增 id。
參考文件
為什麼 mysql 的自增主鍵不單調也不連續
《mysql技術內幕——innodb儲存引擎》 第 6 章:鎖
MySQL設定主鍵自增和非主鍵自增
mysql 每張表只能有1個自動增長字段,這個自動增長字段即可作為主鍵,也可以用作非主鍵使用,但是請注意將自動增長字段當做非主鍵使用時必須必須為其新增唯一索引,否則系統將會報錯。例如 將自動增長字段設定為主鍵 create table t1 id int auto increment primary...
mysql建表語句主鍵自增 MYSQL索引 上
前言 今天同事做資料清理的時候發現我這邊有一張表沒有主鍵,這個表有兩個字段,id和name,id作者唯一索引,在我印象裡,乙個資料表如果沒有主鍵索引,它會內部建立主鍵索引,建立的標準就是唯一性,我覺得應該是使用id建立內部的主鍵索引。所以,會有兩個問題 第乙個問題,如果確實使用id建立了內部的主鍵索...
mysql的自增主鍵
autotest表是測試表 mysql版本為5.7 結構如下 1.insert into指定自增主鍵的值時,自增主鍵會變嗎?以下是表中資料的初始值 接著插入自增主鍵9 如果insert指定自增主鍵的值不會導致auto increment值的變化的話,那麼接著不指定自增主鍵的值執行insert語句時,...