交易期间密钥 'PRIMARY' 的重复条目“...”
Duplicate entry '...' for key 'PRIMARY' during the transaction
这件事情是昨晚发生在我身上的。我非常熟悉错误的性质,但我仍然无法弄清楚是什么原因造成的。我可能有预感,但我不确定。我将从一些基本的应用信息开始:
我的应用有 3 个实体:Loan
、SystemPage
和 TextPage
。每当有人添加贷款时,一个或多个系统页面就会被添加到数据库中。基本上,它是这样的:
if ( $form->isValid()){
$this->em->getConnection()->beginTransation();
$this->em->persist($loan);
$this->em->flush();
while ($someCondition){
$page = new SystemPage();
//... Fill the necessary data into page
$page->setObject($loan);
$this->em->persist($page);
}
$this->em->flush();
$this->em->getConnection()->commit();
}
请忽略潜在的错别字,我是根据字面意思写的
实体 Loan
映射到 table loans
,SystemPage
映射(通过继承映射)到 system_pages
和 base_pages
。后面两个都有 id
字段设置为 AUTO_INCREMENT
.
我的直觉:还有一个 table 叫做 text_pages
。鉴于一方面 text_pages
和 base_pages
以及另一方面 system_pages
和 base_pages
共享 ID,我认为它可以很容易地导致这个:
User1: Create BasePage, acquire autoincrement ID (value = 1)
User2: Create BasePage, acquire autoincrement ID (value = 1)
User1: Create TextPage, use the ID from step 1
User2: Create SystemPage, use the ID from step 2
这个理论的两个问题:
- 交易。这就是为什么我首先使用它们
- 在出错时,其他用户activity在应用程序上没有其他
重要提示:稍等一分钟,重新提交成功。
这会不会是一些奇怪的 MySQL
事务隔离错误?任何提示将不胜感激...
编辑:
数据库模式的一部分:
请忽略塞尔维亚语的列名
flush() 操作在单个事务中刷新所有更改,因此此处有冗余代码...
你没有说你是否可以重现这个错误,如果你能提供数据库模式会很方便。
这个问题似乎没有正确答案,只有猜测,所以我会根据自己遇到这样的问题的经验提供一些解决思路:
您提到该应用程序上没有其他 activity,但我会通过查看查询日志进行三重检查。必须执行重复的查询。
可能表单不小心提交了两次。用户双击提交按钮,或者如果 UI 没有响应,他们会再次单击。您可以通过查看 Apache 日志文件了解您表单上同一时间戳的 POST 请求来检查这个想法。您可能需要实施一些 javascript 代码来防止双击您的表单页面提交按钮。
您的预感可能非常接近正确,因为存在某种竞争条件。使用事务不会阻止竞争条件,但它们确实提供了优雅回滚的方法。将您的代码包装在 try/catch 块中,以便您可以捕获 Mysql 异常并向用户显示友好错误和重试选项。
这件事情是昨晚发生在我身上的。我非常熟悉错误的性质,但我仍然无法弄清楚是什么原因造成的。我可能有预感,但我不确定。我将从一些基本的应用信息开始:
我的应用有 3 个实体:Loan
、SystemPage
和 TextPage
。每当有人添加贷款时,一个或多个系统页面就会被添加到数据库中。基本上,它是这样的:
if ( $form->isValid()){
$this->em->getConnection()->beginTransation();
$this->em->persist($loan);
$this->em->flush();
while ($someCondition){
$page = new SystemPage();
//... Fill the necessary data into page
$page->setObject($loan);
$this->em->persist($page);
}
$this->em->flush();
$this->em->getConnection()->commit();
}
请忽略潜在的错别字,我是根据字面意思写的
实体 Loan
映射到 table loans
,SystemPage
映射(通过继承映射)到 system_pages
和 base_pages
。后面两个都有 id
字段设置为 AUTO_INCREMENT
.
我的直觉:还有一个 table 叫做 text_pages
。鉴于一方面 text_pages
和 base_pages
以及另一方面 system_pages
和 base_pages
共享 ID,我认为它可以很容易地导致这个:
User1: Create BasePage, acquire autoincrement ID (value = 1)
User2: Create BasePage, acquire autoincrement ID (value = 1)
User1: Create TextPage, use the ID from step 1
User2: Create SystemPage, use the ID from step 2
这个理论的两个问题:
- 交易。这就是为什么我首先使用它们
- 在出错时,其他用户activity在应用程序上没有其他
重要提示:稍等一分钟,重新提交成功。
这会不会是一些奇怪的 MySQL
事务隔离错误?任何提示将不胜感激...
编辑:
数据库模式的一部分:
请忽略塞尔维亚语的列名
flush() 操作在单个事务中刷新所有更改,因此此处有冗余代码...
你没有说你是否可以重现这个错误,如果你能提供数据库模式会很方便。
这个问题似乎没有正确答案,只有猜测,所以我会根据自己遇到这样的问题的经验提供一些解决思路:
您提到该应用程序上没有其他 activity,但我会通过查看查询日志进行三重检查。必须执行重复的查询。
可能表单不小心提交了两次。用户双击提交按钮,或者如果 UI 没有响应,他们会再次单击。您可以通过查看 Apache 日志文件了解您表单上同一时间戳的 POST 请求来检查这个想法。您可能需要实施一些 javascript 代码来防止双击您的表单页面提交按钮。
您的预感可能非常接近正确,因为存在某种竞争条件。使用事务不会阻止竞争条件,但它们确实提供了优雅回滚的方法。将您的代码包装在 try/catch 块中,以便您可以捕获 Mysql 异常并向用户显示友好错误和重试选项。