ODP.NET的首次查询总是很慢

本文关键字:查询 NET ODP | 更新日期: 2023-09-27 18:20:50

注意:我考虑先在DBA Exchange上发布这篇文章,但考虑到这是一个.NET客户端问题,我认为最好先在这里询问。

我有两个函数存储在我的Oracle 11g开发服务器中,它们是使用ODP.NET调用的(使用Oracle.ManagedDataAccess,而不是Oracle.DataAccess)

在SQL Developer中,这两个函数的速度非常快(这是有道理的,它们只是在大约20000条记录上选择的简单查询),但当使用ODP.Net.从我的C#应用程序中启动时,性能(用System.Diagnostics.Stopwatch衡量)并不出色

以下是结果:(忽略"转换时间和合成时间,它们不是查询过程的一部分")

Connecting time - GET_TVM_ALL: 00:00:00.0553501
Query time - GET_TVM_ALL: 00:00:05.3467058
Conversion time: 00:00:07.6508273
Connecting time - GET_TVM_STATUS_ALL_FUNC: 00:00:00.0006773
Query time - GET_TVM_STATUS_ALL_FUNC: 00:00:00.0256008
Conversion time: 00:00:03.7280097
Composing time: 00:00:00.0157274
Total Elapsed: 00:00:16.7796351

GET_VM_ALL的5秒执行时间高得离谱。更令人惊讶的是,第二个查询要快得多。这很奇怪,因为毫无疑问,这是一个对超过20倍记录量的更复杂的查询。

所以我改变了它们,结果是:

Connecting time - GET_TVM_STATUS_ALL_FUNC: 00:00:00.0573807
Query time - GET_TVM_STATUS_ALL_FUNC: 00:00:05.2981962
Conversion time: 00:00:03.6474905
Connecting time - GET_TVM_ALL: 00:00:00.0007322
Query time - GET_TVM_ALL: 00:00:00.0070785
Conversion time: 00:00:07.2473809
Composing time: 00:00:00.0154049
Total Elapsed: 00:00:16.2268687

正如您所看到的,无论内容如何,第一个查询似乎总是很慢。为了证明这一点,我做了一个愚蠢的伪函数:

CREATE OR REPLACE FUNCTION GET_DUMMY
RETURN SYS_REFCURSOR
AS
    -- REFCURSOR to return data
    pCursor SYS_REFCURSOR;
    BEGIN
        OPEN pCursor FOR SELECT 1 FROM DUAL;      
        RETURN pCursor;
    END;

现在,从我的代码中调用它,让我们看看:

Connecting time - GET_DUMMY: 00:00:00.0581149
Query time - GET_DUMMY: 00:00:05.4103165
Conversion time: 00:00:00.0005617
Connecting time - GET_TVM_STATUS_ALL_FUNC: 00:00:00.0006580
Query time - GET_TVM_STATUS_ALL_FUNC: 00:00:00.0759243
Conversion time: 00:00:03.7577602
Connecting time - GET_TVM_ALL: 00:00:00.0000489
Query time - GET_TVM_ALL: 00:00:00.0037654
Conversion time: 00:00:07.5071360
Composing time: 00:00:00.0152159
Total Elapsed: 00:00:16.7819147

这证明了这一点,我运行的第一个查询总是很慢。

额外信息:我正在为我调用的每个函数打开和关闭一个新的连接。

顺便说一句,这是我的助手功能:

public static List<T> ExecuteFunction<T>(string strConnection, string strFunction, OracleDbType returnType, List<DataOracleParameter> parameterList) where T : new()
{
    Stopwatch watch = new Stopwatch();
    using (OracleConnection objConnection = new OracleConnection(strConnection))
    {
        // Create the command object and set attributes
        OracleCommand objCommand = new OracleCommand(strFunction, objConnection);
        objCommand.CommandType = CommandType.StoredProcedure;
        // Set the return parameter and type
        OracleParameter returnValue = new OracleParameter();
        returnValue.OracleDbType = returnType;
        returnValue.Direction = ParameterDirection.ReturnValue;
        objCommand.Parameters.Add(returnValue);
        // Set additional parameters
        if (parameterList != null && parameterList.Count > 0)
        {
            foreach (DataOracleParameter parameter in parameterList)
            {
                OracleParameter inputValue = new OracleParameter();
                inputValue.ParameterName = parameter.ParameterName;
                inputValue.OracleDbType = parameter.ParameterType;
                inputValue.Value = parameter.ParameterValue;
                inputValue.Direction = ParameterDirection.Input;
                objCommand.Parameters.Add(inputValue);
            }
        }
        // Create a data adapter to use with the data set
        OracleDataAdapter dataAdapter = new OracleDataAdapter(objCommand);
        // Create and fill the dataset
        DataSet dataSet = new DataSet();
        watch.Start();
        dataAdapter.Fill(dataSet);
        watch.Stop();
        Console.WriteLine("Query time - {0}: {1}", strFunction, watch.Elapsed);
        List<T> valueList = dataSet.Tables[0].ToList<T>();
        return valueList;
    }
}

ODP.NET的首次查询总是很慢

首先,我建议您调整OracleCommand对象上的FetchSize。

我今天遇到了这个问题,它让我想起了十年前微软Oracle驱动程序的一个问题。当我们使用参数时,速度很慢,但如果我们转换为文字,它会像预期的那样工作。参数,我一直认为,是一个最佳实践,所以这对我来说非常困惑

原来是微软的适配器,连他们都承认这是垃圾。切换到ODP.net修复了它。

快进到今天。。。我在使用Oracle的Managed ODP.net时也遇到了同样的现象。当我使用参数(也就是正确的方法)时,甚至需要花费FOREVER才能运行查询执行。

using (OracleCommand cmd = new OracleCommand(sql, conn))
{
    cmd.Parameters.Add("FROM_DATE", fromDate);
    cmd.Parameters.Add("DISTRIBUTOR_ID", distributorId);
    using (OracleDataReader reader = cmd.ExecuteReader()) // Bottleneck here
    {
    }
}

当我转向文字(同样,这是一种可怕的做法)时,它立刻就消失了。

sql = sql.Replace(":DISTRIBUTOR_ID", distributorId.ToString())
    .Replace(":FROM_DATE", string.Format("'{0:dd-MMM-yyyy}'", fromDate));
using (OracleCommand cmd = new OracleCommand(sql, conn))
{
    using (OracleDataReader reader = cmd.ExecuteReader())
    {
    }
}

令人失望。。。管理ODP的问题?侥幸我不会将其用作标准实践,但目前,我将保留此特定代码中的文字。我的应用程序控制这些值,因此它是SQL注入安全的。

附言:我知道我应该使用Oracle的to_date和显式参数声明。

我也有同样的问题,我重命名了iis应用程序池,简称它,它解决了我的问题,虽然不理解,但它对我来说是