国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

一篇文章帶你讀懂MySQL和InnoDB

數(shù)據(jù)庫 MySQL 其他數(shù)據(jù)庫
作為一名開發(fā)人員,在日常的工作中會(huì)難以避免地接觸到數(shù)據(jù)庫,無論是基于文件的 sqlite 還是工程上使用非常廣泛的 MySQL、PostgreSQL,但是一直以來也沒有對(duì)數(shù)據(jù)庫有一個(gè)非常清晰并且成體系的認(rèn)知,所以最近兩個(gè)月的時(shí)間看了幾本數(shù)據(jù)庫相關(guān)的書籍并且閱讀了 MySQL 的官方文檔,希望對(duì)各位了解數(shù)據(jù)庫的、不了解數(shù)據(jù)庫的有所幫助。

作為一名開發(fā)人員,在日常的工作中會(huì)難以避免地接觸到數(shù)據(jù)庫,無論是基于文件的 sqlite 還是工程上使用非常廣泛的 MySQL、PostgreSQL,但是一直以來也沒有對(duì)數(shù)據(jù)庫有一個(gè)非常清晰并且成體系的認(rèn)知,所以最近兩個(gè)月的時(shí)間看了幾本數(shù)據(jù)庫相關(guān)的書籍并且閱讀了 MySQL 的官方文檔,希望對(duì)各位了解數(shù)據(jù)庫的、不了解數(shù)據(jù)庫的有所幫助。

本文中對(duì)于數(shù)據(jù)庫的介紹以及研究都是在 MySQL 上進(jìn)行的,如果涉及到了其他數(shù)據(jù)庫的內(nèi)容或者實(shí)現(xiàn)會(huì)在文中單獨(dú)指出。

數(shù)據(jù)庫的定義

很多開發(fā)者在最開始時(shí)其實(shí)都對(duì)數(shù)據(jù)庫有一個(gè)比較模糊的認(rèn)識(shí),覺得數(shù)據(jù)庫就是一堆數(shù)據(jù)的集合,但是實(shí)際卻比這復(fù)雜的多,數(shù)據(jù)庫領(lǐng)域中有兩個(gè)詞非常容易混淆,也就是數(shù)據(jù)庫和實(shí)例:

  • 數(shù)據(jù)庫:物理操作文件系統(tǒng)或其他形式文件類型的集合;
  • 實(shí)例:MySQL 數(shù)據(jù)庫由后臺(tái)線程以及一個(gè)共享內(nèi)存區(qū)組成;

對(duì)于數(shù)據(jù)庫和實(shí)例的定義都來自于 MySQL 技術(shù)內(nèi)幕:InnoDB 存儲(chǔ)引擎 一書,想要了解 InnoDB 存儲(chǔ)引擎的讀者可以閱讀這本書籍。

數(shù)據(jù)庫和實(shí)例

在 MySQL 中,實(shí)例和數(shù)據(jù)庫往往都是一一對(duì)應(yīng)的,而我們也無法直接操作數(shù)據(jù)庫,而是要通過數(shù)據(jù)庫實(shí)例來操作數(shù)據(jù)庫文件,可以理解為數(shù)據(jù)庫實(shí)例是數(shù)據(jù)庫為上層提供的一個(gè)專門用于操作的接口。

在 Unix 上,啟動(dòng)一個(gè) MySQL 實(shí)例往往會(huì)產(chǎn)生兩個(gè)進(jìn)程, mysqld 就是真正的數(shù)據(jù)庫服務(wù)守護(hù)進(jìn)程,而 mysqld_safe是一個(gè)用于檢查和設(shè)置 mysqld 啟動(dòng)的控制程序,它負(fù)責(zé)監(jiān)控 MySQL 進(jìn)程的執(zhí)行,當(dāng) mysqld 發(fā)生錯(cuò)誤時(shí), mysqld_safe 會(huì)對(duì)其狀態(tài)進(jìn)行檢查并在合適的條件下重啟。

MySQL 的架構(gòu)

MySQL 從***個(gè)版本發(fā)布到現(xiàn)在已經(jīng)有了 20 多年的歷史,在這么多年的發(fā)展和演變中,整個(gè)應(yīng)用的體系結(jié)構(gòu)變得越來越復(fù)雜:

 

最上層用于連接、線程處理的部分并不是 MySQL 『發(fā)明』的,很多服務(wù)都有類似的組成部分;第二層中包含了大多數(shù) MySQL 的核心服務(wù),包括了對(duì) SQL 的解析、分析、優(yōu)化和緩存等功能,存儲(chǔ)過程、觸發(fā)器和視圖都是在這里實(shí)現(xiàn)的;而第三層就是 MySQL 中真正負(fù)責(zé)數(shù)據(jù)的存儲(chǔ)和提取的存儲(chǔ)引擎,例如:InnoDB、MyISAM 等,文中對(duì)存儲(chǔ)引擎的介紹都是對(duì) InnoDB 實(shí)現(xiàn)的分析。

數(shù)據(jù)的存儲(chǔ)

在整個(gè)數(shù)據(jù)庫體系結(jié)構(gòu)中,我們可以使用不同的存儲(chǔ)引擎來存儲(chǔ)數(shù)據(jù),而絕大多數(shù)存儲(chǔ)引擎都以二進(jìn)制的形式存儲(chǔ)數(shù)據(jù);這一節(jié)會(huì)介紹 InnoDB 中對(duì)數(shù)據(jù)是如何存儲(chǔ)的。

