日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

第四十一期:一道经典的MySQL面试题,答案出现三次反转

發布時間:2023/12/10 数据库 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 第四十一期:一道经典的MySQL面试题,答案出现三次反转 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前幾天偶然看到大家在討論一道面試題,而且答案也不夠統一,我感覺蠻有意思,在此就做一個解讀,整個過程中確實會有幾處反轉。

作者:楊建榮的學習筆記來源:今日頭條

前幾天偶然看到大家在討論一道面試題,而且答案也不夠統一,我感覺蠻有意思,在此就做一個解讀,整個過程中確實會有幾處反轉。

我們先來看下題目:

一張表,里面有ID自增主鍵,當insert了17條記錄之后,刪除了第15,16,17條記錄,再把MySQL重啟,再Insert一條記錄,這條記錄的ID是18還是15.

和后面的一些題目整體來看,難度不大,都是一些看起來很基礎的問題,但是這道題目引起了我的注意,因為這道題目的背景過于開放,所以答案也是不固定的,而這也是我們在技術學習中需要保持的嚴謹態度。

首先這道題整體來看,想表達的是對于MySQL中自增列的理解。

按照我們常規理解的邏輯,ID自增,應該是18,按照這個邏輯怎么都不應該是15吧?

但是這個答案對嗎?顯然不是,我們進入第一輪反轉。

確實,對于自增列的問題,這個是MySQL里面飽受詬病的老問題了。如果節點重啟,會從數據列中按照max(id)+1的方式來處理,在多環境歷史數據歸檔的情況下,如果主庫重啟,很可能會出現數據不一致的情況,記得在MySQL bug中很多人留言,說十多年前的老問題了,怎么還不解決。

而在OpenWorld上面Percona CEO Peter也再次提到了這個問題。

我認真查了一下這個bug的歷史,巧合的是,這個問題是Peter在十幾年前提出的,時光荏苒,一直沒有修復。

好的,按照MySQL bug的思路來理解,答案應該是15了。

但是這個答案對嗎?顯然不是,我們進入第二輪反轉。

這個題目的背景是不夠清晰的,這個表的存儲引擎沒有說是InnoDB還是MyISAM,所以存在不確定性,這么說的意義在于,自增列的信息在MyISAM和InnoDB中的維護邏輯是不大一樣的,在MyISAM中是存儲持久化在文件中的,當數據庫重啟之后,是可以通過持久化的信息持續對ID進行自增的,而InnoDB的自增列信息既不在.frm文件,也不在.ibd文件中,所以在此啟動的時候會按照max(id)+1的算法進行修復。

所以如果是MyISAM,則答案應該是18,而如果是InnoDB,則答案是15。

我們可以綜合對比,用一個小的測試來模擬復現,我們選擇的是MySQL 5.7環境。

為了對比明顯,我們創建兩張表test_innodb和test_myisam,分別對應InnoDB和MyISAM存儲引擎,來做同樣的操作,看看重啟后的差異情況。

>>create table test_innodb(id int primary key auto_increment,name varchar(30)) engine=innodb; >>create table test_myisam(id int primary key auto_increment,name varchar(30)) engine=myisam;

插入幾行數據,查看數據:

>>insert into test_innodb(name) values('aa'),('bb'),('cc');Query OK, 3 rows affected (0.00 sec) Records: 3 Duplicates: 0 Warnings: 0 >>insert into test_myisam(name) values('aa'),('bb'),('cc'); Query OK, 3 rows affected (0.00 sec) Records: 3 Duplicates: 0 Warnings: 0

查看兩張表的數據情況,數據是完全一樣。

>>select *from test_innodb;+----+------+| id | name |+----+------+| 1 | aa || 2| bb || 3 | cc |+----+------+3 rows in set (0.00 sec) >>select *from test_myisam;+----+------+| id | name |+----+------+| 1 | aa || 2| bb || 3 | cc |+----+------+3 rows in set (0.00 sec)

?

在1,2,3的基礎上,我們繼續插入值為5,跳過id值為4。

>>insert into test_innodb(id,name) values(5,'ee');Query OK, 1 row affected (0.00 sec) >>insert into test_myisam(id,name) values(5,'ee'); Query OK, 1 row affected (0.00 sec)

此時查看test_innodb自增列已經開始增長,值為6。

