当前位置:网站首页> MySQL進階系列:鎖-InnoDB中鎖的情况

MySQL進階系列:鎖-InnoDB中鎖的情况

2022-06-24 16:01:00 紀先生

持續創作,加速成長!這是我參與「掘金日新計劃 · 6 月更文挑戰」的第6天,點擊查看活動詳情

MySQL鎖-InnoDB中鎖的情况

mysql> select @@version;
+-----------+
| @@version |
+-----------+
| 5.7.21    |
+-----------+
1 row in set (0.01 sec)

一,鎖的基本介紹

相對其他數據庫而言,MySQL的鎖機制比較簡單,其最顯著的特點是不同的存儲引擎支持不同的鎖機制。比如,MyISAM和MEMORY存儲引擎采用的是錶級鎖(table-level locking);InnoDB存儲引擎既支持行級鎖(row-level locking),也支持錶級鎖,但默認情况下是采用行級鎖。

錶級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖沖突的概率最高,並發度最低。

行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖沖突的概率最低,並發度也最高。

行級鎖類型:

Record Lock(記錄鎖):當個記錄的鎖(鎖住單條記錄)

記錄鎖只會鎖住索引的記錄,如果InnoDB存儲錶在建立的時候沒有任何索引,那麼這個鎖會使用隱式的主鍵來進行鎖定,如下圖

Gap Lock(間隙鎖):鎖定一個範圍,不包括記錄本身(只鎖數據前面的GAP)

如下圖為的鎖就是GAP鎖,就是不允許其他事務在索引列8之前的間隙插入新的記錄,也就是(3 , 8)這個區間。gap鎖 的作用僅僅是為了防止插入幻影記錄的而已

Next-Key Lock(臨鍵鎖):同時鎖住記錄和記錄前面的GAP,也就是Next-Key Lock = Record Lock + Gap Lock。

二,鎖的分類

共享鎖 Share Locks (簡稱S鎖,屬於行鎖)

排它鎖 Exclusive Locks (簡稱X鎖,屬於行鎖)

意向共享鎖 Intention Share Locks (簡稱IS鎖,屬於錶鎖)

意向排它鎖 Intention Exclusive Locks (簡稱IX鎖,屬於錶鎖)

自增鎖 AUTO-INC Locks(屬於錶鎖)

下面具體介紹下每種類型的鎖,我們先建一張innodb的錶,sql如下

create table tab_with_index(id int,name varchar(10)) engine=innodb;
alter table tab_with_index add index id(id);
insert into tab_with_index values(1,'1'),(2,'2'),(3,'3'),(4,'4');

共享鎖

共享鎖就是多個事務對於同一個數據可以共享一把鎖,都能訪問數據庫,但是只能讀不能修改;

事務A:

select * from tab_with_index lock in share mode;

事務B:

select * from tab_with_index where id =1; // 可以查詢數據

update tab_with_index set name = 'aa' where id = 1 ;

注意:這裏的修改語句會堵塞住,直到事務A提交之後才能操作成功。

排它鎖

排它鎖不能與其他鎖並存,如一個事務獲取了一個數據行的排它鎖,其他事務就不能在獲取該行的鎖,只有當前獲取排它鎖的事務可以對數據進行修改。(delete,update,create默認是排它鎖)

事務A:

select * from tab_with_index where id =1 for update;

事務B:

select * from tab_with_index where id =1; //可以獲取結果

select * from tab_with_index where id =1 for update; // 堵塞

select * from tab_with_index where id = 1 lock for share mode; // 堵塞

注意:事務B兩個sql都會堵塞住,也就是獲取不到共享鎖也獲取不到排它鎖,直到事務A提交之後才能操作成功。

意向共享鎖和意向排它鎖

意向共享鎖:錶示事務准備給數據行加入共享鎖,也就是說一個數據行在加共享鎖之前必須先獲取該錶的IS鎖。

意向排它鎖:錶示事務准備給數據行加入排它鎖,也就是說一個數據行加排它鎖之前必須先獲取該錶的IX鎖。

IS鎖和IX鎖是錶級鎖,他們的提出僅僅為了在之後加錶級別的S鎖和X鎖時可以快速判斷錶中的記錄是否被上鎖,以避免用遍曆的方式來查看錶中有沒有上鎖的記錄,也就是說其實IS鎖和IX鎖是兼容的,IX鎖和IX鎖是兼容的。 《MySQL是怎樣運行的》

自增鎖

針對自增列自增長的一個特殊的錶級別鎖。

