Php 学说上的竞合
我有一个应用程序,运行在php+mysql平台上,使用Doctrine2框架。我需要在一个http请求期间执行3个db查询:第一次插入,第二次选择,第三次更新。更新取决于SELECT查询的结果。并发http请求的概率很高。如果出现这种情况,并且数据库查询混淆(例如INS1、INS2、SEL1、SEL2、UPD1、UPD2),则会导致数据不一致。如何确保INS-SEL-UPD操作的原子性?我是否需要使用某种类型的锁,或者事务就足够了 保证在所有情况下都能使用表宽。但它们非常糟糕,因为它们会阻止并发,而不是处理并发。 但是,如果脚本在很短的时间内持有锁,那么这可能是一个可接受的解决方案 如果您的表使用InnoDB引擎(不支持MyISAM的事务),事务是最有效的解决方案,但也是最复杂的解决方案 根据您的特定需要(在同一个表中,第一次插入、第二次选择、第三次更新取决于选择查询的结果):Php 学说上的竞合,php,mysql,concurrency,transactions,doctrine-orm,Php,Mysql,Concurrency,Transactions,Doctrine Orm,我有一个应用程序,运行在php+mysql平台上,使用Doctrine2框架。我需要在一个http请求期间执行3个db查询:第一次插入,第二次选择,第三次更新。更新取决于SELECT查询的结果。并发http请求的概率很高。如果出现这种情况,并且数据库查询混淆(例如INS1、INS2、SEL1、SEL2、UPD1、UPD2),则会导致数据不一致。如何确保INS-SEL-UPD操作的原子性?我是否需要使用某种类型的锁,或者事务就足够了 保证在所有情况下都能使用表宽。但它们非常糟糕,因为它们会阻止并发
(*)通常,此选择未返回的行仍可以插入到并发事务中,也就是说,在整个事务过程中,不保证不存在该行,除非执行该操作。来自@YaK的答案实际上是一个很好的答案。一般来说,你应该知道如何处理锁 具体地说,您的代码应该如下所示:
$em->getConnection()->beginTransaction();
try {
$toUpdate = $em->find('Entity\WhichWillBeUpdated', $id, \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE);
// this will append FOR UPDATE http://docs.doctrine-project.org/en/2.0.x/reference/transactions-and-concurrency.html
$em->persist($anInsertedOne);
// you can flush here as well, to obtain the ID after insert if needed
$toUpdate->changeValue('new value');
$em->persist($toUpdate);
$em->flush();
$em->getConnection()->commit();
} catch (\Exception $e) {
$em->getConnection()->rollback();
throw $e;
}
对于获取更新的每个后续请求,将等待一个已获取锁的进程完成此事务。Mysql将在事务成功或失败后自动释放锁。默认情况下,innodb锁定超时为50秒。因此,如果您的进程没有在50秒内完成事务,它将回滚并自动释放锁。实体上不需要任何其他字段。事务不会阻止线程B读取线程A未锁定的值。 因此,必须使用锁来防止并发访问 @Gediminas解释了如何将锁与条令一起使用。 但是使用锁可能会导致死锁或锁超时。 条令将这些SQL错误呈现为可重试的异常。 如果您处于高并发环境中,这些异常通常是正常的。 它们可能经常发生,您的应用程序应该正确处理它们 每次条令引发RetryableException时,正确的处理方法是重试整个事务 尽管看起来很容易,但有一个陷阱。条令2 EntityManager在发生RetryableException后变得不可用,您必须重新创建一个新的条令2 EntityManager以重播整个事务
我写了一个完整的例子。您打算从一个表或多个表中读/写吗?如果您选择事务,这个问题会变得太大,在一般情况下无法解决。我最好的建议是:阅读大量关于并发的知识,了解MySQL锁/事务是如何工作的。您可能需要详细描述程序的逻辑(插入什么,在什么情况下更新哪一行)。@puty:对于许多编辑,这是非常有用的。我一开始误解了你最初的问题。我看不出你的问题其实很具体。