【问题标题】:Migrating legacy data with ef core is very slow使用 ef core 迁移遗留数据非常慢
【发布时间】:2021-09-14 12:39:34
【问题描述】:

这么快的背景。我正在设置代码以将在许多方面一团糟的遗留数据库迁移到 Entity Framework Core 中的新数据库设计代码。我为新数据库制作了新模型,并在 ef core 中自动生成了旧数据库以简化操作。

我正在创建的其中一个表基于旧数据库中的 2 个表,它们具有基本相同的数据(但模型不同),它们加起来代表略超过 300,000 行。因此,我在新数据库中创建了 300,000 个新数据库模型及其相关表,总计约 120 万行。

我的问题是,当我运行大约 1000 行的测试并推断完成整个迁移的时间时,它大约需要 3.5 小时。即使对于如此大量的行,这也感觉很慢。

请注意,这两个数据库都在我的计算机上,因此任何网络延迟都不会影响它

下面是我的代码逻辑示例:

//old rows are selected earlier in the function from the legacyDbContext
//provider = IServiceProvider. I use dependency injection for this
foreach(var oldRow in oldRows)
{
    using(var scope = provider.CreateScope())
    {
        var legacyDbContext = scope.ServiceProvider.GetRequiredService<LegacyDbContext>();
        var newDatabaseDbContext = scope.ServiceProvider.GetRequiredService<NewDatabaseDbContext>();
        var newRow = new NewRow();
        newDatabaseContext.NewRows.Add(newRow);
        newDatabaseContext.SaveChanges(); //generates the id for the newRow
  
        //transfer data from oldRow to newRow with some very light processing
        //Example
        newRow.Name = oldRow.Name;
        newRow.IsActive = Convert.ToBoolean(oldRow.IsActive) 
        //for some reason boolean values were saved as C# short ints 
        //which corresponds to tinyint on the database side

        var oldRelatedItems = legacyDbContext.oldRelatedItems
            .Where(m => m.oldItemId = oldItem.Id)
            .ToList();
        //in generally this list's count is only 2, sometimes 3
        foreach(var oldRelatedItem in oldRelatedItems) 
        {
            var newRelatedItem = new NewRelatedItem();
            newDatabaseContext.NewRelatedItems.Add(newRelatedItem);

            newRelatedItem.newItem = newItem;

            //transfer data from oldRelatedItem to newRelatedItem

            newDatabaseContext.SaveChanges();
        }
     
        //Some more data transferred

        newDatabaseContext.SaveChanges();
    }
}

这里需要注意的是旧数据库没有任何外键。有些列包含其他表中的行 ID,但它们没有配置为外键(我告诉过你这个数据库很乱)。所以这可能会成为一个瓶颈。

我尝试了几件大都无效的事情。我尝试完全在 using 语句中运行 foreach 循环,最后只保存上下文 1 次。这节省了一些时间,但不多(5 分钟 215 左右)。

我也尝试将 for 循环作为 Parallel.ForEach 循环运行,但实际上使用这种方法会增加外推时间(虽然我可能错误地使用了这个函数)。

对如何提高代码性能有任何想法吗?最终迁移只需运行 1 次,因为整个项目正在从头开始重建(这真的很糟糕)。尽管如此,为了我自己的理解,我还是想知道如何改进这一点,这样迁移可能不会需要几天时间(请记住,这个问题只针对几个表)。

【问题讨论】:

  • 在我看来,精心设计的 SQL 语句执行起来会比这段代码快得多。

标签: c# sql asp.net-core entity-framework-core ef-core-3.1


【解决方案1】:

也许可以试试这样的。

主要主题是:

  • 将上下文创建排除在循环之外

  • 尝试对最后的所有内容使用批量插入

     using(var scope = provider.CreateScope())
     {
         //can this live outside the loop?
         var legacyDbContext = scope.ServiceProvider.GetRequiredService<LegacyDbContext>();
    
         var newRows = new List<NewRow>();
         var newRelatedItems = new List<NewRelatedItem>();
    
         foreach(var oldRow in oldRow){
             var newRow = new NewRow();
    
             newRow.Name = oldRow.Name;
             newRow.IsActive = Convert.ToBoolean(oldRow.IsActive);
    
             var oldRelatedItems = legacyDbContext.oldRelatedItems
                 .Where(m => m.oldItemId = oldItem.Id)
                 .ToList();
    
             foreach(var oldRelatedItem in oldRelatedItems) 
             {
                 var newRelatedItem = new NewRelatedItem();
    
                 //is this supposed to be newRow?
                 newRelatedItem.newRow = newRow;
                 //newRelatedItem.newItem = newItem;
    
                 newRelatedItems.Add(newRelatedItem);
             }
         }
    
         var newDatabaseDbContext = scope.ServiceProvider.GetRequiredService<NewDatabaseDbContext>();
         newDatabaseDbContext.BulkInsert(newRows);
         newDatabaseDbContext.BulkInsert(newRelatedItems);
     }
    

【讨论】:

  • 我会试试这个。是的,它应该是 newRow。显然这些不是真实姓名,我显然没有仔细检查一致性。
猜你喜欢
  • 2020-07-21
  • 1970-01-01
  • 2021-11-23
  • 1970-01-01
  • 2018-06-19
  • 2019-08-03
  • 1970-01-01
  • 2020-07-09
  • 2020-12-03
相关资源
最近更新 更多