mysql如何实现逻辑自增_每日一面 - mysql 的自增 id 的实现逻辑是什么样子的?
Key TakeAways
InnoDB 引擎中 有三種 AutoIncrement 鎖模式:
innodb_autoinc_lock_mode=0(traditional lock mode):獲取表鎖,語句執行結束后釋放
innodb_autoinc_lock_mode=1(consecutive lock mode,MySQL 8.0 之前默認 ):對于不確定插入數量的語句(例如INSERT ... SELECT, REPLACE ... SELECT和LOAD DATA)和 innodb_autoinc_lock_mode=0 一樣,其他的確定數量的語句在執行前先批量獲取 id,之后再執行語句。
innodb_autoinc_lock_mode=2(interleaved lock mode,MySQL 8.0+ 默認 ):采用樂觀鎖, CAS 更新計數器獲取。
AutoIncrement 計數器在 MySQL 8.0 之前,存儲在內存中,在 MySQL 8.0 之后,持久化存儲到磁盤。通過每次更新寫入 Redo Log,并在檢查點刷入 innodb 引擎表中記錄下來。
AutoIncrement 的 id 可以讓新數據聚集在一起,利于大部分 OLTP 業務(訪問頻率在最近一天,一周,或者幾個月內比較活躍,而超過一段時間內的數據很少訪問)。如果是這類業務推薦使用自增主鍵,將業務主鍵(UUID)作為二級的唯一索引使用。
如果考慮分布式性能以及避免 AutoIncrement 帶來的鎖性能問題,可以考慮使用 ID 生成器生成:全局趨勢增長的主鍵
為何主鍵要 Auto Increment 而不是 UUID
MySQL InnoDB 引擎默認主鍵索引是 B+ 樹索引,也是聚集索引,為何叫聚集索引呢?
以 InnoDB 作為存儲引擎的表,表中的數據都會有一個主鍵,即使你不創建主鍵,系統也會幫你創建一個隱式的主鍵。這是因為 InnoDB 是把數據存放在 B+ 樹中的,而 B+ 樹的鍵值就是主鍵,在 B+ 樹的葉子節點中,存儲了表中所有的數據。這種以主鍵作為 B+ 樹索引的鍵值而構建的 B+ 樹索引,我們稱之為聚集索引。
存儲中,聚集索引的數據,會根據索引的值,對應的數據也會聚集存儲在一起:
MySQL 讀取磁盤上的數據是一頁一頁讀取的,如果某條我們要處理的數據在某一頁中,但是這一頁其他數據我們都不關心,這樣的請求多了,性能會急劇下降,類似于 CPU 的 false sharing:
按照 B+ 樹的原理,AutoIncrement 的 ID 能保證最新的數據在一頁中被讀取,而且減少了 B+ 樹分裂翻轉。 UUID 由于無序,插入時,B+ 樹會不斷翻轉,并且最新的數據可能不在同一頁。很可能會出現,最新一條數據,和好幾年前的數據在同一頁。
在大部分 OLTP 類業務中,例如購物和支付交易的訂單,節日促銷的抽獎活動這類業務都有這樣的使用場景,訪問頻率在最近一天,一周,或者幾個月內比較活躍,而超過一段時間內的數據很少訪問。如果是這類業務推薦使用自增主鍵,將業務主鍵(UUID)作為二級的唯一索引使用。 如果考慮分布式性能以及避免 AutoIncrement 帶來的鎖性能問題,可以考慮使用 ID 生成器生成全局趨勢增長的主鍵,例如 Twitter 的 Snowflake 算法生成的前面是時間戳的主鍵id,或者是 類似于這種 "時間+業務+自增"(例如 20210105105811233ORD0000001) 字符串,作為主鍵id,這樣其實也能近似保證熱數據聚集存儲在一起,也就是 MySQL 一頁一頁讀取能命中更多要讀取處理的數據
AutoIncrement 原理
我們這里只關心 InnoDB 引擎的。
AutoIncrement 最大值
AutoIncrement 最大值,和列類型相關。最大可以設置列類型為 UNSIGNED BIGINT,這樣最大值就是 18446744073709551615。 超過這個值繼續生成則還是 18446744073709551615。不會再增加。
AutoIncrement 鎖模式
獲取 AutoIncrement 最新值,需要涉及到鎖。目前有三種鎖模式,對應 innodb_autoinc_lock_mode 的值, 0 ,1,2. MySQL 8.0 之后,默認為 2, 在這之前,默認為 1
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) 這種情況下,針對未知數量批量插入(例如INSERT ... SELECT, REPLACE ... SELECT和LOAD DATA)才會采用AUTO-INC鎖這種方式,而針對已知數量的普通插入,則采用了一種新的輕量級的互斥鎖來分配auto_increment列的值。這種鎖,只會持續到獲取一定數量的 id,不會等待語句執行結束在釋放。也就是拿輕量級鎖提前分配好所需數量的 id 之后釋放鎖,再執行語句。當然,如果其他事務已經持有了AUTO-INC鎖,則simple inserts需要等待。當然,這種情況下,可能產生的間隙更多。
innodb_autoinc_lock_mode=2(interleaved lock mode) 這種模式下任何類型的inserts都不會采用AUTO-INC鎖,性能最好,但是在同一條語句內部產生auto_increment值間隙。其實這個就是所有語句對于同一個值進行 Compare-And-Set 更新,類似于樂觀鎖。這個鎖模式對statement-based replication的主從同步都有一定問題。因為同步傳輸的是語句,而不是行值,語句執行后的差異導致主從可能主鍵不一致。
AutoIncrement 存儲
AutoIncrement 計數器在 MySQL 8.0 之前,存儲在內存中,每次啟動時通過以下語句初始化:
SELECT MAX(ai_col) FROM table_name FOR UPDATE;
在 MySQL 8.0 之后,持久化存儲到磁盤。通過每次更新寫入 Redo Log,并在檢查點刷入 innodb 引擎表中記錄下來。
所以,在MySQL 8.0 之前,如果 rollback 導致某些值沒有使用,重啟后,這些值還是會使用。但是在 MySQL 8.0 之后就不會了。
每日一刷,輕松提升技術,斬獲各種offer:
總結
以上是生活随笔為你收集整理的mysql如何实现逻辑自增_每日一面 - mysql 的自增 id 的实现逻辑是什么样子的?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 浙江省高校计算机等级考试三级考什么容易,
- 下一篇: centos7 安装mysql_Cent