>>show create table test_innodb\G CREATE TABLE `test_innodb` ( `id` int(11) NOT AUTO_INCREMENT, `name` varchar(30) DEFAULT , PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf81 row in set (0.00 sec)

刪除id=5的記錄

delete from test_innodb where id=5;Query OK, 1 row affected (0.01 sec)

刪除記錄之后,自增列還是保持不變。

show create table test_innodb\G CREATE TABLE `test_innodb` ( `id` int(11) NOT AUTO_INCREMENT, `name` varchar(30) DEFAULT , PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf81 row in set (0.00 sec)

同理test_myisam也做同樣的測試,結果是完全一樣的,在此略過日志。

我們停止數據庫

>>shutdown;Query OK, 0 rows affected (0.00 sec)

重啟數據庫

#mysqld_safe --defaults-file=/data/mysql_5723/my.cnf &

此時查看test_innodb和test_myisam的自增列就開始出現差異了。

MyISAM存儲引擎的表test_myisam的自增列還是不變,為6。

show create table test_myisam\G CREATE TABLE `test_myisam` ( `id` int(11) NOT AUTO_INCREMENT, `name` varchar(30) DEFAULT , PRIMARY KEY (`id`)) ENGINE=MyISAM AUTO_INCREMENT=6DEFAULT CHARSET=utf81 row in set (0.00 sec)

而InnoDB存儲引擎的表test_innodb的自增列卻變為了4。

>>show create table test_innodb\G *************************** 1. row *************************** Table: test_innodbCreate Table:CREATE TABLE `test_innodb`( `id` int(11) NOT AUTO_INCREMENT, `name` varchar(30) DEFAULT , PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8

我們繼續插入一條數據,保持id列自增。

>>insert into test_innodb(name) values('ee');Query OK, 1 row affected (0.00 sec) >>insert into test_myisam(name) values('ee'); Query OK, 1 row affected (0.00 sec)

可以看到兩張表的id列已經分道揚鑣了。

>>select *from test_innodb; +----+------+| id | name |+----+------+| 1 | aa || 2 | bb || 3 | cc || 4 | ee |+----+------+4 rows in set (0.00 sec) >>select *from test_myisam; +----+------+| id | name |+----+------+| 1 | aa || 2 | bb || 3 | cc || 6 | ee |+----+------+4 rows in set (0.00 sec)

小結:對于MyISAM和InnoDB的表,因為存儲引擎對于自增列的實現機制不同,ID值也可能會有所不同,對于InnoDB存儲引擎的表,ID是按照max(id)+1的算法來計算的。

但是這個答案對嗎?顯然不是,因為還是不夠嚴謹,我們進入第三輪反轉。

這個問題不夠嚴謹是因為技術是逐步發展的,這個問題在MySQL 8.0中有了答案,對于InnoDB的自增列信息,如果斷電之后會直接丟失,很可能造成級聯從庫間的數據同步出現問題,而在MySQL 8.0之后,這個信息寫入了共享表空間中,所以服務重啟之后,還是可以繼續追溯這個自增列的ID變化情況的。

限于篇幅,因為測試日志是很相似的,我就直接給出測試后的日志,這是在數據庫重啟之后的自增列情況,可以看到test_innodb和test_myisam的自增列是完全一樣的。

mysql> show create table test_myisam\G*************************** 1. row *************************** Table: test_myisamCreate Table: CREATE TABLE `test_myisam`( `id` int(11) NOT AUTO_INCREMENT, `name` varchar(30) DEFAULT , PRIMARY KEY (`id`)) ENGINE=MyISAM AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci1 row in set (0.00 sec) mysql> show create table test_innodb\G *************************** 1. row *************************** Table: test_innodbCreate Table: CREATE TABLE `test_innodb` ( `id` int(11) NOT AUTO_INCREMENT, `name` varchar(30) DEFAULT , PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT= 6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci1 row in set (0.00 sec)

我們做一個小結:

在MySQL 8.0之前:

1)如果是MyISAM表,則數據庫重啟后,ID值為18

2)如果是InnoDB表,則數據庫重啟后,ID值為15

在MySQL 8.0開始,

1)如果是MyISAM表,則數據庫重啟后,ID值為18

2)如果是InnoDB表,則數據庫重啟后,ID值為18

此處需要補充的是,對于ID自增列,在MySQL 5.7中可以使用sys schema來進行有效監控了,可以查看視圖schema_auto_increment_columns 來進行列值溢出的有效判斷。

更難能可貴的是,如果是MySQL 5.7版本以下,雖然沒有sys schema特性,但是可以復用MySQL 5.7中的schema_auto_increment_columns 的視圖語句,也是可以對列值溢出進行有效判斷的。

閱讀目錄(置頂)(長期更新計算機領域知識)https://blog.csdn.net/weixin_43392489/article/details/102380691

閱讀目錄(置頂)(長期更新計算機領域知識)https://blog.csdn.net/weixin_43392489/article/details/102380882

閱讀目錄(置頂)(長期科技領域知識)https://blog.csdn.net/weixin_43392489/article/details/102600114

?

總結

以上是生活随笔為你收集整理的第四十一期:一道经典的MySQL面试题,答案出现三次反转的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。