MySQL 中两个请求同时操作相同的表或记录时,发生一个处理失败或超时的问题的原因分析和解决办法

在 MySQL 中,两个请求同时操作相同的表或记录时,如果使用了锁机制(如 FOR UPDATE、事务、或是表锁),可能发生一个处理失败或超时的情况。

下面是一些常见情况分析及解决方案:


一、常见原因分析

1. 事务锁冲突(行锁或表锁)
  • 如果两个请求都开启了事务,并操作相同的行或表,会发生等待锁的情况。

  • 如果其中一个事务失败或等待超时(默认 innodb_lock_wait_timeout=50s),就会报错。

示例:

请求1:

START TRANSACTION;
UPDATE orders SET status='done' WHERE id=123; -- 持有行锁

请求2(几乎同时):

START TRANSACTION;
UPDATE orders SET status='cancel' WHERE id=123; -- 阻塞直到超时或前者提交/回滚

如果请求1迟迟不提交或回滚,请求2最终会失败报错:

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

2. 死锁
  • 两个事务互相等待对方释放锁,形成死锁,MySQL 会主动回滚其中一个。

示例:

请求1:

START TRANSACTION;
UPDATE table_a SET ... WHERE id=1;
-- 等待 table_b 的锁

请求2:

START TRANSACTION;
UPDATE table_b SET ... WHERE id=2;
-- 等待 table_a 的锁

会导致死锁,MySQL 报错:

ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

3. 显式表锁

使用 LOCK TABLES 会锁整个表,其他请求不能访问:

LOCK TABLES orders WRITE;

另一个请求就会被阻塞,直到释放锁。


二、解决方案建议

1. 减少锁定时间
  • 操作尽量短小、快速。

  • 尽早 COMMITROLLBACK

2. 设置合理的等待超时时间
  • innodb_lock_wait_timeout:默认 50 秒。可以根据业务特性调整。

SET innodb_lock_wait_timeout = 10;
3. 捕获并重试事务失败

对于 1213、1205 之类的错误,应用层建议进行捕获并自动重试。

4. 保持一致的访问顺序,避免死锁

始终按相同顺序访问多个表或行。

5. 使用更小粒度的锁
  • 避免 LOCK TABLES

  • 使用行级锁(InnoDB 默认支持)。

6. 开启死锁日志分析
SHOW ENGINE INNODB STATUS;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值