面試官:MySQL 自增主鍵一定是連續的嗎?大部分人都會答錯!

来源:https://www.cnblogs.com/javastack/archive/2023/05/29/17440848.html
-Advertisement-
Play Games

## 測試環境: > MySQL版本:8.0 資料庫表:T (主鍵id,唯一索引c,普通欄位d) ![](http://img.javastack.cn/1685072039483867.png) 如果你的業務設計依賴於自增主鍵的連續性,這個設計假設自增主鍵是連續的。但實際上,這樣的假設是錯的,因為 ...


測試環境:

MySQL版本:8.0

資料庫表:T (主鍵id,唯一索引c,普通欄位d)

如果你的業務設計依賴於自增主鍵的連續性,這個設計假設自增主鍵是連續的。但實際上,這樣的假設是錯的,因為自增主鍵不能保證連續遞增。

推薦一個開源免費的 Spring Boot 實戰項目:

https://github.com/javastacks/spring-boot-best-practice

一、自增值的屬性特征:

1. 自增主鍵值是存儲在哪的?

MySQL5.7版本

在 MySQL 5.7 及之前的版本,自增值保存在記憶體里,並沒有持久化。每次重啟後,第一次打開表的時候,都會去找自增值的最大值 max(id),然後將 max(id)+1 作為這個表當前的自增值。

MySQL8.0之後版本

在 MySQL 8.0 版本,將自增值的變更記錄在了 redo log 中,重啟的時候依靠 redo log 恢復重啟之前的值。

可以通過看表詳情查看當前自增值,以及查看表參數詳情AUTO_INCREMENT值(AUTO_INCREMENT就是當前數據表的自增值)

2. 自增主鍵值的修改機制?

在表t中,我定義了主鍵id為自增值,在插入一行數據的時候,自增值的行為如下:

  1. 如果插入數據時 id 欄位指定為 0、null 或未指定值,那麼就把這個表當前的 AUTO_INCREMENT 值填到自增欄位;
  2. 如果插入數據時 id 欄位指定了具體的值,就直接使用語句里指定的值。

根據要插入的值和當前自增值的大小關係,自增值的變更結果也會有所不同。假設,某次要插入的值是 X,當前的自增值是 Y。

  1. 如果 X<Y,那麼這個表的自增值不變;
  2. 如果 X≥Y,就需要把當前自增值修改為新的自增值。

二、新增語句自增主鍵是如何變化的:

我們執行以下SQL語句,來觀察自增主鍵是如何進行變化的

insert into t values(null, 1, 1);

流程圖如下所示

流程步驟:

  • AUTO_INCREMENT=1 (表示下一次插入數據時,如果需要自動生成自增值,會生成 id=1。)
  • insert into t values(null, 1, 1) (執行器調用 InnoDB 引擎介面寫入一行,傳入的這一行的值是 (0,1,1))
  • get AUTO_INCREMENT=1 (InnoDB 發現用戶沒有指定自增 id 的值,獲取表 t 當前的自增值 1 )
  • AUTO_INCREMENT=2 insert into t values(1, 1, 1) (將傳入的行的值改成 (1,1,1),並把自增值改為2)
  • insert (1,1,1) 執行插入操作,至此流程結束

大家可以發現,在這個流程當中是先進行自增值的+1,在進行新增語句的執行的。大家可以發現這個操作並沒有進行原子操作,如果SQL語句執行失敗,那麼自增是不是就不會連續了呢?

三、自增主鍵值不連續情況:(唯一主鍵衝突)

當我執行以下SQL語句時

insert into t values(null, 1, 1);

第一次我們可以進行新增成功,根據自增值的修改機制。如果插入數據時 id 欄位指定為 0、null 或未指定值,那麼就把這個表當前的 AUTO_INCREMENT 值填到自增欄位;

當我們第二次在執行以下SQL語句時,就會出現錯誤。因為我們表中c欄位是唯一索引,會出現Duplicate key error錯誤導致新增失敗。

