mysql limit 1 for update的锁类型

最近遇到一个业务问题,购物券号是预先产生的,然后进行客户ID的绑定,从SQL上来讲基本上有两种方式实现

方式一:

begin;
select * from purchase_code where code_type = 0 limit 1 for update;
update purchase_code set code_type=1,user_id=123 where purchase_code_id = 1
commit

方式二:

update purchase_code set code_type=1,user_id=123 where code_type=0 limit 1;

之前一直担心方式一会出现问题;主要是担心会因为并发问题,导致选出来的行被其他线程所修改掉,因为一直会觉得第一种方式加的是行锁,需要查出来继续判断一下code_type才能进行更新。进行测试了一下发现上面两种方式加的都是表锁。

因为什么原因上面加的是表锁呢?

经过测试,mysql加锁有这样的特点:

主键查询加行锁

begin;
select * from t_user_role where role_id = "1111" limit 1 for update ;

此时可以对其他行加读锁和写锁

select * from t_user_role where role_id = 'operator';
select * from t_user_role where role_id = 'operator' for update

锁升级的情况

role_id为varchar类型,但是用int类型也能搜索出来,但是此时加的是表锁

select * from t_user_role where role_id = 1111 for update;

当按非主键查询的时候,此时mysql加的是表锁

 此时下面的查询都会卡住

select * from t_user_role where role_type = 2 limit 1 for update ;

select * from t_user_role where role_id = 'operator' for update;

如果对role_type加上一个key索引,情况就变得有意思

除了最后一次查询被卡住外,其他查询全部能返回

select * from t_user_role where role_type = 2 limit 1 for update ;

select * from t_user_role where role_id = 'operator' for update;

select * from t_user_role where role_id = '2222' for update;

select * from t_user_role where role_type = 1 limit 1 for update ;

 研究innodb的锁机制发现

innodb是通过索引实现行锁,只有通过索引项查询的时候,才会加行锁,否则统一加的是表锁。因此role_type上加索引之后,锁加在了role_type的索引上,从而上面其他三能顺利获得锁,但是此时已然无法获取表锁

猜你喜欢

转载自blog.csdn.net/luwei9233/article/details/88311973