面試必備常見存儲引擎與鎖的分類,請查收

我們在上篇文章中提到了記錄鎖(行鎖)、間隙鎖和臨鍵鎖,後臺有小夥伴催我更新一下其他的鎖。拖延症又犯了,趁週末,今天我們來總結一下MyISAMInnoDB引擎下鎖的種類及使用方法。

MySQL的四大常見存儲引擎

談到MyISAMInnoDB了我們先來了解一下什麼是存儲引擎吧。MySQL中的數據用各種不同的技術存儲在文件(或者內存)中,這些技術中的每一種技術都使用不同的存儲機制、索引技巧、鎖定水平並且最終提供廣泛的不同的功能,我們把這些不同的技術以及配套的相關功能稱爲存儲引擎(也稱作表類型)。MySQL默認配置了許多不同的存儲引擎,我們可以選擇不同的存儲引擎來滿足我們對數據的處理(存儲、檢索等)需求,以改善我們應用程序的整體功能。正因爲MySQL存儲引擎的多樣性,使得MySQL深受廣大開發者的垂青。

我們的前提條件:我用的是5.7.24-log版本,可以在Navicat中通過SELECT VERSION();命令查看。那麼MySQL都有哪些存儲引擎呢?我們可以使用sql命令SHOW ENGINES;來查看,結果如下:

  • Engine:表示儲存引擎名稱;
  • Support:表示MySQL是否支持該存儲引擎,DEFAULT爲默認的存儲引擎;
  • Comment:是對該存儲引擎的功能描述,例如:InnoDB支持事務、行級鎖定和外鍵;
  • Transactions:是否支持事務;
  • XA:存儲引擎是否支持分佈式事務;
  • Savepoints:存儲引擎是否支持保存點。

接着讓我們來說一下其中比較常見的四大存儲引擎吧。

InnoDB

InnoDBMySQLMySQL5.5以後)的默認存儲引擎,支持事務、行級鎖和外鍵,被用來處理大量短期事務。如果使用到外鍵、需求併發程度較高、數據一致性要求較高的話,那麼通常選擇InnoDB引擎,這也是互聯網大廠使用InnoDB存儲引擎的原因。除非有非常特別的原因需要使用其他的存儲引擎,否則建議優先考慮InnoDB。但是對比MyISAMInnoDB寫的處理效率會差一些,並且會佔用更多的磁盤空間以保留數據和索引。

MyISAM

MyISAM提供了大量的特性,包含全文索引、壓縮、空間行數等,支持3種不同的存儲格式,分別是:靜態表、動態表、壓縮表。

  • 靜態表:表中的字段都是非變長字段,這樣每個記錄都是固定長度的。優點是存儲非常迅速,容易緩存,出現故障容易恢復;缺點是佔用的空間通常比動態表多(因爲存儲時會按照列的寬度定義補足空格),在取數據的時候,默認會把字段後面的空格去掉,如果不注意會把數據本身帶的空格也會忽略。
  • 動態表:表中的字段都是變長字段,記錄不是固定長度的。這樣存儲的優點是佔用的空間相對較少;缺點是頻繁的更新、刪除數據容易產生碎片,需要定期執行OPTIMIZE TABLE或者myisamchk -r命令來改善性能,並且出現故障的時候恢復相對比較困難。
  • 壓縮表:壓縮表由myisamchk工具創建,佔據非常小的空間,因爲每條記錄都是被單獨壓縮的,所以只有非常小的訪問開支。

MyISAM中,數據文件和索引文件可以放置在不同的目錄(在創建表的時候通過DATA DIRECTORYINDEX DIRECTORY語句指定文件的絕對路徑),平均分配IO,獲取更快的訪問速度。但是MyISAM不支持事務,不支持外鍵,也不支持行級鎖,支持表級鎖,有個缺陷就是崩潰後無法恢復。如果應用程序以檢索爲主,只有少量的插入、更新和刪除操作,並且對事物的完整性、併發程度不是很高的話,通常建議選擇MyISAM存儲引擎。

Memory

Memory存儲引擎使用存在內存中的內容來創建表,所以它的訪問速度非常快,並且默認使用HASH索引。但是一旦服務器關閉或者mysqld守護進程崩潰時,所有的Memory數據都會丟失,但表還會繼續存在,獲得速度的同時也帶來了一些缺陷。

它要求存儲在Memory數據表裏的數據使用的是長度不變的格式,這意味着不能使用BLOBTEXT這樣的長度可變的數據類型,VARCHAR是一種長度可變的類型,但因爲它在MySQL內部當做長度固定不變的CHAR類型,所以可以使用。

