【问题标题】:Best way to insert milions of records插入数百万条记录的最佳方法
【发布时间】:2020-01-31 16:27:08
【问题描述】:

我正在使用 C# asp.net core、linQ 和 T-SQL 中的托管服务。

我需要在我的数据库中插入一条一条记录。 当然这不是一个快速的操作,但我在这个领域没有那么有经验,所以也许我做错了。 这是我在经理中的代码:

    public void StrategyMassive(string foldpathsave)
    {
        using (IServiceScope scope = _services.CreateScope())
        {
            List<string> filesreading = new List<string>();

            VUContext _context = scope.ServiceProvider.GetRequiredService<VUContext>();

            List<string> filesnumber = File.ReadAllLines(foldpathsave).ToList();

            filesreading = filesnumber.ToList();

            filesreading.RemoveRange(0, 2);

            foreach (string singlefile in filesreading)
            {

                //INTERNAL DATA NORMALIZATION

                _repository.ImportAdd(_context, newVUL, newC2, newC3, newDATE);

                _repository.Save(_context);

            }
        }
    }

这是我的存储库界面:

    public void ImportAdd(VUContext _context, AVuTable newVUL, ACs2Table newC2, ACs3Table newC3, ADateTable newDATe)
    {
        _context.AVuTable.Add(newVU);

        _context.ADateTable.Add(newDATE);

        if (newC2 != null)
        {
            _context.ACs2Table.Add(newC2);
        }

        if (newC3 != null)
        {
            _context.ACs3Table.Add(newC3);
        }

        public void Save(VUContext _context)

        {
            _context.SaveChanges();
        }
    }

我知道这一切都很简单,那么我怎样才能加快这个插入的速度呢?

【问题讨论】:

  • BULK 操作在这里会快得多,而不是让 C# 一次读取每一行,然后一次插入一行。
  • 将插入内容分成更小的批次。 -- mssqltips.com/sqlservertip/5636/…
  • 不要这样做,根据你的数据库进行批量插入。

标签: c# sql-server linq tsql asp.net-core


【解决方案1】:

开始不要使用最慢的方法。

从您实际加载文件的方式开始。 它继续通过不使用 SqlBulkCopy(可能在多个线程中)将数据写入数据库。

您所做的可能是最慢的方式 - 因为 EntityFramework 不是 ETL 工具。

顺便说一句,每个项目的一个事务 (SaveChanges) 也无济于事。它是一个超级慢的解决方案,真的真的超级慢。

我设法每个线程每秒处理大约 64k 行,并行运行 4-6 个线程。

【讨论】:

  • 我知道,但是需要一个一个插入数据。我以前用数据分页管理过,而且速度非常快。
  • 所以,基本上:你的要求是慢。和他们一起生活。多线程可能效果最好(显然,每个线程都有自己的 dbcontext)。
  • 如何在多个线程中管理它?以前从来没有这样做过。
  • 从文档开始。
【解决方案2】:

根据我的经验,SqlBulkCopy 是最快的方法。 filesnumber 听起来用词不当,我怀疑您正在阅读经过一些规范化过程后要加载到 SQL Server 的分隔文件列表。如果您在最初将数据加载到临时文件后在服务器端进行规范化,那可能会更快。这是一个来自分隔文件的示例 SqlBulkCopy:

