精通MySQL之鎖篇

努力的老劉發表於2021-02-01
  • 老劉是即將找工作的研究生,自學大資料開發,一路走來,感慨頗深,網上大資料的資料良莠不齊,於是想寫一份詳細的大資料開發指南。這份指南把大資料的【基礎知識】【框架分析】【原始碼理解】都用自己的話描述出來,讓夥伴自學從此不求人。
  • 您的點贊是我持續更新的動力,禁止白嫖,看了就要有收穫,一起加油。

今天給大家分享的是大資料開發基礎部分MySQL的鎖,鎖在MySQL知識點中屬於比較重要的部分,大家一定要好好體會老劉的話,MySQL鎖篇的大綱如下:

精通MySQL之鎖篇

看完老劉這篇內容後,希望你們能夠掌握以下內容:

  1. MySQL的鎖分類
  2. 表級鎖中表鎖、後設資料鎖的原理
  3. 行鎖的原理、記錄鎖和間隙鎖的使用區別、死鎖的原理和死鎖場景

MySQL鎖介紹

為什麼有鎖?

多個程式對MySQL表或記錄進行訪問,就會產生競態條件,為了解決這個問題,就提出了鎖。

MySQL中的鎖如下圖:

精通MySQL之鎖篇

MySQL表級鎖

表級鎖是由MySQL SQL layer層實現,只要鎖了一張表,只能對這張表操作。它有兩種:一是表鎖、二是後設資料鎖。

表鎖

表鎖有兩種表現形式:一是表共享讀鎖(Table Read Lock)、二是表獨佔寫鎖(Table Write Lock)。

我們採用手動增加表鎖,採用如下SQL語句:

lock table 表名稱 read(write),表名稱2 read(write),其他;

刪除表鎖,採用如下SQL語句:

unlock tables;

檢視錶鎖情況:

show open tables;

光說不練假把式,老劉用一個案例給各位夥伴好好講講表鎖,大家跟著一起練。

表鎖演示

--新建表
CREATE TABLE mylock (
id int(11) NOT NULL AUTO_INCREMENT,
NAME varchar(20) DEFAULT NULL,
PRIMARY KEY (id)
);
INSERT INTO mylock (id,NAME) VALUES (1, 'a');
INSERT INTO mylock (id,NAME) VALUES (2, 'b');
INSERT INTO mylock (id,NAME) VALUES (3, 'c');
INSERT INTO mylock (id,NAME) VALUES (4, 'd');

演示表讀鎖

1、我們先給表mylock加表讀鎖

session1: lock table mylock read;

2、加完表讀鎖後,session1還是能對該表進行查詢

session1: select * from mylock;

精通MySQL之鎖篇

3、只要鎖了這張表,就只能先對這張表操作,不能訪問別的表,直到這張表被釋放。我用session1訪問表tdep就不能進行訪問,如圖:

精通MySQL之鎖篇

4、雖然對這張表加鎖了,但我們別的session可以對它進行訪問。

session2:select * from mylock;

精通MySQL之鎖篇

5、但如果session2要對mylock表進行修改,那就不行了,表讀鎖不允許加寫鎖,只允許加讀鎖,這也叫表共享讀。

session2:update mylock set name='x' where id=2;

精通MySQL之鎖篇

6、這個時候session1釋放,session2才能進行修改。

session1:unlock tables;
session2:Rows matched: 1 Changed: 1 Warnings: 0 -- 修改執行完成

精通MySQL之鎖篇

表讀鎖的內容就演示完了,現在開始演示表寫鎖的內容。

1、先給mylock加寫鎖

session1: lock table mylock write;

2、session1可以訪問mylock表,但不能訪問其他表。

精通MySQL之鎖篇

3、session1也可以對該表進行修改,但是session2對該表進行讀取和修改就不行。

精通MySQL之鎖篇

4、當session1釋放表寫鎖後,session2才能獲取。

session1:unlock tables;

精通MySQL之鎖篇

session2:4 rows in set (41.65 sec)

精通MySQL之鎖篇

通過這個例子,大家要明白讀鎖就是表共享讀鎖,自己可以用,其他人也可以訪問;寫鎖就是表獨佔寫鎖,意思就是隻有自己能夠訪問, 別的人不能訪問。

後設資料鎖

什麼是後設資料鎖?

