【问题标题】:How to run 'SELECT FOR UPDATE' in Laravel 3 / MySQL如何在 Laravel 3 / MySQL 中运行“SELECT FOR UPDATE”
【发布时间】:2013-12-18 13:26:28
【问题描述】:

我正在尝试使用 Laravel 3 执行 SELECT ... FOR UPDATE 查询:

SELECT * from projects where id = 1 FOR UPDATE;
UPDATE projects SET money = money + 10 where id = 1;

我已经尝试了好几个小时了:

DB::connection()->pdo->exec($query);

DB::query($query)

我也尝试在查询中添加START TRANSACTION; ... COMMIT; 我试图在两个不同的部分将SELECTUPDATE 分开,如下所示:

DB::query($select);
DB::query($update);

有时我会受到 0 行的影响,有时我会收到类似这样的错误:

SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active.  Consider using PDOStatement::fetchAll().  Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute.    
SQL: UPDATE `sessions` SET `last_activity` = ?, `data` = ? WHERE `id` = ?

我想锁定行以更新敏感数据,使用 Laravel 的数据库连接。

谢谢。

【问题讨论】:

  • 当您收到错误“update sessions SET (...)”时,第一个查询是否正确执行?
  • 当您使用带有 SELECT 的 DB::query() 时,PDO 希望您在下一个查询之前耗尽缓冲区。所以使用 DB::query($select)->closeCursor() 或 DB::connection()->pdo->exec($select) 来避免 SQL 错误。
  • 你能试试我下面的解决方案吗?我一直来检查它是否有效!
  • @colinm,仅供参考,您的评论只是您前一天可以阅读的答案的一部分。

标签: mysql laravel locking innodb laravel-3


【解决方案1】:
  1. 我肯定会使用不同的查询,这样您就可以控制自己在做什么。
  2. 我会使用交易。 如果我们阅读this simple explanations,pdo 事务非常简单。他们为我们提供了这个简单但完整的示例,说明了我们应该期望的一切(将$db 视为您的DB::connection()->pdo)。

    try {
     $db->beginTransaction();
    
     $db->exec("SOME QUERY");
    
     $stmt = $db->prepare("SOME OTHER QUERY?");
     $stmt->execute(array($value));
    
     $stmt = $db->prepare("YET ANOTHER QUERY??");
     $stmt->execute(array($value2, $value3));
    
     $db->commit();
    } 
    catch(PDOException $ex) {
     //Something went wrong rollback!
     $db->rollBack();
     echo $ex->getMessage();
    }
    
  3. 让我们来看看你的真实陈述。对于第一个SELECT ...,我不会使用exec,而是query,因为如上所述here

    PDO::exec() 不会从 SELECT 语句返回结果。为一个 SELECT 语句,您只需要在程序期间发出一次, 考虑发出 PDO::query()。对于您需要发布的声明 多次,使用 PDO::prepare() 准备一个 PDOStatement 对象和 使用 PDOStatement::execute() 发出语句。

    并将其结果分配给一些临时变量,例如

    $result= $db->query ($select);

  4. 执行后,我会调用$result->fetchAll()$result->closeCursor(),因为我们可以读到here

    如果您在发布之前未获取结果集中的所有数据 您下次调用 PDO::query() 时,您的调用可能会失败。称呼 PDOStatement::closeCursor() 释放数据库资源 在发出下一次调用之前与 PDOStatement 对象关联 到 PDO::query()。

  5. 那你可以exec更新了

    $result= $db->exec($update);
    
  6. 毕竟,以防万一,我会再次致电$result->fetchAll()$result->closeCursor()

【讨论】:

    【解决方案2】:

    如果您只需将money 增加10,则无需在更新前锁定行。只需执行更新查询即可完成这项工作。 SELECT 查询只会减慢您的脚本速度,在这种情况下无济于事。

    UPDATE projects SET money = money + 10 where id = 1;
    

    【讨论】:

    • 为什么我不需要锁定行?当多个查询(比如说 50 个)同时运行时会发生什么?如何在不锁定行的情况下确保这些值 100% 是最新的、准确且安全的?
    • 如果只运行UPDATE 查询,无论更新前的数字是多少,它总是会将数字加一。 mysql 中的更新是按顺序进行的,不需要锁定行。如果您必须使用货币价值,例如首先SELECT money FROM projects WHERE id=1,然后UPDATE money = money + 10,然后INSERT INTO log (money, project_id, time) VALUES ($select['money'], 1, NOW()),以记录货币何时变为什么价值,那将是另一回事。在这种情况下,您必须通过SELECT ... FOR UPDATEUPDATE.. 锁定行
    【解决方案3】:

    如果目标是

    使用 Laravel 的数据库连接锁定行以更新敏感数据。

    也许你可以使用 PDO 事务:

        DB::connection()->pdo->beginTransaction();
        DB::connection()->pdo->commit();
        DB::connection()->pdo->rollBack();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-03-10
      • 1970-01-01
      • 2011-05-17
      • 1970-01-01
      • 2022-12-17
      • 1970-01-01
      • 1970-01-01
      • 2018-08-18
      相关资源
      最近更新 更多