void Main()
{
  Stopwatch sw = new Stopwatch();
  sw.Start();
  string sqlConnectionString = @"server=.\SQLExpress2012;Trusted_Connection=yes;Database=SampleDb";

  string path = @"d:\temp\SampleTextFiles";
  string fileName = @"combDoubledX.csv";

  using (OleDbConnection cn = new OleDbConnection(
    "Provider=Microsoft.ACE.OLEDB.12.0;Data Source="+path+
    ";Extended Properties=\"text;HDR=No;FMT=Delimited\";"))

  using (SqlConnection scn = new SqlConnection( sqlConnectionString ))
  {
  OleDbCommand cmd = new OleDbCommand("select * from "+fileName, cn);

  SqlBulkCopy sbc = new SqlBulkCopy(scn, SqlBulkCopyOptions.TableLock,null);

  sbc.ColumnMappings.Add(0,"[Category]");
  sbc.ColumnMappings.Add(1,"[Activity]");
  sbc.ColumnMappings.Add(5,"[PersonId]");
  sbc.ColumnMappings.Add(6,"[FirstName]");
  sbc.ColumnMappings.Add(7,"[MidName]");
  sbc.ColumnMappings.Add(8,"[LastName]");
  sbc.ColumnMappings.Add(12,"[Email]");

  cn.Open();
  scn.Open();

  SqlCommand createTemp = new SqlCommand();
  createTemp.CommandText = @"if exists
   (SELECT * FROM tempdb.sys.objects 
   WHERE object_id = OBJECT_ID(N'[tempdb]..[##PersonData]','U'))
   BEGIN
        drop table [##PersonData];
   END

  create table ##PersonData 
  (
    [Id] int identity primary key,
    [Category] varchar(50), 
    [Activity] varchar(50) default 'NullOlmasin', 
    [PersonId] varchar(50), 
    [FirstName] varchar(50), 
    [MidName] varchar(50), 
    [LastName] varchar(50), 
    [Email] varchar(50)
  )
"; 
  createTemp.Connection = scn;
  createTemp.ExecuteNonQuery();

  OleDbDataReader rdr = cmd.ExecuteReader();

  sbc.NotifyAfter = 200000;
  //sbc.BatchSize = 1000;
  sbc.BulkCopyTimeout = 10000;
  sbc.DestinationTableName = "##PersonData";
  //sbc.EnableStreaming = true;

  sbc.SqlRowsCopied += (sender,e) =>
    {
    Console.WriteLine("-- Copied {0} rows to {1}.[{2} milliseconds]", 
      e.RowsCopied, 
      ((SqlBulkCopy)sender).DestinationTableName,
      sw.ElapsedMilliseconds);
    };

  sbc.WriteToServer(rdr);

  if (!rdr.IsClosed) { rdr.Close(); }

  cn.Close();
  scn.Close();
  }
  sw.Stop();
  sw.Dump();
}

以及该文件中的几行示例:

"Computer Labs","","LRC 302 Open Lab","","","10057380","Test","","Cetin","","5550123456","","cb@nowhere.com"
"Computer Labs","","LRC 302 Open Lab","","","123456789","John","","Doe","","5551234567","","jdoe@somewhere.com"
"Computer Labs","","LRC 302 Open Lab","","","012345678","Mary","","Doe","","5556666444","","mdoe@here.com"

您可以创建并运行任务列表从源中执行 SqlBulkCopy 读取(SqlBulkCopy 支持一系列读取器)。

【讨论】:

    【解决方案3】:

    为了更快的操作,您需要减少数据库往返次数

    在 EF Core 中使用 批处理语句 功能

    您可以看到此功能仅在 EF Core 中可用,因此如果您仍在使用 EF 6,则需要迁移到使用 EF Core。

    Compare EF Core & EF6

    要使此功能正常工作,您需要将保存操作移到循环之外。

    批量插入

    批量插入功能旨在成为插入大量数据库记录的最快方式

    Bulk Copy Operations in SQL Server

    要使用它,您需要使用 SQL Server 的 SqlBulkCopy 类,并且您的代码需要大量返工。

    【讨论】:

    • 他也不允许使用它。荒谬,但要求就是这样。
    猜你喜欢
    • 2020-08-10
    • 1970-01-01
    • 2023-01-31
    • 2015-12-31
    • 1970-01-01
    • 1970-01-01
    • 2012-05-30
    • 1970-01-01
    • 2014-03-25
    相关资源
    最近更新 更多