例如:

  • AUTO_INCREMENT=2 (表示下一次插入數據時,如果需要自動生成自增值,會生成 id=2。)
  • insert into t values(null, 1, 1) (執行器調用 InnoDB 引擎介面寫入一行,傳入的這一行的值是 (0,1,1))
  • get AUTO_INCREMENT=2 (InnoDB 發現用戶沒有指定自增 id 的值,獲取表 t 當前的自增值 2 )
  • AUTO_INCREMENT=3 insert into t values(2, 1, 1) (將傳入的行的值改成 (2,1,1),並把自增值改為3)
  • insert (2,1,1) 執行插入操作,由於已經存在 c=1 的記錄,所以報 Duplicate key error,語句返回。

可以看到,這個表的自增值改成 3,是在真正執行插入數據的操作之前。這個語句真正執行的時候,因為碰到唯一鍵 c 衝突,所以 id=2 這一行並沒有插入成功,但也沒有將自增值再改回去。所以,在這之後,再插入新的數據行時,拿到的自增 id 就是 3。也就是說,出現了自增主鍵不連續的情況。

四、自增主鍵值不連續情況:(事務回滾)

其實事務回滾原理也和上面一樣,都是因為異常導致新增失敗,但是自增值沒有進行回退。

五、自增主鍵值不連續情況:(批量插入)

批量插入數據的語句,MySQL 有一個批量申請自增 id 的策略:

  1. 語句執行過程中,第一次申請自增 id,會分配 1 個;
  2. 1 個用完以後,這個語句第二次申請自增 id,會分配 2 個;
  3. 2 個用完以後,還是這個語句, 第三次申請自增 id,會分配 4 個;
  4. 依此類推,同一個語句去申請自增 id,每次申請到的自增 id 個數都是上一次的兩倍。

執行以下SQL語句(在表t中先新增了4條數據,在創建表tt把表t數據進行批量新增)

insert into t values(null, 1,1);
insert into t values(null, 2,2);
insert into t values(null, 3,3);
insert into t values(null, 4,4);
create table tt like t;
insert into tt(c,d) select c,d from t;

insert into tt values(null, 5,5);

第一次申請到了 id=1,第二次被分配了 id=2 和 id=3, 第三次被分配到 id=4 到 id=7。當我們再執行 insert into t2 values(null, 5,5),實際上插入的數據就是(8,5,5),出現了自增主鍵不連續的情況。

六、自增主鍵值的優化

1.什麼是自增鎖

自增鎖是一種比擬非凡的表級鎖。並且在事務向蘊含了 AUTO_INCREMENT 列的表中新增數據時就會去持有自增鎖,假如事務 A 正在做這個操作,如果另一個事務 B 嘗試執行 INSERT語句,事務 B 會被阻塞住,直到事務 A 開釋自增鎖。

2.自增鎖有哪些優化

在 MySQL 5.0 版本的時候,自增鎖的範圍是語句級別。也就是說,如果一個語句申請了一個表自增鎖,這個鎖會等語句執行結束以後才釋放。顯然,這樣設計會影響併發度。在MySQL 5.1.22 版本引入了一個新策略,新增參數 innodb_autoinc_lock_mode,預設值是 1。

傳統模式(Traditional)

這個參數的值被設置為 0 時,表示採用之前 MySQL 5.0 版本的策略,即語句執行結束後才釋放鎖;

傳統模式他可以保證數據一致性,但是如果有多個事務併發的執行 INSERT 操作,AUTO-INC的存在會使得 MySQL 的性能略有降落,因為同時只能執行一條 INSERT 語句。

間斷模式(Consecutive)

這個參數的值被設置為 1 時:普通 insert 語句,自增鎖在申請之後就馬上釋放;類似 insert … select 這樣的批量插入數據的語句,自增鎖還是要等語句結束後才被釋放;

