首页 > 解决方案 > 使用异步时,SqlClient 连接池已满

问题描述

我有一个使用 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
}

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

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

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

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

我有哪些选择?

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

标签: asynchronous.net-coreasync-awaitsqlclient

解决方案


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

正如 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 -my-database-calls-be-asynchronous.aspx


推荐阅读