在 InnoDB 存儲(chǔ)引擎中,所有的數(shù)據(jù)都被邏輯地存放在表空間中,表空間(tablespace)是存儲(chǔ)引擎中***的存儲(chǔ)邏輯單位,在表空間的下面又包括段(segment)、區(qū)(extent)、頁(page):

 

同一個(gè)數(shù)據(jù)庫實(shí)例的所有表空間都有相同的頁大小;默認(rèn)情況下,表空間中的頁大小都為 16KB,當(dāng)然也可以通過改變 innodb_page_size 選項(xiàng)對(duì)默認(rèn)大小進(jìn)行修改,需要注意的是不同的頁大小最終也會(huì)導(dǎo)致區(qū)大小的不同:

從圖中可以看出,在 InnoDB 存儲(chǔ)引擎中,一個(gè)區(qū)的大小最小為 1MB,頁的數(shù)量最少為 64 個(gè)。

如何存儲(chǔ)表

MySQL 使用 InnoDB 存儲(chǔ)表時(shí),會(huì)將表的定義和數(shù)據(jù)索引等信息分開存儲(chǔ),其中前者存儲(chǔ)在 .frm 文件中,后者存儲(chǔ)在 .ibd 文件中,這一節(jié)就會(huì)對(duì)這兩種不同的文件分別進(jìn)行介紹。

.frm 文件

無論在 MySQL 中選擇了哪個(gè)存儲(chǔ)引擎,所有的 MySQL 表都會(huì)在硬盤上創(chuàng)建一個(gè) .frm 文件用來描述表的格式或者說定義; .frm 文件的格式在不同的平臺(tái)上都是相同的。

 

  1. CREATE TABLE test_frm(  
  2.     column1 CHAR 
  3.  
  4. ),  
  5.     column2 INTEGER  
  6. ); 

 

當(dāng)我們使用上面的代碼創(chuàng)建表時(shí),會(huì)在磁盤上的 datadir文件夾中生成一個(gè) test_frm.frm 的文件,這個(gè)文件中就包含了表結(jié)構(gòu)相關(guān)的信息:

 

MySQL 官方文檔中的 11.1 MySQL .frm File Format 一文對(duì)于 .frm 文件格式中的二進(jìn)制的內(nèi)容有著非常詳細(xì)的表述,在這里就不展開介紹了。

.ibd 文件

InnoDB 中用于存儲(chǔ)數(shù)據(jù)的文件總共有兩個(gè)部分,一是系統(tǒng)表空間文件,包括 ibdata1、 ibdata2 等文件,其中存儲(chǔ)了 InnoDB 系統(tǒng)信息和用戶數(shù)據(jù)庫表數(shù)據(jù)和索引,是所有表公用的。

當(dāng)打開 innodb_file_per_table 選項(xiàng)時(shí), .ibd 文件就是每一個(gè)表獨(dú)有的表空間,文件存儲(chǔ)了當(dāng)前表的數(shù)據(jù)和相關(guān)的索引數(shù)據(jù)。

如何存儲(chǔ)記錄

與現(xiàn)有的大多數(shù)存儲(chǔ)引擎一樣,InnoDB 使用頁作為磁盤管理的最小單位;數(shù)據(jù)在 InnoDB 存儲(chǔ)引擎中都是按行存儲(chǔ)的,每個(gè) 16KB 大小的頁中可以存放 2-200 行的記錄。

當(dāng) InnoDB 存儲(chǔ)數(shù)據(jù)時(shí),它可以使用不同的行格式進(jìn)行存儲(chǔ);MySQL 5.7 版本支持以下格式的行存儲(chǔ)方式:

 

Antelope 是 InnoDB 最開始支持的文件格式,它包含兩種行格式 Compact 和 Redundant,它最開始并沒有名字;Antelope 的名字是在新的文件格式 Barracuda 出現(xiàn)后才起的,Barracuda 的出現(xiàn)引入了兩種新的行格式 Compressed 和 Dynamic;InnoDB 對(duì)于文件格式都會(huì)向前兼容,而官方文檔中也對(duì)之后會(huì)出現(xiàn)的新文件格式預(yù)先定義好了名字:Cheetah、Dragon、Elk 等等。

兩種行記錄格式 Compact 和 Redundant 在磁盤上按照以下方式存儲(chǔ):

 

Compact 和 Redundant 格式***的不同就是記錄格式的***個(gè)部分;在 Compact 中,行記錄的***部分倒序存放了一行數(shù)據(jù)中列的長(zhǎng)度(Length),而 Redundant 中存的是每一列的偏移量(Offset),從總體上上看,Compact 行記錄格式相比 Redundant 格式能夠減少 20% 的存儲(chǔ)空間。

行溢出數(shù)據(jù)

當(dāng) InnoDB 使用 Compact 或者 Redundant 格式存儲(chǔ)極長(zhǎng)的 VARCHAR 或者 BLOB 這類大對(duì)象時(shí),我們并不會(huì)直接將所有的內(nèi)容都存放在數(shù)據(jù)頁節(jié)點(diǎn)中,而是將行數(shù)據(jù)中的前 768 個(gè)字節(jié)存儲(chǔ)在數(shù)據(jù)頁中,后面會(huì)通過偏移量指向溢出頁。

 

但是當(dāng)我們使用新的行記錄格式 Compressed 或者 Dynamic 時(shí)都只會(huì)在行記錄中保存 20 個(gè)字節(jié)的指針,實(shí)際的數(shù)據(jù)都會(huì)存放在溢出頁面中。

 

當(dāng)然在實(shí)際存儲(chǔ)中,可能會(huì)對(duì)不同長(zhǎng)度的 TEXT 和 BLOB 列進(jìn)行優(yōu)化,不過這就不是本文關(guān)注的重點(diǎn)了。

