我如何才能确保我不会;t执行同一数据库事务两次

How can I ensure that I don't perform the same database transaction twice?

本文关键字:事务 数据库 两次 确保 执行      更新时间:2023-09-26

我正在通过网络对数据库执行插入操作,我想确保从不尝试插入已经插入的数据。客户端在web浏览器中运行,使用Javascript向服务器发送XMLHttpRequest,其中包含要插入的数据。如果出现错误,我必须稍后再试——例如,可能存在暂时的网络错误。然而,据我所知,我无法保证我会收到插入成功的通知,因为在请求到达服务器后,但在返回成功回复之前,网络可能会瘫痪。虽然这在我看来不太可能,但如果真的发生了,数据库会完全混乱,因为无法知道某些条目不应该存在。

使用插入的数据的存在或不存在,这样,如果出现第二个相同的事务,它就会被忽略。类似于:

INSERT MyTable (Value1, Value2, Value3)
SELECT @Value1, @Value2, @Value3
WHERE
   NOT EXISTS (
      SELECT *
      FROM dbo.MyTable T
      WHERE
          T.Value1 = @Value1
          AND T.Value2 = @Value2
   )
;

请注意,这并不能完全解决两个客户端试图同时执行相同操作的潜在竞争条件——EXISTS子句在INSERT操作之前及时执行,这可能会导致两个客户端尝试插入。因此,您必须添加诸如WITH (UPDLOCK, HOLDLOCK)之类的锁定(这对并发性来说很可怕,因为如果行不存在,它就必须使用范围锁定,这可以阻止不相关的数据插入),或者在客户端中适当地容忍关于重复密钥的错误。

如果只要客户端发送数据,数据就可以被复制,那么您可以通过以下几种方式来适应:

  1. 将其他列添加到表中,直到它是唯一的。

  2. 或者,创建一个作为事务一部分的时间戳/序列号,并在插入(在数据库事务中)后将该序列号记录到表中。将插入与缺少此序列号相关联。

为您的客户端开发一些策略,将已发送的请求保留到可以确认为止,这可能是一个额外的保护层,同时可以从一开始就防止一些重复尝试——如果您现在没有得到确认,但它稍后到达,则确认/删除排队的已发送事务。

为了将其推广到防止重复的DELETEUPDATE,您应该添加rowversion类型的列。在所有查询的WHERE子句中包含此值,如果自上次读取该行以来,其他人接触过该行,则这些查询将不会影响任何行。您仍然必须检测到没有任何行受到影响,然后再做一些明智的事情——这可能非常困难——但至少您已经检测到并避免了编辑冲突或重复操作。

如果问题不能孤立于对特定行的特定编辑,那么请提出一个新问题,提供一些具体的例子,以便您可以获得更好的建议。给我一个评论,并附上新问题的链接,我会看一看。

尝试为字段添加一些约束:

ALTER TABLE Persons ADD CONSTRAINT MyTable UNIQUE (Value1, Value2, Value3)