我编写了一个在连接到远程oracle数据库(master)的多台客户机上运行的应用程序,以同步它们的本地开源数据库(slave)。这到目前为止工作正常。但有时需要对本地表进行完全初始化(删除后插入所有主数据库行)。如果主表足够大(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);
}
}
}
}
当取消注释三个reader。*中的任何一个时,所请求的列数据的内存似乎由ODP.NET(OracleInternal.Network.OraBuf)在内部固定每个记录。要求几千条记录,这似乎不是问题。但是当获取100k +记录时,内存分配会达到数百MB,从而导致OutOfMemoryException。指定列的数据越多,OOM发生得越快(主要是NVARCHAR2)。另外调用GC.Collect()手动不做任何事情。图像中显示的GC.Collect()是在内部完成的(我自己没有调用)。
由于我没有将读取数据存储在任何地方,我希望在迭代DbDataReader时不会缓存数据。你能帮我理解这里发生了什么以及如何避免它吗?
当使用带有托管驱动程序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();
}
}