想要了解更多與 InnoDB 存儲(chǔ)引擎中記錄的數(shù)據(jù)格式的相關(guān)信息,可以閱讀 InnoDB Record Structure

數(shù)據(jù)頁結(jié)構(gòu)

頁是 InnoDB 存儲(chǔ)引擎管理數(shù)據(jù)的最小磁盤單位,而 B-Tree 節(jié)點(diǎn)就是實(shí)際存放表中數(shù)據(jù)的頁面,我們?cè)谶@里將要介紹頁是如何組織和存儲(chǔ)記錄的;首先,一個(gè) InnoDB 頁有以下七個(gè)部分:

 

每一個(gè)頁中包含了兩對(duì) header/trailer:內(nèi)部的 Page Header/Page Directory 關(guān)心的是頁的狀態(tài)信息,而 Fil Header/Fil Trailer 關(guān)心的是記錄頁的頭信息。

在頁的頭部和尾部之間就是用戶記錄和空閑空間了,每一個(gè)數(shù)據(jù)頁中都包含 Infimum 和 Supremum 這兩個(gè)虛擬的記錄(可以理解為占位符),Infimum 記錄是比該頁中任何主鍵值都要小的值,Supremum 是該頁中的***值:

 

User Records 就是整個(gè)頁面中真正用于存放行記錄的部分,而 Free Space 就是空余空間了,它是一個(gè)鏈表的數(shù)據(jù)結(jié)構(gòu),為了保證插入和刪除的效率,整個(gè)頁面并不會(huì)按照主鍵順序?qū)λ杏涗涍M(jìn)行排序,它會(huì)自動(dòng)從左側(cè)向右尋找空白節(jié)點(diǎn)進(jìn)行插入,行記錄在物理存儲(chǔ)上并不是按照順序的,它們之間的順序是由 next_record 這一指針控制的。

B+ 樹在查找對(duì)應(yīng)的記錄時(shí),并不會(huì)直接從樹中找出對(duì)應(yīng)的行記錄,它只能獲取記錄所在的頁,將整個(gè)頁加載到內(nèi)存中,再通過 Page Directory 中存儲(chǔ)的稀疏索引和 n_owned、 next_record 屬性取出對(duì)應(yīng)的記錄,不過因?yàn)檫@一操作是在內(nèi)存中進(jìn)行的,所以通常會(huì)忽略這部分查找的耗時(shí)。

InnoDB 存儲(chǔ)引擎中對(duì)數(shù)據(jù)的存儲(chǔ)是一個(gè)非常復(fù)雜的話題,這一節(jié)中也只是對(duì)表、行記錄以及頁面的存儲(chǔ)進(jìn)行一定的分析和介紹,雖然作者相信這部分知識(shí)對(duì)于大部分開發(fā)者已經(jīng)足夠了,但是想要真正消化這部分內(nèi)容還需要很多的努力和實(shí)踐。

索引

索引是數(shù)據(jù)庫中非常非常重要的概念,它是存儲(chǔ)引擎能夠快速定位記錄的秘密武器,對(duì)于提升數(shù)據(jù)庫的性能、減輕數(shù)據(jù)庫服務(wù)器的負(fù)擔(dān)有著非常重要的作用;索引優(yōu)化是對(duì)查詢性能優(yōu)化的最有效手段,它能夠輕松地將查詢的性能提高幾個(gè)數(shù)量級(jí)。

索引的數(shù)據(jù)結(jié)構(gòu)

在上一節(jié)中,我們談了行記錄的存儲(chǔ)和頁的存儲(chǔ),在這里我們就要從更高的層面看 InnoDB 中對(duì)于數(shù)據(jù)是如何存儲(chǔ)的;InnoDB 存儲(chǔ)引擎在絕大多數(shù)情況下使用 B+ 樹建立索引,這是關(guān)系型數(shù)據(jù)庫中查找最為常用和有效的索引,但是 B+ 樹索引并不能找到一個(gè)給定鍵對(duì)應(yīng)的具體值,它只能找到數(shù)據(jù)行對(duì)應(yīng)的頁,然后正如上一節(jié)所提到的,數(shù)據(jù)庫把整個(gè)頁讀入到內(nèi)存中,并在內(nèi)存中查找具體的數(shù)據(jù)行。

 

B+ 樹是平衡樹,它查找任意節(jié)點(diǎn)所耗費(fèi)的時(shí)間都是完全相同的,比較的次數(shù)就是 B+ 樹的高度;在這里,我們并不會(huì)深入分析或者動(dòng)手實(shí)現(xiàn)一個(gè) B+ 樹,只是對(duì)它的特性進(jìn)行簡(jiǎn)單的介紹。

聚集索引和輔助索引

數(shù)據(jù)庫中的 B+ 樹索引可以分為聚集索引(clustered index)和輔助索引(secondary index),它們之間的***區(qū)別就是,聚集索引中存放著一條行記錄的全部信息,而輔助索引中只包含索引列和一個(gè)用于查找對(duì)應(yīng)行記錄的『書簽』。

聚集索引

InnoDB 存儲(chǔ)引擎中的表都是使用索引組織的,也就是按照鍵的順序存放;聚集索引就是按照表中主鍵的順序構(gòu)建一顆 B+ 樹,并在葉節(jié)點(diǎn)中存放表中的行記錄數(shù)據(jù)。

 

  1. CREATE TABLE users(  
  2.     id INT NOT NULL 
  3.     first_name VARCHAR 
  4. 20  
  5. NOT NULL 
  6.     last_name VARCHAR 
  7. 20  
  8. NOT NULL 
  9.     age INT NOT NULL 
  10.     PRIMARY KEY(id),  
  11.     KEY(last_name, first_name, age)  
  12.     KEY(first_name)  
  13. ); 

 

