第3关:行锁

任务描述

本关任务:使用共享锁更新表中数据。

相关知识

在事务的隔离级别内容中,能够了解到两个不同的事务在并发的时候可能会发生数据的影响。细心的话可以发现事务隔离级别章节中,脏读、不可重复读、幻读三个问题都是由事务 A 对数据进行修改、增加,事务 B 总是在做读操作。如果两事务都在对数据进行修改则会导致另外的问题:丢失更新

这就是本实训所要学习的主题,同时引出并发事务对数据修改的解决方案。

为了完成本关任务,你需要掌握:1.丢失更新的定义及产生原因;2.如何解决丢失更新问题。

丢失更新的定义及产生原因

丢失更新就是两个不同的事务在某一时刻对同一数据进行读取后,先后进行修改,导致第一次操作数据丢失:

该问题和之前事务并发出现的几个问题需要区分开, 因为解决方案不是一类!此类问题,只能用最高隔离级别 Serializable 或者手动使用锁来解决,本关我们就一起学习如何使用锁来解决。

如何解决丢失更新问题

由第一关介绍存储引擎后,我们知道 InnoDB 存储引擎才支持事务,因此我们使用 行锁 来解决,我们还介绍了行锁包括悲观锁和乐观锁,悲观锁包含了读锁和写锁,下面我们就使用悲观锁的两种方式来解决。

使用共享锁

共享锁(S),又称为读锁,获得共享锁之后,针对同一份数据,多个读操作可以同时进行,互不影响,但无法修改和删除数据。

例如:我在客户端 A 给数据 C 添加了共享锁,此时我在客户端 B 只能添加共享锁进行查看,没有修改的权利,如果我想要在客户端 B 进行修改,我只能在 Acommit才能进行修改。

在查询语句后面增加LOCK IN SHARE MODEMySQL 会对查询结果中的每行都加共享锁:

 
  1. select ... lock in share mode;

使用排他锁

排他锁(X),又称为写锁、独占锁。获得排他锁后,在当前写操作没有完成前,它会阻断其他写锁和读锁。

排它锁就是我在客户端 A 给数据 C 添加了排它锁,那么我在客户端 B 只能在客户端 A commit之后,才能select数据。

换句话说,只要我在客户端 B 用锁进行了查询,那我我都需要等待 A commit之后,如果此时我客户端 B 不加锁,我是可以查询到的。

添加排它锁的方式:

 
  1. select ... for update;

编程要求

在右侧命令行中编写代码,开启两个命令行窗口连接数据库,体验共享锁的使用,(当一个事务 A 连接在做更新操作的时候,另一个事务 B 连接不能进行更新操作,事务 A 提交后,事务 B 则可进行更新操作并提交,此时更新操作不会被丢失),具体操作如下:

  1. 点击+添加一个命令行窗口,在两个命令行中连接数据库(用户名为root,密码为123123);

  2. 假设源命令行窗口为 A 连接,新增加的窗口为 B 连接,

    ,请在 A 中连接数据库后,执行平台 /data/workspace/myshixun/src/step3/table.sqlsql文件,将会为你创建好mydb数据库和account表,执行命令为:

     
      
    1. source /data/workspace/myshixun/src/step3/table.sql;
  3. AB 连接中均开启事务,使用共享锁查询account表中原有余额,并在 A 连接中将 id1的余额改为0id2的余额改为200,在 B 连接中将id2的余额改为0

  4. 此时 B 连接中在等待 A 连接的更新操作完成(提交事务),先后将事务 AB 提交即可,平台将输出你更新后的用户余额。

测试说明

平台会对你编写的代码进行测试:

预期输出:

 
  1. +----+------+-------+
  2. | id | name | money |
  3. +----+------+-------+
  4. | 1 | A | 0 |
  5. | 2 | B | 0 |
  6. +----+------+-
    ①:首先打开两个命令窗口,
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/89e06952da0744cc9f6f0c5df82b2119.png)
    
    
    ②:在第一个窗口中执行如下命令:
    
    mysql -uroot -p123123
    source /data/workspace/myshixun/src/step3/table.sql;
    begin;
    ③:在第二个窗口中执行如下命令:
    
    mysql -uroot -p123123
    begin;
    ④:切到第一个窗口:
    
    select * from account lock in share mode;
    ⑤:切回第二个窗口:
    
    use mydb;
    select * from account lock in share mode;
    ⑥:再切到第一个窗口:
    
    update account set money=0 where id='1';//此时窗口在等待
    ⑦:回到第二个窗口:
    
    update account set money=0 where id='2';//命令执行失败
    ⑧:回到第一个窗口,上一个命令被执行,接着更新表中信息:
    
    update account set money=200 where id='2';
    commit;
    ⑨:切到第二个窗口:
    
    update account set money=0 where id='2';//由于窗口一已经commit,因此再次执行命令时成功
    commit;
    ⑩:点击评测
    
    ------+
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小珠佩奇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值