首页 > 解决方案 > PHP/MySQL - 在哪里可靠地处理插入自动重新请求(锁定表) - 客户端或服务器?

问题描述

我有这样的功能,并希望在尝试失败时自动处理插入(取决于错误原因) - 它必须是锁定条件。

这是一个企业应用程序,因此我不依赖客户端服务器之间的大量流量,但仍需要可靠地处理插入。

我应该在哪里放置自动重新提交逻辑(api 请求,或在实际的 php 代码中)以及如何实现它?

public function createOne($params = [])
{    
    $sql = "insert
            into actionitems(
                    actionitemid,
                    assignorid,
                    ownerid,
                    altownerid,
                    duedate,
                    ecd,
                    criticality,
                    actionitemtitle,
                    actionitemstatement,
                    closurecriteria
            )
            values(
                    :actionitemid
                    :assignorid,
                    :ownerid,
                    :altownerid,
                    :duedate,
                    :ecd,
                    :criticality,
                    :actionitemtitle,
                    :actionitemstatement,
                    :closurecriteria
            )";
    try
    {      
          $this->db->beginTransaction();
          $this->db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
          $locksql = $this->db->query("lock tables actionitem write");
          $statement = $this->db->prepare($sql); 
          $statement->execute([
                ':actionitemid' => $params['actionitemid'],
                ':assignorid' => $params['assignor'],
                ':ownerid' =>  $params['owner'],
                ':altownerid' => $params['altowner'],
                ':duedate' => $params['duedate'],
                ':ecd' => $params['ecd'],      
                ':criticality' => $params['criticality'],
                ':actionitemtitle' => $params['actionitemtitle'],
                ':actionitemstatement' => $params['actionitemstatement'],
                ':closurecriteria' => $params['closurecriteria'],
          ]); 
          return json_encode(['Result' => 'Success'], JSON_PRETTY_PRINT);
          $this->db->commit();
    }
    catch (PDOExcetption $e)
    {
         $this->db->rollBack();
         return json_encode(['Result' => 'Action Item Was Not Created ' , 'Message' => $e->getMessage(0)]);
    }
}

标签: phpmysql

解决方案


我会在 PHP 中做这样的事情:

public function createOne($params = [])
{    
    // this should be set right after connecting - not here
    $this->db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);

    $sql = "insert into actionitems ... ";
    $statement = $this->db->prepare($sql);

    for ($attempts = 10; $attempts > 0; --$attempts) {
        try {
            $this->db->beginTransaction();
                $this->db->exec("lock tables actionitem write");
                $statement->execute($params);
            $this->db->commit();
            return json_encode(['Result' => 'Success'], JSON_PRETTY_PRINT);
        } catch (PDOExcetption $e) {
            $this->db->rollBack();
            if ($attempts == 1) { // this is the last attempt
                return json_encode(['Result' => 'Action Item Was Not Created ' , 'Message' => $e->getMessage(0)]);
            } else {
                // do nothing and retry
            }
        }
    }
}

笔记:

正如我在评论中所写 - 我不在乎它为什么会失败(因为它应该是一种非常罕见的情况)并且只是捕获异常并重试。

我不知道您为什么要锁定表格以进行简单的插入。我将其保留在答案中,但我不知道为什么简单的插入会失败。至少在 InnoDB 上它不应该,否则任何尝试都会失败。

我还建议使用第三方库。例如,使用 Laravel 的数据库库,它可能看起来像这样:

public function createOne($params = [])
{
    $attempts = 10;
    try {
        $this->db->transaction(function () use ($params) {
            $this->db->statement("lock tables actionitem write");
            $this->db->table('actionitems')->insert($params);
        }, $attempts);
        return json_encode(['Result' => 'Success'], JSON_PRETTY_PRINT);
    } catch (Exception $e) {
        return json_encode(['Result' => 'Action Item Was Not Created ' , 'Message' => $e->getMessage(0)]);
    }
}

更少的代码通常会导致更少的错误。

如果您不想使用第三方库,您也可以自己编写该函数(例如在 trait 中)。你可以在这里查看他们在 laravel 中是如何做到的。


推荐阅读