” 。 $txt 。 “</p>”; //打开新条目 $newStartTime = $stop->modify('+1 秒'); $entry = 新条目(); $entry->setStart( $newStartTime ); $entry->setOperator( $r->getOperator() ); $entry->setPosition( $r->getPosition() ); $entry->setStudent( $r->getStudent() ); $em->坚持($entry); //在自动打开新条目之前断言没有未来的条目 $futureE = $this->checkFutureEntries($r->getPosition(),true); $openE = $this->checkOpenEntries($r->getPosition(), true); if ($futureE !== 0 || $openE !== 0) { $txt =“尝试为”打开一个新条目。 $r->getOperator()->getSignature() 。 ”第二天在同一位置(“.$r->getPosition()->getName().”),但存在冲突的条目。”; $messages .= “
” 。 $txt 。 “</p>”; }else { $em->flush(); //store to DB $txt = "A new entry was opened for " . $r->getOperator()->getSignature() . " in the same position (" . $r->getPosition()->getName() . ")"; $messages .= "<p>" . $txt . "</p>"; } } } } return $messages; }</pre> <p>我什至在这里使用 checkOpenEntries() 运行额外的检查,以查看此时该位置是否存在任何 stoptime == NULL 的条目。最初,我认为这是多余的,因为我认为如果一个请求正在数据库上运行和操作,则另一个请求只有在第一个请求完成后才会启动。</p> <pre class="brush:php;toolbar:false;">private function checkOpenEntries($position,$checkRelatives = false) { $positionsToCheck = array(); if ($checkRelatives == true) { $positionsToCheck = $position->getRelatedPositions(); $positionsToCheck[] = $position; } else { $positionsToCheck = array($position); } //Get all open entries for position $repository = $this->em->getRepository('App\Entity\Poslog\Entry'); $query = $repository->createQueryBuilder('e') ->where('e.stop is NULL and e.position IN (:positions)') ->setParameter('positions', $positionsToCheck) ->getQuery(); $results = $query->getResult(); if(!isset($results[0])) { return 0; //tells caller that there are no open entries } else { if (count($results) === 1) { return $results[0]; //if exactly one open entry, return that object to caller } else { $body = 'Found more than 1 open log entry for position ' . $position->getName() . ' in ' . $position->getACRGroup()->getName() . ' this should not be possible, there appears to be corrupt data in the database.'; $this->email($body); $output['success'] = false; $output['message'] = $body . ' An automatic email has been sent to ' . $this->globalParameters->get('poslog-email-to') . ' to notify of the problem, manual inspection is required.'; $output['logdata'] = null; return $this->prepareResponse($output); } } }</pre> <p>我是否需要使用某种“锁定数据库”方法来启动此功能才能实现我想要做的事情?</p> <p>我已经测试了所有功能,并且当我模拟各种状态时(即使不应该如此,也为停止时间输入 NULL 等),一切都正常。大多数情况下,一切都运行良好,但在月中的某一天,这种事情发生了......</p>
P粉9211651812023-09-06 11:35:04
您永远无法保证顺序(或隐式独占访问)。尝试一下,你就会把自己挖掘得越来越深。
正如 Matt 和 KIKO 在评论中提到的,您可以使用约束和事务,这些应该会有很大帮助,因为您的数据库将保持干净,但请记住您的应用程序需要能够捕获数据库层产生的错误。 绝对值得首先尝试。
处理此问题的另一种方法是强制数据库/应用程序级别锁定。
数据库级锁定更加粗糙,如果您在某个地方忘记释放锁定(在长时间运行的脚本中),则非常不可原谅。
MySQL 文档:
锁定整个表通常是一个坏主意,但它是可行的。这很大程度上取决于应用程序。
一些开箱即用的 ORM 支持对象版本控制,如果版本在执行过程中发生更改,则会抛出异常。理论上,您的应用程序会遇到异常,重试时会发现其他人已经填充了该字段,并且不再是更新的候选者。
应用程序级锁定更加细粒度,但代码中的所有点都需要遵守锁定,否则,您将回到方#1。如果您的应用程序是分布式的(比如 K8S,或者只是部署在多个服务器上),那么您的锁定机制也必须是分布式的(不是实例本地的)