Question

I have a cluster of 3 percona db servers, and this table:

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

Each server (ids 1,2,3) runs this query every second:

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

(e.g. insert into metric set metricid = 31396887217, host = 1396887217, name = 3)

And pretty fast I'm running into "ERROR 1213 (40001) at line 1: Deadlock found when trying to get lock; try restarting transaction" - given that I provide ids that don't overlap since each server makes an id with different prefix, why do deadlocks happen at all?

Was it helpful?

Solution

The conflict occurs because an INSERT actually locks the "gap" that it's inserting into. In the case of inserting into the end of a table, it actually locks the gap that is after all the existing rows (and theoretically extending out into infinity.

The situation is complicated by the fact that you have a second UNIQUE KEY constraint, so when you run two concurrent sessions, InnoDB needs to create two gap locks, one on the PRIMARY KEY and one on the UNIQUE KEY. Therefore there's a race condition possible, where the two sessions may acquire their locks in such a way that they end up waiting on each other.

I'm wondering why you have an AUTO_INCREMENT primary key, but you're overriding the auto-increment feature. Using the auto-increment may help, in that auto-increment is resolved with a very brief table lock, and therefore you could avoid the deadlock because you'll only have one gap lock. I haven't tested this idea, though.

See also:

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top