执行非查询时SqlException'timeout expired',但插入了数据

时间:2014-11-18 19:42:42

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

我在负载测试场景下遇到了奇怪的行为:后端(sql server 2012)正在超载并且一些命令超时(这仍然是预期的,因为后端服务器是半故意慢的HW);但我们的平台经常(延迟时间越来越长)重试超时操作 - 经过几次重试后突然开始接收'无法插入重复的密钥' 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在他的回答中指出

2 个答案:

答案 0 :(得分:7)

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

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

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

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

答案 1 :(得分:-1)

根据文档SqlException Class

  

SQL Server返回警告或抛出时引发的异常   错误。这个类不能被继承。

我的经验是,如果你有一个SQL异常,那么它来自SQL。

超时是SQL设置。您可以将其设置为SSMS。

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

  

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

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