show variables like 'innodb_autoinc_lock_mode'; 
-- 默認值1,代錶連續,事務未提交則ID永久丟失

三,InnoDB鎖

1、事務及其ACID屬性

事務是由一組SQL語句組成的邏輯處理單元,事務具有4屬性,通常稱為事務的ACID屬性。

原子性(Actomicity):事務是一個原子操作單元,其對數據的修改,要麼全都執行,要麼全都不執行。 一致性(Consistent):在事務開始和完成時,數據都必須保持一致狀態。 隔離性(Isolation):數據庫系統提供一定的隔離機制,保證事務在不受外部並發操作影響的“獨立”環境執行。 持久性(Durable):事務完成之後,它對於數據的修改是永久性的,即使出現系統故障也能够保持。

2、並發事務帶來的問題

相對於串行處理來說,並發事務處理能大大增加數據庫資源的利用率,提高數據庫系統的事務吞吐量,從而可以支持更多用戶的並發操作,但與此同時,會帶來一下問題:

髒讀: 一個事務正在對一條記錄做修改,在這個事務並提交前,這條記錄的數據就處於不一致狀態;這時,另一個事務也來讀取同一條記錄,如果不加控制,第二個事務讀取了這些“髒”的數據,並據此做進一步的處理,就會產生未提交的數據依賴關系。這種現象被形象地叫做“髒讀”

不可重複讀:一個事務在讀取某些數據已經發生了改變、或某些記錄已經被删除了!這種現象叫做“不可重複讀”。

幻讀: 一個事務按相同的查詢條件重新讀取以前檢索過的數據,卻發現其他事務插入了滿足其查詢條件的新數據,這種現象就稱為“幻讀”

上述出現的問題都是數據庫讀一致性的問題,可以通過事務的隔離機制來進行保證。

數據庫的事務隔離越嚴格,並發副作用就越小,但付出的代價也就越大,因為事務隔離本質上就是使事務在一定程度上串行化,需要根據具體的業務需求來决定使用哪種隔離級別

髒讀不可重複讀幻讀
read uncommitted
read committed
repeatable read
serializable

可以通過檢查InnoDB_row_lock狀態變量來分析系統上的行鎖的爭奪情况:

mysql> show status like 'innodb_row_lock%';
+-------------------------------+-------+
| Variable_name                 | Value |
+-------------------------------+-------+
| Innodb_row_lock_current_waits | 0     |
| Innodb_row_lock_time          | 18702 |
| Innodb_row_lock_time_avg      | 18702 |
| Innodb_row_lock_time_max      | 18702 |
| Innodb_row_lock_waits         | 1     |
+-------------------------------+-------+
--如果發現鎖爭用比較嚴重,如InnoDB_row_lock_waits和InnoDB_row_lock_time_avg的值比較高

3、InnoDB的行鎖模式及加鎖方法

共享鎖(S) :又稱讀鎖(lock in share mode)。允許一個事務去讀一行,阻止其他事務獲得相同數據集的排他鎖。若事務T對數據對象A加上S鎖,則事務T可以讀A但不能修改A,其他事務只能再對A加S鎖,而不能加X鎖,直到T釋放A上的S鎖。這保證了其他事務可以讀A,但在T釋放A上的S鎖之前不能對A做任何修改。 ​ 排他鎖(X) :又稱寫鎖(for update)。允許獲取排他鎖的事務更新數據,阻止其他事務取得相同的數據集共享讀鎖和排他寫鎖。若事務T對數據對象A加上X鎖,事務T可以讀A也可以修改A,其他事務不能再對A加任何鎖,直到T釋放A上的鎖。

mysql InnoDB引擎默認的修改數據語句:update,delete,insert都會自動給涉及到的數據加上排他鎖,select語句默認不會加任何鎖類型,如果加排他鎖可以使用select …for update語句,加共享鎖可以使用select … lock in share mode語句。所以加過排他鎖的數據行在其他事務中是不能修改數據的,也不能通過for update和lock in share mode鎖的方式查詢數據,但可以直接通過select …from…查詢數據,因為普通查詢沒有任何鎖機制。

4、InnoDB行鎖實現方式

InnoDB行鎖是通過給索引上的索引項加鎖來實現的,這一點MySQL與Oracle不同,後者是通過在數據塊中對相應數據行加鎖來實現的。InnoDB這種行鎖實現特點意味著:只有通過索引條件檢索數據,InnoDB才使用行級鎖,否則,InnoDB將使用錶鎖!

1、在不通過索引條件查詢的時候,innodb使用的是錶鎖而不是行鎖