服務器需要足夠的內存來維持在同一時間內使用的MEMORY表,當不再使用MEMORY表時,要釋放MEMORY表所佔用的內存,應該執行DELETE FROMtruncate table或者刪除整個表。每個MEMORY表中放置的數據量的大小,受到max_heap_table_size系統變量的約束,這個系統變量的初始值是16M,同時在創建MEMORY表時可以使用MAX_ROWS子句來指定表中的最大行數。它通常用於更新不太頻繁的小表。

Merge

Merge存儲引擎是一組MyISAM表的組合,這些MyISAM表結構必須完全相同,Merge表本身沒有數據,對Merge類型的表進行查詢、更新、刪除的操作,實際上是對內部的MyISAM表進行的。Merge表在磁盤上保留兩個文件,一個是.frm文件存儲表定義、一個是.MRG文件存儲Merge表的組成等。MERGE表的優點在於可以突破對單個MyISAM表大小的限制,並且通過將不同的表分佈在多個磁盤上,可以有效地改善MERGE表的訪問效率。

我們可以通過show create table 表名 命令來查看錶使用的引擎,由以下代碼可以看出test表使用的是MyISAM存儲引擎。

<pre class="custom" data-tool="mdnice編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">CREATE TABLEtest(idint(1) NOT NULL AUTO_INCREMENT,namevarchar(8) DEFAULT NULL,ageint(11) DEFAULT NULL, PRIMARY KEY (id) ) ENGINE=MyISAM AUTO_INCREMENT=46 DEFAULT CHARSET=utf8 </pre>



相對其他數據庫而言,MySQL的鎖機制比較簡單,其最顯著的特點是不同的存儲引擎支持不同的鎖機制。比如,MyISAMMEMORY存儲引擎採用的是表級鎖(table-level locking);BDB存儲引擎(5.1之後就不直接支持了,因爲BDBoracle收購了)採用的是頁面鎖(page-level locking),但也支持表級鎖;InnoDB存儲引擎既支持行級鎖(row-level locking),也支持表級鎖,但默認情況下是採用行級鎖。接下來就讓我們來了解一下MyISAMInnoDB鎖的具體分類與使用方法。

MyISAM鎖

MyISAM存儲引擎支持的表級鎖分爲表共享讀鎖(Table Read Lock)和表獨佔寫鎖(Table Write Lock),以下簡稱讀鎖和寫鎖。先看一下他們的特性:

  • 讀鎖:不會阻礙其它進程的讀,但是會阻礙寫,只有當讀鎖釋放之後,纔會執行其它進程的寫--讀鎖阻塞寫鎖,但是不阻塞讀鎖;
  • 寫鎖:會阻礙其他進程的讀和寫,只有當寫鎖釋放,纔會執行其它寫操作--寫鎖阻塞讀鎖和寫鎖;

接下來讓我們用例子來演示一下上邊的結論,在演示之前,先讓我們來說幾個命令吧。

  • LOCK TABLE 表名 WRITE/READ:給表加寫鎖或者讀鎖;
  • UNLOCK TABLES:給表解鎖

演示一:表共享讀鎖

session1給表test加讀鎖時,session1只能讀取當前表的數據,不可以讀其他表,也不可以修改test和其他表; session2可以讀取test表數據,更新test表阻塞,但是可以修改和查詢其他表數據。

演示二:表獨佔寫鎖

session1給表test加寫鎖時,可以更新test表,讀test表阻塞,但是不可以修改和查詢其他表數據; session2查詢和更新test表阻塞,但是可以查詢和更新其他表。

另外我們還可以使用show open tables命令來查看在表緩存中當前被打開的非TEMPORARY表的鎖使用情況,其中In_use表示有鎖正在使用。

也可以使用show status like 'table%'命令來查看鎖的爭奪情況,其中Table_locks_waited爲等待次數,每等待一次,值就加一,值越大,表示存在越嚴重的表級鎖爭用;Table_locks_immediate爲產生表級鎖定的次數,表示可以立即獲取鎖的查詢次數,每立即獲取鎖,值加一。

MyISAM默認是使用select語句加讀鎖,增刪改操作加寫鎖。MyISAM是偏讀鎖,讀寫調度寫優先,不適合做寫爲主的表的引擎。因爲寫鎖後,其他線程不能做任何操作,大量更新會使查詢很難得到鎖,從而永遠阻塞。

InnoDB鎖

上篇文章中我們講過了記錄鎖(行鎖)、間隙鎖和臨鍵鎖,這裏就不再贅述了。接下來我們按照鎖的模式講一下InnoDB裏的共享鎖、排他鎖和意向鎖,其中共享鎖和排他鎖屬於行級鎖,行級鎖都是基於索引項的,如果沒有索引項,則添加的是表級鎖;意向鎖屬於表級鎖。

共享鎖:Shared Locks,簡稱S鎖

