在被另一个伴随请求读取之前更新行 MySQL

Update row before being read by another concomitant request MySQL

我正在编写一个 PHP 页面,为每个请求提供一个响应,该响应是来自 table 的一个名为 TOKENS 的值,每个检索到的令牌不得提供给其他请求,所以我' m 将名为“used”的字段更新为 1,以便使用“WHERE used=0”条件无法再次检索该行。

<?php
// read an un used token
$sql = "SELECT tokenvalue FROM TOKENS WHERE used=0 ORDER BY ID DESC LIMIT 1;";
$result = $conn->query($sql);

if (!$result) {
    die('Invalid query: ' . $conn->error);
}
if ($result->num_rows > 0) {
    while($row = $result->fetch_assoc()) {
        // set row status to used so it can't be retrieved again
        $sql = "UPDATE TOKENS SET used=1;";
        $conn->query($sql);

        echo ($row["tokenvalue"]);
        break;
    }
}
?>

我面临的问题是,当我通过一次发送 50 个请求来测试脚本时,许多响应返回与其他响应相同的标记,这意味着它们交织在一起,一些请求在更新之前检索了行第一个请求获得同一行。这是一些请求的输出,粗体哈希值是重复的。

79835adf60b70abd4046b4ca3d3db74c 2f44bfa1cd1d66e4da39b7242e88ede1 2f44bfa1cd1d66e4da39b7242e88ede1 2f44bfa1cd1d66e4da39b7242e88ede1 4320f3a5273b493258ba66db4028fcc2 79835adf60b70abd4046b4ca3d3db74c 9d3cb74140368befbe6fd9dc0ca209a1 47a8e01fec82a44af3b1bd63f3e77816 47a8e01fec82a44af3b1bd63f3e77816 47a8e01fec82a44af3b1bd63f3e77816 2a9ae219d2d57f9988caff3baf48b9b3

我在Whosebug上搜索过,但没有发现类似的问题。 如何让一个请求只读一次行?

我找到了解决方案,感谢评论的人,在 select 请求中单独使用 FOR UPDATE 是不够的, SELECT 和 UPDATE 必须包含在事务中,像这样:

 <?php
    // Begin transaction
    $conn->begin_transaction();
    $sql = "SELECT tokenvalue FROM TOKENS WHERE used=0 ORDER BY ID DESC LIMIT 1;";
    $result = $conn->query($sql);
    
    if ($result->num_rows > 0) {
        while($row = $result->fetch_assoc()) {
            // set row status to used so it can't be retrieved again
            $sql = "UPDATE TOKENS SET used=1;";
            $conn->query($sql);
    
            echo ($row["tokenvalue"]);
            break;
        }
    }
    // finish transaction
    $conn->commit();
?>