前言
如果一個線程回滾,例如唯一鍵沖突的情況回滾時,回滾了sql語句,但是并沒有把自增的值也-1。那么就會導致下一條插入的數據自增id出現了跳躍。
自增主鍵為什么不是連續的?
- 前言
- 執行時機
- 為什么自增主鍵不是連續的
- 為什么不回滾自增主鍵
- innodb_autoinc_lock_mode
- 設置成2導致的問題
執行時機
mysql的自增主鍵+1的時機是在mysql真正執行之前,獲取當前值并加1。
為什么自增主鍵不是連續的
如果一個線程回滾,例如唯一鍵沖突的情況回滾時,回滾了sql語句,但是并沒有把自增的值也-1。那么就會導致下一條插入的數據自增id出現了跳躍。
為什么不回滾自增主鍵
之所以不讓自增主鍵回滾是因為:
- 假如兩個線程一個獲取了id =2 ,另一個是id =3 。id=3的完成之后,如果id=2的需要回滾,那么后面再來一個插入請求,就會獲取id=2的值,然后再來一個數據就會獲取3從而導致主鍵沖突。
如果要解決這個沖突:- 就要每次插入時還要去數據庫里查詢一下是否有這個自增主鍵。這是非常耗時的,本來一個非常簡單的操作,現在就會變得非常復雜。
- 還有一種方法就是擴大每次獲取自增值的鎖時間,對于自增值的鎖一般是很快的獲取之后+1然后就會釋放。如果要防止上面的沖突,就是等這個sql執行完成之后,再允許其它線程獲取這個鎖。
這顯然也會大大降低并發值,因此mysql放棄了連續。
innodb_autoinc_lock_mode
那么后來mysql添加了一個參數innodb_autoinc_lock_mode,
- 如果設置成0:就是會等一個寫入執行完成之后,再讓另一個線程獲取自增的值
- 如果設置成1:那么對于單個的insert值,就會獲取到自增值就直接釋放。但是對于批量寫入或者批量替換的請求,那么就會等這個操作執行完成之后,再讓另一個線程獲取自增值。
- 如果設置成2,那么所有的insert操作都會獲取到自增值之后直接釋放。不等sql執行結束。
設置成2導致的問題
如果再批量插入A的時候,id是自增1,2,3,4,6,7,另一個線程B也插入了一條數據5,
但是在寫入binlog的時候,是一個線程一個線程寫入的。
那么線程B插入的數據對應的id只能是線程A開頭的或者結尾,如果binlog的format= statement。就會導致主備數據不一致。
所以這這又是一個將binlog的format= row的原因。
這里需要注意 如果線程A不是批量寫入的語句。只是一個普通的insert語句ID 1,然后又有另一個線程B獲取自增建ID 2。但是再寫入binlog時,線程B先寫入了,然后線程A寫入。
這種情況下 即使binlog的format=statement 也不會導致主備數據的數據不一致。在binlog中還有一個步長的參數,在執行線程B時,步長會為2,ID還是2,執行線程A時,步長是1,ID還是1。