0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

MySQL自增主鍵一定是連續(xù)的嗎?

dyquk4xk2p3d ? 來(lái)源:CSDN ? 2023-06-11 11:35 ? 次閱讀

測(cè)試環(huán)境:

MySQL版本:8.0

數(shù)據(jù)庫(kù)表:T (主鍵id,唯一索引c,普通字段d)

27e06496-05e9-11ee-962d-dac502259ad0.png

如果你的業(yè)務(wù)設(shè)計(jì)依賴于自增主鍵的連續(xù)性,這個(gè)設(shè)計(jì)假設(shè)自增主鍵是連續(xù)的。但實(shí)際上,這樣的假設(shè)是錯(cuò)的,因?yàn)樽栽鲋麈I不能保證連續(xù)遞增。

一、自增值的屬性特征:

1. 自增主鍵值是存儲(chǔ)在哪的?

MySQL5.7版本

在 MySQL 5.7 及之前的版本,自增值保存在內(nèi)存里,并沒(méi)有持久化。每次重啟后,第一次打開(kāi)表的時(shí)候,都會(huì)去找自增值的最大值max(id),然后將max(id)+1作為這個(gè)表當(dāng)前的自增值。

MySQL8.0之后版本

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

可以通過(guò)看表詳情查看當(dāng)前自增值,以及查看表參數(shù)詳情AUTO_INCREMENT值(AUTO_INCREMENT就是當(dāng)前數(shù)據(jù)表的自增值)

27eca3dc-05e9-11ee-962d-dac502259ad0.png

2. 自增主鍵值的修改機(jī)制?

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

如果插入數(shù)據(jù)時(shí) id 字段指定為 0、null 或未指定值,那么就把這個(gè)表當(dāng)前的AUTO_INCREMENT值填到自增字段;

如果插入數(shù)據(jù)時(shí) id 字段指定了具體的值,就直接使用語(yǔ)句里指定的值。

根據(jù)要插入的值和當(dāng)前自增值的大小關(guān)系,自增值的變更結(jié)果也會(huì)有所不同。假設(shè),某次要插入的值是 X,當(dāng)前的自增值是 Y。

如果 X

如果 X≥Y,就需要把當(dāng)前自增值修改為新的自增值。

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

我們執(zhí)行以下SQL語(yǔ)句,來(lái)觀察自增主鍵是如何進(jìn)行變化的

insertintotvalues(null,1,1);

流程圖如下所示

27fc6628-05e9-11ee-962d-dac502259ad0.png

流程步驟:

AUTO_INCREMENT=1(表示下一次插入數(shù)據(jù)時(shí),如果需要自動(dòng)生成自增值,會(huì)生成 id=1。)

insert into t values(null, 1, 1)(執(zhí)行器調(diào)用 InnoDB 引擎接口寫(xiě)入一行,傳入的這一行的值是 (0,1,1))

get AUTO_INCREMENT=1(InnoDB 發(fā)現(xiàn)用戶沒(méi)有指定自增 id 的值,獲取表 t 當(dāng)前的自增值 1 )

AUTO_INCREMENT=2 insert into t values(1, 1, 1)(將傳入的行的值改成 (1,1,1),并把自增值改為2)

insert (1,1,1)執(zhí)行插入操作,至此流程結(jié)束

大家可以發(fā)現(xiàn),在這個(gè)流程當(dāng)中是先進(jìn)行自增值的+1,在進(jìn)行新增語(yǔ)句的執(zhí)行的。大家可以發(fā)現(xiàn)這個(gè)操作并沒(méi)有進(jìn)行原子操作,如果SQL語(yǔ)句執(zhí)行失敗,那么自增是不是就不會(huì)連續(xù)了呢?

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

當(dāng)我執(zhí)行以下SQL語(yǔ)句時(shí)

insertintotvalues(null,1,1);

第一次我們可以進(jìn)行新增成功,根據(jù)自增值的修改機(jī)制。如果插入數(shù)據(jù)時(shí) id 字段指定為 0、null 或未指定值,那么就把這個(gè)表當(dāng)前的AUTO_INCREMENT值填到自增字段;

當(dāng)我們第二次在執(zhí)行以下SQL語(yǔ)句時(shí),就會(huì)出現(xiàn)錯(cuò)誤。因?yàn)槲覀儽碇衏字段是唯一索引,會(huì)出現(xiàn)Duplicate key error錯(cuò)誤導(dǎo)致新增失敗。

280643aa-05e9-11ee-962d-dac502259ad0.png

例如:

AUTO_INCREMENT=2(表示下一次插入數(shù)據(jù)時(shí),如果需要自動(dòng)生成自增值,會(huì)生成 id=2。)

