工作需要,接触到以下两个mysql sql语法:
select lock in share mode
select for update
从官网上查找到对应的章节,属于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');
session 1:
set autocommit = 0;
select * from tb_test where id = 1 lock in share mode;
open session 2:
update tb_test set col1 = 'BBB' where id = 1;
这个时候可以观察到session2处于blocking状态….
直到
session 1:
commit;
这个时候session2更新成功了。
这里也就验证了lock in share mode可以在事务中保证锁定的行不被其他session所更改。
使用lock in share mode具有很高的风险,看下面的案例:
session 1:
set autocommit = 0;
select * from tb_test where id = 1 lock in share mode;
open session2:
set autocommit = 0;
select * from tb_test where id = 1 lock in share mode;
这个时候两个session同时持有id = 1
这行数据的共享锁。这个时候我们在session 1里面执行update操作:
session 1:
update tb_test set col1 = 'AAA' where id = 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
这个时候mysql检测到会发生死锁,会中断当前事务该语句的执行,重新开启一个新的事务(应该就是相当于session2先退出事务,然后再开启一个事务吧)。
这个时候session1可以更新成功了。
上面的例子可以看出使用lock in share mode
比较危险,很可能因为其他session同时加了这种锁,导致当前session无法进行更新,进而阻塞住。
select for update加的是排它锁,所以没有上面lock in share mode
所产生的死锁,因为一个session加了这种锁,其他session除了读取操作,其他操作都不能进行,如更改操作,或者加锁,共享锁和排它锁都不可以。
下面演示一下用法:
session 1:
set autocommit = 0;
select * from tb_test where id = 1 for update;
open session 2:
update tb_test set col1 = 'BBB' where id = 1;
这个时候session 2处于blocking状态
我们手动kill掉session 2, 按Ctrl + C
。
然后执行:
session 2:
set autocommit = 0;
select * from tb_test where id = 1 for update;
还是blocking状态,证明其他session的事务不能对已经加了排它锁(for update)的行再加排它锁。
kill掉,再来
session 2:
set autocommit = 0;
select * from tb_test where id = 1 lock in share mode;
还是blocking状态,证明其他session的事务不能对已经加了排它锁(for update)的行再加共享锁(lock in share mode)。
当然,如果使用select for update
的时候,如果锁定当前行的事务一直不退出,将会导致其他进行这个行更改操作的session一直阻塞。(没有试是否有超时的情况)
因此,无论在使用select lock in share mode 或者 select for update,都应该尽快释放锁。