如果使用上面的 SQL 在數(shù)據(jù)庫中創(chuàng)建一張表,B+ 樹就會(huì)使用 id 作為索引的鍵,并在葉子節(jié)點(diǎn)中存儲(chǔ)一條記錄中的所有信息。

 

圖中對(duì) B+ 樹的描述與真實(shí)情況下 B+ 樹中的數(shù)據(jù)結(jié)構(gòu)有一些差別,不過這里想要表達(dá)的主要意思是:聚集索引葉節(jié)點(diǎn)中保存的是整條行記錄,而不是其中的一部分。

聚集索引與表的物理存儲(chǔ)方式有著非常密切的關(guān)系,所有正常的表應(yīng)該有且僅有一個(gè)聚集索引(絕大多數(shù)情況下都是主鍵),表中的所有行記錄數(shù)據(jù)都是按照聚集索引的順序存放的。

當(dāng)我們使用聚集索引對(duì)表中的數(shù)據(jù)進(jìn)行檢索時(shí),可以直接獲得聚集索引所對(duì)應(yīng)的整條行記錄數(shù)據(jù)所在的頁,不需要進(jìn)行第二次操作。

輔助索引

數(shù)據(jù)庫將所有的非聚集索引都劃分為輔助索引,但是這個(gè)概念對(duì)我們理解輔助索引并沒有什么幫助;輔助索引也是通過 B+ 樹實(shí)現(xiàn)的,但是它的葉節(jié)點(diǎn)并不包含行記錄的全部數(shù)據(jù),僅包含索引中的所有鍵和一個(gè)用于查找對(duì)應(yīng)行記錄的『書簽』,在 InnoDB 中這個(gè)書簽就是當(dāng)前記錄的主鍵。

輔助索引的存在并不會(huì)影響聚集索引,因?yàn)榫奂饕龢?gòu)成的 B+ 樹是數(shù)據(jù)實(shí)際存儲(chǔ)的形式,而輔助索引只用于加速數(shù)據(jù)的查找,所以一張表上往往有多個(gè)輔助索引以此來提升數(shù)據(jù)庫的性能。

一張表一定包含一個(gè)聚集索引構(gòu)成的 B+ 樹以及若干輔助索引的構(gòu)成的 B+ 樹。

如果在表 users 中存在一個(gè)輔助索引 (first_name,age),那么它構(gòu)成的 B+ 樹大致就是上圖這樣,按照 (first_name,age) 的字母順序?qū)Ρ碇械臄?shù)據(jù)進(jìn)行排序,當(dāng)查找到主鍵時(shí),再通過聚集索引獲取到整條行記錄。

 

上圖展示了一個(gè)使用輔助索引查找一條表記錄的過程:通過輔助索引查找到對(duì)應(yīng)的主鍵,***在聚集索引中使用主鍵獲取對(duì)應(yīng)的行記錄,這也是通常情況下行記錄的查找方式。

索引的設(shè)計(jì)

索引的設(shè)計(jì)其實(shí)是一個(gè)非常重要的內(nèi)容,同時(shí)也是一個(gè)非常復(fù)雜的內(nèi)容;索引的設(shè)計(jì)與創(chuàng)建對(duì)于提升數(shù)據(jù)庫的查詢性能至關(guān)重要,不過這不是本文想要介紹的內(nèi)容,有關(guān)索引的設(shè)計(jì)與優(yōu)化可以閱讀 數(shù)據(jù)庫索引設(shè)計(jì)與優(yōu)化 一書,書中提供了一種非常科學(xué)合理的方法能夠幫助我們?cè)跀?shù)據(jù)庫中建立最適合的索引,當(dāng)然作者也可能會(huì)在之后的文章中對(duì)索引的設(shè)計(jì)進(jìn)行簡(jiǎn)單的介紹和分析。

我們都知道鎖的種類一般分為樂觀鎖和悲觀鎖兩種,InnoDB 存儲(chǔ)引擎中使用的就是悲觀鎖,而按照鎖的粒度劃分,也可以分成行鎖和表鎖。

并發(fā)控制機(jī)制

樂觀鎖和悲觀鎖其實(shí)都是并發(fā)控制的機(jī)制,同時(shí)它們?cè)谠砩暇陀兄举|(zhì)的差別;

  • 樂觀鎖是一種思想,它其實(shí)并不是一種真正的『鎖』,它會(huì)先嘗試對(duì)資源進(jìn)行修改,在寫回時(shí)判斷資源是否進(jìn)行了改變,如果沒有發(fā)生改變就會(huì)寫回,否則就會(huì)進(jìn)行重試,在整個(gè)的執(zhí)行過程中其實(shí)都沒有對(duì)數(shù)據(jù)庫進(jìn)行加鎖;
  • 悲觀鎖就是一種真正的鎖了,它會(huì)在獲取資源前對(duì)資源進(jìn)行加鎖,確保同一時(shí)刻只有有限的線程能夠訪問該資源,其他想要嘗試獲取資源的操作都會(huì)進(jìn)入等待狀態(tài),直到該線程完成了對(duì)資源的操作并且釋放了鎖后,其他線程才能重新操作資源;

雖然樂觀鎖和悲觀鎖在本質(zhì)上并不是同一種東西,一個(gè)是一種思想,另一個(gè)是一種真正的鎖,但是它們都是一種并發(fā)控制機(jī)制。