insert into t values(null, 1, 1)(執(zhí)行器調(diào)用 InnoDB 引擎接口寫(xiě)入一行,傳入的這一行的值是 (0,1,1))

get AUTO_INCREMENT=2(InnoDB 發(fā)現(xiàn)用戶沒(méi)有指定自增 id 的值,獲取表 t 當(dāng)前的自增值 2 )

AUTO_INCREMENT=3 insert into t values(2, 1, 1)(將傳入的行的值改成 (2,1,1),并把自增值改為3)

insert (2,1,1)執(zhí)行插入操作,由于已經(jīng)存在 c=1 的記錄,所以報(bào)Duplicate key error,語(yǔ)句返回。

可以看到,這個(gè)表的自增值改成 3,是在真正執(zhí)行插入數(shù)據(jù)的操作之前。這個(gè)語(yǔ)句真正執(zhí)行的時(shí)候,因?yàn)榕龅轿ㄒ绘I c 沖突,所以 id=2 這一行并沒(méi)有插入成功,但也沒(méi)有將自增值再改回去。所以,在這之后,再插入新的數(shù)據(jù)行時(shí),拿到的自增 id 就是 3。也就是說(shuō),出現(xiàn)了自增主鍵不連續(xù)的情況。

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

其實(shí)事務(wù)回滾原理也和上面一樣,都是因?yàn)楫惓?dǎo)致新增失敗,但是自增值沒(méi)有進(jìn)行回退。

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

批量插入數(shù)據(jù)的語(yǔ)句,MySQL 有一個(gè)批量申請(qǐng)自增 id 的策略:

語(yǔ)句執(zhí)行過(guò)程中,第一次申請(qǐng)自增 id,會(huì)分配 1 個(gè);

1 個(gè)用完以后,這個(gè)語(yǔ)句第二次申請(qǐng)自增 id,會(huì)分配 2 個(gè);

2 個(gè)用完以后,還是這個(gè)語(yǔ)句, 第三次申請(qǐng)自增 id,會(huì)分配 4 個(gè);

依此類推,同一個(gè)語(yǔ)句去申請(qǐng)自增 id,每次申請(qǐng)到的自增 id 個(gè)數(shù)都是上一次的兩倍。

執(zhí)行以下SQL語(yǔ)句(在表t中先新增了4條數(shù)據(jù),在創(chuàng)建表tt把表t數(shù)據(jù)進(jìn)行批量新增)

insertintotvalues(null,1,1);
insertintotvalues(null,2,2);
insertintotvalues(null,3,3);
insertintotvalues(null,4,4);
createtablettliket;
insertintott(c,d)selectc,dfromt;

insertintottvalues(null,5,5);

第一次申請(qǐng)到了 id=1,第二次被分配了 id=2 和 id=3, 第三次被分配到 id=4 到 id=7。當(dāng)我們?cè)賵?zhí)行insert into t2 values(null, 5,5),實(shí)際上插入的數(shù)據(jù)就是(8,5,5),出現(xiàn)了自增主鍵不連續(xù)的情況。

2812d1e2-05e9-11ee-962d-dac502259ad0.png

六、自增主鍵值的優(yōu)化

1.什么是自增鎖

自增鎖是一種比擬非凡的表級(jí)鎖。并且在事務(wù)向蘊(yùn)含了AUTO_INCREMENT列的表中新增數(shù)據(jù)時(shí)就會(huì)去持有自增鎖,假如事務(wù) A 正在做這個(gè)操作,如果另一個(gè)事務(wù) B 嘗試執(zhí)行 INSERT語(yǔ)句,事務(wù) B 會(huì)被阻塞住,直到事務(wù) A 開(kāi)釋自增鎖。

2.自增鎖有哪些優(yōu)化

在 MySQL 5.0 版本的時(shí)候,自增鎖的范圍是語(yǔ)句級(jí)別。也就是說(shuō),如果一個(gè)語(yǔ)句申請(qǐng)了一個(gè)表自增鎖,這個(gè)鎖會(huì)等語(yǔ)句執(zhí)行結(jié)束以后才釋放。顯然,這樣設(shè)計(jì)會(huì)影響并發(fā)度。在MySQL 5.1.22 版本引入了一個(gè)新策略,新增參數(shù)innodb_autoinc_lock_mode,默認(rèn)值是 1。

傳統(tǒng)模式(Traditional)

這個(gè)參數(shù)的值被設(shè)置為 0 時(shí),表示采用之前 MySQL 5.0 版本的策略,即語(yǔ)句執(zhí)行結(jié)束后才釋放鎖;

傳統(tǒng)模式他可以保證數(shù)據(jù)一致性,但是如果有多個(gè)事務(wù)并發(fā)的執(zhí)行 INSERT 操作,AUTO-INC的存在會(huì)使得 MySQL 的性能略有降落,因?yàn)橥瑫r(shí)只能執(zhí)行一條 INSERT 語(yǔ)句。

