我今天注意到我的应用程序中出现了一些错误,它们试图将记录插入表中并因临时网络延迟而收到超时错误。应用程序被编码以识别这种情况并在这种情况下重试,这是它顺从地做的。但是在重试时它遇到了主键违规 - 主要是因为第一个插入语句实际已经完成,但是超时已经发生了将响应传回给客户端。应用程序认为主键违规是严重的逻辑错误,不应该发生,因此它会中止整个过程。
对我来说,问题是什么层在逻辑上应该负责处理这类事情?理想情况下,我会认为SQL客户端库(在本例中为ADO.NET 4.0)应该这样做,但它没有我所知道的自动重试机制。鉴于它没有,似乎有一种情况可以在SQL客户端库周围使用低级包装器,但我不知道如何在没有更多访问有关何时发生超时的信息的情况下编写它:例如在这种例子中,有可能
a)INSERT语句只是使用自动递增键插入新记录,因此在超时后重试会导致插入重复记录,或者 b)主键违规WAS实际上是一个逻辑错误,并且如果没有发生超时,初始尝试插入记录会产生相同的违规
OTOH我确信我能想到一个例子,是否只能在应用程序级别确定是否重试(特别是如果它需要用户确认)。
我实际上有点惊讶我以前从未见过这种特别错误的序列,因为它似乎很可能在实践中。
答案 0 :(得分:0)
显然,这个特定问题的一个解决方案是使用'SET XACT_ABORT ON'。 据报道,如果客户端上出现运行时错误,则会导致SQL Server回滚语句。我很好奇为什么它实际上不是默认行为。
答案 1 :(得分:0)
IDENTITY
属性does not guarantee uniqueness,永远。这意味着当您在给定表上具有并发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,因此可以禁用连接池并自行处理连接管理。如果连接在池关闭时超时,则它将在服务器端出错并破坏事务,从而导致回滚。