0

我今天注意到我的应用程序出现了几个错误,它试图将记录插入表中,但由于临时网络延迟而收到超时错误。应用程序被编码为识别这一点并在这种情况下重试,它乖乖地这样做了。但是在重试时,它遇到了主键违规 - 主要是因为第一个插入语句实际上已经完成,但是发生了超时,将响应传输回客户端。应用程序认为主键违规是一个不应该发生的严重逻辑错误,因此它中止了整个过程。

对我来说,问题是逻辑上应该由哪一层负责处理这类事情?理想情况下,我会认为 SQL 客户端库(在本例中为 ADO.NET 4.0)应该这样做,但它没有我所知道的自动重试机制。鉴于它没有,似乎有一个可以使用 SQL 客户端库的低级包装器的情况,但是如果没有更多关于超时发生时间的信息的访问权限,我看不出它是如何编写的:例如在这个某种例子有可能

a) INSERT 语句只是使用自动递增键插入新记录,因此在超时后重试会导致插入重复记录,或者 b) 主键违规实际上是一个逻辑错误,并且最初尝试插入如果没有发生超时,aa 记录将产生相同的违规

OTOH 我敢肯定我可以想到是否重试只能在应用程序级别确定的示例(尤其是在需要用户确认的情况下)。

实际上,我有点惊讶我以前从未见过这种特别的错误序列,因为在实践中似乎很有可能。

4

2 回答 2

0

显然,解决这个特定问题的一种方法是使用“SET XACT_ABORT ON”。据报道,如果客户端出现运行时错误,这会导致 SQL 服务器回滚语句。我很好奇为什么它实际上不是默认行为。

于 2013-04-23T06:27:53.223 回答
0

IDENTITY属性不保证唯一性,永远。这意味着当您INSERT在给定表上有并发语句时,您的自动递增值可能会在多个活动事务中重复。

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE解决此问题的一种方法是在开始交易之前发出声明。这将防止插入冲突,这意味着不能并行执行两个插入。

另一种更常见的方法是简单地重新尝试您的事务或语句,直到您没有收到主键违规。仅当您不执行标识插入并且让 SQL Server 处理主键值并且它是自动递增的时,才应该这样做。

以一种可接受的方式处理此问题的另一种方法是使用UNIQUEIDENTIFIER类型主键。这些几乎可以保证是独一无二的。您可以在很长一段时间后开始接收重复;这将需要与重试相同的逻辑,并且您会丢失主键索引的物理布局优化。这与其说是建议,不如说是一种替代或说明。

总而言之,如果您想处理错误并且您使用的是 SQL Server 2012,您可以简单地使用以下模板来确保您的事务被适当地回滚并且错误浮出水面:

BEGIN
    SET NOCOUNT ON;
    SET XACT_ABORT OFF;

    BEGIN TRANSACTION;
        BEGIN TRY
            -- Do stuff here
        END TRY
        BEGIN CATCH
            ROLLBACK TRANSACTION;
            THROW;
        END CATCH
        
    COMMIT TRANSACTION;
END

另请注意,此模板不适用于对存储过程的嵌套调用,如果以这种方式使用,它将回滚整个过程链。

编辑

由于您使用的是 ADO .NET,因此您可以禁用连接池并自行处理连接管理。如果在池关闭时连接超时,它将在服务器端发生故障并破坏事务,从而导致回滚。

于 2014-02-18T20:09:05.283 回答