使用 ADO.NET 对 Oracle DB 的异步调用

Asynchronous calls to Oracle DB using ADO.NET

我正在尝试使用 .NET Framework 4.5 中可用的异步和等待功能执行多个数据库调用。这是我第一次实现此功能。

如果每个查询需要 7 秒,则过去需要 35 秒(5 个查询 * 7 秒)。通过以下实现,我预计它应该在接近 7-9 秒的时间内获取并填充 asp 页面中的控件。然而,它仍然需要 35 秒,证明了我的同步行为。

谁能帮我看看下面这个异步实现哪里出了问题。

我很感激任何意见,几天以来我一直在为这个问题烦恼

    protected void Page_Load(object sender, System.EventArgs e)
    {
        RegisterAsyncTask(new PageAsyncTask(FillControlsAsync));
    }

    public async Task FillControlsAsync()
    {
         Task[] tasks = new Task[]{
         PopulateControlTask(query1, "controlID1"),
         PopulateControlTask(query2, "controlID2"),
         PopulateControlTask(query3, "controlID3"),
         PopulateControlTask(query4, "controlID4"),
         PopulateControlTask(query5, "controlID5")
        });

        await Task.WhenAll(tasks);
    }
    public async Task PopulateControlTask(string query, string control)
    {
       await Task.Run(() =>
           {
               DataSet ds;
               OracleCommand cmd;
               OracleDataAdapter da;
               try
               {
                   if (!Page.IsPostBack)
                   {
                       cmd = new OracleCommand(query, cn);
                       da = new OracleDataAdapter(cmd);
                       ds = new DataSet();
                       da.Fill(ds);
                       switch (control)
                       {
                           case "controlID1":
                                //some custom code for control 1
                                // like attaching the datasource to control.
                               break;
                           case "controlID2":
                               //some custom code for control 2
                               break;
                            case "controlID2":
                            //some custom code for control 3
                            break;
                            case "controlID3":
                            //some custom code for control 4
                            break;
                            case "controlID4":
                            //some custom code for control 5
                            break;
                    }
                }
            }
            catch(Exception e)
            {
                 //some error handling here
            }
        });
    }

asyncawait 用于异步代码。通常,如果您有一个可扩展的数据库,您可以使您的数据库调用异步,从而扩展您的服务器。请注意 async 在 ASP.NET 上的主要好处是可扩展性, 而不是 响应时间。

但是,正如其他人所指出的,Oracle 不支持异步代码。

但这并不重要,因为您发布的代码实际上并不是异步的!这就是我所说的 "fake asynchronous",因为它只是使用 Task.Run 而不是使用自然异步 API 将同步工作推到后台线程。 (但正如已经指出的那样,在这种情况下(即 Oracle),您没有任何可以使用的自然异步 API。

所以,你最终得到的是并行,而不是异步。特别是,代码将自身分散到 5 个线程池线程上来完成它的工作。

现在,您需要做的第一件事是问问自己是否真的想要服务器上的并行性。您的请求将占用 5 个线程而不是 1 个(或 0 个)线程。这会极大地影响 Web 服务器的可伸缩性(以一种糟糕的方式)。另外,请考虑后端的功能。如果它是单个服务器并且这些查询都在单个硬盘驱动器上访问单个数据库,那么并行化其中的 5 个是否真的会产生任何好处,或者它是否会因为磁盘争用而变得更糟甚至更糟? (您应该能够启动一个快速控制台应用程序来测试您的数据库在空闲和负载时如何响应串行请求和并行请求)。

我发现绝大多数时候,答案是 "no, I do not want to bring my entire db server to its knees for this one request" - 换句话说,避免服务器上的并行性。

但是,如果您权衡了各种选择并决定是,那么您的情况是在 ASP.NET、 然后 上适合并行性的罕见情况之一,您应该询问您在此处发布的问题:为什么这些 运行 顺序 而不是 同时? (旁注:它是 sequential vs concurrent 这里不是synchronous vs asynchronous)

答:不知道。

但我有一个猜测:如果数据库连接(cn 在您的代码片段中)是共享的,那么很可能数据库连接本身一次仅限于一个查询。其他数据库连接系统也有类似的限制。我要尝试的第一件事是为每个查询提供自己的连接。

也就是说,如果您想要并行化您的网络服务器。这是一个 "if".

为了跟进有关 Oracle 的异步实现的其他答案(没有足够的声誉来评论),Oracle 的异步方法并不是真正的异步,只是在幕后调用同步方法,所以调用它们的性能比同步方法。

您可以观看 https://github.com/oracle/dotnet-db-samples/issues/144 看看他们是否会提供真正的异步实现。截至 2022 年第一季度,Oracle .NET 团队表示可能是 2022 年第四季度。