asynchronous - 使用异步时,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
从“传统”代码切换可以立即解决问题。
我有哪些选择?
- 从默认值增加最大池大小
100
?试过了200
,没有帮助。我应该尝试10000
吗? - 使用
OpenAsync
而不是Open
?没有帮助。 - 我以为我遇到了这个问题https://github.com/dotnet/SqlClient/issues/18但不,我使用的是较新版本,
SqlClient
据说已修复。据说。 - 根本不
async
与数据库访问一起使用?嗯…… async
像这个答案所暗示的那样使用时,我们真的必须想出自己的节流机制吗?我很惊讶没有内置的解决方法......
PS 仔细查看进程转储 - 我检查了Tasks
报告,发现实际上有数以万计的阻塞任务处于等待状态。并且有 200 个数据库查询任务(这是连接池的大小)等待查询完成。
解决方案
好吧,经过一番挖掘、调查源代码和大量阅读之后,async
对于 DB 调用来说,这似乎并不总是一个好主意。
正如 Stephen Cleary(写了很多关于它的书的异步之神)所指出的那样——它真的很吸引我:
如果您的后端是单个 SQL Server 数据库,并且每个请求都 访问该数据库,那么使您的 Web 服务异步没有任何好处。
所以,是的,async
可以帮助你释放一些线程,但是这些线程做的第一件事就是冲回数据库。
还有这个:
旧式的常见场景是
client <-> API <-> DB,
,在该架构中,不需要异步数据库访问
但是,如果您的数据库是集群或云或其他一些“自动缩放”的东西 - 是的,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
推荐阅读
- c# - 从 WordNet 中提取名词
- python - 查找列表索引大于第二个列表中元素的有效解决方案
- amazon-product-api - amazon Product Advertising api 是否适用于 Wholefoods 和 Amazon Fresh?
- reactjs - 尝试使用子节点创建新节点时出现 FIREBASE 和 React Native 错误(然后不是函数)
- ios - 如何在 iOS Mac Catalyst 应用程序中获取 .app 文件?
- javascript - Google 电子表格脚本编辑器:onEdit setValue 或 clearContent
- c - 如何从页表中获取帧号?
- javascript - 如何在 Vue.Js 中从计算结果中提取 JSON 密钥
- python - IBM-Watson Text to Speech 引发“403:Forbidden”错误
- html - HTML水平规则是否有等效的Unicode字符
/