限制 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
防止另一个连接在 SELECT
和 UPDATE
之间偷偷摸摸, 也 抓住那一行。
如果这一行的操作只需要几秒钟,那么这种技术就太过分了。因此,我假设这将花费很长时间,因此有两个单独的事务——一个到 'grab' 行,另一个到 'release' 它。
请注意,我没有在 'release' UPDATE
周围放置 BEGIN
和 COMMIT
。任何独立查询本身就是一个事务(假设 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
工作正常。
此技术使您能够查看谁拥有什么物品。 (并不是说你可以用这些知识做很多事情。)
(可能还有其他方法。)
我有一个 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
防止另一个连接在 SELECT
和 UPDATE
之间偷偷摸摸, 也 抓住那一行。
如果这一行的操作只需要几秒钟,那么这种技术就太过分了。因此,我假设这将花费很长时间,因此有两个单独的事务——一个到 'grab' 行,另一个到 'release' 它。
请注意,我没有在 'release' UPDATE
周围放置 BEGIN
和 COMMIT
。任何独立查询本身就是一个事务(假设 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
工作正常。
此技术使您能够查看谁拥有什么物品。 (并不是说你可以用这些知识做很多事情。)
(可能还有其他方法。)