MySQL鎖機制 一、鎖的分類 二、表鎖 三、行鎖 四、索引失效行鎖變表鎖問題 五、間隙鎖的危害

MySQL主要有表鎖,行鎖和頁鎖,頁鎖用得少,本文主要介紹表鎖和行鎖。

一、鎖的分類

從對數據的操作類型來分,可以分爲讀鎖和寫鎖;從對數據操作粒度來分,可分爲表鎖和行鎖。

  • 讀鎖(共享鎖):針對同一份數據,多個讀操作可以同時進行而不會互相影響;

  • 寫鎖(排他鎖):當前寫操作沒有完成前,會阻斷其他寫鎖和讀鎖;

  • 表鎖:鎖住被操作的整張表;

  • 行鎖:鎖住被操作表中的被操作行,其他行不受影響。

二、表鎖

1. 介紹:

表鎖偏向MyISAM存儲引擎,開銷小,加鎖快,無死鎖,粒度大,併發性差。下面建表演示表鎖的用法。

create table mylock (
    id int not null primary key auto_increment,
    name varchar(20)
) engine myisam;

insert into mylock(name) values('a');
insert into mylock(name) values('b');
insert into mylock(name) values('c');
insert into mylock(name) values('d');
insert into mylock(name) values('e');

這裏用了MyISAM引擎,這個引擎是寫優先的,加了寫鎖後,其他線程不能對被鎖的表做任何操作,即使是查詢,所以如果寫操作很多,就會導致其他線程的讀操作難以執行,大量的查詢sql被阻塞。

  • 增加表鎖的語法:
lock table 表名1 read(write), 表名2 read(write) ……;
  • 查看錶上加過的鎖;
show open tables;
  • 給mylock表加讀鎖,tblA加寫鎖:
lock table mylock read, tblA write;
  • 釋放鎖:
unlock tables;

2. 表鎖演示:

讀鎖:

首先給mylock表加上讀鎖,然後打開兩個session,暫且將左邊的稱爲session1,右邊的稱爲session2,如下:

然後進行如下操作:

  • 在session1中執行lock table mylock read,然後執行select * from mylock;,結果是可以查詢出數據。即自己加了讀鎖,自己是可以查的;

  • 在session2中執行select * from mylock;,結果也是可以查詢出數據。說明讀鎖,大家都可以讀數據;

  • 在session1中執行update mylock set name = 'aa' where id = 1;,結果報瞭如下錯誤:

ERROR 1099 (HY000): Table 'mylock' was locked with a READ lock and can't be updated
  • session1給mylock表加了讀鎖,那麼session1能讀其他的表嗎?我現在執行select * from tblA;,結果是不行的,報瞭如下的錯誤:
ERROR 1100 (HY000): Table 'tblA' was not locked with LOCK TABLES
  • session2能讀tblA表嗎?執行select * from tblA;,結果是可以的。

  • session2中執行update mylock set name = 'aa' where id = 1;,結果如下:

一直卡着不動,說明阻塞了,要直到mylock表解鎖才能成功。

表讀鎖總結:

操作 當前session 其他session
讀當前表 Y Y
讀其他表 N Y
寫當前表 N 阻塞,直到鎖被釋放
寫其他表 N Y

寫鎖:

mylock表加上寫鎖,lock table mylock write,然後在session1和session2中對當前表和其他表進行讀寫操作,最後結論如下:

操作 當前session 其他session
讀當前表 Y 阻塞,直至鎖被釋放
讀其他表 N Y
寫當前表 Y 阻塞,直到鎖被釋放
寫其他表 N Y

對於表讀鎖和表寫鎖,總結起來就是加了讀鎖,當前session只能讀當前表,其他session只有寫當前表會被阻塞;加了寫鎖,當前session只能對當前表進行讀寫,其他session對當前表的讀寫都會被阻塞。所以表鎖一般偏讀,也就是一般不會加表寫鎖,加寫鎖可能會導致大量的查詢被阻塞。

