0

我们正在构建一个 asp.net core 3 应用程序,它使用 ef core 3.0 和 Pomelo.EntityFrameworkCore.MySql provider 3.0。

现在我们正在尝试将所有数据库调用从同步替换为异步,例如:

//from
dbContext.SaveChanges();
//to
await dbContext.SaveChangesAsync();

不幸的是,当我们这样做时,我们会遇到两个问题:

  1. 与同步调用的相同测试相比,与服务器的连接数显着增长
  2. 我们应用程序的平均处理速度显着下降

异步使用 ef core 和 mysql 的推荐方法是什么?任何将 ef-core 3 与 MySql 异步使用的工作示例或证据将不胜感激。

4

2 回答 2

1

如果没有看到更多代码,很难说这里的问题是什么。您能否为我们提供一个重现该问题的小型示例应用程序?

任何DbContext实例都只使用一个数据库连接来进行正常操作,而与您调用同步方法还是异步方法无关。

与同步调用的相同测试相比,与服务器的连接数显着增长

我们在谈论什么样的测试?它们是自动化的吗?如果是这样,有多少测试正在运行?由于异步调用的性质,如果您并行运行 1000 个测试,每个测试都有自己的DbContext,您最终将获得 1000 个并行连接。

虽然使用 Pomelo,但您不会像使用 Oracle 的提供程序那样额外获得 1000 个线程。

更新:

我们测试 asp.net 核心调用 (mvc),它进入 db 并读取和写入一些东西。50 个线程,使用限制为 500 的 DbContextPool。如果我使用 dbContext.SaveChanges()、Add(),所有上下文方法同步,我将获得大约 50 个与 MySql 的连接,使用 dbContext.SaveChangesAsnyc() 以及 AddAsnyc、ReadAsync 等,我最终看到最多 250 个与 MySql 的连接,页面的平均响应时间下降了 2 到 3 倍。

(我说的是 ASP.NET 和下面的请求。并行运行测试用例也是如此。)

如果您一直使用异步方法,则不会阻塞,因此您的 50 个线程可以自由处理接下来的 50 个请求,而数据库仍在执行前 50 个请求的查询。

这将一次又一次地发生,因为 ASP.NET 处理您的请求的速度可能比您的数据库返回其结果的速度要快。所以你最终会得到很多并行的数据库查询。

执行 Sync 方法时不会发生这种情况,因为每个线程都会阻塞,并且您最终会得到最多 50 个并行查询(每个线程一个)。

所以这是预期的行为,只是异步方法调用的结果。

您始终可以修改代码或 Web 服务器配置以限制并发 ASP.NET 请求的数量。

50 个线程,使用限制为 500 的 DbContextPool。

另请注意,DbContextPool不限制DbContext可以同时存在的对象数量,而只限制池中保留的对象数量。所以如果你设置DbContextPool为 500,你可以创建超过 500 个上下文,但使用后只有 500 个会保持活动状态。

更新:

@roji有一个非常有趣的关于无锁数据库池编程的低级讨论,它解决了这种行为并采取了你的立场,连接池中应该有一个上限,当超过时会导致阻塞,这是一个很好的案例对于这种行为。

根据来自 MySqlConnector 的@bgrainger,这就是它已经实现的方式(文档没有明确说明这一点,但他们现在这样做了)。连接字符串选项的MaxPoolSize默认值为100,因此,如果您使用连接池并且不覆盖此值并且不使用多个连接池,则在给定时间不应有超过 100 个活动连接。

来自GitHub

这是一个文档错误,如果您将文档解释为意味着您可以创建无限数量的连接。

当 pooling 为 时true,每个连接池(每个唯一的连接字符串都有一个)只允许MaximumPoolSize同时打开连接。每个额外的调用MySqlConnection.Open都会阻塞,直到连接返回到池中。

当 pooling 为false时,可以同时打开的连接数没有限制;由用户来管理并发。

于 2019-11-30T00:34:19.523 回答
1

正如Bradley Grainger评论Pooling=false中提到的那样,检查您的连接字符串中是否有。

从我的连接字符串中删除后pooling=false,我的应用程序运行速度实际上快了 3 倍。

于 2021-03-14T18:17:07.573 回答