樂觀鎖不會(huì)存在死鎖的問題,但是由于更新后驗(yàn)證,所以當(dāng)沖突頻率和重試成本較高時(shí)更推薦使用悲觀鎖,而需要非常高的響應(yīng)速度并且并發(fā)量非常大的時(shí)候使用樂觀鎖就能較好的解決問題,在這時(shí)使用悲觀鎖就可能出現(xiàn)嚴(yán)重的性能問題;在選擇并發(fā)控制機(jī)制時(shí),需要綜合考慮上面的四個(gè)方面(沖突頻率、重試成本、響應(yīng)速度和并發(fā)量)進(jìn)行選擇。

鎖的種類

對(duì)數(shù)據(jù)的操作其實(shí)只有兩種,也就是讀和寫,而數(shù)據(jù)庫在實(shí)現(xiàn)鎖時(shí),也會(huì)對(duì)這兩種操作使用不同的鎖;InnoDB 實(shí)現(xiàn)了標(biāo)準(zhǔn)的行級(jí)鎖,也就是共享鎖(Shared Lock)和互斥鎖(Exclusive Lock);共享鎖和互斥鎖的作用其實(shí)非常好理解:

  • 共享鎖(讀鎖):允許事務(wù)對(duì)一條行數(shù)據(jù)進(jìn)行讀取;
  • 互斥鎖(寫鎖):允許事務(wù)對(duì)一條行數(shù)據(jù)進(jìn)行刪除或更新;

而它們的名字也暗示著各自的另外一個(gè)特性,共享鎖之間是兼容的,而互斥鎖與其他任意鎖都不兼容:

 

稍微對(duì)它們的使用進(jìn)行思考就能想明白它們?yōu)槭裁匆@么設(shè)計(jì),因?yàn)楣蚕礞i代表了讀操作、互斥鎖代表了寫操作,所以我們可以在數(shù)據(jù)庫中并行讀,但是只能串行寫,只有這樣才能保證不會(huì)發(fā)生線程競(jìng)爭(zhēng),實(shí)現(xiàn)線程安全。

鎖的粒度

無論是共享鎖還是互斥鎖其實(shí)都只是對(duì)某一個(gè)數(shù)據(jù)行進(jìn)行加鎖,InnoDB 支持多種粒度的鎖,也就是行鎖和表鎖;為了支持多粒度鎖定,InnoDB 存儲(chǔ)引擎引入了意向鎖(Intention Lock),意向鎖就是一種表級(jí)鎖。

與上一節(jié)中提到的兩種鎖的種類相似的是,意向鎖也分為兩種:

  • 意向共享鎖:事務(wù)想要在獲得表中某些記錄的共享鎖,需要在表上先加意向共享鎖;
  • 意向互斥鎖:事務(wù)想要在獲得表中某些記錄的互斥鎖,需要在表上先加意向互斥鎖;

隨著意向鎖的加入,鎖類型之間的兼容矩陣也變得愈加復(fù)雜:

 

意向鎖其實(shí)不會(huì)阻塞全表掃描之外的任何請(qǐng)求,它們的主要目的是為了表示是否有人請(qǐng)求鎖定表中的某一行數(shù)據(jù)。

有的人可能會(huì)對(duì)意向鎖的目的并不是完全的理解,我們?cè)谶@里可以舉一個(gè)例子:如果沒有意向鎖,當(dāng)已經(jīng)有人使用行鎖對(duì)表中的某一行進(jìn)行修改時(shí),如果另外一個(gè)請(qǐng)求要對(duì)全表進(jìn)行修改,那么就需要對(duì)所有的行是否被鎖定進(jìn)行掃描,在這種情況下,效率是非常低的;不過,在引入意向鎖之后,當(dāng)有人使用行鎖對(duì)表中的某一行進(jìn)行修改之前,會(huì)先為表添加意向互斥鎖(IX),再為行記錄添加互斥鎖(X),在這時(shí)如果有人嘗試對(duì)全表進(jìn)行修改就不需要判斷表中的每一行數(shù)據(jù)是否被加鎖了,只需要通過等待意向互斥鎖被釋放就可以了。

鎖的算法

到目前為止已經(jīng)對(duì) InnoDB 中鎖的粒度有一定的了解,也清楚了在對(duì)數(shù)據(jù)庫進(jìn)行讀寫時(shí)會(huì)獲取不同的鎖,在這一小節(jié)將介紹鎖是如何添加到對(duì)應(yīng)的數(shù)據(jù)行上的,我們會(huì)分別介紹三種鎖的算法:Record Lock、Gap Lock 和 Next-Key Lock。

Record Lock

記錄鎖(Record Lock)是加到索引記錄上的鎖,假設(shè)我們存在下面的一張表 users:

 

  1. CREATE TABLE users(  
  2.     id INT NOT NULL AUTO_INCREMENT,  
  3.     last_name VARCHAR 
  4. 255  
  5. NOT NULL 
  6.     first_name VARCHAR 
  7. 255  
  8. ),  
  9.     age INT 
  10.     PRIMARY KEY(id),  
  11.     KEY(last_name),  
  12.     KEY(age)  
  13. ); 

 

如果我們使用 id 或者 last_name 作為 SQL 中 WHERE 語句的過濾條件,那么 InnoDB 就可以通過索引建立的 B+ 樹找到行記錄并添加索引,但是如果使用 first_name 作為過濾條件時(shí),由于 InnoDB 不知道待修改的記錄具體存放的位置,也無法對(duì)將要修改哪條記錄提前做出判斷就會(huì)鎖定整個(gè)表。

Gap Lock