間斷模式(Consecutive)

這個(gè)參數(shù)的值被設(shè)置為 1 時(shí):普通 insert 語(yǔ)句,自增鎖在申請(qǐng)之后就馬上釋放;類似insert … select這樣的批量插入數(shù)據(jù)的語(yǔ)句,自增鎖還是要等語(yǔ)句結(jié)束后才被釋放;

間斷模式他可以保證數(shù)據(jù)一致性,但是如果有多個(gè)事務(wù)并發(fā)的執(zhí)行 INSERT 批量操作時(shí),就會(huì)進(jìn)行鎖等待狀態(tài)。如果我們業(yè)務(wù)插入數(shù)據(jù)量很大時(shí),這個(gè)時(shí)候MySQL的性能就會(huì)大大下降。

穿插模式(Interleaved)

這個(gè)參數(shù)的值被設(shè)置為 2 時(shí),所有的申請(qǐng)自增主鍵的動(dòng)作都是申請(qǐng)后就釋放鎖。

穿插模式他沒(méi)有進(jìn)行任何的上鎖設(shè)置。在一定情況下是保證了MySQL的性能,但是他無(wú)法保證數(shù)據(jù)的一致性。如果我們?cè)诖┎迥J较逻M(jìn)行主從復(fù)制時(shí),如果你的binlog格式不是row格式,主從復(fù)制就會(huì)出現(xiàn)不一致。

七、MySQL8.0做了哪些優(yōu)化

在MySQL8.0之后版本,已經(jīng)默認(rèn)設(shè)置為innodb_autoinc_lock_mode=2,binlog_format=row.。這樣更有利與我們?cè)趇nsert … select這種批量插入數(shù)據(jù)的場(chǎng)景時(shí),既能提升并發(fā)性,又不會(huì)出現(xiàn)數(shù)據(jù)一致性問(wèn)題。





審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • SQL
    SQL
    +關(guān)注

    關(guān)注

    1

    文章

    751

    瀏覽量

    43989
  • MYSQL數(shù)據(jù)庫(kù)

    關(guān)注

    0

    文章

    95

    瀏覽量

    9372

原文標(biāo)題:被問(wèn)懵了:MySQL 自增主鍵一定是連續(xù)的嗎?

