Sql server 2005 如何在不耗尽内存的情况下将一个大文件从磁盘读取到数据库

Sql server 2005 如何在不耗尽内存的情况下将一个大文件从磁盘读取到数据库,sql-server-2005,csv,out-of-memory,sqlbulkcopy,file-processing,Sql Server 2005,Csv,Out Of Memory,Sqlbulkcopy,File Processing,我觉得不好意思问这个问题,因为我觉得我应该已经知道了。然而,考虑到我不…,我想知道如何将大文件从磁盘读取到数据库,而不会出现OutOfMemory异常。具体来说,我需要加载CSV(或者真正的制表符分隔文件) 我正在试验CSVReader,具体来说,但我确信我做得不对。其中一些演示了如何读取任何大小的流文件,这正是我想要的(只是我需要从磁盘读取),但我不知道我可以创建什么类型的IDataReader 我直接从磁盘读取数据,我试图确保我不会因为一次读取太多数据而耗尽内存,这一点如下。我禁不住想,我应

我觉得不好意思问这个问题,因为我觉得我应该已经知道了。然而,考虑到我不…,我想知道如何将大文件从磁盘读取到数据库,而不会出现OutOfMemory异常。具体来说,我需要加载CSV(或者真正的制表符分隔文件)

我正在试验
CSVReader
,具体来说,但我确信我做得不对。其中一些演示了如何读取任何大小的流文件,这正是我想要的(只是我需要从磁盘读取),但我不知道我可以创建什么类型的
IDataReader

我直接从磁盘读取数据,我试图确保我不会因为一次读取太多数据而耗尽内存,这一点如下。我禁不住想,我应该能够使用
BufferedFileReader
或类似的东西,我可以指向文件的位置并指定缓冲区大小,然后
CsvDataReader
期望一个
IDataReader
作为第一个参数,它可以使用它。请告诉我我的错误的方式,让我摆脱我的
GetData
方法与它的任意文件分块机制,并帮助我解决这个基本问题

    private void button3_Click(object sender, EventArgs e)
    {   
        totalNumberOfLinesInFile = GetNumberOfRecordsInFile();
        totalNumberOfLinesProcessed = 0; 

        while (totalNumberOfLinesProcessed < totalNumberOfLinesInFile)
        {
            TextReader tr = GetData();
            using (CsvDataReader csvData = new CsvDataReader(tr, '\t'))
            {
                csvData.Settings.HasHeaders = false;
                csvData.Settings.SkipEmptyRecords = true;
                csvData.Settings.TrimWhitespace = true;

                for (int i = 0; i < 30; i++) // known number of columns for testing purposes
                {
                    csvData.Columns.Add("varchar");
                }

                using (SqlBulkCopy bulkCopy = new SqlBulkCopy(@"Data Source=XPDEVVM\XPDEV;Initial Catalog=MyTest;Integrated Security=SSPI;"))
                {
                    bulkCopy.DestinationTableName = "work.test";

                    for (int i = 0; i < 30; i++)
                    {
                        bulkCopy.ColumnMappings.Add(i, i); // map First to first_name
                    }

                    bulkCopy.WriteToServer(csvData);

                }
            }
        }
    }

    private TextReader GetData()
    {
        StringBuilder result = new StringBuilder();
        int totalDataLines = 0;
        using (FileStream fs = new FileStream(pathToFile, FileMode.Open, System.IO.FileAccess.Read, FileShare.ReadWrite))
        {
            using (StreamReader sr = new StreamReader(fs))
            {
                string line = string.Empty;
                while ((line = sr.ReadLine()) != null)
                {
                    if (line.StartsWith("D\t"))
                    {
                        totalDataLines++;
                        if (totalDataLines < 100000) // Arbitrary method of restricting how much data is read at once.
                        {
                            result.AppendLine(line);
                        }
                    }
                }
            }
        }
        totalNumberOfLinesProcessed += totalDataLines;
        return new StringReader(result.ToString());
    }
private void按钮3\u单击(对象发送者,事件参数e)
{   
totalNumberOfLinesInFile=GetNumberOfRecordsInFile();
totalNumberOfLinesProcessed=0;
while(totalNumberOfLinesProcessed
实际上,您的代码正在读取文件中的所有数据,并保存在
文本阅读器中(内存中)。然后从
TextReader
读取数据以保存服务器

如果数据太大,
TextReader
中的数据大小会导致内存不足。请这边试试

1) 从文件中读取数据(每行)

2) 然后将每一行插入服务器

内存不足的问题将得到解决,因为在处理时,内存中只有每条记录

伪码

begin tran

While (data = FilerReader.ReadLine())
{
  insert into Table[col0,col1,etc] values (data[0], data[1], etc)
}

end tran

我只想添加使用BufferedFileReader和readLine方法,并按照上面的方式进行详细的操作

基本上了解这里的反应

BufferedFileReader是从文件中读取数据的类(BufferedFileReader) 也应该有一个读线器。 CSVReader是一个用于读取数据的util类,假设数据的格式正确

SQlBulkCopy您随时都可以使用

第二选项


您可以直接转到数据库的导入工具。如果文件格式正确,并且程序的孔点仅此而已。那也会更快

可能不是您要寻找的答案,但这正是设计的目的。

我认为您可能对数据的大小有误解。每次我遇到这个问题,都不是数据的大小,而是在数据上循环时创建的对象的数量

查看while循环,在方法按钮3中向数据库添加记录\u单击(对象发送者,事件参数e):

在这里,每次迭代都要声明并实例化两个对象——这意味着对于您读取的每个文件块,您将实例化200000个对象;垃圾收集器将无法跟上

为什么不在while循环之外声明对象呢

TextReader tr = null;
CsvDataReader csvData = null;
这样,gc将有一半的机会。您可以通过对while循环进行基准测试来证明这一点,在您仅创建了几千个对象之后,您无疑会注意到性能大幅下降。

伪代码:

while (!EOF) {
   while (chosenRecords.size() < WRITE_BUFFER_LIST_SIZE) {
      MyRecord record = chooseOrSkipRecord(file.readln());
      if (record != null) {
         chosenRecords.add(record)
      }
   }  
   insertRecords(chosenRecords) // <== writes data and clears the list
}
while(!EOF){
while(chosenRecords.size()insertRecords(chosenRecords)//与其逐个读取csv行并逐个插入db,我建议读取一个块并将其插入数据库。重复此过程,直到读取整个文件

您可以在内存中缓冲,比如说一次1000行csv,t
while (!EOF) {
   while (chosenRecords.size() < WRITE_BUFFER_LIST_SIZE) {
      MyRecord record = chooseOrSkipRecord(file.readln());
      if (record != null) {
         chosenRecords.add(record)
      }
   }  
   insertRecords(chosenRecords) // <== writes data and clears the list
}
int MAX_BUFFERED=1000;
int counter=0;
List<List<String>> bufferedRows= new ...

while (scanner.hasNext()){
  List<String> rowEntries= getData(scanner.getLine())
  bufferedRows.add(rowEntries);

  if (counter==MAX_BUFFERED){
    //INSERT INTO DATABASE
    //append all contents to a string buffer and create your SQL INSERT statement
    bufferedRows.clearAll();//remove data so it could be GCed when GC kicks in
  }
}