mysql 的子查询会导致锁表? lock wait timeout exceeded; try restarting transactio的处理

本文围绕MySQL子查询导致锁表及“lock wait timeout exceeded”异常展开。介绍了事务概念及隔离性问题,不同数据库隔离级别实现,指出MySQL默认隔离级别易造成子查询锁读表。最后给出两个解决方案:更改事务隔离级别;操作前创建新表关联更新。

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

mysql 的子查询会导致锁表? lock wait timeout exceeded; try restarting transactio的处理

问题

“lock wait timeout exceeded; try restarting transactio” 是非常常见的mysql异常。意思就是获取锁失败了,无法执行操作。

如何查看到底是哪个锁导致的,网上文章一搜一大把,不赘述。其实主要就是以下三张表就可以了。

select * from information_schema.innodb_locks;
select * from information_schema.innodb_trx;
select * from information_schema.innodb_lock_waits;

配合show full prcesslist基本可以定位了

今天说说一个update会锁子查询的问题。SQL如下

update A set afield1 = (select count(1) from B where id = A.id) where id = 123;
UPDATE A  SET afield1 = '有' WHERE bfield1 IN (SELECT DISTINCT bfield1 FROM B WHERE bfield1 = '1');

这两个sql都会导致 B表被锁定,可能有些读者会认为B表只是读取呀,不是只有写入的时候才会锁吗,为什么会锁呢?这牵扯一个事务的概念。

事务

事务是为了保证数据库的一致性和完整性所设立的概念。大学都学过事务有原子性、一致性、隔离性、原子性,其余的都好说好理解,其实容易产生理解问题的是隔离性。

因为隔离性并不好实现,在一个事务往往既有查询又有插入又有删除,在你查询之后,删除之前,数据往往发生改变,这会带来一个一些脏读,不可重复读等问题。因此最完美的隔离当然是从时间上隔离,一个事务执行完了,再执行另外一个事务,肯定完全不会产生问题。

但是而在实际生产过程中为了效率我们往往并不这样做,而是有选择的牺牲了部分隔离的特性来换取并发效率的提升。因此数据库隔离有四个级别分别是:

√: 可能出现 ×: 不会出现

隔离级别脏读不可重复读幻读
Read uncommitted(读取未提交)
Read committed(读取提交)×
Repeatable read(可重复读)××
Serializable(串行)×××

上图中的Serializable就是按照时间完全串行执行的,最大程度的保证了隔离级别,但是带来的问题往往是效率很低。而Read uncommitted则因为能读取还未提交的数据,不符合隔离的初衷,应用场景寥寥。因此目前主流的数据库往往都采用了Read committed和Repeatable read。

不同数据库的隔离级别实现

不同的数据库有不同的倾向和实现,甚至于在这些隔离界别上还会再细化分出更细的隔离级别,会跟上面列出会稍有区别。比如db2还有什么Cursor Stability(游标稳定级别).

但是主流的比如oracle、sql server 默认是下基本上是Read committed,因为这是业界比较公认的合适的,能够满足绝大部分的应用场景。但是,mysql比较特殊,它的隔离级别默认是Repeatable read。这给很多程序员其实造成了一些困扰。因为他有很大可能会造成子查询锁读表的情况,造成生产故障,因为mysql要保证在这个事务中每个select是可以重复读的会产生锁表

因此其实很多mysql的衍生版本比如阿里云的rds已经把隔离级别默认调整为Read committed

你可以使用下命令查看你的mysql的隔离级别:

SELECT @@tx_isolation

可以使用以下命令更改:

SET session TRANSACTION ISOLATION LEVEL READ COMMITTED; 

回到问题

结论其实很清楚了,两个解决方案:

1、更改数据库事务隔离级别

2、执行操作前,先创建另外一张表B1,再做相应的更新关联,锁B1表,而不会锁B表。

其他的资料

想要深入了解一些其他内容的,移步以下链接:

https://blog.youkuaiyun.com/wudongxu/article/details/6966052

http://www.cnblogs.com/crazylqy/p/7611069.html

https://www.cnblogs.com/annsshadow/p/5037667.html

### MySQL 更新时出现等待超时问题的解决方案 当遇到 `Lock wait timeout exceeded; try restarting transaction` 的错误时,这通常明当前事务因长时间未能获取到所需的资源而被回滚。以下是针对该问题的具体分析和解决方案: #### 1. 背景描述 此错误通常是由于多个并发事务争夺同一资源而导致定冲突引起的[^3]。具体现为某个事务试图修改已被另一个未提交事务占用的数据。 --- #### 2. 原因分析 - **长事务持有**:某些事务运行时间过长,在其完成之前阻止了其他事务对该数据的操作。 - **死情况**:两个或更多事务相互依赖对方持有的,形成循环等待。 - **高并发环境下的竞争**:在高并发场景下,频繁读写相同记录可能导致争用加剧。 - **配置参数不足**:默认情况下,InnoDB 存储引擎中的 `innodb_lock_wait_timeout` 参数设置为较低值(如 50 秒),可能不足以满足复杂业务需求[^3]。 --- #### 3. 解决方案 ##### 3.1 查询并终止阻塞事务 通过查看当前活动会话及其状态来定位哪个事务正在占用所需资源,并考虑手动中断它以释放: ```sql -- 查看所有进程列 SHOW PROCESSLIST; -- 杀掉指定 ID 的线程 KILL <thread_id>; ``` 如果发现某条语句执行时间异常延长,则可能是造成堵塞的原因之一[^3]。 ##### 3.2 扩展等待超时时限 调整 InnoDB 默认等待时限可以给事务更多的机会去获得必要的而不至于立即失败。可以通过修改全局变量实现这一点: ```sql SET GLOBAL innodb_lock_wait_timeout = 120; ``` 注意重启服务可能会恢复原始设定,因此建议将其加入 my.cnf 配置文件永久生效[^3]: ```ini [mysqld] innodb_lock_wait_timeout=120 ``` ##### 3.3 优化 SQL 逻辑减少范围及时长 重新审视涉及更新操作的相关代码片段,确保它们遵循最佳实践原则,比如只加载必要字段而非整张扫描;利用索引加速检索过程从而缩短加周期等等[^4]。例如对于批量处理任务来说,分批次提交而不是一次性全部做完往往能有效缓解压力。 ##### 3.4 使用乐观机制替代悲观策略 传统方式采用显式的行级排他控制访问权限容易引发此类问题,转而应用版本号验证方法可以在一定程度上规避这些问题的发生几率[^5]。即每次变更前先校验目标对象最新版本号是否匹配预期值,如果不符则提示用户刷新页面后再试。 --- ### 示例代码展示如何增加等待时间 下面是一个简单的例子说明怎样动态改变 session 层面的等待阈值以便临时解决问题: ```sql -- 设置当前连接内的等待时间为 300 秒 SET SESSION innodb_lock_wait_timeout = 300; -- 尝试再次执行原来失败的 DML 操作 DELETE FROM facebook_posts WHERE id = 7048962; ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值