【问题标题】:Why are asynchronous calls to my database desireable?为什么需要对我的数据库进行异步调用?
【发布时间】:2014-03-10 17:46:42
【问题描述】:

我编写了一个与 MSSQL 数据库交互的服务器。它目前是用 .NET 4.0 编写的,并使用 NHibernate 作为 ORM 从数据库中检索信息。在阅读 .NET 4.5 和 async/await 关键字的介绍时,我了解到,不幸的是,NHibernate does not have support for async/await

我不明白为什么向数据库发出异步调用会有好处。不是所有的请求都在数据库级别排队吗?异步不会只是增加故障点而不改进任何东西吗?

【问题讨论】:

    标签: c# .net sql-server asynchronous


    【解决方案1】:

    一般来说,这样做的好处是在运行可能代价高昂(异步)的操作时不会阻塞当前正在执行的线程。在 WPF / Windows 窗体应用程序的上下文中,这意味着您没有阻塞 UI 线程(如果请求来自该线程)并且您的应用程序保持响应。

    在 Web 应用程序(例如 IIS)的上下文中,这意味着您在等待结果时正在释放池中的线程。由于您没有锁定线程,因此可以重复使用它来接受另一个请求,并在接受的连接方面产生更好的性能(不一定是时间/请求)。

    【讨论】:

    • 谢谢。这是一个相当有用的解释。我正在开发一个 Web API,所以没有阻塞的 UI 线程,但我可以看到你关于线程池的观点。似乎这就是人们“为”诸如 NodeJS 之类的东西而“隐式”处理它的论点。
    【解决方案2】:

    难道不是所有的请求都在数据库级别排队吗?

    没有。阅读Understanding how SQL Server executes a query。任何名副其实的数据库服务器都可以同时运行数百个请求。仅当请求相关时(例如,您需要将查询 1 的输出作为参数传递给查询 2)或在事务约束下操作(事务中的任何时间只能激活一条语句)时,才需要序列化。

    异步调用至少有两个主要优点:

    • 资源使用情况。不考虑其他任何事情,只需将编程模型更改为事件驱动的异步模型将导致您的应用程序可以驱动的吞吐量增加一个数量级。当然,这适用于后端应用程序(例如 Web 服务器),而不适用于客户端用户驱动的应用程序,该应用程序将无法发送超出用户启动的内容之外的任何内容。阅读从High Performance Windows programs 链接的文章。这也很重要,尽管有点过时:Asynchronous Pages in ASP.NET 2.0

    • 重叠请求。同步模型不允许在当前查询完成之前向后端发出查询。很多时候,应用程序拥有发出两个或多个不相关请求所需的信息(参数),但它确实可以。进行异步调用允许控制线程发出所有并行请求,并在它们全部完成后恢复。

    非 NHibernate 的 .Net 4.5 任务都没有很好地支持异步 DB 编程。老老实实的BeginExecuteXXX 实际上要强大得多,虽然编程起来有点神秘。

    【讨论】:

    • 嘿,你比我快 32 秒。老鼠。
    【解决方案3】:

    NHibernate 可以支持 真正的异步 调用。我已经在自己的分支上实现了它

    https://github.com/ReverseBlade/nhibernate-core/tree/nh_4.5.1

    您可以检查并编译。它是针对 .net 4.5.1 编译的。它与标准 nhibernate 兼容并通过所有测试。 然后你可以使用 .ToListAsync(); 之类的东西。或 GetAsync(),它将进行真正的异步调用。

    如果您需要帮助,可以写评论。祝你好运

    【讨论】:

    • 如果你已经让它工作了,你怎么还没有将它作为拉取请求提交给 NHibernate?只是好奇。另外,为什么要删除所有 IDBCommand 接口以支持 DBCommand?
    • 因为它针对.net 4.5.1(实际上我可以将其重写为针对.net 4.5),因为.net 4中不存在ExecuteReaderAsync方法。所以它不会合并到主分支,因为那么 nh 将无法在 .net 4 中工作。对于 DBCommand,将来您会看到它也将在 nhibernate 中运行。这是调用异步方法的唯一方法。 IDbCommand 不支持异步
    • 好的。凉爽的。你有一个测试用例我可以用来查看我是否正确使用异步,或者一个 git 存储库,我可以看到你使用这个版本的 NHibernate 的几行?我会试着在这个周末试一试,如果我成功了,我会回来报告。 :)
    • 我还没有提交测试。但这很简单,只需执行await session.Query<SomeEntity>().Where(...).ToListAsync(); 请注意,您必须从 Visual Studio 编译它,在这种情况下显式引用了 Antr.Runtime。没有 ILMerge(我不喜欢它)
    • 还有其他的异步方法,比如 session.GetAsync()
    【解决方案4】:

    好消息。 NHibernate 从 v 5.0 开始支持 async/await 开箱即用

    【讨论】:

      【解决方案5】:

      您可能会将语言功能与设计模式混淆; async 是帮助您管理后台任务的语法糖,而异步任务只是意味着您正在运行两个或更多线程。

      仅仅因为 NHibernate 不支持async 并不意味着你不能异步运行。这对用户非常有益,因为您不想在对数据库/服务执行(相对)长时间运行的查询时冻结 UI,尤其是在服务器陷入困境的情况下。

      我想您可以将此视为故障点,但实际上只是几个方面:

      1. 例外情况 - 无论如何你都会在一个线程上遇到这个问题,但你应该优雅地处理你遇到的任何数据库错误。
      2. UI 管理 - 您不希望用户以触发多个查询的方式与 UI 交互,因此您可能会禁用按钮等。
      3. 结果处理 - 查询完成后,您需要确保将数据编组回 UI 线程。在 C# 中,这可以通过 Invoke/BeginInvoke 来完成,尽管您是在 WinForms 还是 WPF 中决定了细节。

      编辑:

      假设 WPF 和至少 .NET 4.0 的一些示例框架代码

      Task.Factory.StartNew(() =>
      {
          using (var client = new dbClient())
          {
              // Perform query here
      
              this.Dispatcher.BeginInvoke(new Action(() =>
              {
                  // Set data source, etc, i.e.
                  this.Items = result;
              }));
          }
      }).ContinueWith(ex => Logger.LogException(ex), TaskContinuationOptions.OnlyOnFaulted);
      

      【讨论】:

      • 语言功能完全正确。但是,异步代码与运行更多线程无关。正确使用时,不需要额外的线程来服务,例如。请求完成时内核触发的事件之外的“等待”数据库请求。可悲的是,NHibernate 只支持同步等待,所以无论如何它都会吃掉一个线程。
      【解决方案6】:

      你说:

      难道不是所有的请求都在数据库级别排队吗?

      如果您所说的“队列”是指“单一服务队列”,那么答案是否定的。 SQL Server 是一种高度异步和多线程的服务,可以同时为许多查询提供服务。

      即使在物理级别,排队(即物理设备服务)也会同时在 CPU 内核数量和组成磁盘阵列的物理磁盘数量上进行拆分。

      因此,对 SQL Server 进行异步调用的原因是能够将部分多线程/多服务能力利用到您自己的服务中。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-01-20
        • 1970-01-01
        • 2022-12-05
        • 1970-01-01
        • 2015-04-05
        • 2016-08-12
        • 1970-01-01
        相关资源
        最近更新 更多