間斷模式他可以保證數據一致性,但是如果有多個事務併發的執行 INSERT 批量操作時,就會進行鎖等待狀態。如果我們業務插入數據量很大時,這個時候MySQL的性能就會大大下降。

穿插模式(Interleaved)

這個參數的值被設置為 2 時,所有的申請自增主鍵的動作都是申請後就釋放鎖。

穿插模式他沒有進行任何的上鎖設置。在一定情況下是保證了MySQL的性能,但是他無法保證數據的一致性。如果我們在穿插模式下進行主從複製時,如果你的binlog格式不是row格式,主從複製就會出現不一致。

七、MySQL8.0做了哪些優化

在MySQL8.0之後版本,已經預設設置為 innodb_autoinc_lock_mode=2binlog_format=row.。這樣更有利與我們在 insert … select 這種批量插入數據的場景時,既能提升併發性,又不會出現數據一致性問題。

版權聲明:本文為CSDN博主「又 欠」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。原文鏈接:https://blog.csdn.net/qq_48157004/article/details/128356734

近期熱文推薦:

1.1,000+ 道 Java面試題及答案整理(2022最新版)

2.勁爆!Java 協程要來了。。。

3.Spring Boot 2.x 教程,太全了!

4.別再寫滿屏的爆爆爆炸類了,試試裝飾器模式,這才是優雅的方式!!

5.《Java開發手冊(嵩山版)》最新發佈,速速下載!

