【问题标题】:Simultaneous requests slow down asp net core API同时请求减慢asp net core API
【发布时间】:2022-10-19 14:48:03
【问题描述】:

TLDR;我有一个位于 AWS 的 ASP.NET Core 5.0 API。它对 MSSQL db 进行大量调用以返回约 1-4k 行数据。单个请求很好,大约需要 500 毫秒,但是当多个请求大约在同一时间 (4-5) 进入时,请求会减慢到每次调用约 2000 毫秒。这是怎么回事?

没有什么比我上面所说的更多的了。我打开一个到我们的数据库的连接,然后初始化一个 SqlCommand。

using (var connection = new SqlConnection(dbConnection))
connection.Open();
using (SqlCommand command = new SqlCommand(strSQLCommand))

我已经尝试使用 SqlDataAdapter 填充数据表并使用 SqlDataReader 填充自定义对象,无论哪种方式我都会遇到类似的减速。如上所述,查询返回约 1-4k 行不同类型的数据。而Postman表示,解压后返回的Json数据大小约为1.95MB。仅当多个请求大约在同一时间进入时才会出现减速。我不知道与数据库的多个连接是否有问题,或者是否与数据大小和可用内存有关。分页不是一个选项,请求需要返回那么多数据。

这一切都发生在 HttpGet 函数中

[HttpGet]
[Route("Foo")]
[Consumes("application/json")]
[EnableCors("DefaultPolicy")]
public IActionResult Foo([FromHeader] FooRequest request)
{
///stuff

DataTable dt = new DataTable();
using (var connection = new SqlConnection(_dataDBConnection))
{
    timer.Start();
    connection.Open();
    using (SqlCommand command = new SqlCommand(
        "SELECT foo.name, bar.first, bar.second, bar.third, bar.fourth 
         FROM dbo.foo with(nolock)
         JOIN dbo.bar with(nolock) ON bar.name = foo.name
         WHERE bar.date = @date", connection))
    {
        command.Parameters.AddWithValue("@date", request.Date.ToString("yyyyMMdd"));
        using (SqlDataAdapter adapter = new SqlDataAdapter(command))
        {
            adapter.Fill(dt);
        }
    }

    timer.Stop();

    long elapsed = timer.ElapsedMilliseconds;
}

///Parse the data from datatable into a List<object> and return

///I've also used a DataReader to put the data directly into the List<object> but experienced the same slowdown.

///response is a class containing an array of objects that returns all the data from the SQL request
return new JsonResult(response);
}

任何见解将不胜感激!

--附加测试后编辑---

[HttpGet]
[Route("Foo")]
[Consumes("application/json")]
[EnableCors("DefaultPolicy")]
public IActionResult Foo([FromHeader] FooRequest request)
{
///stuff

using (var connection = new SqlConnection(_dataDBConnection))
{
    connection.Open();
    ///This runs significantly faster
    using (SqlCommand command = new SqlCommand(@"dbo.spGetFoo", connection))
    {
        command.CommandType = CommandType.StoredProcedure;
        command.Parameters.AddWithValue("@date", request.date.ToString("yyyyMMdd"));

        using (SqlDataReader reader = command.ExecuteReader())
        {
            while (reader.Read())
            {
                ///Add data to list to be returned
            }
        }
    }
}

///Parse the data from datatable into a List<object> and return

///I've also used a DataReader to put the data directly into the List<object> but experienced the same slowdown.

///response is a class containing an array of objects that returns all the data from the SQL request
return new JsonResult(response);
}

【问题讨论】:

  • 那么,大概您正在使网络或数据库服务器饱和;您有一个返回 1k-4k 行的查询;是否有可能不要那样做?或者至少:不是每次通话?也许是本地缓存,也许在服务器上做更多,所以你不需要取回所有东西?
  • @MarcGravell,在不过多涉及调用性质的情况下,我会说每个请求都是独一无二的,以至于缓存无济于事。并且需要所有数据。我们有一个 PHP api,我们正在用这个 asp.net 核心 API 替换它,而 PHP 似乎没有这个问题。所以,大概不是数据库问题。
  • 是否可以获取整个 SQL 事务的代码块?
  • @Tachyon 我已经包含了上面的块,我认为我不应该分享我正在做的确切的 SQL 调用,但足以说它正在访问一个有几百万行的表,加入一个有十亿行的表,并返回几千行。直接在 SQL Management Server 中运行查询只需要几毫秒,所以我不认为查询本身是问题所在。
  • 做了一些进一步的测试,即使我将 TOP 1 放入命令中以便它只检索第一行,我仍然会遇到减速。我还将它从适配器更改为 SqlDataReader,并且可以说减速发生在 command.ExecuteReader() 上。

标签: c# rest asp.net-core


【解决方案1】:

我认为您的想法是正确的,它不应该是数据库问题。

我认为 Session 可能是一个嫌疑人。如果您使用 ASP.NET Core 应用程序中的会话,请求被排队并处理一个 一。因此,最后一个请求可以留在队列中,而 正在处理以前的请求。

另一个可能是在您的管道中运行的一些 MVC,它可以带来 会话不问你。

另外,另一个可能的原因是所有线程在 ASP.NET Core Thread Pool are busy。 在这种情况下,将创建一个新线程来处理新请求 这需要额外的时间。

这只是我的想法,任何其他原因都是可能的。希望它可以帮助你。

【讨论】:

  • ASP.NET Core 会话不会一次将一个请求排队 :)
【解决方案2】:

首先要注意的是,您的操作方法不是异步的。这里要注意的第二件事是,使用适配器填充数据集是我多年未见的东西。利用小巧玲珑!最后,对适配器的Fill() 方法的调用很可能是同步的。转移到 Dapper 并使用异步调用来最大化您的 ASP.net 吞吐量。

【讨论】:

  • Fill() 方法不是我多年来没有使用过的东西,而且还是测试不同方法的产物。它恰好是我写这篇文章时留下的那个。我还使用了 SqlDataReader 来获得类似的结果。我很欣赏这些建议,我可能会考虑使用 Dapper 作为替代方案,但我认为这不一定是问题的答案。我尝试使函数 async Task<IActionResult> 并使用 await connection.openasync() 并读取 async 无济于事
  • 如果它不是一个明确的答案,那很好,不用担心。如果你已经让你的项目完全异步,那么我建议你监控数据库本身并确保查询在 500 毫秒内响应。如果是这种情况,那么服务器上还有更多工作可以完成,但是如果数据库级别的查询时间随着负载的增加而增加,那么很可能是您的数据库引擎无法同时承担负载。祝您查询顺利。
【解决方案3】:

刚刚注意到连接没有关闭,如果您的连接池有限,这可能会导致速度变慢

【讨论】:

    猜你喜欢
    • 2015-09-21
    • 2021-11-03
    • 2021-08-19
    • 1970-01-01
    • 2017-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-26
    相关资源
    最近更新 更多