开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第9天,点击查看活动详情
前言
在项目当中,积分的兑换,商品的交易,线程资源的占用都会用到锁,这篇文章就给你介绍下Mysql中的各种悲观锁。
悲观锁
悲观锁性格比较emo,不爱和别人分享,在拿到数据后总认为会有人抢它的数据,因此他会给他已经拿到的数据加上一个锁,别人想用他的数据的时候就必须阻塞等待直到可以拿到锁为止。例如在mysql中的表锁、行锁、读写锁都是悲观锁,在Java中的sychronized、Lock的实现类也是悲观锁。
表锁
锁定的一张表,锁定粒度大,开销小,加锁快,MyISAM和InnoDB存储引擎都支持,不会出现死锁,因为一起行会把SQL需要的全部锁都获取到,并发度低。
行锁
锁定行,锁定粒度小,开销大,加锁慢,InnoDB支持,InnoDB默认采用,会出现死锁,并发度高。
读锁
读锁:读锁也称为共享锁(S锁),如果事务A给数据对象Data添加了读锁,那么事务A只能读该数据对象而不能修改;事务B、事务C都只能给数据对象再次加读锁,而不能加写锁,这样就可以保证该数据对象数据的不变。
-- 开启共享锁
select... lock in share mode;
这里我们可以证明一下
- 表是长这样,目前只有id是主键
- 关闭事务的自动提交
-- 查看自动提交,1-开启,0-关闭
SELECT @@autocommit;
-- 关闭自动提交
set autocommit=0
- 验证读锁可以重复添加
在窗口1上先执行这个事务,不要commit提交;
begin;
select * from exam_record where uid = 1001 lock in share mode;
再新开一个窗口2执行同样的读锁事务,发现事务执行成功,证明读锁可以重复加在同一个数据对象上
- 验证添加读锁后不能添加写锁
而如果事务A在加了读锁后,事务B再去更新创建写锁时就会一直等待事务A释放读锁
写锁
写锁:写锁也称为排他锁(X锁),如果事务A给数据对象Data添加了写锁,那么事务A可以读Data也可以写Data,其它事务在事务A释放锁之前不能再给Data添加写锁或读锁,也就是其它事务不能读或者写Data了。使用delete、update就已经添加了写锁,使用select时需要在语句后添加for update;
另外,添加写锁如果有索引就是使用的行锁,没有索引就是使用的表锁。
-- 开启排它锁
select... for update;
- 验证添加写锁,其他事务不能够再添加锁
窗口1执行update别提交
窗口2获取不到锁直接报错了
而如果超时之前窗口1执行commit后,窗口2获取到锁正常提交
接着看给uid加了索引后的效果
alter table exam_record add index uidIndex (uid);
直接查uid=1002就成功了,说明给指定字段加了索引后,用的就是行锁了。