【问题标题】:Out of memory exception while moving CSV data (3 million data) into DataTable using c#使用 c# 将 CSV 数据(300 万数据)移动到 DataTable 时出现内存不足异常
【发布时间】:2017-11-03 06:04:11
【问题描述】:

我有 300 万行和 40 列的 CSV 文件。我想使用 SqlBulkCopy 概念将 CSV 数据移动到 SQL 服务器中。为此,我使用 CSV 阅读器将每一列和每一行移动到数据表中。使用 while 循环在数据表中插入行时,出现内存异常。 注意:438184(以下示例的 i 值)记录插入成功。之后我得到了内存异常。

SqlConnection con = new SqlConnection(connectionString);
        con.Open();
        SqlCommand SqlCmd = new SqlCommand();
        SqlCmd.CommandTimeout = 0;
        SqlCmd.Connection = con;
SqlTransaction transaction = con.BeginTransaction();
        var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
        StreamReader streamReader = new StreamReader(fileStream);
        CsvReader reader = new CsvReader(streamReader);
        DataTable table = new DataTable();
        reader.ReadHeaderRecord();
        foreach (var c in reader.HeaderRecord)
        {
            table.Columns.Add(c);
        }
        table.Rows.Clear();

        int i = 0;

        while (reader.HasMoreRecords)
        {

          DataRecord record = reader.ReadDataRecord();
          table.Rows.Add(record[0], record[1], record[2], record[3], record[4], record[5], record[6], record[7], record[8], record[9], record[10], record[11], record[12], record[13], record[14], record[15], record[16], record[17], record[18], record[19], record[20], record[21], record[22], record[23], record[24], record[25], record[26], record[27], record[28], record[29], record[30], record[31], record[32], record[33], record[34], record[35], record[36], record[37], record[38], record[39]);              
            i++;
        }
        SqlBulkCopy copy = new SqlBulkCopy(con, SqlBulkCopyOptions.KeepIdentity, transaction);
        copy.DestinationTableName = "SampleCSV";
        copy.WriteToServer(table);
        transaction.Commit();
            con.Close();

谁能建议我如何解决这个问题?

【问题讨论】:

标签: c# csv datatable sqlbulkcopy


【解决方案1】:

如果您遇到内存异常,这意味着您无法一次加载 DataTable 中的所有行。您需要在每行 X 执行一次 SqlBulkCopy 并清除 DataTable,然后再进行更多处理。

例如,以下代码将在DataTable 中每加载 100,000 行执行一次 SqlBulkCopy,然后在加载更多行之前清除所有行。

int i = 0;

while (reader.HasMoreRecords)
{
    // INSERT rows every x
    if(i % 100000 == 0)
    {
        ExecuteBulkCopy(conn, transaction, table);
        table.Rows.Clear();
    }

    DataRecord record = reader.ReadDataRecord();
    table.Rows.Add(record[0], record[1], record[2], record[3], record[4], record[5], record[6], record[7], record[8], record[9], record[10], record[11], record[12], record[13], record[14], record[15], record[16], record[17], record[18], record[19], record[20], record[21], record[22], record[23], record[24], record[25], record[26], record[27], record[28], record[29], record[30], record[31], record[32], record[33], record[34], record[35], record[36], record[37], record[38], record[39]);              
    i++;
}
// INSERT remaining row
if(table.Rows.Count > 0)
{
    ExecuteBulkCopy(conn, transaction, table);
}

public void ExecuteBulkCopy(SqlConnection con, SqlTransaction transaction, DataTable table)
{
    SqlBulkCopy copy = new SqlBulkCopy(con, SqlBulkCopyOptions.KeepIdentity, transaction);
    copy.DestinationTableName = "SampleCSV";
    copy.WriteToServer(table);
    transaction.Commit();
}

编辑:回答子问题

如果我使用 Bulk insert 或任何其他方法,我可以减少这个时间吗?

SqlBulkCopy 是最快的插入方式。甚至我的库 .NET Bulk Operations 在后台使用 SqlBulkCopy。

300万条数据20分钟是很慢的,不过也可以根据你的表(char列、触发器、索引等)正常

这里有两个对性能影响最大的配置。

使用 BatchSize

例如 5000 与非常大的批次相比,以较小的批次插入多次通常更快。

使用 TableLock 选项

new SqlBulkCopy(connectionString, SqlBulkCopyOptions.TableLock)) 通过锁定表,SqlBulkCopy 将执行得更快。

【讨论】:

  • 使用 SqlBulkCopy,将 300 万条数据 (1.08 GB) 移动到 SQL Server 需要 20 分钟。如果我使用 Bulk insert 或任何其他方法,我可以减少这个时间吗?
【解决方案2】:

您可以指定批量大小

查看 SqlBulkCopy.BatchSize 属性的更多详细信息

https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlbulkcopy.batchsize(v=vs.110).aspx

或者,如果您的目标表架构是固定的,那么您可以使用 SSIS 包。

【讨论】:

    【解决方案3】:
       SqlBulkCopy copy = new SqlBulkCopy(con, SqlBulkCopyOptions.KeepIdentity, transaction);
       //you can define any Banch of Data insert
       copy.BatchSize(10000);
       copy.DestinationTableName = "SampleCSV";
       copy.WriteToServer(table);
       transaction.Commit();
       con.Close();
    

    【讨论】:

    • 执行“while 循环”时出现内存异常。它在执行“SqlBulkCopy”代码之前被抛出。所以我的问题没有解决。您能建议我另一种解决方案吗?
    猜你喜欢
    • 2018-08-24
    • 2020-01-30
    • 2011-01-07
    • 2023-04-10
    • 2013-10-09
    • 1970-01-01
    • 1970-01-01
    • 2017-05-13
    • 1970-01-01
    相关资源
    最近更新 更多