mysql insert 死锁_MySQL Insert 死锁

insert 上锁步骤

insert语句上锁的大致过程如下:

1、在行所在的间隙上申请“意向插入锁”。2、申请所要插入行的“排他锁”。3、如果在第二步的时候引发了唯一键冲突,那么陷入冲突的事务,要把上锁的过程分两步,先申请行的“共享锁”,然后再申请“排他锁”; 如果有多个事物陷入冲突,那么他们一定都会申请到“共享锁”,然后在申请排他锁的相互等待(死锁),这个时候 MySQL 会选择牺牲掉其中一些事务,让其中的一个完成。

复现 insert 死锁

按理论一步一步的复现 insert 死锁的场景。

会话一会话二会话三

create table t(x int primary key);

start transaction;

insert into t(x) values(1024);

insert into t(x) values(1024);

insert into t(x) values(1024);

rollback

commit successful

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

show engine innodb status 输出中关于死锁的信息如下。

------------------------

LATEST DETECTED DEADLOCK

------------------------

2019-09-03 19:29:30 0x7f9ab00b4700

*** (1) TRANSACTION:

TRANSACTION 2759830, ACTIVE 9 sec inserting

mysql tables in use 1, locked 1

LOCK WAIT 4 lock struct(s), heap size 1136, 2 row lock(s)

MySQL thread id 13, OS thread handle 140302402717440, query id 1221 127.0.0.1 root update

insert into t(x) values(1024)

*** (1) WAITING FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 16 page no 4 n bits 72 index PRIMARY of table `tempdb`.`t` trx id 2759830 lock_mode X insert intention waiting

Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0

0: len 8; hex 73757072656d756d; asc supremum;;

*** (2) TRANSACTION:

TRANSACTION 2759831, ACTIVE 6 sec inserting

mysql tables in use 1, locked 1

4 lock struct(s), heap size 1136, 2 row lock(s)

MySQL thread id 14, OS thread handle 140302355220224, query id 1222 127.0.0.1 root update

insert into t(x) values(1024)

*** (2) HOLDS THE LOCK(S):

RECORD LOCKS space id 16 page no 4 n bits 72 index PRIMARY of table `tempdb`.`t` trx id 2759831 lock mode S

Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0

0: len 8; hex 73757072656d756d; asc supremum;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 16 page no 4 n bits 72 index PRIMARY of table `tempdb`.`t` trx id 2759831 lock_mode X insert intention waiting

Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0

0: len 8; hex 73757072656d756d; asc supremum;;

*** WE ROLL BACK TRANSACTION (2)

MySQL 为什么要这么做

大前提是这样的,两个事务插入同一条记录,必定有一个要失败。死锁也是必定要有一个失败,那么何不把并发插入也合并到死锁的处理流程中去呢?想到就要做到,那把上行锁的过程拆一下, 先上“S 共享锁” 再上“X 排他锁”,由于“S”可以共享所以两个都能完成这一步,第二小要“X”所以两个事务就相互等待(死锁)。

解决方案

如果你在业务上还真遇到了上面的场景,然而 MySQL 死锁了。你又不想让它死锁,那怎么办呢?MySQL 不是把上锁的步骤拆开了吗?那我们把它合起来。

select ... for update 直接对行上排他锁,通过它我们可以把分开的两步合起来,on duplicate key update 当遇到冲突时会直接更新,不会再报错;结合这两大神器代码可以改成如下的形式。

会话一会话二会话三

create table t(x int primary key);

start transaction;

select x from t for update; insert into t(x) values(1024) on duplicate key update x = 1024;

select x from t for update; insert into t(x) values(1024) on duplicate key update x = 1024;

select x from t for update; insert into t(x) values(1024) on duplicate key update x = 1024;

rollback

commit successful

commit successful

副作用

由于 select for update 是排他锁,所以并发性上会有些问题,建议与read-committed一起使用。

彩蛋

根据分析可以知道问题发生在行锁上,把隔离级别调整成 READ-COMMITTED 也只会影响 gap 锁,我以为这个隔离级别下不会再有插入意向锁(一种特别的 gap)了,没想到还有。