文章出處:【微信號(hào):良許Linux,微信公眾號(hào):良許Linux】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    怎么簡(jiǎn)單實(shí)現(xiàn)由Labview讀取的串口數(shù)據(jù)寫(xiě)入mysql5.7數(shù)據(jù)庫(kù)中?

    怎么簡(jiǎn)單實(shí)現(xiàn)由Labview讀取的串口數(shù)據(jù)寫(xiě)入mysql5.7數(shù)據(jù)庫(kù)中? 已實(shí)現(xiàn):串口數(shù)據(jù)的接收處理 mysql5.7的安裝(已測(cè)試數(shù)據(jù)庫(kù)正常運(yùn)行) 愿付費(fèi)解決此問(wèn)題(QQ:8
    發(fā)表于 01-11 22:05

    #硬聲創(chuàng)作季 【MySQL調(diào)優(yōu)】為什么推薦使用整型的主鍵而不是UUID

    數(shù)據(jù)庫(kù)MySQL
    Mr_haohao
    發(fā)布于 :2022年09月14日 07:41:14

    阿里云mysql數(shù)據(jù)庫(kù)怎么設(shè)置主鍵和時(shí)間格式怎么顯示時(shí)分秒?

    `需要將測(cè)試的數(shù)據(jù)保存到阿里云mysql數(shù)據(jù)庫(kù)上,利用NI的數(shù)據(jù)庫(kù)工具包怎么創(chuàng)建表實(shí)現(xiàn)主鍵?還有保存的時(shí)間數(shù)據(jù)只顯示日期,不顯示時(shí)分秒(用DB Tools Format Datet
    發(fā)表于 11-13 10:39

    Python常用運(yùn)算寫(xiě)法

    在學(xué)習(xí)任何種編程語(yǔ)言,運(yùn)算方法的學(xué)習(xí)是不可避免的,其中運(yùn)算符++的使用也是其中的重點(diǎn),很多人容易弄不明白其用法,尤其是在Python中,其用法更加的不同!我們可以寫(xiě)個(gè)實(shí)例來(lái)說(shuō)明
    發(fā)表于 04-09 17:45

    labview向oracle插入數(shù)據(jù),怎樣可以主鍵1?如果不插入主鍵的字段,會(huì)報(bào)插入的數(shù)目與表中的數(shù)據(jù)不相等

    本帖最后由 電子人steve 于 2018-5-23 20:30 編輯 labview向oracle數(shù)據(jù)庫(kù)插入數(shù)據(jù)時(shí),怎樣可以主鍵1啊,服務(wù)器數(shù)據(jù)庫(kù)表中設(shè)置了個(gè)
    發(fā)表于 05-23 18:53

    MySQL表分區(qū)類型及介紹

    表分區(qū)是將個(gè)表的數(shù)據(jù)按照一定規(guī)則水平劃分成不同的邏輯塊,并分別進(jìn)行物理存儲(chǔ),這個(gè)規(guī)則就叫做分區(qū)函數(shù),可以有不同的分區(qū)規(guī)則。通過(guò)show plugins語(yǔ)句查看當(dāng)前MySQL是否支持表分區(qū)功能
    發(fā)表于 06-29 16:31

    關(guān)于MySQL的基礎(chǔ)知識(shí)簡(jiǎn)析

    系統(tǒng)環(huán)境MAC OS 10.10MySQL版本,5.7.9Mac 安裝mysql很簡(jiǎn)單,官網(wǎng)下載安裝包,雙擊安裝就可以了,有幾個(gè)地方需要注意在Mac下用DMG包新安裝mysql,在安裝完畢最后
    發(fā)表于 11-03 11:50

    21個(gè)MySQL表設(shè)計(jì)的經(jīng)驗(yàn)準(zhǔn)則

    主鍵設(shè)計(jì)的話,最好不要與業(yè)務(wù)邏輯有所關(guān)聯(lián)。有些業(yè)務(wù)上的字段,比如身份證,雖然是唯的,些開(kāi)發(fā)者喜歡用它來(lái)做主鍵,但是不是很建議哈。主鍵最好
    的頭像 發(fā)表于 01-12 10:07 ?541次閱讀

    MySQL主鍵一定是連續(xù)的嗎?

    眾所周知,主鍵可以讓聚集索引盡量地保持遞增順序插入,避免了隨機(jī)查詢,從而提高了查詢效率
    的頭像 發(fā)表于 02-20 18:06 ?681次閱讀

    MySQL主鍵一定是連續(xù)的嗎?

    如果你的業(yè)務(wù)設(shè)計(jì)依賴于主鍵連續(xù)性,這個(gè)設(shè)計(jì)假設(shè)主鍵
    的頭像 發(fā)表于 03-21 16:55 ?571次閱讀

    主鍵不用隨機(jī)字符串用什么?主鍵?

    主鍵不用隨機(jī)字符串用什么?主鍵?主鍵就是最佳
    的頭像 發(fā)表于 05-09 09:04 ?561次閱讀
    <b class='flag-5'>主鍵</b>不用隨機(jī)字符串用什么?<b class='flag-5'>主鍵</b><b class='flag-5'>自</b><b class='flag-5'>增</b>?

    線上MySQLid用盡怎么辦?

    MySQLid都定義了初始值,然后不斷加步長(zhǎng)。雖然自然數(shù)沒(méi)有上限,但定義了表示這個(gè)數(shù)的字節(jié)長(zhǎng)度,計(jì)算機(jī)存儲(chǔ)就有上限。
    的頭像 發(fā)表于 05-22 10:23 ?464次閱讀
    線上<b class='flag-5'>MySQL</b>的<b class='flag-5'>自</b><b class='flag-5'>增</b>id用盡怎么辦?

    id的機(jī)制不同在mysql的索引結(jié)構(gòu)以及優(yōu)缺點(diǎn)

    的時(shí)候,mysql官方推薦不要使用uuid或者不連續(xù)不重復(fù)的雪花id(long形且唯,單機(jī)遞增),而是推薦連續(xù)
    的頭像 發(fā)表于 06-30 10:19 ?748次閱讀
    id的機(jī)制不同在<b class='flag-5'>mysql</b>的索引結(jié)構(gòu)以及優(yōu)缺點(diǎn)

    MySQL索引的常用知識(shí)點(diǎn)

    索引結(jié)構(gòu):B+樹(shù) 索引其實(shí)是種數(shù)據(jù)結(jié)構(gòu) 注意B+樹(shù)是MySQL,索引默認(rèn)的結(jié)構(gòu);張表至少有個(gè)索引(主鍵索引),是可以有多個(gè)索引的
    的頭像 發(fā)表于 09-30 16:43 ?403次閱讀

    主鍵去哪了?---次開(kāi)發(fā)過(guò)程中的思考

    TABLE `example_table` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主鍵
    的頭像 發(fā)表于 09-05 14:12 ?151次閱讀
    <b class='flag-5'>自</b><b class='flag-5'>增</b><b class='flag-5'>主鍵</b>去哪了?---<b class='flag-5'>一</b>次開(kāi)發(fā)過(guò)程中的思考