【问题标题】:SqlException 'timeout expired' when executing nonquery, but data are inserted执行非查询时出现SqlException 'timeout expired',但插入了数据
【发布时间】:2014-11-18 19:42:42
【问题描述】:

我在负载测试场景下遇到了奇怪的行为:后端(sql server 2012)超载并且一些命令超时(这仍然是预期的,因为后端服务器是半故意的慢硬件);但我们的平台会定期(随着延迟增加)重试超时操作 - 在重试几次后,它突然开始收到“无法插入重复键”SqlException。

我验证了只能生成具有特定唯一键的单行并尝试插入(第一次插入和所有可能的重试总是发生在同一个线程上)。

我还更改了 SP,使其使用显式事务:

BEGIN TRY

    BEGIN TRANSACTION;

    -- Insert into table A

    -- Insert into table B

    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    ROLLBACK TRANSACTION;
    THROW
END CATCH

但问题仍然存在。

有什么想法为什么会发生这种情况?

如何找出超时来自哪里(后端与客户端)?

有没有办法确保操作成功完成或失败(基本上是事务 - 但可能来自客户端代码)?

EDIT01: 我相信解决这个问题的一种方法是利用 ado.net 集成 SQL 服务器分布式事务 - 例如:

using (TransactionScope scope = new TransactionScope())
{
    //Perform the sql commands

    //if above statements throws (e.g. due to timeout) - than the transaction is not commited and it will be rolled back
    scope.Complete()
}

但是:我同意它只会增加复杂性,实际上可能仍然反对同一个问题(usr 概述的两个将军问题)。 因此,最好的方法可能是对客户端和服务器端进行编码以依靠这样的选项 - 再次如 usr 在他的回答中所指出的那样

【问题讨论】:

  • 这可能是由于sql连接中的命令超时造成的。所以我的建议是尝试手动设置 CommandTimeOut 值,因为它的默认值是 30 秒。另请参阅以下链接msdn.microsoft.com/en-us/library/…
  • 请发布您的代码如何识别和重试超时操作。如果是 sqlexception,那么它来自 SQL。
  • @Blam - 是的,它是 SqlException - 但它仍然不一定意味着它来自服务器,不是吗?
  • 查看调用堆栈。但是我从来没有遇到过不是 SQL 异常的 SqlException。我认为超时实际上是从 SQL 返回的。不是放弃 SQL 的命令。在 SSMS 中有一个超时设置。命令放弃 SQL 并让 SQL 旋转而没有任何连接来返回结果将是一个非常糟糕的设计。

标签: c# .net sql-server ado.net sql-server-2012


【解决方案1】:

这是预期的行为。当客户端和服务器之间的通信中断时,客户端不知道操作的结果。它可能从未发送过,或者发送过但未收到,或者已收到但失败,或者已收到但没有成功响应。

这是Two Generals Problem。这是无法解决的(严格定义时)。

您必须解决它。在插入之前检查是否存在或处理重复键异常。

或者,只需增加超时时间。中止最终会成功的其他工作命令对您没有任何好处。中止并重新启动它不会使其运行得更快(巧合除外)。超时主要用于网络错误或失控查询(错误)。

【讨论】:

  • 非常有帮助的答案。您是否有任何资源支持声称这是无法解决的?我相信如果从客户端断开连接,sql应该中止执行的查询。
  • en.wikipedia.org/wiki/Two_Generals'_Problem 服务器找不到连接断开。这是一个对称问题。不可能交换消息以使双方可靠地得出相同的结论。你会在任何分布式系统中找到它。例如,宇宙中没有任何消息队列可以证明完全一次传递。
  • 但是 OP 说他们得到了 SQLtimeout 异常。通信丢失不是 SQL 异常。
  • 查询超时是客户端造成的。当计时器到期时,ADO.NET 会向服务器发送一条关注。这是一种客户端机制。注意可能无法及时到达服务器以取消查询(或根本无法到达)。在这种情况下,会观察到超时异常,但查询已完成。
  • 我相信你 +1(不久前)。如果在服务端强制执行超时,那么我希望将其视为 TSQL 选项(我没有)。
【解决方案2】:

根据文档SqlException Class

当 SQL Server 返回警告或 错误。这个类不能被继承。

我的经验是,如果您遇到 SQL 异常,那么它来自 SQL。

超时是一个 SQL 设置。可以设置SSMS。

好的,我现在相信 usr +1 这来自客户端 SqlCommand.CommandTimeout

获取或设置在终止尝试执行之前的等待时间 命令并产生错误。

这不是我想要的实现方式。 你可能有一个疯狂的查询并失去连接 SQL 会继续运行。

【讨论】:

  • 网络和超时条件有特殊的错误编号。我相信-2就是其中之一。此处引用的文档是错误的。超时是客户端设置。 SSMS 是一个客户端,所以可以设置在那里。
  • @usr 你有来自客户的证据吗?我认为“当 SQL Server 返回警告或错误时”就是这个意思。不是我不相信你,而是你的文档是错误的并不多。
  • 这里有一个更好的帖子:blogs.msdn.com/b/support_sql_france/archive/2012/02/24/… 客户端发送取消,可以通过暂停进程来抑制(作为测试)。
  • @usr 好的,我会再调查一下。我现在没有可以“搞乱”的 SQL 服务器。如果它是由客户端发起的,我仍然希望 SqlException 超时被确认为 SqlExpeption 并且如果它是一个网络被报告为丢失的连接 - 但你并不总是得到你所期望的。
猜你喜欢
  • 2013-05-30
  • 1970-01-01
  • 2020-11-01
  • 2017-01-29
  • 1970-01-01
  • 1970-01-01
  • 2015-07-18
  • 1970-01-01
  • 2013-05-14
相关资源
最近更新 更多