create table tab_no_index(id int,name varchar(10)) engine=innodb;
insert into tab_no_index values(1,'1'),(2,'2'),(3,'3'),(4,'4');
session1session2
set autocommit=0 select * from tab_no_index where id = 1;set autocommit=0 select * from tab_no_index where id =2
select * from tab_no_index where id = 1 for update
select * from tab_no_index where id = 2 for update;

session1只給一行加了排他鎖,但是session2在請求其他行的排他鎖的時候,會出現鎖等待。原因是在沒有索引的情况下,innodb只能使用錶鎖。

2、創建帶索引的錶進行條件查詢,innodb使用的是行鎖

create table tab_with_index(id int,name varchar(10)) engine=innodb;
alter table tab_with_index add index id(id);
insert into tab_with_index values(1,'1'),(2,'2'),(3,'3'),(4,'4');
session1session2
set autocommit=0 select * from tab_with_indexwhere id = 1;set autocommit=0 select * from tab_with_indexwhere id =2
select * from tab_with_indexwhere id = 1 for update
select * from tab_with_indexwhere id = 2 for update;

3、由於mysql的行鎖是針對索引加的鎖,不是針對記錄加的鎖,所以雖然是訪問不同行的記錄,但是依然無法訪問到具體的數據(這裏是錶鎖)

alter table tab_with_index drop index id;
insert into tab_with_index  values(1,'4');
session1session2
set autocommit=0set autocommit=0
select * from tab_with_index where id = 1 and name='1' for update
select * from tab_with_index where id = 1 and name='4' for update 雖然session2訪問的是和session1不同的記錄,但是鎖的是具體錶,所以需要等待鎖

總結

對於InnoDB錶,本文主要討論了以下幾項內容: (1)InnoDB的行鎖是基於索引實現的,如果不通過索引訪問數據,InnoDB會使用錶鎖。 (2)在不同的隔離級別下,InnoDB的鎖機制和一致性讀策略不同。

在了解InnoDB鎖特性後,用戶可以通過設計和SQL調整等措施减少鎖沖突和死鎖,包括:

  • 盡量使用較低的隔離級別; 精心設計索引,並盡量使用索引訪問數據,使加鎖更精確,從而减少鎖沖突的機會;
  • 選擇合理的事務大小,小事務發生鎖沖突的幾率也更小;
  • 給記錄集顯式加鎖時,最好一次性請求足够級別的鎖。比如要修改數據的話,最好直接申請排他鎖,而不是先申請共享鎖,修改時再請求排他鎖,這樣容易產生死鎖;
  • 不同的程序訪問一組錶時,應盡量約定以相同的順序訪問各錶,對一個錶而言,盡可能以固定的順序存取錶中的行。這樣可以大大减少死鎖的機會;
  • 盡量用相等條件訪問數據,這樣可以避免間隙鎖對並發插入的影響; 不要申請超過實際需要的鎖級別;除非必須,查詢時不要顯示加鎖;
  • 對於一些特定的事務,可以使用錶鎖來提高處理速度或减少死鎖的可能。

我是紀先生,用輸出倒逼輸入而持續學習,持續分享技術系列文章,以及全網值得收藏好文,歡迎關注公眾號,做一個持續成長的技術人。

mysql進階系列曆史文章

(也可以在掘金專欄中看其他相關文章)

1. MySQL進階系列:一文了解mysql基礎架構

2. MySQL進階系列:一文了解mysql存儲引擎

3. MySQL進階系列:mysql中MyISAM和InnoDB有什麼區別;

4. MySQL進階系列:mysql中錶設計如何更好的選擇數據類型;

5. MySQL進階系列:數據庫設計中的範式究竟該如何使用

6. MySQL進階系列:一文詳解explain各字段含義

7. MySQL進階系列:為什麼mysql使用B+作為索引的數據結構

8. MySQL進階系列:  你需要知道的一些索引基礎知識;

9. MySQL進階系列:怎麼創建索引更合適;

10. MySQL進階系列:主從複制原理和配置;

11. MySQL進階系列:join連接的原理-3種算法;

12. MySQL進階系列:事務及事務隔離級別;

13. MySQL進階系列:多版本並發控制mvcc的實現;

14. MySQL進階系列:一條sql是怎麼執行的;

15. MySQL進階系列:你需要了解的幾種MySQL日志;

16. MySQL進階系列:MySQL主從複制和原理;

17. MySQL進階系列:MySQL中的鎖-MyISAM篇;

原网站

版权声明
本文为[紀先生]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/175/202206241549548125.html