使用异步时 SqlClient 连接池已满

Ale*_*lex 6 asynchronous sqlclient async-await .net-core

我有一个使用 SQL Server 的繁忙 ASP.NET 5 Core 应用程序(每秒数千个请求)。最近,我们决定尝试将一些热代码路径切换为async数据库访问,但是......应用程序甚至没有启动。我收到此错误:

从池中获取连接之前超时时间已过。发生这种情况的原因可能是所有池连接都在使用中并且已达到最大池大小。

我看到线程池中的线程数量增长到 40...50...100...

我们使用的代码模式非常简单:

using (var cn = new SqlConnection(connenctionStrng))
{
    cn.Open();
    var data = await cn.QueryAsync("SELECT x FROM Stuff WHERE id=@id"); //QueryAsync is from Dapper
}
Run Code Online (Sandbox Code Playgroud)

我做了一个进程转储,所有线程都在线上cn.Open(),只是坐在那里等待。

这主要发生在 IIS 上的应用程序“回收”期间,即应用程序进程重新启动并且 HTTP 请求从一个进程排队到另一个进程时。导致队列中有数万个请求需要处理。

嗯,是的,我明白了。我想我知道发生了什么事。async使应用程序更具规模。当数据库忙于响应我的查询时,控制权将返回给其他线程。它尝试并行打开越来越多的连接。连接池已满。但是为什么关闭的连接在工作完成后没有立即返回到池中呢?

async“传统”代码切换到“传统”代码可以立即解决问题。

我有什么选择?

  • 增加默认的最大池大小100?尝试过200,没有帮助。我应该尝试一下吗10000
  • 使用OpenAsync而不是Open? 没有帮助。
  • 我以为我遇到了这个问题https://github.com/dotnet/SqlClient/issues/18但不,我使用的是较新版本SqlClient,据说已修复。据说。
  • async根本不与数据库访问一起使用?呃……
  • async当像这个答案建议的那样使用时,我们真的必须想出我们自己的节流机制吗?我很惊讶没有内置的解决方法......

PS 仔细查看进程转储 - 我检查了报告Tasks,发现实际上有数以万计的阻塞任务处于等待状态。正好有 200 个数据库查询任务(这是连接池的大小)等待查询完成。

Ale*_*lex 0

好吧,经过一番挖掘、调查源代码和大量阅读后,async对于数据库调用来说,这似乎并不总是一个好主意。

正如 Stephen Cleary(异步之神,写了很多关于它的书)所指出的那样- 它真的很适合我:

如果您的后端是单个 SQL Server 数据库,并且每个请求 都会访问该数据库,那么使您的 Web 服务异步并没有任何好处。

所以,是的,async可以帮助您释放一些线程,但是这些线程所做的第一件事就是冲回数据库

还有这个:

旧式的常见场景是client <-> API <-> DB,,在该架构中不需要异步数据库访问

但是,如果您的数据库是集群、云或其他“自动缩放”的东西,那么async数据库访问就很有意义

这也是archive.orgRIck Anderson 的一篇旧文章,我发现它很有用: https: //web.archive.org/web/20140212064150/http ://blogs.msdn.com/b/rickandy/archive/2009/11/14/should -我的数据库调用-be-asynchronous.aspx