限制 table 的行一次只能由一个脚本使用

Limit the row of the table to be used by one script at a time

我有一个 table tableName 其中包含一些要使用的名称:

id    name    in_use
1     name1   0
2     name2   0

我的脚本从这个 table 和 in_use = 0:

得到名字
SELECT name FROM tableName WHERE in_use = 0 ORDER BY id

并立即将获得的行设置为 in_use = 1:

UPDATE tableName set in_use = 1 WHERE id = '$idObtained'

当脚本完成时,它设置 in_use = 0:

UPDATE tableName set in_use = 0 WHERE id = '$idObtained'

问题是,当脚本将在同一瞬间执行多次时,两个或更多 select 可能会从这个 table.

获得相同的名称

我的目标是避免两个脚本在执行时获得同一行。

有一种方法可以用 PHP / MySQL?

是否在 select 之前锁定 table 并在更新后解锁?

脚本(代码只是一个草稿。只有在PHP和MySQL中可以满足我的需求,我才会开发它):

<?php
    // ...

    // TODO: lock the raw, if found
    $query = "SELECT id, name
    FROM tableName
    WHERE in_use = 0
    ORDER BY id";
    $stmt = $connection->prepare($query);
    $stmt->execute();
    $data = $stmt->fetch();

    if(!empty($data['id'])) {

        $query = "UPDATE tableName
        SET in_use = 1
        WHERE id = '$data[id]'";
        $stmt = $connection->prepare($query);
        $stmt->execute();

        // do some with $data['name]

        $query = "UPDATE tableName
        SET in_use = 0
        WHERE id = '$data[id]'";
        $stmt = $connection->prepare($query);
        $stmt->execute();

        // TODO: unlock the raw
    }
?>

非常感谢。

修正你的方法

BEGIN;
SELECT id, name FROM tableName WHERE in_use = 0 ORDER BY id
                  FOR UPDATE;   -- Add this 
$idObtained = ...
UPDATE tableName set in_use = 1 WHERE id = '$idObtained';
COMMIT;

稍后,完成该行的工作后:

UPDATE tableName set in_use = 0 WHERE id = '$idObtained';

讨论:

FOR UPDATE 防止另一个连接在 SELECTUPDATE 之间偷偷摸摸, 抓住那一行。

如果这一行的操作只需要几秒钟,那么这种技术就太过分了。因此,我假设这将花费很长时间,因此有两个单独的事务——一个到 'grab' 行,另一个到 'release' 它。

请注意,我没有在 'release' UPDATE 周围放置 BEGINCOMMIT。任何独立查询本身就是一个事务(假设 autocommit=ON)。因此,只需一个查询即可轻松完成发布。

替代方法

如果我们可以设计一条既查找 'free' 行又查找 'grabs' 的指令,那么 'simplify' 和 'grab' 是可能的。一种方法包括一个额外的列,说明每个抓取行的连接 'owns'。但是,让我们简单地改变 set_in_use 的工作方式。假设您不会为每个连接抓取超过一行,CONNECTION_ID() 很方便:

$conn = `SELECT CONNECTION_ID()`

UPDATE tbl SET in_use_by = $conn
    WHERE in_use_by IS NULL
    ORDER BY id
    LIMIT 1;      -- grab only 1 row
SELECT ... FROM tbl WHERE in_use_by = $conn;   -- Get the data to process

... process ...

UPDATE tbl SET in_use_by = NULL WHERE in_use_by = $conn;

使用这种技术,不需要显式事务。 autocommit=ON 工作正常。

此技术使您能够查看谁拥有什么物品。 (并不是说你可以用这些知识做很多事情。)

(可能还有其他方法。)