mariadb EF Core慢速批量插入(约80k行)

kognpnkq  于 2022-11-08  发布在  其他
关注(0)|答案(3)|浏览(163)

我有一个Save对象,它有几个关联的集合。对象的总大小如下:

对象之间的关系可以从这个Map中推断出来,并且看起来在数据库中被正确地表示出来。查询也工作得很好。

modelBuilder.Entity<Save>().HasKey(c => c.SaveId).HasAnnotation("DatabaseGenerated",DatabaseGeneratedOption.Identity);
modelBuilder.Entity<Save>().HasMany(c => c.Families).WithOne(x => x.Save).HasForeignKey(x => x.SaveId);
modelBuilder.Entity<Save>().HasMany(c => c.Countries).WithOne(x => x.Save).HasForeignKey(x => x.SaveId);
modelBuilder.Entity<Save>().HasMany(c => c.Provinces).WithOne(x => x.Save).HasForeignKey(x => x.SaveId);
modelBuilder.Entity<Save>().HasMany(c => c.Pops).WithOne(x => x.Save).HasForeignKey(x => x.SaveId);
modelBuilder.Entity<Country>().HasOne(c => c.Save);
modelBuilder.Entity<Country>().HasMany(c => c.Technologies).WithOne(x => x.Country).HasForeignKey(x => new {x.SaveId, x.CountryId});
modelBuilder.Entity<Country>().HasMany(c => c.Players).WithOne(x => x.Country).HasForeignKey(x => new {x.SaveId, x.CountryId});
modelBuilder.Entity<Country>().HasMany(c => c.Families).WithOne(x => x.Country).HasForeignKey(x => new {x.SaveId, x.OwnerId});
modelBuilder.Entity<Country>().HasMany(c => c.Provinces).WithOne(x => x.Owner);
modelBuilder.Entity<Country>().HasKey(c => new { c.SaveId, c.CountryId });
modelBuilder.Entity<Family>().HasKey(c => new { c.SaveId, c.FamilyId });
modelBuilder.Entity<Family>().HasOne(c => c.Save);
modelBuilder.Entity<CountryPlayer>().HasKey(c => new { c.SaveId, c.CountryId, c.PlayerName });
modelBuilder.Entity<CountryPlayer>().HasOne(c => c.Country);
modelBuilder.Entity<CountryPlayer>().Property(c => c.PlayerName).HasMaxLength(100);
modelBuilder.Entity<CountryTechnology>().HasKey(c => new { c.SaveId, c.CountryId, c.Type });
modelBuilder.Entity<CountryTechnology>().HasOne(c => c.Country);
modelBuilder.Entity<Province>().HasKey(c => new { c.SaveId, c.ProvinceId });
modelBuilder.Entity<Province>().HasMany(c => c.Pops).WithOne(x => x.Province);
modelBuilder.Entity<Province>().HasOne(c => c.Save);
modelBuilder.Entity<Population>().HasKey(c => new { c.SaveId, c.PopId });
modelBuilder.Entity<Population>().HasOne(c => c.Province);
modelBuilder.Entity<Population>().HasOne(c => c.Save);

我从一个文件中解析整个save,所以我不能一个一个地添加所有集合。解析后,我得到了一个Save及其所有关联的集合,总共有80k个对象,其中没有一个在数据库中出现。
然后,当我调用dbContext.Add(save)时,它需要大约44秒来处理,RAM使用量从100mb上升到大约700mb。
然后,当我调用dbContext.SaveChanges()时(我也尝试了EF扩展中的常规BulkSaveChanges()方法,没有明显的区别),它需要额外的60s,RAM使用率高达1.3Gb。
这是怎么回事?为什么这么长时间和这么多的内存使用?实际上传到数据库只需要大约最后5秒钟。
PS:我也试过禁用变化检测,没有效果。
PS2:实际使用情况和注解中要求的完整代码:

public class HomeController : Controller
{
    private readonly ImperatorContext _db;

    public HomeController(ImperatorContext db)
    {
        _db = db;
    }

    [HttpPost]
    [RequestSizeLimit(200000000)]
    public async Task<IActionResult> UploadSave(List<IFormFile> files)
    {
        [...]
        await using (var stream = new FileStream(filePath, FileMode.Open))
        {
            var save = ParadoxParser.Parse(stream, new SaveParser());
            if (_db.Saves.Any(s => s.SaveKey == save.SaveKey))
            {
                 response = "The save you uploaded already exists in the database.";
            }
            else
            {
                 _db.Saves.Add(save);
            }
            _db.BulkSaveChanges();
        }
        [...]
    }

}
jhdbpxl9

jhdbpxl91#

从nugets下载EFCore.BulkExtensions
删除“_db.BulkSaveChanges();“并替换为“_db.Saves.Add(保存);“与此代码

_db.Saves.BulkInsert(save);
2lpgd968

2lpgd9682#

编辑:1.确保问题不在数据库上。
执行您自己的命令以查看它的运行速度。
1.通过为每个工作单元使用新上下文来保持活动上下文图较小,同时尝试关闭AutoDetechChangesEnabled
3.将许多命令成批处理在一起
这是一篇关于实体框架和慢速批量INSERT的好文章

4dc9hkyq

4dc9hkyq3#

我建议您看一下N.EntityFrameworkCore.Extension。它是EFCore 6.0.8+的批量扩展框架

Install-Package N.EntityFrameworkCore.Extensions

https://www.nuget.org/packages/N.EntityFrameworkCore.Extensions
一旦你安装了nuget包,你就可以直接在DbContext示例上使用BulkInsert()方法。它支持BulkDelete、BulkInsert、BulkMerge等等。

批量删除()

var dbcontext = new MyDbContext();  
var orders = dbcontext.Orders.Where(o => o.TotalPrice < 5.35M);  
dbcontext.BulkDelete(orders);

大量插入()

var dbcontext = new MyDbContext();  
var orders = new List<Order>();  
for(int i=0; i<10000; i++)  
{  
   orders.Add(new Order { OrderDate = DateTime.UtcNow, TotalPrice = 2.99 });  
}  
dbcontext.BulkInsert(orders);

相关问题