网站首页 > 教程文章 正文
前言
前几天,知识星球中的一个小伙伴,问了我一个问题:在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,数据库的隔离级别是:REPEATABLE-READ。
创建的索引如下:
其中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);
执行结果跟主键的情况也是一样的。
此时,开启了另外一个事务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不管操作的是哪一行数据,都会一直等待直到超时。
求一键三连:点赞、转发、在看。
猜你喜欢
- 2025-04-06 超级详细的zabbix环境搭建和测试(看我的教程,大神你也可以)
- 2025-04-06 oracle中merge into语句详解(oracle的merge语句)
- 2025-04-06 SQL——UPDATE:更新数据(更新sql语句)
- 2025-04-06 什么是 SQL 事务,如何创建 SQL 事务
- 2025-04-06 Win11学院:如何修复Windows Update服务丢失问题
- 2025-04-06 TiDB 专用语法,优化器注释和表属性
- 2025-04-06 20000 字干货笔记,一天搞定 MySQL
- 2025-04-06 数据库:Mysql中“select ... for update”排他锁分析
- 2025-04-06 【Mybatis实战第4天】三步搞定 Mybatis更新(update)操作
- 2025-04-06 超详细的Oracle19c修改数据库用户名教程
- 05-08云虚拟电脑与操作云电脑:相同还是不同?
- 05-08【三.丰.云】免费虚拟主机和免费云服务器,真的不错
- 05-08三丰云免费虚拟主机和云服务器评测
- 05-08阿贝云:免费的虚拟主机和云服务器,让我爱上云计算
- 05-08云服务器与虚拟主机的区别解析(云服务器与虚拟主机的区别解析图)
- 05-08(可以搭建游戏无盘)推荐一个免费云服务器,免费虚拟主机
- 05-08【阿贝云】免费云主机,免费虚拟主机
- 05-08免费云服务器和虚拟主机(免费云服务器和虚拟主机的区别)
- 最近发表
- 标签列表
-
- location.href (44)
- document.ready (36)
- git checkout -b (34)
- 跃点数 (35)
- 阿里云镜像地址 (33)
- qt qmessagebox (36)
- md5 sha1 (32)
- mybatis plus page (35)
- semaphore 使用详解 (32)
- update from 语句 (32)
- vue @scroll (38)
- 堆栈区别 (33)
- 在线子域名爆破 (32)
- 什么是容器 (33)
- sha1 md5 (33)
- navicat导出数据 (34)
- 阿里云acp考试 (33)
- 阿里云 nacos (34)
- redhat官网下载镜像 (36)
- srs服务器 (33)
- pico开发者 (33)
- https的端口号 (34)
- vscode更改主题 (35)
- 阿里云资源池 (34)
- os.path.join (33)