記錄鎖是在存儲(chǔ)引擎中最為常見的鎖,除了記錄鎖之外,InnoDB 中還存在間隙鎖(Gap Lock),間隙鎖是對(duì)索引記錄中的一段連續(xù)區(qū)域的鎖;當(dāng)使用類似 SELECT*FROM users WHERE id BETWEEN10AND20FOR UPDATE;的 SQL 語句時(shí),就會(huì)阻止其他事務(wù)向表中插入 id=15 的記錄,因?yàn)檎麄€(gè)范圍都被間隙鎖鎖定了。

間隙鎖是存儲(chǔ)引擎對(duì)于性能和并發(fā)做出的權(quán)衡,并且只用于某些事務(wù)隔離級(jí)別。

雖然間隙鎖中也分為共享鎖和互斥鎖,不過它們之間并不是互斥的,也就是不同的事務(wù)可以同時(shí)持有一段相同范圍的共享鎖和互斥鎖,它唯一阻止的就是其他事務(wù)向這個(gè)范圍中添加新的記錄。

Next-Key Lock

Next-Key 鎖相比前兩者就稍微有一些復(fù)雜,它是記錄鎖和記錄前的間隙鎖的結(jié)合,在 users 表中有以下記錄:

 

  1. +------+-------------+--------------+-------+ 
  2.  
  3. |   id | last_name   | first_name   |   age | 
  4.  
  5. |------+-------------+--------------+-------| 
  6.  
  7. |    | stark       | tony         |   21 | 
  8.  
  9. |   | tom         | hiddleston   |  30  | 
  10.  
  11. |   | morgan      | freeman      | 40  | 
  12.  
  13.  | jeff        | dean         |  50  | 
  14.  
  15. | 2  | donald      | trump        | 80  | 
  16.  
  17. +------+-------------+--------------+-------+ 

 

如果使用 Next-Key 鎖,那么 Next-Key 鎖就可以在需要的時(shí)候鎖定以下的范圍:

 

  1. (-∞, 21 
  2.  
  3. 21 30 
  4.  
  5. 30 ,  40 
  6.  
  7. 40 , 50 
  8.  
  9. 50 ,80
  10.  
  11. 80 , ∞) 

 

既然叫 Next-Key 鎖,鎖定的應(yīng)該是當(dāng)前值和后面的范圍,但是實(shí)際上卻不是,Next-Key 鎖鎖定的是當(dāng)前值和前面的范圍。

當(dāng)我們更新一條記錄,比如 SELECT*FROM users WHERE age=30FOR UPDATE;,InnoDB 不僅會(huì)在范圍 (21,30] 上加 Next-Key 鎖,還會(huì)在這條記錄后面的范圍 (30,40] 加間隙鎖,所以插入 (21,40]范圍內(nèi)的記錄都會(huì)被鎖定。

Next-Key 鎖的作用其實(shí)是為了解決幻讀的問題,我們會(huì)在下一節(jié)談事務(wù)的時(shí)候具體介紹。

死鎖的發(fā)生

既然 InnoDB 中實(shí)現(xiàn)的鎖是悲觀的,那么不同事務(wù)之間就可能會(huì)互相等待對(duì)方釋放鎖造成死鎖,最終導(dǎo)致事務(wù)發(fā)生錯(cuò)誤;想要在 MySQL 中制造死鎖的問題其實(shí)非常容易:

 

兩個(gè)會(huì)話都持有一個(gè)鎖,并且嘗試獲取對(duì)方的鎖時(shí)就會(huì)發(fā)生死鎖,不過 MySQL 也能在發(fā)生死鎖時(shí)及時(shí)發(fā)現(xiàn)問題,并保證其中的一個(gè)事務(wù)能夠正常工作,這對(duì)我們來說也是一個(gè)好消息。

事務(wù)與隔離級(jí)別

在介紹了鎖之后,我們?cè)賮碚務(wù)剶?shù)據(jù)庫中一個(gè)非常重要的概念 —— 事務(wù);相信只要是一個(gè)合格的軟件工程師就對(duì)事務(wù)的特性有所了解,其中被人經(jīng)常提起的就是事務(wù)的原子性,在數(shù)據(jù)提交工作時(shí),要么保證所有的修改都能夠提交,要么就所有的修改全部回滾。

但是事務(wù)還遵循包括原子性在內(nèi)的 ACID 四大特性:原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)和持久性(Durability);文章不會(huì)對(duì)這四大特性全部展開進(jìn)行介紹,相信你能夠通過 Google 和數(shù)據(jù)庫相關(guān)的書籍輕松獲得有關(guān)它們的概念,本文***要介紹的就是事務(wù)的四種隔離級(jí)別。

幾種隔離級(jí)別

事務(wù)的隔離性是數(shù)據(jù)庫處理數(shù)據(jù)的幾大基礎(chǔ)之一,而隔離級(jí)別其實(shí)就是提供給用戶用于在性能和可靠性做出選擇和權(quán)衡的配置項(xiàng)。

ISO 和 ANIS SQL 標(biāo)準(zhǔn)制定了四種事務(wù)隔離級(jí)別,而 InnoDB 遵循了 SQL:1992 標(biāo)準(zhǔn)中的四種隔離級(jí)別: READ UNCOMMITED、 READ COMMITED、 REPEATABLE READ和 SERIALIZABLE;每個(gè)事務(wù)的隔離級(jí)別其實(shí)都比上一級(jí)多解決了一個(gè)問題:

  • RAED UNCOMMITED:使用查詢語句不會(huì)加鎖,可能會(huì)讀到未提交的行(Dirty Read);
  • READ COMMITED:只對(duì)記錄加記錄鎖,而不會(huì)在記錄之間加間隙鎖,所以允許新的記錄插入到被鎖定記錄的附近,所以再多次使用查詢語句時(shí),可能得到不同的結(jié)果(Non-Repeatable Read);
  • REPEATABLE READ:多次讀取同一范圍的數(shù)據(jù)會(huì)返回***次查詢的快照,不會(huì)返回不同的數(shù)據(jù)行,但是可能發(fā)生幻讀(Phantom Read);
  • SERIALIZABLE:InnoDB 隱式地將全部的查詢語句加上共享鎖,解決了幻讀的問題;

