8

我正在编写一个 PHP 函数,它将大量数据存储/更新到一个表中,这可能会导致死锁。我尝试调查如何使用 Doctrine 重试失败的交易,但遗憾的是在网上找不到任何信息。我最终写了以下代码

 $retry = 0;
 $done = false;
 while (!$done and $retry < 3) {
     try {

         $this->entityManager->flush();
         $done = true;

     } catch (\Exception $e) {
         sleep(1);

         $retry++;
     }
 }

 if ($retry == 3) {
     throw new Exception(
         "[Exception: MySQL Deadlock] Too many people accessing the server at the same time. Try again in few minutes"
     );
 }

我的问题:这种方法是否有可能在数据库中插入重复项?如果是这样,我怎样才能强制 Doctrine 回滚交易?

4

4 回答 4

16

死锁返回错误 1213,您应该在客户端处理该错误

请注意,死锁和锁等待是不同的东西。在僵局中,没有“失败”的交易:他们都是有罪的。不能保证哪一个会被回滚。

您必须使用rollback,您的样式代码将插入重复。例如你应该:

$retry = 0;

$done = false;


$this->entityManager->getConnection()->beginTransaction(); // suspend auto-commit

while (!$done and $retry < 3) {

    try {

        $this->entityManager->flush();

        $this->entityManager->getConnection()->commit(); // commit if succesfull

        $done = true;

    } catch (\Exception $e) {

        $this->entityManager->getConnection()->rollback(); // transaction marked for rollback only

        $retry++;

    }

}

希望这有帮助。

于 2012-10-31T03:03:15.470 回答
3

这就是我使用 Sf2.7 和学说 2.4.7 重试失败事务的方式:

use Doctrine\Bundle\DoctrineBundle\Registry;
use Doctrine\ORM\EntityManager;

class Foo
{
    /**
     * @var Registry
     */
    protected $doctrine;

    public function __construct(Registry $registry)
    {
        $this->doctrine = $registry;
    }

    protected function doSomething($entity, $attempt)
    {
        $em = $this->getEntityManager();
        $conn = $em->getConnection();
        try{
            $conn->beginTransaction();
            $entity->setBar("baz");
            $em->flush();
            $conn->commit();
        } catch(\PDOException $e){
            $conn->rollBack();
            $attempt++;
            if($attempt <= 3){
                $this->doSomething($repayment, $attempt);
            }
        }
    }

    /**
     * @return EntityManager
     */
    protected function getEntityManager()
    {
        /** @var EntityManager $em */
        $em = $this->doctrine->getManager();
        if(!$em->isOpen()){
            $this->doctrine->resetManager();
            $em = $this->doctrine->getManager();
        }

        return $em;
    }
}
于 2015-09-02T23:46:28.987 回答
2

重试事务片段

  • 获得连接没有任何麻烦。
  • 正确处理循环

代码

$connection = ...
$retry = 0;
$maxRetries = 3;

while ($retry < $maxRetries) {
    try {
        $connection->beginTransaction();

        // do stuff

        $connection->commit();

        break;
    } catch (Throwable $exception) {
        $connection->rollBack();

        $retry++;

        if ($retry === $maxRetries) {
            throw $exception;
        }
    }
}

有关 Doctrine 交易的更多信息,请访问https://www.doctrine-project.org/projects/doctrine-dbal/en/2.7/reference/transactions.html

如何获得连接:

只需注入 Doctrine 的EntityManager( Interface) 或Connection例如。通过构造函数

联系

public function __construct(Connection $connection)
{
    $this->connection = $connection;
}

或实体管理器

public function __construct(EntityManagerInterface $entityManager)
{
    $this->entityManager = $entityManager;
}

并获得连接$this->entityManager->getConnection()

于 2018-04-23T14:30:13.277 回答
-5

如果您没有使用 ORM,那么使用它会自动管理死锁情况。

于 2012-10-30T08:58:05.133 回答