MySQL 行鎖和表鎖的含義及區別

不可能每天都是好日子,有了不順心的日子,好日子纔會閃閃發亮。mysql

1、前言

對於行鎖和表鎖的含義區別,在面試中應該是高頻出現的,咱們應該對MySQL中的鎖有一個系統的認識,更詳細的須要自行查閱資料,本篇爲歸納性的總結回答。面試

MySQL經常使用引擎有MyISAM和InnoDB,而InnoDB是mysql默認的引擎。MyISAM不支持行鎖,而InnoDB支持行鎖和表鎖。sql

如何加鎖? 併發

MyISAM在執行查詢語句(SELECT)前,會自動給涉及的全部表加讀鎖,在執行更新操做(UPDATE、DELETE、INSERT等)前,會自動給涉及的表加寫鎖,這個過程並不須要用戶干預,所以用戶通常不須要直接用LOCK TABLE命令給MyISAM表顯式加鎖。線程

顯式加鎖:設計

上共享鎖(讀鎖)的寫法:lock in share mode,例如:code

select  math from zje where math>60 lock in share mode;

上排它鎖(寫鎖)的寫法:for update,例如:索引

select math from zje where math >60 for update;

2、表鎖

不會出現死鎖,發生鎖衝突概率高,併發低。進程

MyISAM引擎

MyISAM在執行查詢語句(select)前,會自動給涉及的全部表加讀鎖,在執行增刪改操做前,會自動給涉及的表加寫鎖。事務

MySQL的表級鎖有兩種模式:

  • 表共享讀鎖
  • 表獨佔寫鎖

讀鎖會阻塞寫,寫鎖會阻塞讀和寫

  • 對MyISAM表的讀操做,不會阻塞其它進程對同一表的讀請求,但會阻塞對同一表的寫請求。只有當讀鎖釋放後,纔會執行其它進程的寫操做。
  • 對MyISAM表的寫操做,會阻塞其它進程對同一表的讀和寫操做,只有當寫鎖釋放後,纔會執行其它進程的讀寫操做。

MyISAM不適合作寫爲主表的引擎,由於寫鎖後,其它線程不能作任何操做,大量的更新會使查詢很可貴到鎖,從而形成永遠阻塞

3、行鎖

會出現死鎖,發生鎖衝突概率低,併發高。

在MySQL的InnoDB引擎支持行鎖,與Oracle不一樣,MySQL的行鎖是經過索引加載的,也就是說,行鎖是加在索引響應的行上的,要是對應的SQL語句沒有走索引,則會全表掃描,行鎖則沒法實現,取而代之的是表鎖,此時其它事務沒法對當前表進行更新或插入操做。

CREATE TABLE `user` (
`name` VARCHAR(32) DEFAULT NULL,
`count` INT(11) DEFAULT NULL,
`id` INT(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8

-- 這裏,咱們建一個user表,主鍵爲id



-- A經過主鍵執行插入操做,但事務未提交
update user set count=10 where id=1;
-- B在此時也執行更新操做
update user set count=10 where id=2;
-- 因爲是經過主鍵選中的,爲行級鎖,A和B操做的不是同一行,B執行的操做是能夠執行的



-- A經過name執行插入操做,但事務未提交
update user set count=10 where name='xxx';
-- B在此時也執行更新操做
update user set count=10 where id=2;
-- 因爲是經過非主鍵或索引選中的,升級爲爲表級鎖,
-- B則沒法對該表進行更新或插入操做,只有當A提交事務後,B纔會成功執行

for update

若是在一條select語句後加上for update,則查詢到的數據會被加上一條排它鎖,其它事務能夠讀取,但不能進行更新和插入操做

-- A用戶對id=1的記錄進行加鎖
select * from user where id=1 for update;

-- B用戶沒法對該記錄進行操做
update user set count=10 where id=1;

-- A用戶commit之後則B用戶能夠對該記錄進行操做

行鎖的實現須要注意:

  1. 行鎖必須有索引才能實現,不然會自動鎖全表,那麼就不是行鎖了。
  2. 兩個事務不能鎖同一個索引。
  3. insert,delete,update在事務中都會自動默認加上排它鎖。

行鎖場景:

A用戶消費,service層先查詢該用戶的帳戶餘額,若餘額足夠,則進行後續的扣款操做;這種狀況查詢的時候應該對該記錄進行加鎖。

不然,B用戶在A用戶查詢後消費前先一步將A用戶帳號上的錢轉走,而此時A用戶已經進行了用戶餘額是否足夠的判斷,則可能會出現餘額已經不足但卻扣款成功的狀況。

爲了不此狀況,須要在A用戶操做該記錄的時候進行for update加鎖

擴展:間隙鎖

當咱們用範圍條件而不是相等條件檢索數據,並請求共享或排他鎖時,InnoDB會給符合條件的已有數據記錄的索引項加鎖;對於鍵值在條件範圍內並不存在的記錄,叫作間隙

InnoDB也會對這個"間隙"加鎖,這種鎖機制就是所謂的間隙鎖

-- 用戶A
update user set count=8 where id>2 and id<6

-- 用戶B
update user set count=10 where id=5;

若是用戶A在進行了上述操做後,事務還未提交,則B沒法對2~6之間的記錄進行更新或插入記錄,會阻塞,當A將事務提交後,B的更新操做會執行。

建議:

  • 儘量讓全部數據檢索都經過索引來完成,避免無索引行鎖升級爲表鎖
  • 合理設計索引,儘可能縮小鎖的範圍
  • 儘量減小索引條件,避免間隙鎖
  • 儘可能控制事務大小,減小鎖定資源量和時間長度
相關文章
相關標籤/搜索