MySQL 之共享锁和排他锁

共享锁

又称读锁,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问数据。共享锁和共享锁是不冲突的,但是和排他锁是冲突的。
加共享锁可以使用select * from user where id =1 lock in share mode语句。

排他锁

又称写锁,排他锁就是不能与其他锁并存,如果一个事务获取了一条数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是对于已经获取了排他锁的事务是可以对数据进行读取和修改。
加排他锁可以使用select * from user where id =1 for update语句。

注意:
mysql InnoDB引擎默认的修改语句,update、insert、delete都会自动给涉及的数据加上排他锁,select语句默认不会加任何锁类型。
加过排他锁的数据行在其他事务中是不能修改数据的,也不能通过for updatelock in share mode锁的方式查询数据,但是可以直接通过select ... from .... 查询数据,因为普通查询没有任何锁机制。

事物命令

begin:开启事务
commit:提交事务
rollback:回滚事务
提交事务和回滚事务会释放锁。

事物实战

对数据加排他锁

对id=1的数据加排他锁。
在这里插入图片描述
发现使用共享锁和排他锁方式读取数据会处于阻塞状态,因为id=1数据被加上了排他锁,此处阻塞是等待排他锁释放。
共享锁方式查询:
在这里插入图片描述
排他锁方式查询:
在这里插入图片描述
锁等待超时,重新开始事务。
在这里插入图片描述

对数据加共享锁

对id=1的数据加共享锁。
在这里插入图片描述
发现使用共享锁和普通查询都成查询数据,因为共享锁不冲突,普通查询无锁机制;使用排他锁会阻塞等待锁的释放,因为排他锁和共享锁是冲突的。
在这里插入图片描述

验证InnoDb引擎中修改语句自动加排他锁

开启事务并更新id=1数据行:
在这里插入图片描述
发现使用共享锁方式读数据会阻塞,表示上面的update语句自动加上了排他锁:
在这里插入图片描述

死锁案例

案例说明:
老公和老婆都使用了共享锁查询数据库,当老公更新数据库时,由于老婆拿着共享锁,导致老公在更新数据时等待老婆释放锁,老婆进行更新数据库时,由于老公拿着共享锁,导致老婆也在更新数据时等待老公释放锁,就形成了死锁现象,你等我,我等你。

mysql解决死锁的方案:
在mysql中,锁等待超时后,会自动释放锁并结束当前事物。所以处理死锁的方案是释放掉一方的锁,会优先释放后等待的锁,这样就可以保证一方更新成功,但是性能很低,因为数据库频繁在解决死锁问题。

两个事务同时对id=1数据行加上共享锁,当第一个事务更新数据时,会等待第二个事务释放锁,当第二个事务更新数据时,在尝试获取锁的时候检测到存在死锁,通过释放该锁并重新开始事务来解决死锁,随之第一个事务获取到锁并更新数据成功。
在这里插入图片描述
在这里插入图片描述

丢失更新场景

案例说明:
老公在ATM上取钱,老婆在柜台存钱,比如账户中有1000元。老公首先执行查询操作,查询到账户余额为1000,此时程序将1000拿到内存中,老公取了200元,程序执行更新操作将账户余额改为800,但是老公的程序没有commit的时候,老婆查询账户,此时账户余额还是1000元,老婆存入200元,程序执行了更新操作将账户余额改为1200,然后老公将更新语句提交,接着老婆也将更新语句提交。最后余额为1200,这就是丢失更新的问题。

可以使用悲观锁和乐观锁解决丢失更新问题,悲观锁和乐观锁不是数据库中真正的锁,是解决丢失更新定义的名词。

悲观锁(更新多,查询少时用)
悲观锁是使用数据库中的排他锁来实现的,就是我们在操作数据库时采用悲观的态度,认为别人会在此时并发访问数据库。当老公使用排他锁查询余额后,老婆再使用排他锁查询余额时,因为老婆已经拿到了排他锁,导致老婆不能加锁,所以老婆只能等待老公执行完毕,释放锁以后才能继续操作。

乐观锁(更新少,查询多时用)
乐观锁是使用版本号的方式进行控制的,在数据库表中有一列版本号。从数据库中查询的时候,将版本号也查询过来,在进行更新操作的时候,将版本号加1,查询条件的版本号还是查询过来的版本号。比如,老公执行查询操作的时候,select money,version from account where name='aaa'; 假设此时查询到的版本号为 0,老公在进行更新操作的时候 update account set money=money+100,version=version+1 where name='aaa' and version=0; 未提交时老婆来查询,查询到的版本号依然是 0,老婆也执行更新操作 update account set money=money+100,version=version+1 where name='aaa' and version=0; 现在老公提交了事务,老婆再提交事务的时候发现版本号为 0 的记录没有了,所以就避免了数据丢失的问题。不过这种情况也导致了多个用户更新操作时,只有一个用户的更新被执行。

行级别的锁:
select * from employee where employeeID=9857 for update;
where 后边是索引列

不是索引列那么就为表级别的锁。

并发事物丢失更新解决

  • 排它锁(悲观所)
    读取一行数据时添加排他锁,限制这个事务未提交其他事务不能修改该行数据。

  • 乐观锁(添加版本控制)
    在更新数据时查看和查询时数据版本是否一致,不一致通知客户数据被修改,让其刷新数据后重新修改。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值