在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

線上MySQL的自增id用盡怎么辦?

馬哥Linux運維 ? 來源:馬哥Linux運維 ? 2023-05-22 10:23 ? 次閱讀

MySQL的自增id都定義了初始值,然后不斷加步長。雖然自然數沒有上限,但定義了表示這個數的字節長度,計算機存儲就有上限。比如,無符號整型(unsigned int)是4個字節,上限就是2^32 - 1。那自增id用完,會怎么樣?

表定義自增值id

表定義的自增值達到上限后的邏輯是:再申請下一個id時,得到的值保持不變。

mysql> create table t(id int unsigned auto_increment primary key) auto_increment=4294967295;Query OK, 0 rows affected (0.01 sec)mysql> insert into t values(null);Query OK, 1 row affected (0.00 sec)mysql> show create table t;+-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+| Table | Create Table                                                                                                                                                                      |+-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+| t     | CREATE TABLE `t` (  `id` int unsigned NOT NULL AUTO_INCREMENT,  PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=4294967295 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci |+-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+1 row in set (0.00 sec)//成功插入一行 4294967295mysql> insert into t values(null);ERROR 1062 (23000): Duplicate entry '4294967295' for key 't.PRIMARY'

第一個insert成功后,該表的AUTO_INCREMENT還是4294967295,導致第二個insert又拿到相同自增id值,再試圖執行插入語句,主鍵沖突。

2^32 - 1(4294967295)不是一個特別大的數,一個頻繁插入刪除數據的表是可能用完的。建表時就需要考慮你的表是否有可能達到該上限,若有,就應創建成8字節的bigint unsigned。

InnoDB系統自增row_id

若你創建的InnoDB表未指定主鍵,則InnoDB會自動創建一個不可見的,6個字節的row_id。InnoDB維護了一個全局的dict_sys->row_id值。

9b7523e8-f77e-11ed-90ce-dac502259ad0.jpg

所有無主鍵的InnoDB表,每插入一行數據,都將當前的dict_sys->row_id作為要插入數據的row_id,然后把dict_sys->row_id加1。

代碼實現時row_id是個長度為8字節的無符號長整型(bigint unsigned)。但InnoDB在設計時,給row_id留的只是6個字節的長度,這樣寫到數據表中時只放了最后6個字節,所以row_id能寫到數據表中的值,就有兩個特征:

row_id寫入表中的值范圍,是從0到2^48 - 1

當dict_sys.row_id=2^48時,如果再有插入數據的行為要來申請row_id,拿到以后再取最后6個字節的話就是0

即寫入表的row_id從0~2^48 - 1。達到上限后,下個值就是0,然后繼續循環。

2^48 - 1已經很大,但若一個MySQL實例活得久,還是可能達到上限。

InnoDB里,申請到row_id=N后,就將這行數據寫入表中;若表中已經存在row_id=N的行,新寫入的行就會覆蓋原有的行。

驗證該結論:通過gdb修改系統的自增row_id。用gdb是為了便于復現問題,只能在測試環境使用。

row_id用完的驗證序列

9b8021d0-f77e-11ed-90ce-dac502259ad0.jpg

row_id用完的效果驗證

可見,在我用gdb將dict_sys.row_id設置為2^48之后,再插入a=2會出現在表t的第一行,因為該值的row_id=0。

之后再插入a=3,由于row_id=1,就覆蓋了之前a=1的行,因為a=1這一行的row_id也是1。

所以應該在InnoDB表中主動創建自增主鍵:當表自增id到達上限后,再插入數據時會報主鍵沖突錯誤。

畢竟覆蓋數據,就意味著數據丟失,影響數據可靠性;報主鍵沖突,插入失敗,影響可用性。一般可靠性優于可用性。

Xid

redo log和binlog有個共同字段Xid,用來對應事務。Xid在MySQL內部是如何生成的呢?

MySQL內部維護了一個全局變量global_query_id

9b881d9a-f77e-11ed-90ce-dac502259ad0.jpg

每次執行語句時,將它賦值給query_id,然后給該變量+1:

9b94a8b2-f77e-11ed-90ce-dac502259ad0.jpg

若當前語句是該事務執行的第一條語句,則MySQL還會同時把query_id賦值給該事務的Xid:

而global_query_id是一個純內存變量,重啟之后就清零了。所以同一DB實例,不同事務的Xid可能相同。

但MySQL重啟之后會重新生成新binlog文件,這就保證同一個binlog文件里的Xid唯一。

雖然MySQL重啟不會導致同一個binlog里面出現兩個相同Xid,但若global_query_id達到上限,就會繼續從0開始計數。理論上還是會出現同一個binlog里面出現相同Xid。

因為global_query_id8字節,上限2^64 - 1。要出現這種情況,需滿足:

執行一個事務,假設Xid是A

接下來執行2^64次查詢語句,讓global_query_id回到A
2^64太大了,這種可能只存在于理論中。

再啟動一個事務,這個事務的Xid也是A

Innodb trx_id

Xid由server層維護

InnoDB內部使用Xid,為了關聯InnoDB事務和server

但InnoDB自己的trx_id,是另外維護的事務id(transaction id)。

InnoDB內部維護了一個max_trx_id全局變量,每次需要申請一個新的trx_id時,就獲得max_trx_id的當前值,然后并將max_trx_id加1。

InnoDB數據可見性的核心思想

每一行數據都記錄了更新它的trx_id,當一個事務讀到一行數據時,判斷該數據是否可見,就是通過事務的一致性視圖與這行數據的trx_id做對比。

對于正在執行的事務,你可以從information_schema.innodb_trx表中看到事務的trx_id。

看如下案例:事務的trx_id

9b9c1502-f77e-11ed-90ce-dac502259ad0.jpg

S2 的執行記錄:

mysql> use information_schema;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A


Database changed


mysql> select trx_id, trx_mysql_thread_id from innodb_trx;
+-----------------+---------------------+
| trx_id          | trx_mysql_thread_id |
+-----------------+---------------------+
| 421972504382792 |                  70 |
+-----------------+---------------------+
1 row in set (0.00 sec)


mysql> select trx_id, trx_mysql_thread_id from innodb_trx;
+---------+---------------------+
| trx_id  | trx_mysql_thread_id |
+---------+---------------------+
| 1355623 |                  70 |
+---------+---------------------+
1 row in set (0.01 sec)

S2從innodb_trx表里查出的這兩個字段,第二個字段trx_mysql_thread_id就是線程id。顯示線程id,是為說明這兩次查詢看到的事務對應的線程id都是5,即S1所在線程。

t2時顯示的trx_id是一個很大的數;t4時刻顯示的trx_id是1289,看上去是一個比較正常的數字。這是為啥?

t1時,S1還未涉及更新,是一個只讀事務。對于只讀事務,InnoDB并不會分配trx_id:

t1時,trx_id的值就是0。而這個很大的數,只是顯示用

直到S1在t3時執行insert,InnoDB才真正分配trx_id。所以t4時,S2查到該trx_id的值就是1289。

除了明顯的修改類語句,若在select 語句后面加上for update,也不是只讀事務。

update 和 delete語句除了事務本身,還涉及到標記刪除舊數據,即要把數據放到purge隊列里等待后續物理刪除,這個操作也會把max_trx_id+1, 因此在一個事務中至少加2

InnoDB的后臺操作,比如表的索引信息統計這類操作,也是會啟動內部事務的,因此你可能看到,trx_id值并不是按照加1遞增的。

t2時查到的很大數字是怎么來的?

每次查詢時,由系統臨時計算:

當前事務的trx變量的指針地址轉成整數,再加上248

這樣可以保證:

因為同一只讀事務在執行期間,它的指針地址不會變,所以無論在 innodb_trx還是在innodb_locks表里,同一個只讀事務查出來的trx_id就會是一樣的

若有并行只讀事務,每個事務的trx變量的指針地址肯定不同。這樣,不同并發只讀事務,查出來的trx_id就是不同的。

為什么要加248?

保證只讀事務顯示的trx_id值比較大,正常情況下就會區別于讀寫事務的id。但trx_id跟row_id的邏輯類似,定義為8個字節。
理論上還是可能出現一個讀寫事務與一個只讀事務顯示的trx_id相同。不過概率很低,也沒有什么實質危害,不管。

為何只讀事務不分配trx_id?

減小事務視圖里面活躍事務數組的大小。因為當前正在運行的只讀事務,不影響數據的可見性判斷。所以,在創建事務的一致性視圖時,InnoDB就只需要拷貝讀寫事務的trx_id

減少trx_id的申請次數。InnoDB執行一個普通的select語句,也要對應一個只讀事務。所以只讀事務優化后,普通查詢語句無需申請trx_id,大大減少并發事務申請trx_id的鎖沖突

由于只讀事務不分配trx_id,顯然trx_id的增速變慢。

但 max_trx_id 會持久化存儲,重啟也不會重置為0。理論上,只要一個MySQL實例跑得夠久,就可能出現max_trx_id達到2^48 - 1,然后從0開始循環。

達到該狀態后,MySQL就會持續出現一個臟讀bug:

首先把當前的max_trx_id先修改成2^48 - 1。這里是可重復讀。

復現臟讀

9ba5815a-f77e-11ed-90ce-dac502259ad0.jpg

9bac4f08-f77e-11ed-90ce-dac502259ad0.jpg

因為系統的max_trx_id被設置成2^48 - 1,所以在session A啟動的事務TA的低水位就是2^48 - 1。

t2時:

session B執行第一條update語句的事務id=2^48 - 1

第二條事務id就是0了,這條update執行后生成的數據版本上的trx_id=0

t3時:

session A執行select的可見性判斷:c=3這個數據版本的trx_id(0),小于事務TA的低水位(2^48 - 1),所以認為該數據可見。

但這是臟讀。

由于低水位值會持續增加,而事務id從0開始計數,導致系統在該時刻后,所有查詢都會出現臟讀。

并且MySQL重啟時max_trx_id也不會清0,即重啟MySQL,這個bug仍然存在。那這bug也是只存在于理論上嗎?

假設一個MySQL實例的TPS是50w,持續這樣,17.8年后就會出現該情況。但從MySQL真正開始流行到現在,恐怕都還沒有實例跑到過這個上限。不過,只要MySQL實例服務時間夠長,就必然會出現該bug。

這也可以加深對低水位和數據可見性的理解

thread_id

系統保存了一個全局變量thread_id_counter

9bb40338-f77e-11ed-90ce-dac502259ad0.jpg

每新建一個連接,就將thread_id_counter賦值給這個新連接的線程變量new_id。

thread_id_counter定義為4個字節,因此達到2^32 - 1,就會重置為0,繼續增加。

9bbb701e-f77e-11ed-90ce-dac502259ad0.jpg

但不會在show processlist看到兩個相同的thread_id。因為MySQL使用了一個唯一數組

9bc3ecda-f77e-11ed-90ce-dac502259ad0.jpg

給新線程分配thread_id時的邏輯:

9bcd8b6e-f77e-11ed-90ce-dac502259ad0.jpg

總結

每種自增id有各自的應用場景,在達到上限后的表現也不同:

表的自增id達到上限后,再申請時它的值就不會改變,進而導致繼續插入數據時報主鍵沖突錯誤

row_id達到上限后,則會歸0再重新遞增,如果出現相同的row_id,后寫的數據會覆蓋之前的數據

Xid只需要不在同一個binlog文件中出現重復值即可。雖然理論上會出現重復值,但是概率極小,可以忽略不計

InnoDB的max_trx_id 遞增值每次MySQL重啟都會被保存起來,所以我們文章中提到的臟讀的例子就是一個必現的bug,好在留給我們的時間還很充裕





審核編輯:劉清

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • TPS
    TPS
    +關注

    關注

    0

    文章

    83

    瀏覽量

    36322
  • MYSQL數據庫
    +關注

    關注

    0

    文章

    96

    瀏覽量

    9464
  • GDB調試
    +關注

    關注

    0

    文章

    24

    瀏覽量

    1499

原文標題:線上 MySQL 的自增 id 用盡怎么辦?

文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運維】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    怎么簡單實現由Labview讀取的串口數據寫入mysql5.7數據庫中?

    怎么簡單實現由Labview讀取的串口數據寫入mysql5.7數據庫中? 已實現:串口數據的接收處理 mysql5.7的安裝(已測試數據庫正常運行) 愿付費解決此問題(QQ:8
    發表于 01-11 22:05

    阿里云mysql數據庫怎么設置主鍵和時間格式怎么顯示時分秒?

    `需要將測試的數據保存到阿里云mysql數據庫上,利用NI的數據庫工具包怎么創建表實現主鍵?還有保存的時間數據只顯示日期,不顯示時分秒(用DB Tools Format Datetime String生成的日期時間字符串),
    發表于 11-13 10:39

    CAXA軟件不能注冊問題怎么辦

    CAXA軟件不能注冊問題怎么辦 在安裝CAXA系列軟件時,會出現安裝到最后階段CAXAInfo.dll以及Caxaview.ocx提示無法注冊。這是由于以前安裝CAXA軟件,
    發表于 10-18 18:33 ?3729次閱讀

    顯示桌面沒了怎么辦

    顯示桌面沒了怎么辦 我的windows xp的顯示桌面的圖標沒有了怎么辦。下載一個放到系統目
    發表于 01-18 19:00 ?3864次閱讀

    電池換新無法可依怎么辦

    電池壞了怎么辦?修。修不好怎么辦?換。
    發表于 03-19 11:23 ?1428次閱讀

    linux無法識別U盤怎么辦

    linux無法識別U盤怎么辦?
    發表于 05-19 09:08 ?1.7w次閱讀
    linux無法識別U盤<b class='flag-5'>怎么辦</b>

    蘋果iPad忘記了Apple ID密碼該怎么辦

    Apple ID密碼忘了怎么辦?蘋果用戶最常見的一個問題是忘記了Apple ID密碼,這樣就沒辦法從App Store下載應用了,那么如何才能將密碼找回來呢?一般在蘋果輸入過ID后,打
    的頭像 發表于 09-11 12:10 ?9w次閱讀
    蘋果iPad忘記了Apple <b class='flag-5'>ID</b>密碼該<b class='flag-5'>怎么辦</b>?

    MySQL主鍵一定是連續的嗎?

    如果你的業務設計依賴于主鍵的連續性,這個設計假設主鍵是連續的。但實際上,這樣的假設是錯的,因為
    的頭像 發表于 03-21 16:55 ?673次閱讀

    MySQL主鍵一定是連續的嗎?

    如果你的業務設計依賴于主鍵的連續性,這個設計假設主鍵是連續的。但實際上,這樣的假設是錯的,因為
    的頭像 發表于 06-11 11:35 ?578次閱讀
    <b class='flag-5'>MySQL</b><b class='flag-5'>自</b><b class='flag-5'>增</b>主鍵一定是連續的嗎?

    id的機制不同在mysql的索引結構以及優缺點

    1.4.效率測試結果 二、使用uuid和id的索引結構對比 2.1.使用id的內部結構
    的頭像 發表于 06-30 10:19 ?850次閱讀
    <b class='flag-5'>id</b>的機制不同在<b class='flag-5'>mysql</b>的索引結構以及優缺點

    電機過熱怎么辦?

    電機過熱怎么辦?WAYON維安PPTC有方案
    的頭像 發表于 11-01 15:08 ?811次閱讀
    電機過熱<b class='flag-5'>怎么辦</b>?

    mysql配置失敗怎么辦

    MySQL是一款廣泛使用的關系型數據庫管理系統,但在配置過程中可能會出現各種問題,導致配置失敗。本文將詳細介紹MySQL配置失敗的常見原因和對應的解決方案,以幫助讀者快速排查和解決問題。 一、檢查
    的頭像 發表于 12-06 11:03 ?3587次閱讀

    MySQL密碼忘記了怎么辦?MySQL密碼快速重置方法步驟命令示例!

    MySQL密碼忘記了怎么辦?MySQL密碼快速重置方法步驟命令示例! MySQL是一種常用的關系型數據庫管理系統,如果你忘記了MySQL的密
    的頭像 發表于 01-12 16:06 ?807次閱讀

    放大器怎么辦?是啥原因造成了放大器的激?

    前陣子,有位做天線的同事,問我放大器怎么辦,是啥原因造成了放大器的激。
    的頭像 發表于 01-18 09:49 ?1980次閱讀
    放大器<b class='flag-5'>自</b>激<b class='flag-5'>怎么辦</b>?是啥原因造成了放大器的<b class='flag-5'>自</b>激?

    蘋果手機id密碼在哪里找 蘋果手機id密碼忘記了怎么辦

    蘋果手機id密碼在哪里找 蘋果手機id密碼忘記了怎么辦? 蘋果手機id密碼在哪里找,若蘋果手機id密碼忘記了,可以通過以下幾種方法來解決這個
    的頭像 發表于 02-18 13:42 ?2468次閱讀
    主站蜘蛛池模板: 在线色网| 亚洲一区二区中文字幕 | 欧洲亚洲一区 | 一级特黄aa毛片免费观看 | 污污的黄色小说 | 婷婷97狠狠的狠狠的爱 | 98pao强力打造高清免费 | 三级网站免费 | 激情.com | 亚洲二区在线 | 天天碰天天干 | 国产精品国产三级国产普通话对白 | 欧美成人精品一区二三区在线观看 | 亚洲欧洲一区 | 欧美一级特黄aa大片视频 | 奇米色影院 | 91久久福利国产成人精品 | 干干干操操操 | 上课被同桌摸下面做羞羞 | 欧美日一区二区三区 | 久久综合九色综合欧美播 | 久久久久四虎国产精品 | 2021国产精品午夜久久 | 午夜影院在线观看免费 | 亚洲人成人 | 青草久操 | 久久99操 | 一区二区三区视频 | 黄网站色视频 | 男男h全肉耽污 | v视界影院最新网站 | 色多多网址| 日本黄色免费网址 | 久久久夜 | 丁香花在线电影小说观看 | 色视频免费看 | 丁香婷五月 | 亚洲 图片 小说 欧美 另类 | 国产伦精品一区二区三区免费 | 国产乱码精品一区二区三区四川人 | 欧美一区二区三区在线观看 |