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
裡面的內容,具體連結如下:
根據官網介紹,這兩個語句是在事務內起作用的,所涉及的概念是行鎖。它們能夠保證當前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,都應該儘快釋放鎖。