我怀疑这个问题与 SQL 服务器和应用程序层之间的交互有关。我将假设您没有在应用程序中使用 APM,因为您没有提及它。更不用说,在大多数人看来,APM 是为了让 UI 工作得更快,对吧?
科学位,集中
默认情况下,ASP.Net/IIS 为您提供有限数量的线程。请记住线程是昂贵的,每个线程都占用调度程序时间并以各种堆栈的形式占用内存,等等。这几乎是世界上所有计算机的缺陷。
在 .net 中,所有工作都在线程上完成。因此,当没有空闲线程时,IIS 会将请求放入队列以等待线程。现在,通常你会认为如果所有线程都在使用中,那么 CPU 利用率就会很高。这是错误的。通常对于现代 CPU,大部分时间它们都处于 I/O 饥饿状态,这意味着它们正在休眠。
在这种情况下,通常会发生一些请求。每次启动他们自己的线程,然后访问数据库。然后他们等待(睡觉)。您的 CPU 利用率达到 0%,而您的所有线程都在使用中。更多的请求进来。它们被放入队列中。数据库请求返回,一些请求出列(但不是全部)。然后队列上的请求超时。
摩尔线!
我们如何解决这个问题?显然,我们希望尽可能快地将 IIS 队列中的大部分工作转移到 SQL 服务器上,对吗?所以显然答案是增加线程数,对吧?现在,正如我之前提到的,线程很昂贵,所以如果你有一个非常强大的 SQL 服务器,你的应用程序服务器仍然会在 SQL 服务器之前放弃幽灵,同时仍然有 0% 的 CPU 利用率。显然更多的线程不会让我们到达我们想要的地方。
异步/等待魔法酱!
公认的解决方案是实际使用异步编程。
但是不是异步/等待 UI 和并行化吗?
不。它最常在 UI 和并行化中展示,因为它的收益最容易可视化。在 1 小时的演示中模拟 100 万次点击/秒的服务要困难得多。
因此,当我们向数据库发送查询时,线程不会在结果上休眠,而是跳回 IIS 队列以服务下一个客户。当结果返回时,通知下一个可用线程并处理它。
因此,使用 async/await 进行数据库调用,您可以最大限度地利用 CPU/网络,并忽略数据库的延迟。事实上,你会发现你应该已经把瓶颈转移到了 SQL 服务器上。
但是我的 API 在哪里?
啊...这是问题所在。异步/等待是相当新的。您需要 VS 2012 和 .net 4.5(很好)才能使用它。此外,大多数数据库 API 还没有完全支持 Async/Await。
例如 Entity Framework,Microsoft 的旗舰 DB 技术仅支持 EF 6.0 ALPHA 中的 async/await(截至撰写本文时),并且很可能仅支持 MS SQL SERVER。