若事務T對數據對象A加上S鎖,則事務T只能讀A;其他事務只能再對A加S鎖,而不能加X鎖,直到T釋放A上的S鎖。這就保證了其他事務可以讀A,但在T釋放A上的S鎖之前不能對A做任何修改。可以通過select ... lock in share mode來加共享鎖,通過CommitRollback來釋放鎖。

排他鎖:Exclusive lock,簡稱X鎖,也叫互斥鎖

若事務T對數據對象A加上X鎖,則只允許T讀取和修改A,其它任何事務都不能再對A加任何類型的鎖(排他鎖不可與其他鎖共存),直到T釋放A上的鎖。它防止任何其它事務獲取資源上的鎖,直到在事務的末尾將資源上的原始鎖釋放爲止。可以通過select ... for update手動加鎖,也可以通過增刪改操作自動加鎖,通過CommitRollback來釋放鎖。

意向鎖:Intention Locks

說起意向鎖,大家先來考慮一下這個問題:假設存在兩個事務A和B對錶test進行操作,首先事務A對第十行數據加了一把讀鎖,鎖住了該行數據,讓這一行只能讀,不能寫;然後事務B想要對該表加一把表級的寫鎖,那麼事務B能否加鎖成功呢?思考兩秒鐘...答案當然是否定的,即事務B無法加鎖成功。如果我們假設它加鎖成功的話,那麼理論上它就能修改表中的任意一行,這將與事務A持有的行級鎖(讀鎖)產生衝突。而數據庫想避免這種衝突的話,就需要將事務B的加鎖申請給阻塞住,直到事務A的行鎖被釋放。那麼問題來了,數據庫是怎麼判斷這種衝突的呢?我們可以想到兩種方案:一、判斷表是否已被其他事務用表鎖鎖表;二、判斷表中的每一行是否已被行鎖鎖住。很顯然,如果採用第二種方法的話,需要一行一行去遍歷整張表,效率太慢進而造成系統消耗,所以我們選擇第一種方法,這也就是意向鎖是表鎖的原因。

意向鎖是放置在資源層次結構的一個級別上的鎖,以保護較低級別資源上的共享鎖或排它鎖,意向鎖無法手動創建。如果對任一結點加鎖時,必須先對它的上層結點加意向鎖也就是如果對一個結點加意向鎖,則說明該結點的下層結點正在被加鎖。意向鎖的執行流程:如果另一個任務試圖在該表級別上應用共享或排它鎖,則受到由第一個任務控制的表級別意向鎖的阻塞,第二個任務在鎖定該表前不必檢查各個頁或行鎖,而只需檢查表上的意向鎖,所以意向鎖不是用來給數據加鎖的,而是用來判斷數據有沒有存在鎖的標誌。下面介紹兩種常用的意向鎖:意向共享鎖(Intent Share Lock,簡稱IS鎖)、意向排它鎖(Intent Exclusive Lock,簡稱IX鎖)。

  • 意向共享鎖(IS):事務打算給數據行加行共享鎖,事務在給一個數據行加共享鎖前必須先取得該表的IS鎖。
  • 意向排他鎖(IX):事務打算給數據行加行排他鎖,事務在給一個數據行加排他鎖前必須先取得該表的IX鎖。

意向鎖的兼容關係如下:

其他鎖簡介(瞭解)

InnoDB還包含插入意向鎖、自增鎖和空間鎖。

  • 插入意向鎖(Insert Intention Locks):是間隙鎖的一種,它的目的是爲了提高插入性能。在多個事務對同一個索引中的同一個範圍區間插入記錄時,如果插入的位置不衝突,不會阻塞彼此,主要是不需要去申請排他鎖。
  • 自增鎖(AUTO-INC Locks):自增鎖是MySQL中一種特殊的鎖,如果表中存在自增字段,MySQL便會自動維護一個自增鎖。和自增鎖相關的一個參數爲(5.1.22版本之後加入)innodb_autoinc_lock_mode,可以設定3個值,0:traditonal(每次都會產生表鎖,可以控制插入順序,效率低);1:consecutive(會產生一個輕量鎖,simple insert會獲得批量的鎖,保證連續插入,默認);2:interleaved(不會鎖表,來一個處理一個,併發最高,會存在複製問題)。

總結

MySQL這三種鎖的特性可大致歸納如下:

  • 表級鎖(偏讀):開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖衝突的概率最高,併發度最低。
  • 行級鎖(偏寫):開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的概率最低,併發度也最高。
  • 頁面鎖:開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表鎖和行鎖之間,併發度一般。

以上就是今天的全部內容了,如果你感興趣的話,可以關注gzh“阿Q說代碼”!你也可以後臺留言領取java乾貨資料:學習筆記與大廠面試題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章