select for update引发死锁分析

本文详细解析了在MySQL InnoDB存储引擎中出现的间隙锁问题,包括如何复现该问题及其实验环境搭建过程,并提供了具体的解决方法,如使用分布式锁和调整事务隔离级别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题复现

问题描述:4场直播同时准备作业的时候,发现有作业准备失败。查看这块逻辑,发现准备作业时会先查询作业是否有这条记录,这里添加了写锁。
在这里插入图片描述
而作业准备前是没有记录的,所以这里就会造成间隙锁。

间隙锁

先营造测试环境,给测试表role字段添加唯一索引。
假设现在只有3条数据。
在这里插入图片描述
接着执行如下命令:

#设置手动提交
set @@autocommit=0;
#查看是否生效
SHOW VARIABLES like '%autocommit%';
#开启事务
START transaction ;
SELECT * FROM `xxl_job_user` where  role =4 for update;

在这里插入图片描述
然后通过role查询等于4的,这里查不到数据库,就会锁住role>=4之后的所有数据。
但是另外的线程还是可以访问表的,在这时插入role=4的就会阻塞。
在这里插入图片描述
间隙锁在InnoDB的唯一作用就是防止其它事务的插入操作,以此来达到防止幻读的发生,所以间隙锁不分什么共享锁与排它锁。
特别注意:
因为是间隙锁,假设我现在插入一条数据,role=8;
在这里插入图片描述
接着执行

SELECT * FROM `xxl_job_user` where  role =4 for update;

之后插入role>8的就会被成功。因为这里间隙锁只会在4~8之间生效。
在这里插入图片描述

间隙锁扩展

在这里插入图片描述

解决方法

第一步:杀死进程id(就是 命令的)
获取trx_mysql_thread_id。

SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX; 
kill 线程ID

第二步:引入分布式锁解决。

其他解决方式
修改事务隔离策略。(没用)

show variables like 'tx_isolation';
SET session TRANSACTION ISOLATION LEVEL  Read committed;

这样间隙锁就失效了,从而升级为表锁。会阻塞所有的请求。
在这里插入图片描述
还是会阻塞
在这里插入图片描述

相关排查指令

#查看当前在锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;
#查看当前锁定的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
#查看当前等锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
show OPEN TABLES where In_use > 0;
SHOW ENGINE INNODB STATUS

在这里插入图片描述

参考:https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html
https://www.cnblogs.com/micrari/p/8029710.html

### SQLSELECT FOR UPDATE的用法及行为 在SQL中,`SELECT FOR UPDATE` 是一种用于确保数据一致性的机制,通常与事务结合使用。它允许用户在查询某些行时,同时锁定这些行,以防止其他事务对这些行进行修改或删除操作[^4]。 #### 1. 基本语法 `SELECT FOR UPDATE` 的基本语法如下: ```sql SELECT column1, column2, ... FROM table_name WHERE condition FOR UPDATE; ``` - `FOR UPDATE` 表示查询的行会被锁定,直到当前事务结束。 - 锁定的行无法被其他事务修改或删除,但可以被读取。 #### 2. 锁定行为 当执行 `SELECT FOR UPDATE` 时,数据库会对查询结果中的每一行加排他锁(exclusive lock)。这意味着: - 其他事务不能修改或删除这些行,直到当前事务提交或回滚[^5]。 - 其他事务仍然可以读取这些行,但如果尝试修改或删除,将被阻塞,直到锁释放。 #### 3. 示例代码 以下是一个典型的 `SELECT FOR UPDATE` 示例: ```sql BEGIN; -- 查询并锁定指定条件的行 SELECT inventory_count FROM products WHERE product_id = 123 FOR UPDATE; -- 更新库存数量 UPDATE products SET inventory_count = inventory_count - 1 WHERE product_id = 123; COMMIT; ``` 上述代码展示了如何在事务中使用 `SELECT FOR UPDATE` 来确保并发环境下的数据一致性[^6]。 #### 4. 注意事项 - **死锁风险**:如果多个事务同时对同一组行执行 `SELECT FOR UPDATE`,可能会导致死锁。因此,在设计应用程序时需要特别注意事务的顺序和范围[^7]。 - **性能影响**:由于 `FOR UPDATE` 会锁定行,可能导致其他事务等待,从而降低系统吞吐量。应尽量减少锁定的行数和时间。 - **隔离级别**:`SELECT FOR UPDATE` 的行为可能受到数据库隔离级别的影响。例如,在可重复读(Repeatable Read)或串行化(Serializable)隔离级别下,锁定的范围可能更大[^8]。 #### 5. 应用场景 - 银行转账:确保账户余额在更新前不会被其他事务修改。 - 库存管理系统:避免多个用户同时扣减库存导致超卖问题。 - 订单处理:确保订单状态在更新前不会被其他事务修改。 --- ###
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值