後設資料,英語縮寫是MDL,在 MySQL 5.5 版本中引入了 MDL,當對一個表做增刪改查操作的時候,自動加 MDL 讀鎖;當要對錶做結 構變更操作的時候,自動加 MDL 寫鎖。

注意:這個鎖是自動提交的,要先開始事務,然後進行增刪改查時,會自動加MDL讀鎖。

現在開始後設資料鎖的演示,大家跟著老劉一起練習。

1、session1開啟事務,給表自動加讀鎖。

session1: begin;
          select * from mylock;

精通MySQL之鎖篇

2、session2對該表進行修改時就會造成阻塞。

session2: alter table mylock add f int;

精通MySQL之鎖篇

3、session1提交事務 或者 rollback 釋放讀鎖 。

4、session2修改完成。

精通MySQL之鎖篇

MySQL行級鎖

MySQL的行級鎖是由儲存引擎實現的,利用儲存引擎鎖住索引項來實現,主要講InnoDB行級鎖。

InnoDB行級鎖,按鎖範圍分為三種:

  • 記錄鎖:鎖定索引中的一條記錄。
  • 間隙鎖:鎖的是縫,要麼鎖住索引記錄中間的值,要麼鎖住第一個索引記錄前面的值或者最後一個索引記錄後面的值。
  • Next-key locks:記錄鎖和間隙鎖的組合(可以不看)

InnoDB行級鎖,按功能分:

  • 共享讀鎖(S):手動新增,允許一個事務去讀一行,其他事務可以讀資料,但不能修改資料。

    SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE -- 共享讀鎖 手動新增
  • 排他寫鎖(X):自動新增,指的是一個事務在一行資料加上排他鎖後,其他事務不能再在其上加其他的鎖

InnoDB也實現了表級鎖,也就是意向鎖,意向鎖是mysql內部使用的,不需要使用者干預。

兩階段鎖(2PL)

兩階段鎖講的是鎖操作分為兩個階段:加鎖階段和解鎖階段。

加鎖階段:只加鎖,不放鎖。

解鎖階段:只放鎖,不加鎖。

行鎖演示

InnoDB行鎖是通過給索引上的索引項加鎖來實現的,因此只有通過索引條件檢索的資料,InnoDB才會使用行級鎖,否則,InnoDB將使用表鎖!

行讀鎖

1、我們利用session1給id=1的行加讀鎖,使用索引。

session1: begin;
          select * from mylock where ID=1 lock in share mode; 

精通MySQL之鎖篇

2、由於行鎖鎖定的是行,所以利用session2修改別的行例如id=2是可以的,修改id=1就不行了。

session2:update mylock set name='M' where id=2;
session2:update mylock set name='M' where id=1;

精通MySQL之鎖篇

3、當session1提交後,session2才可以修改。

session1: commit; 
session2:update mylock set name='M' where id=1; 
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

精通MySQL之鎖篇

關於行鎖,我們要注意使用索引加行鎖 ,未鎖定的行可以訪問。

行讀鎖升級為表鎖

mylock表中id加了索引,name沒有加索引,當我們對name加行讀鎖時,就會出現行讀鎖升級為表鎖。

1、session1開啟事務

session1: begin;

2、手動加name='c'的行讀鎖,未使用索引

select * from mylock where name='c' lock in share mode;

3、session2修改阻塞,未用索引行鎖升級為表鎖

update mylock set name='N' where id=2;

4、session1提交事務或者 rollback 釋放讀鎖

commit;

5、session2就會修改成功

update mylock set name='N' where id=2;

行寫鎖

1、session1開啟事務並且手動加id=1的行寫鎖。

session1: begin;
          select * from mylock where id=1 for update;

2、這裡有一個特別重要的知識點,很多人會弄錯!

排他鎖鎖住一行資料後,其他事務就不能讀取和修改該行資料,其實不是這樣的!

排他鎖指的是一個事務在一行資料加上排他鎖後,其他事務不能再在其上加其他的鎖。MySQL InnoDB引擎預設的修改資料語句:update,delete,insert都會自動給資料加上排他鎖,select語句預設不會加任何鎖型別,如果加排他鎖可以使用select …for update語句,加共享鎖可以使用select … lock in share mode語句。所以其他事務是不能修改加過排他鎖的資料行,其他事務也不能通過for update和lock in share mode鎖的方式查詢資料,但可以直接通過select …from…查詢資料,因為普通查詢沒有任何鎖機制。

所以session2可以訪問id=1的資料行。

session2: select * from mylock where id=1 ;

