1.1 MySQL鎖的由來
客戶端發往 MySQL 的一條條 SQL 語句,實際上都可以理解成一個個單獨的事務(一條sql語句默認就是一個事務)。而事務是基于數據庫連接的,每個數據庫連接在 MySQL 中,又會用一條工作線程來維護,也意味著一個事務的執行,本質上就是一條工作線程在執行,當出現多個事務同時執行時,這種情況則被稱之為并發事務,所謂的并發事務也就是指多條線程并發執行。
多線程并發執行自然就會出問題,也就是事務中的臟寫、臟讀、不可重復讀及幻讀問題。而對于這些問題又可以通過調整事務的隔離級別來避免,那為什么調整事務的隔離級別后能避免這些問題產生呢?這是因為不同的隔離級別中,工作線程執行SQL語句時,用的鎖粒度、類型不同。
1.2 鎖定義
由以上可知,數據庫的鎖機制本身是為了解決并發事務帶來的問題而誕生的,主要是確保數據庫中,多條工作線程并行執行時的數據安全性。
鎖是計算機協調多個進程或線程并發訪問某一資源的機制。在數據庫中,除傳統的計算資源(CPU、RAM、I/O)的爭用以外,數據也是一種供許多用戶共享的資源。如何保證數據并發訪問的一致性、有效性是所有數據庫必須解決的一個問題,鎖沖突也是影響數據庫并發訪問性能的一個重要因素。從這個角度來說,鎖對數據庫而言顯得尤其重要,也更加復雜。
1.3 鎖分類
MySQL的鎖機制與索引機制類似,都是由存儲引擎負責實現的,這也就意味著不同的存儲引擎,支持的鎖也并不同,這里是指不同的引擎實現的鎖粒度不同。但除開從鎖粒度來劃分鎖之外,其實鎖也可以從其他的維度來劃分,因此也會造出很多關于鎖的名詞,下面先簡單梳理一下MySQL的鎖體系:
-
以鎖粒度的維度劃分
-
全局鎖:鎖定數據庫中的所有表。加上全局鎖之后,整個數據庫只能允許讀,不允許做任何寫操作
-
表級鎖:每次操作鎖住整張表。主要分為三類
- 表鎖(分為表共享讀鎖 read lock、表獨占寫鎖 write lock)
- 元數據鎖(meta data lock,MDL):基于表的元數據加鎖,加鎖后整張表不允許其他事務操作。這里的元數據可以簡單理解為一張表的表結構
- 意向鎖(分為意向共享鎖、意向排他鎖):這個是InnoDB中為了支持多粒度的鎖,為了兼容行鎖、表鎖而設計的,使得表鎖不用檢查每行數據是否加鎖,使用意向鎖來減少表鎖的檢查
-
行級鎖:每次操作鎖住對應的行數據。主要分為三類
- 記錄鎖 / Record 鎖:也就是行鎖,一條記錄和一行數據是同一個意思。防止其他事務對此行進行update和delete,在 RC、RR隔離級別下都支持
- 間隙鎖 / Gap 鎖:鎖定索引記錄間隙(不含該記錄),確保索引記錄間隙不變,防止其他事務在這個間隙進行insert,產生幻讀。在RR隔離級別下都支持
- 臨鍵鎖 / Next-Key 鎖:間隙鎖的升級版,同時具備記錄鎖+間隙鎖的功能,在RR隔離級別下支持
-
-
以互斥性的角度劃分
- 共享鎖 / S鎖:不同事務之間不會相互排斥、可以同時獲取的鎖
- 排他鎖 / X鎖:不同事務之間會相互排斥、同時只能允許一個事務獲取的鎖
- 共享排他鎖 / SX鎖:MySQL5.7版本中新引入的鎖,主要是解決SMO帶來的問題
-
以操作類型的維度劃分
- 讀鎖:查詢數據時使用的鎖
- 寫鎖:執行插入、刪除、修改、DDL語句時使用的鎖
-
以加鎖方式的維度劃分
- 顯示鎖:編寫SQL語句時,手動指定加鎖的粒度
- 隱式鎖:執行SQL語句時,根據隔離級別自動為SQL操作加鎖
-
以思想的維度劃分
- 樂觀鎖:每次執行前認為自己會成功,因此先嘗試執行,失敗時再獲取鎖
- 悲觀鎖:每次執行前都認為自己無法成功,因此會先獲取鎖,然后再執行
放眼望下來,是不是看著還蠻多的,但總歸說來說去其實就共享鎖、排他鎖兩種,只是加的方式不同、加的地方不同,因此就演化出了這么多鎖的稱呼。