如何在不耗尽内存的情况下从磁盘读取大型文件到数据库

csv file-processing out-of-memory sqlbulkcopy sql-server-2005

因为我觉得我应该已经知道这个问题,我感到很尴尬。但是,鉴于我不....我想知道如何从磁盘读取大型文件到数据库而不会得到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());
    }

一般承认的答案

可能不是你正在寻找的答案,但这就是BULK INSERT的设计目标。


热门答案

实际上你的代码正在从文件读取所有数据并保存到TextReader (在内存中)。然后从TextReader读取数据以保存服务器。

如果数据太大, TextReader数据大小会导致内存不足。请尝试这种方式。

1)从File中读取数据(每行)。

2)然后将每行插入Server。

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

伪代码

begin tran

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

end tran



许可下: CC-BY-SA with attribution
不隶属于 Stack Overflow
这个KB合法吗? 是的,了解原因
许可下: CC-BY-SA with attribution
不隶属于 Stack Overflow
这个KB合法吗? 是的,了解原因