理解mariadb僵局
来自(稍微)更复杂的现实生活场景。我正在尝试使用一行来同步对另一(外部)资源的访问。它基本上是有效的,但我一直看到“僵局”,并试图了解原因 它至少在mariadb版本10.2.22(阿尔卑斯)和10.1.38(ubuntu)中出现 下表:理解mariadb僵局,mariadb,mysql-python,Mariadb,Mysql Python,来自(稍微)更复杂的现实生活场景。我正在尝试使用一行来同步对另一(外部)资源的访问。它基本上是有效的,但我一直看到“僵局”,并试图了解原因 它至少在mariadb版本10.2.22(阿尔卑斯)和10.1.38(ubuntu)中出现 下表: CREATE TABLE dlist ( dnum INTEGER AUTO_INCREMENT PRIMARY KEY, dname VARCHAR(64), dlnum INTEGER, last_u
CREATE TABLE dlist (
dnum INTEGER AUTO_INCREMENT PRIMARY KEY,
dname VARCHAR(64),
dlnum INTEGER,
last_update DATETIME,
CONSTRAINT UNIQUE dlist_nn (dname, dlnum));
我有几个并发进程正在插入/更新表中的行。同时,我有一个进程试图“窃取”最近未更新的行
插入:
INSERT INTO dlist (dname, dlnum, last_update)
VALUES (%s, %s, NOW())
ON DUPLICATE KEY
UPDATE last_update = NOW(), dnum = LAST_INSERT_ID(dnum);
删除:
DELETE FROM dlist
WHERE (NOW() - INTERVAL 20 SECOND) > last_update
LIMIT 1
RETURNING dnum, dname, dlnum;
问题是,我看到在插入和删除端都报告了相当频繁的死锁。mysql python报告的消息:
(1213, 'Deadlock found when trying to get lock; try restarting transaction')
我可以通过重试来解决这个问题,但为什么会发生这种情况?有没有办法重新构造SQL来防止它?我不明白为什么单次插入和/或删除会导致“死锁”
要复制的完整源代码(提供用户名、密码、数据库名作为参数):
似乎不需要
dnum
;摆脱它。对于PK,推广独特的:
PRIMARY KEY(dname, dlnum)
这可能对所述问题有帮助,也可能没有帮助。不,那么
DELETE
没有索引,因此它必须扫描表的很多部分,可能是全部。通过添加
INDEX(last_update)
更多
如果无法摆脱dnum
,那么这可能会有所帮助:将索引集更改为
PRIMARY KEY(dname, dlnum),
INDEX(dnum), -- This is the minimum to keep AUTO_INCREMENT happy
INDEX(last_update)
(我不知道这是否有助于进一步避免死锁。但是,当一个表有两个唯一键(PK+a unique)时,我发现了一些微妙的问题)。似乎没有必要使用
dnum
;摆脱它。对于PK,推广独特的:
PRIMARY KEY(dname, dlnum)
这可能对所述问题有帮助,也可能没有帮助。不,那么
DELETE
没有索引,因此它必须扫描表的很多部分,可能是全部。通过添加
INDEX(last_update)
更多
如果无法摆脱dnum
,那么这可能会有所帮助:将索引集更改为
PRIMARY KEY(dname, dlnum),
INDEX(dnum), -- This is the minimum to keep AUTO_INCREMENT happy
INDEX(last_update)
(我不知道这是否有助于进一步避免死锁。但是,当一个表有两个唯一键(PK+a unique)时,我会发现一些微妙的问题。当执行更新或删除时,它会锁定该表。不能进行其他操作。delete可能正在进行全表扫描,以查找符合where子句条件的记录。您可以将主键唯一性的责任转移到其他地方。从主键中删除自动增量。删除更新的ON DUPLICATE KEY子句。您的应用程序或mysql可以首先生成UUID,然后使用UUID作为dnum将记录插入表中。你为什么要不断地删除这些记录?您可以忽略旧的/无效的记录吗?您正在使用诸如returning子句之类的功能,这些功能仅在mariadb中可用,而在mysql中不可用。因此我删除了mysql标签,因为codr不适用于mysql。谢谢@Shadow没有意识到这是一个错误only@Noremac-InnoDB不会“锁定表”进行更新或删除。为什么UUIDs会有帮助?@RickJames正在扫描表并导致死锁。你不会说那是锁吧?他需要一个独特的值来跟踪记录。uuid/guid可以在插入之前以最小的冲突风险生成。它消除了对自动递增主键的需要。当执行更新或删除时,它会锁定表。不能进行其他操作。delete可能正在进行全表扫描,以查找符合where子句条件的记录。您可以将主键唯一性的责任转移到其他地方。从主键中删除自动增量。删除更新的ON DUPLICATE KEY子句。您的应用程序或mysql可以首先生成UUID,然后使用UUID作为dnum将记录插入表中。你为什么要不断地删除这些记录?您可以忽略旧的/无效的记录吗?您正在使用诸如returning子句之类的功能,这些功能仅在mariadb中可用,而在mysql中不可用。因此我删除了mysql标签,因为codr不适用于mysql。谢谢@Shadow没有意识到这是一个错误only@Noremac-InnoDB不会“锁定表”进行更新或删除。为什么UUIDs会有帮助?@RickJames正在扫描表并导致死锁。你不会说那是锁吧?他需要一个独特的值来跟踪记录。uuid/guid可以在插入之前以最小的冲突风险生成。它不需要自动递增的主键。
dnum
实际上是表提供的关键同步数据:即,它是控制对外部资源访问的索引。在实际代码中,它是通过cursor.lastrowid
返回的。换句话说,每个插入器都试图创建/更新一个对(dlname,dlnum)
唯一的外部资源。返回的dnum
标识要用于该操作的名称。然后,删除端尝试“窃取”并使用其中一个未使用的。如果另一个插入器试图获取相同的(dlname,dlnum)
,它只会启动一个新的插入器(使用一个新的dnum
)。但是,上次更新的索引为+1。有了这些,我再也看不到僵局了。我想知道,这是否真的“解决”了它,或者只是让它发生的可能性小得多。回答了我自己的问题:在运行了几分钟后,死锁仍然发生。(另外,我已将插入客户端的等待时间缩短为0.05秒,并在删除程序上添加了0.03秒的等待时间。)答案仍然很有用。@GilHamilton-感谢您对索引(上次更新)
的反馈。您的经验与我的猜测一致。@GilHamilton-我还添加了另一个可能的提示。dnum
实际上是表中提供的关键同步数据:即,它是控制对外部资源访问的索引。在实际代码中,它被返回vi