【问题标题】:Looping in to insert bulk data in Oracle is throwing out of memory exception在 Oracle 中循环插入批量数据会引发内存不足异常
【发布时间】:2018-01-24 06:59:49
【问题描述】:

我有一个包含 900 万到 1300 万条记录的文件。由于列表有大小限制,我已将代码分成 100 万条记录的块并尝试插入记录。

这里是读取和分块文件的代码:

public static void InsertBulkData(string file)
    {
        List<FileData> lstFileData = new List<FileData>();
        using (FileStream fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
        {
            using (BufferedStream bs = new BufferedStream(fs))
            {
                using (StreamReader sr = new StreamReader(bs))
                {
                    string line = string.Empty;
                    while ((line = sr.ReadLine()) != null)
                    {
                        FileData obj = new FileData();
                        obj.Property1= line.Substring(5, 9).Trim();
                        obj.Property2= line.Substring(19, 40).Trim();

                        lstFileData.Add(obj);
                        if (lstFileData.Count == 1000000)
                        {
                            InsertDataIntoDatabase(lstFileData);
                            lstFileData = null;
                            GC.Collect();
                            lstFileData = new List<FileData>();

                        }

                    }

                }
            }
        }
    }

现在我们进入循环 9 次。但是当我谈到保存数据时,它只保存第一次。休息 8 次就会抛出异常。

  private static void InsertDataIntoDatabase(List<FileData> lstFileData)
    {
        String query = @"INSERT INTO table
                                        (
                                          PrimaryCol,
                                          Column1,
                                          Column2

                                        )
                                        VALUES
                                        (
                                          primaryCol.NEXTVAL,
                                          :Property1,
                                          :Property2,
                                          )";
        using (OracleConnection Conn = new OracleConnection())
        {
            try
            {
                Conn.ConnectionString = ConfigurationManager.ConnectionStrings["connection"].ToString();
                Conn.Open();
                using (var command = Conn.CreateCommand())
                {
                    command.CommandText = query;
                    command.CommandType = CommandType.Text;
                    command.BindByName = true;
                    // In order to use ArrayBinding, the ArrayBindCount property
                    // of OracleCommand object must be set to the number of records to be inserted
                    command.ArrayBindCount = lstFileData.Count;
                    command.Parameters.Add(":Property1", OracleDbType.Varchar2, lstFileData.Select(c => c.Property1).ToArray(), ParameterDirection.Input);
                    command.Parameters.Add(":Property2", OracleDbType.Varchar2, lstFileData.Select(c => c.Property2).ToArray(), ParameterDirection.Input);

                    command.ExecuteNonQuery();
                    Conn.Close();
                }
            }
            catch (Exception ex)
            {
                Conn.Close();
                Console.WriteLine("Exception is:" + ex.InnerException);
            }
        }
    }

所以在第一次迭代后,我们在 command.ExecuteNonQuery() 中遇到错误。谁能说出原因以及可能的解决方案是什么?

【问题讨论】:

  • 有什么异常?
  • 系统内存不足异常
  • 我认为在 InsertBulkData 方法中创建 lstFileData 时使用大内存在每个步骤的末尾清空您的列表,而不是 om new
  • 第一次插入但下次它会抛出错误。如果我们创建 2M 的块并将其全部插入,我们就有 1M 的块,它会插入,但如果我们尝试在循环中插入,则会抛出错误。
  • 已经这样做了。如果你看到我已经将它分配给 null 并且还收集了调用垃圾收集器的空间。

标签: c# oracle bulkinsert


【解决方案1】:

您的问题来自使用达到其最大大小的缓冲区。此外,如果您使用连续插入,您将遇到性能问题。您应该使用另一种方法(使用批量复制)。这是一个示例代码:

  public static void BulkCopy(string connectionString, DataTable dataTableToSend, string SQLTableName)
        {
            SqlConnection connexion = null;

            try
            {
                connexion = new SqlConnection(connectionString);
                BulkCopy(connexion, dataTableToSend, SQLTableName);
            }
            catch (Exception e)
            {

                throw;
            }
            finally
            {
                connexion?.Close();
                connexion?.Dispose();
            }
        }

int Modulo=0;

//Create a datatable MyDataTable with same structure of your table in database
// you can do : select * from MyTable where 1<>1 with command for get structured dataset (and datatable)


 using (System.IO.StreamReader file = new System.IO.StreamReader(fileName, Encoding.UTF8))
 {
     while ((Line = file.ReadLine()) != null)
     {
         counter++;

         DataRow newrow = MyDataTable.NewRow();


         //Set value
         newrow["I_ID"] = counter;
         newrow["L_NomFichier"] = FileObj.Name;
         newrow["D_CreationFichier"] = FileObj.LastWriteTime;
         newrow["L_Ligne"] = Line.DBNullIfNullOrEmpty();
         newrow["D_DATCRE"] = newrow["D_DATMAJ"] = DateTime.Now;
         newrow["C_UTICRE"] = newrow["C_UTIMAJ"] = GlobalParam.User;

         MyDataTable.Rows.Add(newrow);


         if (Modulo % 1000)
         {
         SqlHelper.BulkCopy(DBAccess.CONN_STRING, MyDataTable, "YOURTABLENAME");

         MyDataTable.Rows.Clear();

         }


     }

     SqlHelper.BulkCopy(DBAccess.CONN_STRING, MyDataTable, "YOURTABLENAME");

     MyDataTable.Rows.Clear();

     file.Close();
 }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-07
    • 2023-03-17
    相关资源
    最近更新 更多