使用DbDataReader

时间:2019-04-13 11:03:48

标签: c# odp.net

我编写了一个应用程序,该应用程序在连接到远程Oracle数据库(主服务器)的多个客户端计算机上运行,​​以同步其本地开源数据库(从服务器)。到目前为止,这个工作正常。但是有时本地表需要完全初始化(删除后再插入master数据库的所有行)。如果主表足够大(ColumnCount或DataType / DataSize和某个RowSize),则该应用程序有时会遇到OutOfMemoryException。 该应用程序在装有.NET 4.0的Windows计算机上运行。 ODP.NET的版本是4.122.18.3。 Oracle数据库是12c(12.1.0.2.0)。

我不想向任何用户显示数据(该应用程序在后台运行),否则我可以做一些分页或筛选。由于不是所有的表都包含键或能够自动排序,因此很难部分地获取表。本地表的初始化应在一个事务中完成,而不必进行多次部分提交。 我可以将问题归结为一个简单的代码示例,该示例显示了我所期望的托管内存分配。此时,我不确定如何解释或解决问题。

using (var connection = new OracleConnection(CONNECTION_STRING))
{
    connection.Open();

    using (var command = connection.CreateCommand())
    {
        command.CommandText = STATEMENT;

        using (var reader = command.ExecuteReader())
        {
            while (reader.Read())
            {
                //reader[2].ToString();
                //reader.GetString(2);
                //reader.GetValue(2);
            }
        }
    }
}

取消注释三个读取器中的任何一个时,*会调用ODP.NET(OracleInternal.Network.OraBuf)在内部为每个记录固定请求的列数据的内存。对于请求数千条记录,这似乎不是问题。但是,当获取100k +记录时,内存分配将达到数百MB,这将导致OutOfMemoryException。指定列中的数据越多,OOM发生得越快(通常是NVARCHAR2)。 手动调用GC.Collect()不会执行任何操作。图像中显示的GC.Collect()在内部完成(我自己没有调用)。

Managed Memory Allocation

由于我不在任何地方存储读取的数据,所以我希望在迭代DbDataReader时不会缓存数据。您能帮我了解这里发生了什么以及如何避免吗?

1 个答案:

答案 0 :(得分:0)

当使用托管驱动程序12.1.0.2。使用ExecuteReader()方法读取clob列值时,这似乎是一个已知的错误(Bug 21975120)。解决方法是使用特定于OracleDataReader的方法(例如oracleDataReader.GetOracleValue(i))。可以显式关闭OracleClob值以释放内存分配。

var item = oracleDataReader.GetOracleValue(columnIndex);

if (item is OracleClob clob)
{
    if (clob != null)
    {
        // use clob.Value ...

        clob.Close();
    }
}