MySQL 中默認(rèn)的事務(wù)隔離級(jí)別就是 REPEATABLE READ,但是它通過 Next-Key 鎖也能夠在某種程度上解決幻讀的問題。

 

接下來,我們將數(shù)據(jù)庫中創(chuàng)建如下的表并通過個(gè)例子來展示在不同的事務(wù)隔離級(jí)別之下,會(huì)發(fā)生什么樣的問題:

 

  1. CREATE TABLE test(  
  2.     id INT NOT NULL 
  3.     UNIQUE(id)  
  4. ); 

 

臟讀

當(dāng)事務(wù)的隔離級(jí)別為 READ UNCOMMITED 時(shí),我們?cè)?SESSION2 中插入的未提交數(shù)據(jù)在 SESSION1 中是可以訪問的。

 

不可重復(fù)讀

當(dāng)事務(wù)的隔離級(jí)別為 READ COMMITED 時(shí),雖然解決了臟讀的問題,但是如果在 SESSION1 先查詢了一個(gè)范圍的數(shù)據(jù),在這之后 SESSION2 中插入一條數(shù)據(jù)并且提交了修改,在這時(shí),如果 SESSION1 中再次使用相同的查詢語句,就會(huì)發(fā)現(xiàn)兩次查詢的結(jié)果不一樣。

 

不可重復(fù)讀的原因就是,在 READ COMMITED 的隔離級(jí)別下,存儲(chǔ)引擎不會(huì)在查詢記錄時(shí)添加間隙鎖,鎖定 id<5 這個(gè)范圍。

幻讀

重新開啟了兩個(gè)會(huì)話 SESSION1 和 SESSION2,在 SESSION1中我們查詢?nèi)淼男畔ⅲ瑳]有得到任何記錄;在 SESSION2中向表中插入一條數(shù)據(jù)并提交;由于 REPEATABLE READ 的原因,再次查詢?nèi)淼臄?shù)據(jù)時(shí),我們獲得到的仍然是空集,但是在向表中插入同樣的數(shù)據(jù)卻出現(xiàn)了錯(cuò)誤。

 

這種現(xiàn)象在數(shù)據(jù)庫中就被稱作幻讀,雖然我們使用查詢語句得到了一個(gè)空的集合,但是插入數(shù)據(jù)時(shí)卻得到了錯(cuò)誤,好像之前的查詢是幻覺一樣。

在標(biāo)準(zhǔn)的事務(wù)隔離級(jí)別中,幻讀是由更高的隔離級(jí)別 SERIALIZABLE 解決的,但是它也可以通過 MySQL 提供的 Next-Key 鎖解決:

 

REPERATABLE READ 和 READ UNCOMMITED 其實(shí)是矛盾的,如果保證了前者就看不到已經(jīng)提交的事務(wù),如果保證了后者,就會(huì)導(dǎo)致兩次查詢的結(jié)果不同,MySQL 為我們提供了一種折中的方式,能夠在 REPERATABLE READ 模式下加鎖訪問已經(jīng)提交的數(shù)據(jù),其本身并不能解決幻讀的問題,而是通過文章前面提到的 Next-Key 鎖來解決。

原文鏈接:http://draveness.me/mysql-innodb.html 

責(zé)任編輯:龐桂玉 來源: ITPUB
相關(guān)推薦

2021-05-09 09:06:24

Python批處理命令

2015-10-22 14:32:44

微服務(wù)PaaS應(yīng)用開發(fā)

2022-02-21 09:44:45

Git開源分布式

2021-06-30 00:20:12

Hangfire.NET平臺(tái)

2023-05-12 08:19:12

Netty程序框架

2019-09-24 14:19:12

PythonC語言文章

2021-05-15 09:18:04

Python進(jìn)程

2021-02-02 18:39:05

JavaScript

2020-11-10 10:48:10

JavaScript屬性對(duì)象

2021-01-29 18:41:16

JavaScript函數(shù)語法

2020-02-28 11:29:00

ElasticSear概念類比

2021-06-04 09:56:01

JavaScript 前端switch

2022-12-14 08:03:27

CSS變量前端

2021-07-01 10:01:16

JavaLinkedList集合

2023-05-08 08:21:15

JavaNIO編程

2021-03-05 18:04:15

JavaScript循環(huán)代碼

2021-09-27 09:18:30

ListIterato接口方法

2021-03-09 14:04:01

JavaScriptCookie數(shù)據(jù)

2023-09-06 14:57:46

JavaScript編程語言

2021-05-18 08:30:42

JavaScript 前端JavaScript時(shí)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

