Php 将表中的行限制为一次由一个脚本使用
我有一个表tableName,其中包含一些要使用的名称: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实现这一点 在更新之前锁定表并在更
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;