3、但是不能給它繼續加鎖

session2: select * from mylock where id=1 lock in share mode ;

精通MySQL之鎖篇

4、session1提交事務或者rollback釋放寫鎖,session2才會執行成功

精通MySQL之鎖篇

間隙鎖

精通MySQL之鎖篇

根據檢索條件向左尋找最靠近檢索條件的記錄值A,作為左區間,向右尋找最靠近檢索條件的記錄值B作為右區間,即鎖定的間隙為(A,B)。根據圖中where number=5的話,那麼間隙鎖的區間範圍為(4,11);

間隙鎖防止兩種情況

  1. 防止插入間隙內的資料
  2. 防止已有資料更新為間隙內的資料

間隙情況:

  1. id、number均在間隙內
  2. id、number均在間隙外
  3. id在間隙內、number在間隙外
  4. id在間隙外,number在間隙內
  5. id、number為邊緣資料

非唯一索引等值

update news set number=3 where number=4;

檢索條件number=4,向左取得最靠近的值2作為左區間,向右取得最靠近的5作為右區間,因此,session 1的間隙鎖的範圍(2,4),(4,5),即記錄(id=1,number=2)和記錄(id=3,number=4)之間間隙會被鎖定,記錄(id=3,number=4)和記錄(id=6,number=5)之間間隙被鎖定。

當我們新增資料時,結果如下:

insert into news value(2,3);
均在間隙內,阻塞
insert into news value(7,8);
均在間隙外,成功
insert into news value(2,8);
id在間隙內,number在間隙外,成功
insert into news value(4,8);
id在間隙內,number在間隙外,成功
insert into news value(7,3);
id在間隙外,number在間隙內,阻塞
insert into news value(7,2);
id在間隙外,number為上邊緣資料,阻塞
insert into news value(2,2);
id在間隙內,number為上邊緣資料,阻塞
insert into news value(7,5);
id在間隙外,number為下邊緣資料,成功
insert into news value(4,5);
id在間隙內,number為下邊緣資料,阻塞

我們可以得到只要number(where後面的)在間隙裡(2 3 4),不包含最後一個數(5)則不管id是多少都會阻塞。 如果是下邊緣資料需要看id是否在間隙內。

主鍵索引範圍

由於主鍵不能重複,所以id無邊緣資料。

update news set number=3 where id>1 and id <6;
insert into news value(2,3);
均在間隙內,阻塞
insert into news value(7,8);
均在間隙外,成功
insert into news value(2,8);
id在間隙內,number在間隙外,阻塞
insert into news value(4,8);
id在間隙內,number在間隙外,阻塞
insert into news value(7,3);
id在間隙外,number在間隙內,成功

我們可以得到只要id(在where後面的)在間隙裡(2 4 5),則不管number是多少都會阻塞。

非唯一索引無窮大

update news set number=3 where number=13 ;
insert into news value(11,5);
執行成功
insert into news value(12,11);
執行成功
insert into news value(14,11);
阻塞
insert into news value(15,12);
阻塞

檢索條件number=13,向左取得最靠近的值11作為左區間,向右由於沒有記錄因此取得無窮大作為右區間,因此,session 1的間隙鎖的範圍(11,無窮大),當id和number同時滿足 ,才會阻塞。

死鎖

兩個 session 互相等等待對方的資源釋放之後,才能釋放自己的資源,造成了死鎖,主要是順序出現問題。

我們session1先給id=1加鎖,session2再給id=2加鎖,此時session1想再給id=2加鎖,但session2已經給它加鎖了,就會造成死鎖。

如何解決死鎖?

MySQL預設會主動探知死鎖,並回滾某一個影響最小的事務,等待另一個事務執行完成之後,再重新執行該事務。

總結

本文作為大資料開發指南MySQL的第三篇詳細介紹了MySQL鎖的內容,希望大家能夠跟著老劉的文章,好好捋捋思路,爭取能夠用自己的話把這些知識點講述出來!

儘管當前水平可能不及各位大佬,但老劉會努力變得更加優秀,讓各位小夥伴自學從此不求人!

大資料開發指南地址如下:

  • github:https://github.com/BigDataLaoLiu/BigDataGuide
  • 碼雲:https://gitee.com/BigDataLiu/BigDataGuide

如果有相關問題,聯絡公眾號:努力的老劉。文章都看到這了,點贊關注支援一波!

精通MySQL之鎖篇

相關文章