久久亚洲图片| 久久精品人人爽人人爽| 亚洲欧美亚洲| 一道本一区二区三区| 秋霞影院一区| 色呦呦在线观看视频| av福利在线播放| 成人影院免费观看| 在线视频尤物| 中出在线观看| 未来日记在线观看| 亚洲伦理在线| 黄色片在线免费看| 国产三级视频在线看| 国产高清视频在线观看| 成人免费黄色网页| 成人精品福利| 免费在线观看av| 黄色精品在线观看| 欧美黄色激情| 四季久久免费一区二区三区四区| 超碰在线caoporen| 视频在线这里都是精品| 超碰97国产精品人人cao| heyzo高清在线| 亚洲性色av| 日韩经典一区| 婷婷精品久久久久久久久久不卡| **日韩最新| 一区中文字幕| 精品无人区一区二区| 亚洲制服一区| 成人影院在线| 亚洲欧美亚洲| 另类激情亚洲| 久久激情综合网| 国产91精品露脸国语对白| 成人精品鲁一区一区二区| 99国产精品国产精品毛片| 国产午夜亚洲精品不卡| 中文字幕在线观看一区| 亚洲欧美一区二区三区国产精品| 亚洲一区在线看| 日本精品免费观看高清观看| 欧美另类z0zxhd电影| 亚洲第一区中文字幕| 亚洲级视频在线观看免费1级| 国产亚洲精品久久久久久777| 欧美成人午夜影院| 日韩美女视频中文字幕| 91牛牛免费视频| 蜜桃精品久久久久久久免费影院 | 日韩精品a在线观看91| 台湾色综合娱乐中文网| 天天综合久久| 另类激情亚洲| 成人免费看视频| 亚洲欧美日韩在线| 日本二三区不卡| 亚洲国产日韩欧美在线动漫| 日韩视频精品在线| 日本最新高清不卡中文字幕| 成人动漫在线观看视频| 亚洲人久久久| 网站一区二区三区| 污视频免费在线看| 黄视频在线免费看| 免费一级欧美在线大片| 国产大片一区| 美女久久网站| 久久久久久久久久久99999| 欧美日韩国产麻豆| 亚洲经典中文字幕| 9.1国产丝袜在线观看| 国产日韩欧美一区二区| 国产传媒久久久| 日本激情免费| 久草在线视频福利| 国产精品一区二区三区美女| 亚洲第一网站| 丁香啪啪综合成人亚洲小说| 亚洲午夜精品在线| 亚洲精品91美女久久久久久久| 精品国产依人香蕉在线精品| 国产日韩在线观看av| 中文字幕免费高| 国产在线黄色片| 欧美1—12sexvideos| 国产成人精品亚洲线观看| 婷婷精品进入| 国产精品白丝jk白祙喷水网站| 亚洲乱码国产乱码精品精98午夜| 欧美一区二区三区的| 欧美丰满老妇厨房牲生活 | 99在线精品免费视频九九视| 成人午夜av电影| 婷婷夜色潮精品综合在线| 日韩美女视频在线| 欧美精品videos性欧美| 鲁片一区二区三区| 亚洲另类第一页| 在线免费观看污| 特黄特色欧美大片| 久久国产综合精品| 亚洲精品国产精华液| 亚洲电影av在线| 国产精品激情av电影在线观看| 宅男av一区二区三区| 有色激情视频免费在线| 伊人久久高清| 欧美午夜影院| 久久精品欧美日韩精品| 欧美一卡二卡在线观看| 国产91在线播放九色快色| 天天想你在线观看完整版电影免费| 最近2018年手机中文在线| 91av一区| 一区二区三区精品视频在线观看| 国产精品欧美综合在线| 亚洲国产精品悠悠久久琪琪| 国产玖玖精品视频| 久久久久久久9| 在线观看av黄网站永久| 欧美交a欧美精品喷水| 国产一区视频网站| 欧美亚洲高清一区二区三区不卡| 久久久亚洲影院| 艳母动漫在线免费观看| h网站视频在线观看| 日韩极品在线| 成人av网址在线| 日韩欧美的一区| 亚洲一区二区三区成人在线视频精品| 欧美精品成人网| 一二三四视频在线中文| 国模一区二区三区| 伊人婷婷欧美激情| 不卡毛片在线看| 在线视频亚洲自拍| 91福利在线视频| 日韩欧美视频在线播放| 国产日本一区二区| 尤物yw午夜国产精品视频| 欧美日韩电影一区二区| 亚洲人性生活视频| 美国一区二区| 成人av高清在线| 亚洲第一级黄色片| 精品麻豆av| 男人天堂资源在线| 国产成人影院| 国产日韩欧美亚洲| 中文字幕国产精品| 亚洲一区二区不卡视频| 2021av在线| 91成人免费| 亚洲国产中文字幕在线视频综合 | 免费看黄裸体一级大秀欧美| 亚洲国产日产av| 97精品欧美一区二区三区| 国产无限制自拍| 韩日毛片在线观看| 久久精品免费| 欧美视频一区二| 亚洲淫片在线视频| 婷婷丁香六月天| 亚洲va久久久噜噜噜久久| 久久久久久久国产精品影院| 亚洲一级黄色片| 青少年xxxxx性开放hg| 亚洲羞羞网站| 一区二区三区四区五区在线 | 日韩欧美精品免费在线| 国产精品成人免费电影| 97在线资源在| 国产女人18毛片水真多18精品| 久久色在线观看| 日韩视频在线免费| 激情五月六月婷婷| 中国色在线日|韩| 久久精品国产一区二区三 | 欧美大片免费看| 大肉大捧一进一出好爽视频| 日本.亚洲电影| 国产精品18久久久久久久久久久久 | 亚洲情趣在线观看| 久久免费精品日本久久中文字幕| 国产99久久九九精品无码| 成人在线不卡| a美女胸又www黄视频久久| 中文字幕精品久久久久| 妺妺窝人体色777777| 精品欧美一区二区三区在线观看 | 久久在线观看免费| 日韩色av导航| 欧美a在线视频| 麻豆国产精品| 亚洲国产激情av| 91精品国产91久久久| 成人午夜精品在线|