.NET SqlCommandTimeOut和连接池

4
假设我们正在执行许多不同的sql命令,并且SqlCommand.CommandTimeout保留默认值30秒。假设其中一些sql命令只是长查询,我们可能会遇到超时异常。
如果我说错了,请纠正我,这个异常只是因为.Net不想再等待了,但是如果我们使用连接池,这个连接可能仍然保持打开状态,那么那个sql语句可能仍在SQL服务器端运行吗?还是这两个系统之间存在某种隐藏的通信方式突然停止它,无论我们是否使用连接池?
只想知道机制是什么,它会影响SQL服务器性能吗?我的意思是,如果查询确实很长,例如需要10分钟才能运行,如果它仍在运行,它可能会不必要地减慢服务器速度,因为没有人可以得到结果。
更新
所以,这里我特别问的是连接池,代码肯定会关闭连接并进行异常处理,或者我们可以假设正在使用一个由@dash命名的首选模式。问题是,如果我在SqlConnection对象上调用Close()或Dispose()方法,它将返回到连接池,而不是物理关闭它。
我想知道当它返回到池中时,那个长查询是否仍在SQL Server端运行。如果可能,如何避免这种情况。
再次更新
感谢@dash提到数据库事务,是的,回滚会使它等待,我们还没有关闭连接并将其返回到池中。那么如果只是一个长时间的选择查询或更新,但只涉及一个单独的更新而没有任何数据库事务呢?具体来说,我想知道是否有一种方法可以告诉SQL Server我现在不需要结果,请停止运行它?

1
不,你不能杀死正在运行的 SQL Server 进程,但你真的只需要让 SQL Server 管理它正在做的事情。就像所有事情一样,如果你选择运行一个降低服务器性能的查询,那么这是你的选择 :-) - dash
您可以使用SQL KILL命令终止正在运行的SQL Server进程。KILL SPID,要获取SPID,您可以使用SELECT @@SPID或从其中一个sys表中获取所有当前进程。 - Darren
3个回答

5
这完全取决于您如何执行查询;
想象一下以下查询:
SqlConnection myConnection = new SqlConnection("connection_string");

SqlCommand myCommand = new SqlCommand();
myCommand.Connection = myConnection;
myCommand.CommandType = CommandType.StoredProcedure;
myCommand.CommandTimeout = some_long_time;
myCommand.CommandText = "database_killing_procedure_lol";

myConnection.Open() //Connection's now open

myCommand.ExecuteNonQuery();

两件事情会发生:一是该方法将在command.ExecuteNonQuery()完成之前排队。第二个是我们还将连接池中的一个连接绑定到该方法的持续时间。
如果超时会发生什么?好吧,会抛出一个异常-SqlException,其中Number属性=-2。然而,请记住,在上面的代码中,没有异常管理,所以所有的对象都会超出范围,我们需要等待它们被处置。特别是,在此之前,我们的连接不可重用。
这就是为什么以下模式被认为是首选的原因之一:
using(SqlConnection myConnection = new SqlConnection("connection_string"))
{
    using(SqlCommand myCommand = new SqlCommand())
    {

        SqlCommand myCommand = new SqlCommand();
        myCommand.Connection = myConnection;
        myCommand.CommandType = CommandType.StoredProcedure;
        myCommand.CommandTimeout = some_long_time;
        myCommand.CommandText = "database_killing_procedure_lol";

        myConnection.Open() //Connection's now open

        myCommand.ExecuteNonQuery();    

    }

}

这意味着,一旦查询完成,无论是自然完成(运行到结束)还是通过异常(超时或其他方式),资源都会立即被释放。
在你的具体问题中,有大量需要执行很长时间的查询是不好的,原因有很多。在Web应用程序中,你可能有许多用户争夺有限的资源,如内存、数据库连接、CPU时间等。因此,将任何这些资源与昂贵的操作绑定在一起,都会降低Web应用程序的响应性和性能,或者限制你可以同时服务的用户数量。另外,如果数据库操作很耗费资源,你也可能会占用数据库,进一步限制性能。
出于这个原因,尽管尝试缩短数据库查询的执行时间总是值得的。但如果做不到,那么你就必须小心地考虑可以同时运行这些类型的查询的数量。
编辑:
所以你实际上对SQL Server端发生的事情很感兴趣...答案是...这取决于情况!CommandTimeout实际上是一个客户端事件-你所说的是,如果查询需要超过n秒钟,那么我就不想再等了。SQL Server被告知了这一点,但它仍然必须处理它当前正在做的事情,因此在SQL Server完成查询之前可能需要一些时间。它会尝试优先处理这个问题,但仅限于此。
特别是在事务中,这一点尤其明显;如果您运行的查询被包装在事务中,并且作为异常管理的一部分将其回滚,则必须等待回滚完成。
看到人们惊慌失措并开始发布KILL命令来终止查询所在的SQL进程ID,这种情况非常普遍。如果命令正在运行事务,这通常是一个错误,但对于长时间运行的选择语句通常是可以的。
SQL Server必须管理自己的状态,以保持一致性。客户端不再监听的事实意味着您浪费了工作,但SQL Server仍然必须清理自己的工作。
所以,ASP.Net方面的事情都没问题,因为它不关心,但是SQL Server仍然必须完成它开始的工作,或者达到一个可以安全放弃该工作或回滚任何打开的事务中的任何更改的点。
显然,这可能会对数据库服务器产生性能影响,具体取决于查询!
即使在没有事务的情况下运行长时间的SELECT、UPDATE或INSERT也必须完成。SQL Server将尽快尝试放弃它,但只有在安全的情况下才会这样做。显然,对于UPDATES和INSERTS,它必须达到数据库仍然一致的状态。对于SELECT,它将尝试尽快结束。

非常抱歉,我的问题表述可能不够清晰,我想问的是关于连接池而不是更好地使用“using”关键字的实践方法,请查看我的更新问题。 - Simon Wang
这是同样的事情。如果您在完成连接后不立即释放它们,那么它们将无法在连接池中重复使用,直到被处理。该示例突出了您可能会无意中保留连接的时间比预期更长的情况。最后一部分讨论了执行许多长时间运行查询时可能遇到的性能问题。 - dash
嘿,我刚刚更新了我的问题,请检查一下。我真正提出的问题是:当我关闭连接并返回到连接池时,它是否仍在 SQL Server 端运行?我不是在问 .Net 如何处理连接池。谢谢。 - Simon Wang
答案通常是肯定的 - 它仍将继续运行。我在结尾处添加了一个讨论此事的部分。 - dash
谢谢Dash,虽然找不到正式的文档,但我仍然愿意接受你的答案。 - Simon Wang

0
感谢 @dash 提到关于数据库事务的问题,是的,回滚会让它等待,我们还没有关闭连接并将其返回到池中。那么如果只是一个长时间的查询或者只有一个单独的更新而没有任何数据库事务涉及呢?具体来说,我想知道是否有一种方法可以告诉 SQL Server 我现在不需要结果,请停止运行它?
我认为这个链接会回答你的问题: Link1 Link2

请注意文档中的内容 - “尝试取消 SqlCommand 的执行” - 关键词是“尝试” - 很遗憾,这并不是保证能够成功。 - dash

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接