1. 程式人生 > >mysql lock in share mode 和 select for update

mysql lock in share mode 和 select for update

原文連結:http://blog.csdn.net/d6619309/article/details/52688250

工作需要,接觸到以下兩個MySQL sql語法:

select lock in share mode
select for update
  • 1
  • 2
  • 1
  • 2

從官網上查詢到對應的章節,屬於Locking Reads裡面的內容,具體連結如下:

locking-reads

根據官網介紹,這兩個語句是在事務內起作用的,所涉及的概念是行鎖。它們能夠保證當前session事務所鎖定的行不會被其他session所修改(這裡的修改指更新或者刪除)。兩個語句不同的是,一個是加了共享鎖而另外一個是加了排它鎖。

可以這麼理解,共享鎖允許其他事務加共享鎖讀取,但是,不允許其他事務去做修改,或者加排它鎖。而排它鎖顯得更加嚴格,不允許其他事務加共享鎖或者排它鎖,更加不允許其他事務修改加鎖的行。

說到這裡,行鎖有什麼用呢?設想下面這種場景:

1) 讀取一行資料
2) 根據讀取到的資料去更新其他資料

假設在1)和2)之間,有個其他的user session剛好修改了你讀取的那行資料,那麼你下面的更新就有可能會出錯!因為關聯的資料產生了變化!

行鎖就能夠保證不會出現上面所說的這種尷尬的場景。

實踐了一把,下面記錄它們的用處:

測試用的表結構並插入一行記錄:

use test;
create
table tb_test ( id int primary key, col1 varchar(20) ) engine = innodb default character set = 'utf8';
insert into tb_test(id, col1) values(1, 'AAA');
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

1. select lock in share mode

1.1 使用示例

session 1:

set
autocommit = 0;
select * from tb_test where id = 1 lock in share mode;
  • 1
  • 2
  • 1
  • 2

open session 2:

update tb_test set col1 = 'BBB' where id = 1;
  • 1
  • 1

這個時候可以觀察到session2處於blocking狀態….

直到
session 1:

commit;
  • 1
  • 1

這個時候session2更新成功了。

這裡也就驗證了lock in share mode可以在事務中保證鎖定的行不被其他session所更改。

1.2 注意死鎖

使用lock in share mode具有很高的風險,看下面的案例:

session 1:

set autocommit = 0;
select * from tb_test where id = 1 lock in share mode;
  • 1
  • 2
  • 1
  • 2

open session2:

set autocommit = 0;
select * from tb_test where id = 1 lock in share mode;
  • 1
  • 2
  • 1
  • 2

這個時候兩個session同時持有id = 1這行資料的共享鎖。這個時候我們在session 1裡面執行update操作:

session 1:

update tb_test set col1 = 'AAA' where id = 1;
  • 1
  • 1

卡住了!!!! ????

這個時候session1必須等待session2退出事務或者等待直到鎖超時:

鎖超時的情況:

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

如果我們在session 2裡面執行:

session2:

update tb_test set col1 = 'BBB' where id = 1;

ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

這個時候mysql檢測到會發生死鎖,會中斷當前事務該語句的執行,重新開啟一個新的事務(應該就是相當於session2先退出事務,然後再開啟一個事務吧)。

這個時候session1可以更新成功了。

上面的例子可以看出使用lock in share mode比較危險,很可能因為其他session同時加了這種鎖,導致當前session無法進行更新,進而阻塞住。

2. select for update

select for update加的是排它鎖,所以沒有上面lock in share mode所產生的死鎖,因為一個session加了這種鎖,其他session除了讀取操作,其他操作都不能進行,如更改操作,或者加鎖,共享鎖和排它鎖都不可以。

2.1 使用示例

下面演示一下用法:

session 1:

set autocommit = 0;
select * from tb_test where id = 1 for update;
  • 1
  • 2
  • 1
  • 2

open session 2:

update tb_test set col1 = 'BBB' where id = 1;
  • 1
  • 1

這個時候session 2處於blocking狀態

我們手動kill掉session 2, 按Ctrl + C

然後執行:

session 2:

set autocommit = 0;
select * from tb_test where id = 1 for update;
  • 1
  • 2
  • 1
  • 2

還是blocking狀態,證明其他session的事務不能對已經加了排它鎖(for update)的行再加排它鎖。

kill掉,再來

session 2:

set autocommit = 0;
select * from tb_test where id = 1 lock in share mode;
  • 1
  • 2
  • 1
  • 2

還是blocking狀態,證明其他session的事務不能對已經加了排它鎖(for update)的行再加共享鎖(lock in share mode)。

當然,如果使用select for update的時候,如果鎖定當前行的事務一直不退出,將會導致其他進行這個行更改操作的session一直阻塞。(沒有試是否有超時的情況)

2. 總結

因此,無論在使用select lock in share mode 或者 select for update,都應該儘快釋放鎖。