MySQL基礎(十二):鎖機制

一、鎖的概述

1、什麼是鎖?

鎖是計算機協調多個進程或線程併發訪問某一資源的機制。 

2、鎖的分類

1. 從對數據操作的類型(讀\寫)分:
  讀鎖(共享鎖):針對同一份數據,多個讀操作可以同時進行而不會互相影響。但是誰都不能寫!
  寫鎖(排它鎖):持有寫鎖的線程,讀寫權限都有,而且它會阻斷其他進程對該資源的讀和寫。
'總結:讀鎖(誰都能讀,誰都不能寫)  寫鎖(對該資源獨佔)'

2. 從對數據操作的粒度分:
  表鎖
  行鎖

二、MySQL中的三種鎖

1、表鎖(偏讀)

Myisam一般採用表鎖,它不支持行鎖!

(1)表鎖的特點

偏向MyISAM存儲引擎,開銷小,加鎖快;無死鎖;鎖定粒度大,發生鎖衝突的概率最高,併發度最低

(2)表加讀鎖、寫鎖的語法
給表加讀鎖:
lock table mylock read;
給表加寫鎖:
lock table mylock write;
釋放鎖:
unlock table; # 釋放所有表的所有鎖!mysql好像不支持釋放指定的鎖

注:曉得怎麼釋放指定表的讀鎖、寫鎖的小夥伴,請在評論區寫下語法
mysql若不支持,如果知道爲什麼不支持的,也希望大家說下爲什麼!謝謝各位小夥伴的分享

接下來分析讀鎖的特點!
我們先看,我開了兩個session會話:
在這裏插入圖片描述

(3)讀鎖特性實驗

讀鎖肯定都能讀的,主要驗證寫!

  • 對自己的影響:

在這裏插入圖片描述

  • 對其他人的影響:

在這裏插入圖片描述對於其他人是阻塞狀態!當我們釋放讀鎖,這邊將會成功update!

(4)寫鎖特性實驗
  • 對自己的影響(讀):

在這裏插入圖片描述

  • 對自己的影響(寫):

在這裏插入圖片描述

  • 對他人的影響(讀):

在這裏插入圖片描述阻塞的!

  • 對他人的影響(寫):

在這裏插入圖片描述阻塞的!

(3)結論

MyISAM在執行查詢語句(SELECT)前,會自動給涉及的所有表加讀鎖,在執行增刪改操作前,會自動給涉及的表加寫鎖。
MySQL的表級鎖有兩種模式:

表共享讀鎖(Table Read Lock) 
表獨佔寫鎖(Table Write Lock
鎖類型 自己可讀 自己可寫 他人可讀 他人可寫
讀鎖
寫鎖

結論, 結合上表,所以對MyISAM表進行操作,會有以下情況:

  1. 對MyISAM表的讀操作(加讀鎖),不會阻塞其他進程對同一表的讀請求,但會阻塞對同一表的寫請求。只有當讀鎖釋放後,纔會執行其它進程的寫操作。  
  2. 對MyISAM表的寫操作(加寫鎖),會阻塞其他進程對同一表的讀和寫操作,只有當寫鎖釋放後,纔會執行其它進程的讀寫操作。 

簡而言之,就是讀鎖會阻塞所有寫,但是不會堵塞讀。而寫鎖則會把讀和寫都堵塞,自己獨佔資源

2、行鎖(偏寫)

innodb支持表鎖,與更細粒度的行鎖!innodb一般使用行鎖。

(1) 由於行鎖支持事務,複習老知識
1. 事務(Transaction)及其ACID屬性
	(1) 事務是由一組SQL語句組成的邏輯處理單元,事務具有以下4個屬性,通常簡稱爲事務的ACID屬性。  
	(2) 原子性(Atomicity) :事務是一個原子操作單元,其對數據的修改,要麼全都執行,要麼全都不執行。  
	(3) 一致性(Consistent) :在事務開始和完成時,數據都必須保持一致狀態。這意味着所有相關的數據規則都必須應用於事務的修改,以保持數據的完整性;事務結束時,所有的內部數據結構(如B樹索引或雙向鏈表)也都必須是正確的。  
	(4) 隔離性(Isolation) :數據庫系統提供一定的隔離機制,保證事務在不受外部併發操作影響的“獨立”環境執行。這意味着事務處理過程中的中間狀態對外部是不可見的,反之亦然。  
	(5) 持久性(Durable) :事務完成之後,它對於數據的修改是永久性的,即使出現系統故障也能夠保持。  
2. 併發事務處理帶來的問題
	(1) 更新丟失(Lost Update)
	(2) 髒讀(Dirty Reads)
	(3) 不可重複讀(Non-Repeatable Reads)
	(4) 幻讀(Phantom Reads)
3. 事務隔離級別
	(1) 髒讀”、“不可重複讀”和“幻讀”,其實都是數據庫讀一致性問題,必須由數據庫提供一定的事務隔離機制來解決。
	(2) '事務隔離級別:讀未提交、讀已提交、可重複讀、可序列化'
	(3) 常看當前數據庫的事務隔離級別:show variables like 'tx_isolation'; 
(2)建表sql
# 創建表
create table test_innodb_lock (a int(11),b varchar(16)) engine=innodb;

# 插入數據
insert into test_innodb_lock values(1,'b2'); 
insert into test_innodb_lock values(3,'3'); 
insert into test_innodb_lock values(4,'4000'); 
insert into test_innodb_lock values(5,'5000'); 
insert into test_innodb_lock values(6,'6000'); 
insert into test_innodb_lock values(7,'7000'); 
insert into test_innodb_lock values(8,'8000'); 
insert into test_innodb_lock values(9,'9000'); 
insert into test_innodb_lock values(1,'b1'); 

# 創建索引
create index test_innodb_a_ind on test_innodb_lock(a); 
create index test_innodb_lock_b_ind on test_innodb_lock(b);

# 查看索引、查看test_innodb_lock表
show index from test_innodb_lock;
select * from test_innodb_lock;

在這裏插入圖片描述在這裏插入圖片描述

(3)行鎖加讀鎖、寫鎖語法

對一行或者說一個記錄進行update、delete或者說insert時,會自動爲行加寫鎖!select不會加鎖,有需求需要自己加鎖!

# select加讀鎖
select * from test_innodb_lock lock in share mode

# select加寫鎖
select * from test_innodb_lock for update # 相當於就是給查詢語句披了一個update語句的外衣
(4)讀鎖特性實驗

和表鎖的讀鎖一樣,這個記錄只能讀,誰都不能對這個記錄進行操作!

(5)寫鎖特性實驗

要想看到效果需要關閉autocommit,自動提交會自動釋放寫鎖!
在這裏插入圖片描述兩個會話都需要做!現在我們就需要手動commit才能釋放對行寫鎖!


現在我們執行update,實現行鎖加寫鎖
在這裏插入圖片描述加鎖會話,數據已變更,另外一個會話沒變更,因爲加鎖會話的數據修改沒有提交嘛!現在只是在內存中修改了!現在a爲4的記錄應該處於加了寫鎖的狀態,我們接下來看看其特性:

# 由上圖可知,行加寫鎖,都可以讀
由於加鎖會話,讀的是內存中已修改的數據,另一會話內存中沒有,讀的是硬盤中未修改數據,因此讀的數據不一致

'注意:這裏這個不是髒讀,讀取的是磁盤未修改的數據,這本來就是乾淨的數據!'

我們接下來提交修改:
在這裏插入圖片描述爲什麼還不變?因爲innodb默認隔離級別爲可重複讀,因此它會重複讀取上次的結果!我們上次讀了次磁盤中未修改的數據嘛,這麼說,我新開一個會話執行該命令,我是不是就能立馬看到變化後的值,!當然不新開會話,這個會話,你commit提交一下,也能馬上看到效果

首先行的寫鎖,加鎖會話肯定寫和讀都可以,上面我們知道了其他會話也可以讀!,其他會話的寫是什麼情況呢?

在這裏插入圖片描述阻塞!

(6)無論哪種鎖的,一個共性

無論你是行鎖(讀或寫鎖)還是表鎖(讀或寫鎖),加鎖的那個線程必須釋放當前的鎖,才能去找其他資源,不能喫到碗裏,看着鍋裏的!

(7)無索引,行鎖升級爲表鎖

一個查詢語句如果沒有采用索引,那麼它的type一定是all,全表掃描,因此這時行鎖會升級成表鎖!因此索引一定要好好優化,慎重創建!

  • 先看下,不同行是否可以同時操作:
    在這裏插入圖片描述可以!

  • 刪除索引:
    在這裏插入圖片描述

  • 再次查看不同行是否可以操作:
    在這裏插入圖片描述不行了!因爲升級成了表鎖!

注意:即是你有索引,如果某種原因造成索引失效了,依然會鎖定整張表!
比如索引字段id爲varchar類型,你卻where a = 1,那麼也會造成索引失效!因此sql語句的優化、使用也要慎重!


注意:這裏我在網上搜了好久,都沒找到如何查看當前庫下的行鎖或表鎖數量,也就是我無法直觀得知到底這裏是行鎖還是表鎖!如果有知道的小夥伴請評論區分享!感謝分享!

# 這裏是搜的時候的一些命令
show status like 'innodb_row_lock%'; # 查看行鎖信息
show status like 'Table_locks%'; # 查看錶鎖信息
(8)間隙鎖的危害
  • 間隙鎖帶來的插入問題
    在這裏插入圖片描述

  • 什麼叫間隙鎖?
    當我們用範圍條件而不是相等條件檢索數據,並請求共享或排他鎖時,InnoDB會給符合條件的已有數據記錄的索引項加鎖;對於鍵值在條件範圍內但並不存在的記錄,叫做“間隙(GAP)”,
    InnoDB也會對這個“間隙”加鎖,這種鎖機制就是所謂的間隙鎖(GAP Lock)。

  • 間隙鎖帶來的危害?
    因爲Query執行過程中通過過範圍查找的話,他會鎖定整個範圍內所有的索引鍵值,即使這個鍵值並不存在。
    間隙鎖有一個比較致命的弱點,就是當鎖定一個範圍鍵值之後,即使某些不存在的鍵值也會被無辜的鎖定,而造成在鎖定的時候無法插入鎖定鍵值範圍內的任何數據。在某些場景下這可能會對性能造成很大的危害

(9)總結

Innodb存儲引擎由於實現了行級鎖定,雖然在鎖定機制的實現方面所帶來的性能損耗可能比表級鎖定會要更高一些,但是在整體併發處理能力方面要遠遠優於MyISAM的表級鎖定的。當系統併發量較高的時候,Innodb的整體性能和MyISAM相比就會有比較明顯的優勢了。

但是,Innodb的行級鎖定同樣也有其脆弱的一面,當我們使用不當的時候,可能會讓Innodb的整體性能表現不僅不能比MyISAM高,甚至可能會更差

(10)優化建議
1. 儘可能讓所有數據檢索都通過索引來完成,避免無索引行鎖升級爲表鎖。
2. 儘可能檢索範圍減小,避免或減少間隙鎖
3. 儘量控制事務大小,減少鎖定資源量和時間長度
4. 鎖住某行後,儘量不要去調別的行或表,趕緊處理被鎖住的行然後釋放掉鎖。
5. 涉及相同表的事務,對於調用表的順序儘量保持一致。
6. 在業務環境允許的情況下,儘可能低級別事務隔離

3、頁鎖(瞭解)

開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表鎖和行鎖之間,併發度一般。

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