1

我有一个由 3 个 percona db 服务器组成的集群,这个表:

CREATE TABLE `metric` (
  `metricid` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `host` varchar(50) NOT NULL,
  `userid` int(10) unsigned DEFAULT NULL,
  `name` varchar(255) NOT NULL,
  `sampleid` tinyint(3) unsigned NOT NULL,
  PRIMARY KEY (`metricid`),
  UNIQUE KEY `unique-metric` (`userid`,`host`,`name`,`sampleid`)
) ENGINE=InnoDB AUTO_INCREMENT=1000000000000 DEFAULT CHARSET=utf8

每台服务器(ID 为 1、2、3)每秒运行一次此查询:

insert into metric set metricid = $serverId$now, host = $now, name = $serverId

(例如insert into metric set metricid = 31396887217, host = 1396887217, name = 3

并且很快我在第 1 行遇到“错误 1213 (40001):尝试获取锁定时发现死锁;尝试重新启动事务” - 鉴于我提供的 id 不重叠,因为每个服务器都使用不同前缀的 id ,为什么会发生死锁?

4

1 回答 1

1

发生冲突是因为 INSERT 实际上锁定了它要插入的“间隙”。在插入表末尾的情况下,它实际上锁定了所有现有行之后的间隙(理论上延伸到无穷大。

由于您有第二个 UNIQUE KEY 约束,情况变得复杂,因此当您运行两个并发会话时,InnoDB 需要创建两个间隙锁,一个在 PRIMARY KEY 上,一个在 UNIQUE KEY 上。因此,可能存在竞争条件,两个会话可能会以这样的方式获取它们的锁,从而最终相互等待。

我想知道为什么你有一个 AUTO_INCREMENT 主键,但是你覆盖了自动增量功能。使用自动增量可能会有所帮助,因为自动增量是通过一个非常简短的表锁来解决的,因此您可以避免死锁,因为您只有一个间隙锁。不过,我还没有测试过这个想法。

也可以看看:

于 2014-04-07T16:49:01.533 回答