Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/56.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 将表中的行限制为一次由一个脚本使用_Php_Mysql_Locking_Innodb - Fatal编程技术网

Php 将表中的行限制为一次由一个脚本使用

Php 将表中的行限制为一次由一个脚本使用,php,mysql,locking,innodb,Php,Mysql,Locking,Innodb,我有一个表tableName,其中包含一些要使用的名称: id name in_use 1 name1 0 2 name2 0 我的脚本使用in_use=0从此表中获取第一个名称: 然后立即将获得的行设置为in_use=1: 脚本完成时,将其设置为_use=0: 问题是,当脚本在同一瞬间执行多次时,两个或多个选择可能会从此表中获得相同的名称 我的目标是避免两个脚本在执行时获得相同的行 有没有一种方法可以通过PHP/MySQL实现这一点 在更新之前锁定表并在更

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

id    name    in_use
1     name1   0
2     name2   0
我的脚本使用in_use=0从此表中获取第一个名称:

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

脚本完成时,将其设置为_use=0:

问题是,当脚本在同一瞬间执行多次时,两个或多个选择可能会从此表中获得相同的名称

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

有没有一种方法可以通过PHP/MySQL实现这一点

在更新之前锁定表并在更新之后解锁表是否可以做到这一点

代码中的脚本只是一个草稿。只有在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
    }
?>
非常感谢。

修复您的方法

稍后,使用该行完成工作后:

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

FOR UPDATE防止另一个连接潜入SELECT和UPDATE之间,并抓取该行

如果这一行上的操作只需要几秒钟,那么这种技术就太过分了。因此,我假设这将需要很长时间,因此有两个单独的事务-一个“抓取”行,另一个“释放”行

请注意,我没有在“release”更新中加入BEGIN和COMMIT。假设autocommit=ON,任何独立查询本身都是一个事务。因此,只需一个查询就可以轻松完成发布

替代方法

如果我们能设计出一条既能查找“空闲”行又能“捕获”它的指令,那么就有可能“简化”抓取。一种方法包括一个额外的列,说明哪一个连接“拥有”每一行。但是,让我们简单地改变set_in_use的工作方式。假设每个连接不需要抓取超过一行,那么连接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工作正常

此技术使您能够查看谁拥有哪些项目。并不是说你能用这些知识做很多事情


可能还有其他方法。

最有可能的情况是,您正在查找tableName中的SELECT name,其中in_use=0 ORDER BY id用于更新。。。解释你能发布PHP代码,让我们更好地理解发生了什么?由于您可能还需要使用设置隔离级别,我插入了一个摘录。PHP代码看起来可疑,因为它假设从MySQL返回一条记录。。我假设您需要循环,但我并不真正了解您的用例,在循环之前运行SET in_use=1,在循环之后运行SET in_use=0。代码只是一个草稿。我只会在PHP和MySQL中开发它。我添加了一个计划,并尝试了第二个解决方案,但它会更新in_use_by为NULL的所有行。我认为您没有在更新中添加限制1。@user2342558-很抱歉,非常抱歉。现在修好了。
<?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';
$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;