如果没有死锁,则插入行

Jes*_*sse 5 mysql sql deadlock innodb mariadb

我有一张简单的桌子

CREATE TABLE test (
  col INT,
  data TEXT,
  KEY (col)
);
Run Code Online (Sandbox Code Playgroud)

和一个简单的交易

START TRANSACTION;

SELECT * FROM test WHERE col = 4 FOR UPDATE;

-- If no results, generate data and insert
INSERT INTO test SET col = 4, data = 'data';

COMMIT;
Run Code Online (Sandbox Code Playgroud)

我试图确保同时运行此事务的两个副本导致没有重复行和没有死锁.我也不想承担发电成本datacol = 4不止一次.

我试过了:

  1. SELECT ..(没有FOR UPDATELOCK IN SHARE MODE):

    两个事务都看到没有行col = 4(没有获取锁),并且生成data和插入行的两个副本col = 4.

  2. SELECT .. LOCK IN SHARE MODE

    两个事务都获取共享锁col = 4,生成data并尝试插入行col = 4.两个事务都等待另一个事务释放它们的共享锁,因此它可以INSERT导致ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction.

  3. SELECT .. FOR UPDATE

    希望一个交易SELECT成功并获得一个独占锁定,col = 4另一个交易SELECT将阻止等待第一个交易.

    相反,两个SELECT .. FOR UPDATE查询都成功,事务就像使用一样进入死锁状态SELECT .. LOCK IN SHARE MODE.独家锁定col = 4似乎不起作用.

如何在不导致重复行且没有死锁的情况下编写此事务?

tad*_*man 0

稍微调整一下你的架构:

CREATE TABLE test (
  col INT NOT NULL PRIMARY KEY,
  data TEXT
);
Run Code Online (Sandbox Code Playgroud)

作为col主键,它不能重复。

然后使用该ON DUPLICATE KEY功能:

INSERT INTO test (col, data) VALUES (4, ...)
  ON DUPLICATE KEY UPDATE data=VALUES(data)
Run Code Online (Sandbox Code Playgroud)

  • 您在问题中给出的示例不会生成。它有一个简单的字符串。如果您想避免重复生成,请找出需要为其生成数据的 ID,并使用某种队列来调度这些操作。有多种作业队列系统可供选择。长时间保持打开锁会“乞求”死锁。 (2认同)