mysql> show variables like '%iso%';

+-----------------------+----------------+

| Variable_name | Value |

+-----------------------+----------------+

| transaction_isolation | READ-COMMITTED |

+-----------------------+----------------+

1 row in set (0.00 sec)

执行同样的操作。

会话一会话二会话三

create table t(x int primary key);

start transaction;

insert into t(x) values(1024);

insert into t(x) values(1024);

insert into t(x) values(1024);

rollback

commit successful

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

------------------------

LATEST DETECTED DEADLOCK

------------------------

2020-04-11 13:57:15 0x7ff1bccf1700

*** (1) TRANSACTION:

TRANSACTION 6946, ACTIVE 12 sec inserting

mysql tables in use 1, locked 1

LOCK WAIT 4 lock struct(s), heap size 1136, 2 row lock(s)

MySQL thread id 9, OS thread handle 140676621076224, query id 237 127.0.0.1 root update

insert into t(x) values(1024)

*** (1) HOLDS THE LOCK(S):

RECORD LOCKS space id 5 page no 4 n bits 72 index PRIMARY of table `tempdb`.`t` trx id 6946 lock mode S

Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0

0: len 8; hex 73757072656d756d; asc supremum;;

*** (1) WAITING FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 5 page no 4 n bits 72 index PRIMARY of table `tempdb`.`t` trx id 6946 lock_mode X insert intention waiting

Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0

0: len 8; hex 73757072656d756d; asc supremum;;

*** (2) TRANSACTION:

TRANSACTION 6947, ACTIVE 9 sec inserting

mysql tables in use 1, locked 1

LOCK WAIT 4 lock struct(s), heap size 1136, 2 row lock(s)

MySQL thread id 13, OS thread handle 140676151592704, query id 258 127.0.0.1 root update

insert into t(x) values(1024)

*** (2) HOLDS THE LOCK(S):

RECORD LOCKS space id 5 page no 4 n bits 72 index PRIMARY of table `tempdb`.`t` trx id 6947 lock mode S

Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0

0: len 8; hex 73757072656d756d; asc supremum;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 5 page no 4 n bits 72 index PRIMARY of table `tempdb`.`t` trx id 6947 lock_mode X insert intention waiting

Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0

0: len 8; hex 73757072656d756d; asc supremum;;

*** WE ROLL BACK TRANSACTION (2)

READ-COMMITTED 隔离级别下已经没有 gap 锁了,然而官方文档上说 insert intention 是 gap 的一种,所以 insert intention 应该不存在才对呀。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MySQL中,死锁是指两个或多个事务相互等待对方释放资源的情况,导致事务无法继续执行并被数据库系统检测到。当发生死锁时,MySQL会选择其中一个事务进行回滚,以解开死锁并允许其他事务继续执行。 要解决MySQL中的插入死锁问题,可以考虑以下几点: 1. 使用合理的事务隔离级别:MySQL提供了多个事务隔离级别,如读未提交、读已提交、可重复读和串行化。根据业务需求和并发情况,选择合适的隔离级别可以减少死锁的可能性。 2. 优化事务的执行顺序:在编写代码时,尽量避免多个事务同时对相同的资源进行操作。如果必须同时操作相同的资源,可以通过调整事务的执行顺序来减少死锁的风险。 3. 减少事务的持有时间:长时间持有锁资源是产生死锁的常见原因之一。尽量缩短事务的执行时间,避免长时间占用锁资源,从而减少死锁的可能性。 4. 使用索引和合适的查询语句:良好的索引设计和合适的查询语句可以提高查询效率,减少锁冲突的可能性。确保表中的字段有适当的索引,并编写高效的查询语句。 5. 监控和处理死锁MySQL提供了一些系统变量和工具来监控和处理死锁。可以通过设置合适的参数来记录死锁日志,并及时处理死锁情况,以便快速恢复正常的数据库操作。 请注意,以上仅是一些常见的解决方案,具体的解决方法还需根据具体的业务场景和数据库设计进行调整和优化。在实际应用中,可能需要综合考虑多个因素来解决死锁问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值