3. 表鎖分析:

MySQL中有兩個變量,可以記錄表的鎖定情況,如下:

  • Table_locks_immediate:表示可以立即獲取鎖的查詢次數,每次加1;

  • Table_locks_waited:出現表級鎖爭用而發生等待的次數,每次加1;

查看這兩個變量的值的sql:

show status like 'table%';

三、行鎖

1. 介紹:

行鎖偏向InnoDB存儲引擎,開銷大,加鎖慢,會出現死鎖,粒度小,併發性好。InnoDB支持事務,而MyISAM是不支持事務的,InnoDB默認採用的也是行鎖,下面建表演示表鎖的用法。

create table col_lock(
    id int not null primary key auto_increment,
    name varchar(20)
) engine innodb;

insert into col_lock(name) values('a');
insert into col_lock(name) values('b');
insert into col_lock(name) values('c');
insert into col_lock(name) values('d');
insert into col_lock(name) values('e');

2. 行鎖總結:

innodb支持事務,並且默認是自動提交,爲了演示行鎖,先執行下面的sql把自動提交關閉。

set autocommint = 0;

接下來看看session1和session2的各種操作情況:

操作 當前session 其他session
讀當前行 Y Y
寫當前行 Y 阻塞,直到鎖被釋放
兩個session操作不同的行 Y Y

3. 分析行鎖:

我們可以通過如下sql查看行鎖的爭奪情況:

show status like 'innodb_row_lock%';

執行結果是:

+-------------------------------+-------+
| Variable_name                 | Value |
+-------------------------------+-------+
| Innodb_row_lock_current_waits | 0     |
| Innodb_row_lock_time          | 57446 |
| Innodb_row_lock_time_avg      | 28723 |
| Innodb_row_lock_time_max      | 51618 |
| Innodb_row_lock_waits         | 2     |
+-------------------------------+-------+
  • Innodb_row_lock_current_waits:當前正在等待鎖定的數量
  • Innodb_row_lock_time:從系統啓動到現在鎖定總時長
  • Innodb_row_lock_time_avg:每次等待所花的平均時間
  • Innodb_row_lock_time_max:從系統啓動到現在獲取鎖等待最久的一次花的時間
  • Innodb_row_lock_waits:系統啓動到現在獲取鎖等待的總次數

四、索引失效行鎖變表鎖問題

這個是比較隱蔽的問題,很難發現,但確實存在。比如之前說的varchar類型的沒加單引號,會導致索引失效,那麼這時候行鎖就會變爲表鎖。比如col_lock表的name字段是varchar類型的,先在name字段加索引,然後關閉自動提交,執行下面的語句:

update col_lock set name = aa where id = 1;

然後再另一個session中執行:

update col_lock set name = 'bb' where id = 2;

本來操作的是不同的行,即使第一條語句還沒commit,第二條應該也能執行,但實際上不行,因爲aa沒加單引號,索引失效了,行鎖變成了表鎖。

五、間隙鎖的危害

有個tblA表,age字段是加了索引的,數據如下:

我們在這session1中執行下面的update操作:

update tblA set birth = now() where age > 20 and age < 25;

其實也就是3條記錄都會被更新。執行後,先不提交,在session2中執行如下語句:

insert tblA(age,birth) values(22,now());

表中沒有age爲22的,那現在就插入一條age爲22的記錄,行鎖,兩邊操作不同的行,應該不會有任何影響的,但是現在情況如下:

直接等待鎖都超時了,這就是間隙鎖。session1中commit了之後,session2中的insert語句才能執行成功。

  • 間隙:當我們使用範圍條件檢索數據,請求共享或排他鎖時,innodb會給符合條件的已有數據記錄的索引項加鎖,對於在條件範圍內但是不存在的記錄,比如age爲22在age > 20 and age <25這個範圍內,但是不存在這條記錄,這個就叫做間隙。innodb會對這個間隙加鎖,這就叫間隙鎖。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章