【问题标题】:Insert huge number of rows into database using Entity Framework使用实体框架将大量行插入数据库
【发布时间】:2018-02-04 06:35:18
【问题描述】:

@编辑 我按照Fastest Way of Inserting in Entity Framework 的步骤进行操作,结果更差,所以不是重复的。

我的目标是创建一个种子方法来填充 LocalDb 的一个表。该方法将添加 182500 行(以模拟 500 台设备的年度活动数据)以进行进一步测试。我可能想再运行几次来更改设备的数量,这样会生成更多的行。这就是为什么我需要尽可能高效地插入行。

    protected void SeedReportDataTable(int numberOfTerminals)
    {
        var rand = new Random();
        var tidsList = new List<string>();

        // generuj liste losowych numerow tid
        for (int i = 0; i < numberOfTerminals; i++)
        {
            var randomTid = rand.Next(100000, 1000000).ToString(); // generuj 6-cyfrowy numer tid
            while (tidsList.Contains(randomTid)) { randomTid = rand.Next(100000, 1000000).ToString(); } // elminuj powtorzenia
            tidsList.Add(randomTid);
        }

        // dla kazdego z numerow tid generuj roczna historie aktywnosci
        var recordsList = new BlockingCollection<ReportData>();
        int year = Convert.ToInt32(DateTime.Now.Year);

        Parallel.ForEach(tidsList, tid =>
        {
            // dla kazdego miesiaca
            for (int month = 1; month <= 12; month++)
            {
                // dla kazdego dnia
                for (int day = 1; day <= DateTime.DaysInMonth(year, month); day++)
                {
                    var record = new ReportData
                    {
                        Tid = tid,
                        Active = Convert.ToBoolean(
                            rand.Next(0, 11)), // generuj losowy stan aktywnosci z prawdopodbienstwem 1/10 na bycie nieaktywnym
                        Date = new DateTime(year, month, day)
                    };
                    recordsList.Add(record);
                }
            }
        });
        // dodaj unikalne klucze glowne rekordom przed dodaniem do kontekstu bazy
        var keyValue = 1;

        foreach (var record in recordsList)
        {
            record.Id = keyValue++;
        }

        // podziel liste na czesci
        int chunkSize = 1000;

        for (int recordsSkipped = 0; recordsSkipped < recordsList.Count; recordsSkipped += chunkSize)
        {
            // wymieniaj kontekst
            using (var db = new dbEntities())
            {
                db.Configuration.AutoDetectChangesEnabled = false;
                db.Configuration.ValidateOnSaveEnabled = false;
                // dodawaj do bazy po kawalku
                db.ReportData.AddRange(recordsList.Skip(recordsSkipped).Take(chunkSize));
                db.SaveChanges();
            }
        }
    }

运行此代码需要 30 分钟才能完成。在此之前,我运行了一个以以下结尾的版本:

using (var db = new dbEntities())
{
    db.ReportData.AddRange(recordsList);
    db.SaveChanges();
}

花了 15 分钟,还是比我预想的慢。

为什么我的“改进”失败了?

我能做些什么让它更快地插入行?

【问题讨论】:

  • 也就是说,使用 EF 为具有这么多记录的数据库播种并不是性能最佳的选择。如果在插入大量记录(数百万或更多)时性能至关重要,请考虑使用 Sql Bulk Insert。即使这样,您也应该检查 Sql Server 是否存在瓶颈(即,跨插入的索引更新和统计更新也会减慢此类批处理作业的速度)。
  • It is not a duplicate because I followed tips from that topic and got even worse results 您能否向我们展示您使用SqlBulkCopy 测试的代码版本(并与我们分享执行所需的时间)?
  • BlockingCollection 据我所知没有随机访问,因此循环中重复的recordsList.Skip(...).Take(...) 将花费更多时间,因为在采取之前跳过的元素越多。您需要为 Skip/Take 使用不同的数据结构或使用不同的分块方法。
  • I would have to convert my list to datatable. stackoverflow.com/questions/3913371/sqlbulkcopy-from-a-list

标签: c# entity-framework localdb


【解决方案1】:

当我将我的播种方法添加到 Configuration.cs 并运行 update-database 命令时,插入所有行只需不到 5 分钟。

只调用一次Context.AddRange() 效果最好。

        dbContext.Configuration.AutoDetectChangesEnabled = false;
        dbContext.Configuration.ValidateOnSaveEnabled = false;
        dbContext.ReportData.AddRange(recordsList);
        dbContext.SaveChanges();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-09
    相关资源
    最近更新 更多