处理大数据时出现C#OutOfMemory问题

时间:2012-07-23 15:03:24

标签: c# sql windows-services garbage-collection out-of-memory

在我们的应用程序中,我们使用 Windows服务生成报告。报告数据使用存储过程 SQL Server 获取。在某些情况下,返回的结果集包含250,000条记录(我们无法帮助这一部分,我们需要一次性使用这些数据,因为我们需要对此进行一些计算)。

问题

我们的应用程序是在读者中获取此数据,我们正在自定义对象的自定义集合中转换此数据集。由于数据量巨大,因此无法将完整数据存储在自定义对象中,从而导致内存不足。当我们在执行记录时看到任务管理器的进程使用情况时,它会非常高,甚至CPU利用率。

我不确定在这种情况下应该做些什么。

  
      
  1. 我们可以增加分配给在CLR下运行的单个进程的内存大小吗?
  2.   
  3. 还有其他解决方法吗?
  4.   

任何帮助都会非常感激

  
      
  1. 为什么我需要一次性获取所有数据:我们需要对完整的结果集进行计算
  2.   
  3. 我们正在使用ADO.NET并将数据集转换为我们的自定义对象(集合)
  4.   
  5. 我们的系统是32位
  6.   
  7. 我们无法分页数据
  8.   
  9. 无法将计算结果移至sql server
  10.   

此堆栈跟踪可能会有所帮助:

  

抛出了类型'System.OutOfMemoryException'的异常。服务器   堆栈跟踪:at   System.Collections.Generic.Dictionary 2.ValueCollection.System.Collections.Generic.IEnumerable<TValue>.GetEnumerator() at System.Linq.Enumerable.WhereEnumerableIterator 1.MoveNext()at   System.Collections.Generic.List 1.InsertRange(Int32 index, IEnumerable 1集合)at   System.Collections.Generic.List 1.AddRange(IEnumerable 1个集合)   在MyProject.Common.Data.DataProperty.GetPropertiesForType(Type t)中   C:\阿希什-东西\项目\ HCPA \开发   Branch \ Common \ Benefits.Common \ Data \ DataProperty.shared.cs:第60行   MyProject.Common.Data.Extensions.GetProperties [T](T target)in   C:\阿希什-东西\项目\ HCPA \开发   Branch \ Common \ Benefits.Common \ Data \ Extensions.shared.cs:第30行at   MyProject.Common.Data.Factories.SqlServerDataFactoryContract 1.GetData(String procedureName, IDictionary 2个参数,Nullable 1 languageId, Nullable 1 pageNumber,Nullable`1 pageSize)

谢谢, 阿希什

3 个答案:

答案 0 :(得分:0)

你可以每1000行数据,将你自定义的对象集合序列化到某个地方的磁盘吗?然后当你返回数据时,从那些文件中分页?

有关您的用例的更多信息,以及您需要撤回250万行数据会有所帮助。

答案 1 :(得分:0)

我的第一个问题是可以通过某些存储过程在Sql-Server端进行计算。我怀疑这种方法需要一些Sql-Server jedi ......但无论如何,你有没有考虑过这种方法?

答案 2 :(得分:0)

我很想看到一个代码示例,突出显示您从哪里获得此错误。它是在数据拉动本身(填充读者)还是创建对象并将其添加到自定义集合(填充集合)。

之前我遇到过类似的问题,处理非常大的数据集,但是尽可能长时间地将它留在流中,取得了很大的成功。流将数据直接保存在内存中,在完成构建对象之前,您无法直接访问整个混乱。现在,假设堆栈跟踪显示“MoveNext”操作的错误,这可能对您不起作用。然后我会说尝试将数据块化,一次抓取10k行,我知道这可以用SQL完成。它会使数据读取需要更长的时间。

修改

如果您从数据库中将其读入本地流,然后传递(请注意不要关闭它),那么您不应该遇到这些问题。创建一个数据包装类,您可以使用开放流和开放阅读器传递它。将数据存储在流中,然后使用包装函数从中读取所需的特定数据。诸如GetSumOfXField()AverageOfYValues()等等......数据永远不会存在于实时对象中,但您不必为此继续返回数据库。

伪示例

    public void ReadingTheDataFunction()
    {
        DBDataReader reader = dbCommand.ExecuteReader();
        MyDataStore.FillDataSource(reader)
    }

    private void FillDataSource(DbDataReader reader)
    {
        StreamWriter writer = new StreamWriter(GlobaldataStream);
        while (reader.Read())
            writer.WriteLine(BuildStringFromDataRow(reader));
        reader.close();
    }

    private CustomObject GetNextRow()
    {
        String line = GlobalDataReader.ReadLine();
        //Parse String to Custom Object
        return ret;
    }

从那里你传递MyDataStore,只要流和阅读器没有关闭,你可以移动你的位置,寻找个别条目,编译总和和平均值等等你甚至从来没有真正需要知道你不处理活动对象,只要你只通过接口函数与它交互。