Mysql id 自增空洞問題


場景

最近寫了一個收集號碼的邏輯,早上來 count 了一下 phone 表,發現已經收集到了 33w 條記錄。

> select count(*) from phone

336019

但細心的我留意到似乎有 id 值很大的記錄

> select min(id) from phone

1

> select max(id) from phone

1003498

咂摸着覺着不對味。

原因

查了查資料這還有個術語,叫 MySQL auto_increment 空洞問題,是因為我插入/更新表的事后偷懶使用了 upsert 函數搞出來,生成的 SQL 語句是

INSERT INTO xxtable ON DUPLICATE KEY UPDATE

而上面的語句是屬於 mixed-mode inserts,分配時並不知道是插入還是更新,所以都統統讓 id 自增加大。而且 innodb 的默認 innodb_autoinc_lock_mode 模式為 1,在 mixed-mode inserts 中的確會造成空洞。

inserts mode

插入類型有以下幾種

simple inserts

simple inserts 指的是那種能夠事先確定插入行數的語句,比如 INSERT/REPLACE INTO 等插入單行或者多行的語句,語句中不包括嵌套子查詢。

此外,INSERT INTO ... ON DUPLICATE KEY UPDATE 也除外。

bulk inserts

bulk inserts 批量插入,事先無法確定插入行數的語句。

mixed-mode inserts

simple inserts 類型中有些行指定了 auto_increment 列的值,有些沒有指定,比如:
INSERT INTO t1 (c1,c2) VALUES (1,'a'), (NULL,'b'), (5,'c'), (NULL,'d');

另一種情況 INSERT ... ON DUPLICATE KEY UPDATE 這種語句,可能導致分配的 auto_increment 值沒有被使用。

innodb_autoinc_lock_mode

Mysql 5.1 后加了一個配置叫 innodb_autoinc_lock_mode

innodb_autoinc_lock_mode = 0(traditional lock mode)

傳統的 auto_increment 機制,針對 auto_increment 列的插入操作都會加 AUTO-INC 鎖,分配的值也是一個個分配,是連續的,正常情況下也不會有空洞(當然如果事務rollback了這個auto_increment值就會浪費掉,從而造成空洞)。

innodb_autoinc_lock_mode = 1(consecutive lock mode), Innodb 默認

這種情況下,針對 bulk inserts 才會采用 AUTO-INC 鎖這種方式,而針對 simple inserts,則采用了一種新的輕量級的互斥鎖來分配 auto_increment 列的值。當然,如果其他事務已經持有了 AUTO-INC 鎖,則 simple inserts 需要等待.

需要注意的是,在 innodb_autoinc_lock_mode=1 時,語句之間是可能出現 auto_increment 值的間隔的。比如 mixed-mode inserts 以及 bulk inserts 中都有可能導致一些分配的 auto_increment 值被浪費掉從而導致空洞。后面會有例子。

innodb_autoinc_lock_mode=2(interleaved lock mode)

這種模式下任何類型的 inserts 都不會采用 AUTO-INC 鎖,性能最好,但是在同一條語句內部產生 auto_increment 值空洞。此外,這種模式對 statement-based replication 也不安全。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM