使用异步时 SqlClient 连接池达到极限

SqlClient connection pool maxed out when using async

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

The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.

而且我看到 线程池中的线程数 增长到 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
}

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

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

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

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

我有哪些选择?

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

好吧,经过一些挖掘、调查源代码和大量阅读后,async 似乎并不总是数据库调用的好主意。

正如 Stephen Cleary(写了很多关于它的书的异步之神)所说 nailed it - 它真的让我很满意:

If your backend is a single SQL server database, and every single request hits that database, then there isn't a benefit from making your web service asynchronous.

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

还有这个:

The old-style common scenario was client <-> API <-> DB, and in that architecture there's no need for asynchronous DB access

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

这里还有一篇我认为有用的 archive.org 里克·安德森 (RIck Anderson) 的旧文章:https://web.archive.org/web/20140212064150/http://blogs.msdn.com/b/rickandy/archive/2009/11/14/should-my-database-calls-be-asynchronous.aspx