两个事务持有相同排它锁导致InnoDB死锁

问题描述 投票:0回答:1

我正在使用 InnoDB 运行 MySql v8.0。我有一个简单的交易,其中有一个

SELECT
(不是
SELECT ... FOR UPDATE
),后跟一个
INSERT
。当许多数据库插入同时发生时,我经常遇到死锁。所有插入都使用相同的代码,因此应该具有相同的获取锁顺序。表的主索引基于
id
,它是自动递增的。但死锁似乎是由于二级索引上的同时锁定而发生的。
SHOW ENGINE INNODB STATUS
的输出是:

------------------------
LATEST DETECTED DEADLOCK
------------------------
2024-11-11 05:43:03 70378481278848
*** (1) TRANSACTION:
TRANSACTION 75372951333, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1128, 2 row lock(s), undo log entries 1
MySQL thread id 10225859, OS thread handle 70369406140288, query id 1436757204 172.22.120.99 _99952768SM1GL7w update
insert into shoes
            ( account_id
            , premium
            , name
            , state
            )
          values
            ( 22496527
            , 0
            , 'boots'
            , 'active'
*** (1) HOLDS THE LOCK(S):
RECORD LOCKS space id 1087 page no 634440 n bits 432 index index_shoes_on_account_id_and_premium_and_name of table `staging`.`shoes` trx id 75372951333 lock_mode X
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 1087 page no 634440 n bits 432 index index_shoes_on_account_id_and_premium_and_name of table `staging`.`shoes` trx id 75372951333 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 75372951336, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1128, 2 row lock(s), undo log entries 1
MySQL thread id 10225858, OS thread handle 70373717872512, query id 1436757206 172.22.120.99 _99952768SM1GL7w update
insert into shoes
            ( account_id
            , premium
            , name
            , state
            )
          values
            ( 22496526
            , 0
            , 'boots'
            , 'active'
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 1087 page no 634440 n bits 432 index index_shoes_on_account_id_and_premium_and_name of table `staging`.`shoes` trx id 75372951336 lock_mode X
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 1087 page no 634440 n bits 432 index index_shoes_on_account_id_and_premium_and_name of table `staging`.`shoes` trx id 75372951336 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)

为什么两个事务都对具有相同空间id的记录持有排他锁?由于插入的行不同并且具有不同的索引,为什么这会成为问题呢?这是否与这两个事务都会导致索引大于当前最大索引这一事实有关?即,两者都会导致至上值的更新?

因为两个事务都插入不同的行并将创建不同的索引记录,所以我希望两个事务都能继续进行而不会导致死锁。

编辑:

SHOW CREATE TABLE shoes
的输出是:

CREATE TABLE `shoes` (
  `id` int NOT NULL AUTO_INCREMENT,
  `account_id` int NOT NULL,
  `premium` tinyint(1) DEFAULT '0',
  `name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `state` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `index_shoes_on_account_id_and_premium_and_name` (`account_id`,`premium`,`name`),
  KEY `shoes_account_id_name_idx2` (`account_id`,`name`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
mysql indexing innodb deadlock database-locking
1个回答
0
投票

也许这就是正在发生的事情。

两个

INSERTs
正在将新行插入到BTree处理中

UNIQUE KEY (`account_id`,`premium`,`name`),

INSERT
和下面的
account_id
正在做锁定,以避免其他人潜入该 BTree 中的同一位置。 其他
INSERT
落在同一地点并弃踢。

那么,你会问:“为什么不能解决呢?” 我认为答案是,对于如此罕见的事情来说,解决这个问题的努力太高了。

这应该是一个可行的修复方法:每次

INSERT
之后,检查是否有失败。 如果发生死锁,则重播中止的事务。 到那时,另一个查询将完成并释放锁。

这是许多微妙的僵局之一,导致了在必要时始终检查和重放的规则。

© www.soinside.com 2019 - 2024. All rights reserved.