如何在.Net中有效地处理来自Oracle的慢速CLOB读取?

时间:2013-12-12 09:43:50

标签: c# performance oracle data-access-layer clob

在我的公司,我们有一个WPF应用程序,它可以连接到不同的数据库(MS SQL,MySQL,SQLite和Oracle)。我们有很多表(有时200多个,不要问为什么,它非常复杂),我们的dataaccess层有几个接口和虚拟/重写方法来处理特定数据库中的数据读取。重要的是,因为我们没有像大多数教程所示那样专门在一个表上工作,而是我们动态创建了我们需要运行的特定于数据库的命令。在MS SQL,MySQL和SQLite下一切运行良好,但在Oracle CLOB中读取速度非常慢。要读取2000行数据,需要40-50秒。不幸的是,在大多数情况下,我们不能假设我们不需要CLOB类型,因为我们以xml格式存储财务数据,有时它超过4000,8000甚至10k + char-long大小。我正在使用ODP.Net解决方案来读取数据,这就是我正在做的事情:

我们的环境: Oracle:11.2.0.1.0 VS:2010年,专业版 我的Oracle主页安装引用了Oracle.DataAccess.dll: C:\甲骨文\ ODP.NET \ BIN \ 4 \ Oracle.DataAccess.dll

我的测试代码(来自我们的解决方案):

属性:

private string ConnectionString
{
   get
   {
      return
         string.Format(
         "User Id={0}; Password={1}; POOLING=true;
         Data Source= (DESCRIPTION=(ADDRESS=
         (PROTOCOL=TCP)(HOST={2})(PORT={3})) (CONNECT_DATA=(SID={4})
         (SERVICE_NAME={5})));",
         "ourUser", "ourPassword", "ourHost", "ourPort", "ourSID",
         "ourDatabaseName");
        }
    }

要运行的命令:

string sql = "SELECT * FROM OurTable";
//This table contains at least one CLOB column

我们的测试代码:

List<object[]> readerList = new List<object[]>();

using (Oracle.DataAccess.Client.OracleConnection oraConn = new
    Oracle.DataAccess.Client.OracleConnection(ConnectionString))
{
   oraConn.Open();
   Oracle.DataAccess.Client.OracleCommand oraComm = new
      Oracle.DataAccess.Client.OracleCommand(sql);
   oraComm.CommandType = CommandType.Text;
   oraComm.Connection = oraConn;
   Oracle.DataAccess.Client.OracleDataReader oraReader;
   oraReader = oraComm.ExecuteReader();
   oraComm.InitialLOBFetchSize = -1;

   while (oraReader.Read())
   {
      object[] readObjects = new object[oraReader.FieldCount];
      oraReader.GetValues(readObjects);
      readerList.Add(readObjects);
   }

  oraConn.Close();
}

while迭代运行速度非常慢,除非我们不需要读取CLOB,因为它会很快。 不幸的是我无法创建特定于表的解决方案,因为我并不知道在哪些表上我必须处理(有时会有动态创建的表)。

所以,问题是: 是否有任何解决方案使其与从MS SQL中读取TEXT类型对象一样快?

3 个答案:

答案 0 :(得分:2)

我知道这是旧的,但我遇到了类似的问题。在 OracleCommand 上设置 InitialLOBFetchSize = -1对我来说有很大的不同。我的CLOB列具有一致且合理的大小,因此这个设置对我来说很有意义。

通过将 InitialLOBFetchSize 设置为-1,可以从数据库中获取选择查询的整个LOB数据,而无需选择列表中的主键,ROWID或唯一列。当InitialLOBFetchSize设置为-1时,将在OracleDataReader对象上的Read方法调用期间获取并缓存整个LOB列数据。

更多详情在这里: https://docs.oracle.com/cd/B28359_01/win.111/b28375/featData.htm#BABFGDGJ

答案 1 :(得分:0)

很难确切说明导致缓慢的原因。但我很确定clob不应该像这样正常减速。

最好缩小C#代码(odp.net访问方式)或oracle服务器是否是根本原因。在甲骨文方面有各种方法可以识别。我要尝试的第一件事是使用SET AUTOTRACE ON在sql * plus上执行SQL,这将告诉我们在oracle服务器上的行为。

  • 此外,我们可以提出几点猜测或建议 在你的帐户上。如果您的数据长度始终小于32K 你可以将oracle升级到12c,而不是尝试varchar2而不是clob或 blob,因为12c中的varchar2已经扩展到32K;
  • LOB存储是lob的重要选择。 “选择 dbms_metadata.get_ddl('TABLE','你的表')来自dual;“会告诉你 表格中的选项。对我来说,“在行中启用存储”应该 总是被使用。有关此选项的详细信息,请参阅 http://docs.oracle.com/cd/B28359_01/appdev.111/b28393/adlob_tables.htm#i1012988

答案 2 :(得分:0)

查看Command对象中的属性FetchSize,请参阅此处:Improve ODP.NET Performance

您可以像这样获得行的大小:

   Public Function GetRowSize(ByVal cmd As OracleCommand) As Integer
      Dim dr As OracleDataReader
         dr = cmd.ExecuteReader()
         Return CInt(dr.GetType.GetField("m_rowSize", Reflection.BindingFlags.Instance Or Reflection.BindingFlags.NonPublic).GetValue(dr))
   End Function

在C#中它看起来像这样:

public int GetRowSize(OracleCommand cmd)
{
   OracleDataReader dr = cmd.ExecuteReader();
   return (int)( dr.GetType().GetField("m_rowSize", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(dr) );
}

您可以在启动应用程序时为每个不同的查询执行此功能一次,然后您可以重复使用该值。

我记得前段时间我遇到过类似的问题。我的解决方案是选择没有任何CLOB列的表。为了获得CLOB值,我只为单个CLOB列和单个记录运行一个额外的SELECT(和你一样的OracelDataReader)。

我还发现了这个文档:Obtaining LOB Data