覺得不錯,別忘了隨手點贊+轉發哦!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • # 常用的排序演算法 ## 一、冒泡排序 冒泡排序(Bubble Sort),是一種較簡單的排序演算法。 它重覆地走訪過要排序的元素列,依次比較兩個相鄰的元素,如果順序(如從大到小、首字母從Z到A)錯誤就把他們交換過來。走訪元素的工作是重覆地進行直到沒有相鄰元素需要交換,也就是說該元素列已經排序完成。 ...
  • > 本文首發於公眾號:Hunter後端 > 原文鏈接:[Python連接es筆記二之查詢方式彙總](https://mp.weixin.qq.com/s/0Yn5c-U9pBWrSC5HrCgWog) 上一節除了介紹使用 Python 連接 es,還有最簡單的 query() 方法,這一節介紹一下幾 ...
  • Python3 支持int(整型數據)、float(浮點型數據)、bool(布爾類型) # 1.int(整型數據) 在Python 3里,**只有一種整數類型 int,表示為長整型**。像大多數語言一樣,數值類型的賦值和計算都是很直觀的。 ## 1.1數值運算 編寫程式如下所示 ![image](h ...
  • ## 聊一下MySQL 關於mysql關係型資料庫的一些分析: 1、從性能上:如果我們碰到需要執行耗時特別久,並且執行結果不是很頻繁變動的SQL語句,我們就沒有必要每次都去查詢資料庫,因為每次操作資料庫都很耗時。 2、從併發上:在大併發的情況下(比如618秒殺活動,你敢讓千萬級的請求直接打到資料庫上 ...
  • 歡迎來到系列第八篇,異常處理的深入探討。本文將分五部分展開。首先,我們將學習Python異常處理的基礎知識,理解`try/except`語句的用法。然後,我們將瞭解Python的常見異常類型並通過實例理解它們的作用。第三部分,我們將更深入地解析`try-except`塊,理解其工作原理及更加複雜的用... ...
  • 哈嘍大家好,我是鹹魚 相信小伙伴們在學習 python 數據分析的過程中或多或少都會聽說或者使用過 pandas pandas 是 python 的一個拓展庫,常用於數據分析 今天鹹魚將介紹幾個關於 pandas 導入數據的方法和技巧 ## 從 URL 獲取 csv 數據 關於 pandas 導入 ...
  • ## 教程簡介 Angular Highcharts是一個基於Angular的開源組件,可在Angular應用程式中提供優雅且功能豐富的高圖表可視化,並可與Angular組件無縫配合使用。 [Angular Highcharts入門教程](https://www.itbaoku.cn/tutoria ...
  • ## 教程簡介 Flume是Apache下麵的一個分散式組件,它提供高效,可靠的收集,整合,傳輸日誌數據的服務。Flume可以理解成一個管道,它連接數據的生產者和消費者,它從數據的生產者(Source)獲取數據,保存在自己的緩存(Channel)中,然後通過Sink發送到消費者。它不對數據做保存和復 ...
一周排行
    -Advertisement-
    Play Games
  • C#TMS系統代碼-基礎頁面BaseCity學習 本人純新手,剛進公司跟領導報道,我說我是java全棧,他問我會不會C#,我說大學學過,他說這個TMS系統就給你來管了。外包已經把代碼給我了,這幾天先把增刪改查的代碼背一下,說不定後面就要趕鴨子上架了 Service頁面 //using => impo ...
  • 委托與事件 委托 委托的定義 委托是C#中的一種類型,用於存儲對方法的引用。它允許將方法作為參數傳遞給其他方法,實現回調、事件處理和動態調用等功能。通俗來講,就是委托包含方法的記憶體地址,方法匹配與委托相同的簽名,因此通過使用正確的參數類型來調用方法。 委托的特性 引用方法:委托允許存儲對方法的引用, ...
  • 前言 這幾天閑來沒事看看ABP vNext的文檔和源碼,關於關於依賴註入(屬性註入)這塊兒產生了興趣。 我們都知道。Volo.ABP 依賴註入容器使用了第三方組件Autofac實現的。有三種註入方式,構造函數註入和方法註入和屬性註入。 ABP的屬性註入原則參考如下: 這時候我就開始疑惑了,因為我知道 ...
  • C#TMS系統代碼-業務頁面ShippingNotice學習 學一個業務頁面,ok,領導開完會就被裁掉了,很突然啊,他收拾東西的時候我還以為他要旅游提前請假了,還在尋思為什麼回家連自己買的幾箱飲料都要叫跑腿帶走,怕被偷嗎?還好我在他開會之前拿了兩瓶芬達 感覺感覺前面的BaseCity差不太多,這邊的 ...
  • 概述:在C#中,通過`Expression`類、`AndAlso`和`OrElse`方法可組合兩個`Expression<Func<T, bool>>`,實現多條件動態查詢。通過創建表達式樹,可輕鬆構建複雜的查詢條件。 在C#中,可以使用AndAlso和OrElse方法組合兩個Expression< ...
  • 閑來無聊在我的Biwen.QuickApi中實現一下極簡的事件匯流排,其實代碼還是蠻簡單的,對於初學者可能有些幫助 就貼出來,有什麼不足的地方也歡迎板磚交流~ 首先定義一個事件約定的空介面 public interface IEvent{} 然後定義事件訂閱者介面 public interface I ...
  • 1. 案例 成某三甲醫預約系統, 該項目在2024年初進行上線測試,在正常運行了兩天後,業務系統報錯:The connection pool has been exhausted, either raise MaxPoolSize (currently 800) or Timeout (curren ...
  • 背景 我們有些工具在 Web 版中已經有了很好的實踐,而在 WPF 中重新開發也是一種費時費力的操作,那麼直接集成則是最省事省力的方法了。 思路解釋 為什麼要使用 WPF?莫問為什麼,老 C# 開發的堅持,另外因為 Windows 上已經裝了 Webview2/edge 整體打包比 electron ...
  • EDP是一套集組織架構,許可權框架【功能許可權,操作許可權,數據訪問許可權,WebApi許可權】,自動化日誌,動態Interface,WebApi管理等基礎功能於一體的,基於.net的企業應用開發框架。通過友好的編碼方式實現數據行、列許可權的管控。 ...
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...