首页 > 其他分享 >深入理解select for update的含义和锁机制

深入理解select for update的含义和锁机制

时间:2024-08-04 13:07:55浏览次数:13  
标签:事务 含义 update age user where id select

见博客:

1、深入理解select for update的含义和锁机制

 

2、select for update锁的是行还是表?

如果查询条件字段的不同,主键,索引,普通字段,那么锁的是行还是表,是不同的

select...for update锁详解

 

3、select……for update到底是加了行锁,还是表锁?

参考博客:https://baijiahao.baidu.com/s?id=1780732545537295833&wfr=spider&for=pc

前言

前几天,知识星球中的一个小伙伴,问了我一个问题:在MySQL中,事务A中使用select...for update where id=1锁住了,某一条数据,事务还没提交,此时,事务B中去用select ... where id=1查询那条数据,会阻塞等待吗?

select...for update在MySQL中,是一种悲观锁的用法,一般情况下,会锁住一行数据,但如果没有使用正确的话,也会把整张表锁住。

其实,我之前也在实际项目中试过用,比如:积分兑换礼品的功能。

今天跟大家一起聊聊select...for update这个话题,希望对你会有所帮助。

1. 要什么要用行锁?

假如现在有这样一种业务场景:用户A给你转账了2000元,用户B给你转账了3000元,而你的账户初始化金额是1000元。

在事务1中会执行下面这条sql:

update account set money=money+2000 where id=123;

 在事务2中执行下面这条sql:

update account set money=money+3000 where id=123;

 这两条sql执行成功之后,你的money可能是:3000、4000、6000,这三种情况中的一种。

你之前的想法是,用户A和用户B总共给你转账5000,最终你账户的钱应该是6000才对,3000和4000是怎么来的?

假如事务1在执行update语句的过程中,事务2同时也在执行update语句。

事务1中查询到money是1000,此外事务2也查询到money是1000。

如果事务1先执行update语句,事务2后执行update语句,第一次update的3000,会被后面的4000覆盖掉,最终结果为4000。

如果事务2先执行update语句,事务1后执行update语句,第一次update的4000,会被后面的3000覆盖掉,最终结果为3000。

这两种情况都产生了严重的数据问题。

我们需要有某种机制,保证事务1和事务2要顺序执行,不要一起执行。

这就需要加锁了。

目前MySQL中使用比较多的有:表锁、行锁和间隙锁。

我们这个业务场景,非常时候使用行锁

在事务1执行update语句的过程中,先要把某一行数据锁住,此时,其他的事务必须等待事务1执行完,提交了事务,才能获取那一行的数据。

在MySQL中是通过select...for update语句来实现的行锁的功能。

但如果你在实际工作中使用不正确,也容易把整张表锁住,严重影响性能。

select...where...for update语句的用法是否正确,跟where条件中的参数有很大的关系。

我们一起看看下面几种情况。

假如user表现在有这样的数据库,数据库的版本是:8.0.21。

 创建的索引如下:

 其中id是主键字段,code是唯一索引字段,name是普通索引字段,其他的都是普通字段。

 

2. 主键

当where条件用的数据库主键时。

例如开启一个事务1,在事务中更新id=1的用户的年龄:

begin;select * from user where id=1 for update;update user set age=22 where id=1;

where条件中的id是数据库的主键,并且使用for update关键字,加了一个行锁,这个事务没有commit。 

此时,开启了另外一个事务2,也更新id=1的用户的年龄:

begin;update user set age=23 where id=1;commit;

在执行事务2的sql语句的过程中,会一直等待事务1释放锁。

 如果事务1一直都不释放行锁,事务2最后会报下面这个异常:

 如果此时开始一个事务3,更新id=2的用户的年龄:

begin;update user set age=23 where id=2;commit;

执行结果如下:

由于事务3中更新的另外一行数据,因此可以执行成功。

说明使用for update关键字,锁住了主键id=1的那一行数据,对其他行的数据并没有影响。

 

3. 唯一索引

当where条件用的数据库唯一索引时。

开启一个事务1,在事务中更新code=101的用户的年龄:

begin;select * from user where code='101' for update;update user set age=22 where code='101';

where条件中的code是数据库的唯一索引,并且使用for update关键字,加了一个行锁,这个事务没有commit。

此时,开启了另外一个事务2,也更新code=101的用户的年龄:

begin;update user set age=23 where code='101';commit;

执行结果跟主键的情况是一样的。

 

4. 普通索引

当where条件用的数据库普通索引时。

开启一个事务1,在事务中更新name=周星驰的用户的年龄:

begin;select * from user where name='周星驰' for update;update user set age=22 where name='周星驰';

where条件中的name是数据库的普通索引,并且使用for update关键字,加了一个行锁,这个事务没有commit。

此时,开启了另外一个事务2,也更新name=周星驰的用户的年龄:

begin;update user set age=23 where name='周星驰';commit;

执行结果跟主键的情况也是一样的。

 

5. 主键范围

当where条件用的数据库主键范围时。

开启一个事务1,在事务中更新id in (1,2)的用户的年龄:

begin;select * from user where id in (1,2) for update;update user set age=22 where id in (1,2);

where条件中的id是数据库的主键范围,并且使用for update关键字,加了多个行锁,这个事务没有commit。

此时,开启了另外一个事务2,也更新id=1的用户的年龄:

begin;update user set age=23 where id=1;commit;

执行结果跟主键的情况也是一样的。

此时,开启了另外一个事务2,也更新id=2的用户的年龄:

begin;update user set age=23 where id=2;commit;

执行结果跟主键的情况也是一样的。

 

6. 普通字段

当where条件用的数据库普通字段时。

该字段既不是主键,也不是索引。

开启一个事务1,在事务中更新age=22的用户的年龄:

begin;select * from user where age=22 for update;update user set age=22 where age=22 ;

where条件中的age是数据库的普通字段,并且使用for update关键字,加的是表锁,这个事务没有commit。

此时,开启了另外一个事务2,也更新age=22的用户的年龄:

begin;update user set age=23 where age=22 ;commit;

此时,执行事务2时,会一直阻塞等待事务1释放锁。

调整一下sql条件,查询条件改成age=23:

begin;update user set age=23 where age=23 ;commit;

此时,行事务3时,也会一直阻塞等待事务1释放锁。

也就是说,在for update语句中,使用普通字段作为查询条件时,加的是表锁,而并非行锁。

 

7. 空数据

当where条件查询的数据不存在时,会发生什么呢?

开启一个事务1,在事务中更新id=66的用户的年龄:

begin;select * from user where id=66 for update;update user set age=22 where id=66 ;

这条数据是不存在的。

此时,开启了另外一个事务2,也更新id=66的用户的年龄:

begin;update user set age=23 where id=66 ;commit;

执行结果:

执行成功了,说明这种情况没有加锁。

 

总结

最后给大家总结一下select...for update加锁的情况:

  • 主键字段:加行锁。

  • 唯一索引字段:加行锁。

  • 普通索引字段:加行锁。

  • 主键范围:加多个行锁。

  • 普通字段:加表锁。

  • 查询空数据:不加锁。

如果事务1加了行锁,一直没有释放锁,事务2操作相同行的数据时,会一直等待直到超时。

如果事务1加了表锁,一直没有释放锁,事务2不管操作的是哪一行数据,都会一直等待直到超时。

 

 

 

--

 

 

标签:事务,含义,update,age,user,where,id,select
From: https://www.cnblogs.com/tenWood/p/18341641

相关文章

  • go select case的一个小坑
    业务背景之前在写很多ifelse时,对于不符合的分支条件总是习惯提前返回,减少对后面分支的心智负担,最近在写1个go项目时,对于比较少使用go,在forselect结构里遇到错误返回,导致直接返回了,后续tick就无效了代码抽离简化如下funcConsumer(){ tick:=time.NewTicker(time.Durati......
  • VMware vSphere 8 Update 3 新增功能
    VMwarevSphere8Update3新增功能作者主页:sysin.orgvSphere8.0Update3已于2024-06-25发布,让我们先来了解一下其新增功能。VMwarevSphere8.0Update3下载-企业级工作负载平台又到了这个时候了!是时候对vSphere8进行另一次功能丰富的更新了。隆重推出vSphe......
  • golang 如从一个通道(channel)接收数据时在预期时间没接收到,可以使用select语句和time.A
    在Go语言中,如果希望在从一个通道(channel)接收数据时设置超时,可以使用select语句和time.After函数。以下是一个示例代码,演示了如何实现这个功能:packagemainimport("fmt""time")funcmain(){//创建一个通道ch:=make(chanstring)//启动一......
  • vue el-select实时搜索模糊查询,匹配文字高亮显示
    原文链接:https://blog.csdn.net/weixin_49668076/article/details/122678834参考elementuiselect的远程搜索对应属性的解释<el-selectclass="inputInfo"v-model="searchcursom"filterableclearableremote......
  • conda update python 不会更新,但 conda update --all 会更新
    我正在尝试更新我的venv。这就是我看到的(base_test)>condaupdatepythonCollectingpackagemetadata(current_repodata.json):doneSolvingenvironment:done==>WARNING:Anewerversionofcondaexists.<==currentversion:4.10.3latestversion:24......
  • 自闭症摘帽:含义与希望
    在自闭症领域,“自闭症摘帽”这个术语常常被提及,但其确切含义对于许多人来说可能并不清晰。自闭症摘帽,简单来说,指的是曾经被诊断为自闭症的儿童,经过一段时间的干预、治疗和发展,其症状得到了显著改善,达到了不再符合自闭症诊断标准的程度。这是一个充满希望和挑战的概念,它给那些......
  • 在Mybatis中不对select做任何改变,将物理删除的逻辑迁移到逻辑删除
    假设现在有一个已经完成或者已经到中期的项目,但是忘记做逻辑删除了,更改的办法如下第一步,在项目中添加如下的拦截器importcom.baomidou.mybatisplus.core.toolkit.PluginUtils;importcom.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor;importlom......
  • 如何解决el-select-dropdown的宽度过长
    问题:由于名称有的特别长,导致下拉框的宽度也跟着边长,导致页面效果不好。解决办法:1、首先设置el-select的属性popper-append-to-body为false:不将下拉弹出框插入至body元素<el-selectv-model="listQuery.company":popper-append-to-body="false"class="input-select"filter......
  • [React] Force React to update it's DOM by using flushSync
    Refertohttps://react.dev/reference/react-dom/flushSync Forexample,thebrowseronbeforeprintAPIallowsyoutochangethepageimmediatelybeforetheprintdialogopens.Thisisusefulforapplyingcustomprintstylesthatallowthedocumenttodispl......
  • 动态修改el-select 选中的值字体颜色 和下拉框字体颜色
    <el-table-columnlabel="优先级"width="120"><templateslot-scope="scope"><div:class="{'priorit1':scope.row.taskLevel===1,'priorit2�......