【问题标题】:Duplicate DB Entries on Double Request even after SELECT First即使在 SELECT First 之后,双重请求上的重复数据库条目也是如此
【发布时间】:2017-08-03 12:42:49
【问题描述】:

我不确定我是否在标题中用了正确的词来解释。但它就在这里。

应用:Coldfusion/MySQL

在第一个请求完成之前触发双重请求的不良互联网接收期间异常发生问题。结果生成了两个数据库条目。

应用程序只处理带有 SELECT 查询的双重请求,以检查数据库中是否已经存在相同的记录。在这种情况下,旧记录将使用 UPDATE 语句进行更新。否则,使用 INSERT 语句插入新记录。 SELECT/INSERT/UPDATE 包含在 CFTRANSACTION 中。

<cftransction>
    <cfquery></cfquery> //SELECT
    <cfif SELECT.found>
        <cfquery></cfquery> //UPDATE
    <cfelse>
        <cfquery></cfquery> //INSERT
    </cfif>
</cftransaction>

在大多数情况下,当两个请求有一些延迟时,它可以正常工作。要重现问题,

  1. 我首先尝试通过浏览器同时发起两个请求,但多次尝试都没有问题。
  2. 为了快速发出两个请求,我在页面上的一个之后使用了两个 post 语句(它是一个 AJAX 应用程序)。它也按预期工作,因为第二个请求仅更新先前输入的记录。
  3. 我可以做些什么来更快地完成这两个请求,我在服务器本身(ColdFusion 和 MySQL 都在其中运行)并打开了浏览器。现在,一个带有两个 post 语句的浏览器请求开始频繁重复输入。

当 REQUEST.2 SELECT 在 REQUEST.1 INSERT 之前运行时,上面的代码可能会插入两个条目。服务器端有什么可能的解决方案?

在第一个请求完成之前禁用后续请求,在客户端进行处理。但想在服务器上保护它。

【问题讨论】:

  • 对于插入查询,你使用的是values吗?
  • 您是否查看了诸如REPLACE 或INSERT ... ON DUPLICATE KEY UPDATE 之类的db 选项,而不是SELECT .. INSERT 方法?
  • @DanBracuk 是的,我正在使用 INSERT INTO (col1, col2) 值 ('val1','val2') 首先使用 SELECT 查询检查相同的值。
  • @Leigh 我也会检查这些方法,然后回来。谢谢。
  • 您可以使用select etc where not exists (something) 方法,而不是values 方法。

标签: mysql coldfusion insert duplicates


【解决方案1】:

所以我使用了 cmets 中 @Leigh 提供的解决方案来解决原始问题。只是回答以使其脱颖而出以及我必须做的其他事情才能实际使用此解决方案。

您是否查看了诸如REPLACE 或INSERT ... ON DUPLICATE KEY UPDATE 之类的db 选项,而不是SELECT .. INSERT 方法? ——利

虽然这两种解决方案似乎都可以很好地解决问题,但 REPLACE 更有吸引力,因为它只需要我在带有“REPLACE”的查询中更改“INSERT”关键字

通过文档的快速概述,REPLACE 完全替换并像 INSERT 语句一样工作,但仅在找到具有重复 PRIMARY KEY 或 UNIQUE 索引的行时才执行此目的。

1- 因为我需要三列在这个意义上是唯一的,我在插入它们之前在带有 SELECT 的 WHERE 子句中使用它们。在 REPLACE 可以限制重复行之前,我必须在这三列上添加一个 UNIQUE 索引。

2- 如果发现重复行,REPLACE 首先删除旧行,然后插入新行。此过程也可能会丢失表中其他列的一些值。但是在我的应用程序中,这些列在每个请求上都会更新,而这些列的值是从其他表派生的。所以这对我来说没问题,因为可以为插入的新行更新数据。

【讨论】:

  • 使用 REPLACE 实现后,我记得如果您在这些列组合上使用 UNIQUE 索引,您甚至可以将 INSERT 语句与应用程序的 TRY/CATCH 异常处理一起使用。这不会接受进一步的重复请求,并且只会避免给出任何错误,除非你想在 CATCH 块中......但我仍在使用 REPLACE ,它肯定有其积极的一面,因为来自最新 REQUEST 的数据用于新的行。
【解决方案2】:

会将 select + insert/update 查询组合在一起,但不会阻止第二个 + 请求的执行,即使是在事务完全完成之前来自同一个浏览器的请求。

我在 IE 浏览器上也遇到过类似的问题。用户可以垃圾邮件点击提交,每次点击提交按钮时 IE 都会提交。

我采用的最常见的解决方案是在单击提交按钮后禁用它,或者用类似“请稍候,提交表单...”的消息替换(隐藏)它。我已经使用 JavaScript/jQuery 来完成这项任务。它不是 100% 有效的,并且取决于所使用的浏览器以及是否启用了 JavaScript。

另一种选择是使用命名的 来确保代码只能作为批处理执行。如果几个人同时使用该表单,这可能会导致性能瓶颈。因此,我计划使用会话变量作为命名 名称的一部分。这样,每个用户的锁都是唯一的。

以下是 CF 11 示例:

<cflock name="lckInsUpdt_#listFirst(session.sessionid, '.')#" timeout="300" throwontimeout="no" type="exclusive">
<cftransction>
    <cfquery></cfquery> //SELECT
    <cfif SELECT.found>
        <cfquery></cfquery> //UPDATE
    <cfelse>
        <cfquery></cfquery> //INSERT
    </cfif>
</cftransaction>
</cflock>

【讨论】:

  • 谢谢@ScottJibben。是的,cftransaction 只是为了有目的地处理它所做的事情。它不适用于多个同时请求。客户端限制肯定是最后的手段,但它仍然不是完美的禁用按钮或禁用浏览器/javascript 上的并发请求。 CFLOCK 是我之前知道的,当我知道 CFLOCK 不能用于执行时间有机会增加的逻辑时。它不应该用于查询。
猜你喜欢
  • 2018-08-15
  • 2020-05-17
  • 2012-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-05
  • 2011-07-13
  • 2012-10-19
相关资源
最近更新 更多