itsource

EF 코어 저속 벌크 인서트(80k 행까지)

mycopycode 2022. 9. 14. 22:26
반응형

EF 코어 저속 벌크 인서트(80k 행까지)

나는 가지고 있다Save여러 컬렉션이 연결된 개체입니다.개체의 총 크기는 다음과 같습니다.

여기에 이미지 설명 입력

오브젝트 간의 관계는 이 매핑에서 얻을 수 있으며 데이터베이스에서 올바르게 표현된 것으로 보입니다.쿼리도 문제없이 작동합니다.

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최대 80,000개의 개체를 추가하지만 데이터베이스에 존재하지 않습니다.

그럼, 내가 전화했을 때dbContext.Add(save)처리에는 약 44초가 소요되며 RAM 사용량은 100MB에서 약 700MB로 증가합니다.

그럼, 내가 전화했을 때dbContext.SaveChanges()(레귤러도 해봤는데BulkSaveChanges()큰 차이가 없는 EF Extensions의 방식)에 따라 추가로 60초 정도 소요되며 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();
        }
        [...]
    }

}

EFCore를 다운로드합니다.Nugets로부터의 Bulk Extensions

'_db'를 삭제합니다.Bulk Save Changes();"를 입력하고 "_db"를 바꿉니다.Saves.Add(save);" 이 코드와 함께

_db.Saves.BulkInsert(save);

편집: 1. 문제의 원인이 DB가 아님을 확인합니다.

자체 명령을 실행하여 실행 속도를 확인합니다.

  1. 각 작업단위에 대해 새 컨텍스트를 사용하여 활성 컨텍스트 그래프를 작게 유지하고 AutoDetechChangesEnabled를 끄십시오.

3. 다수의 명령어를 조합하다

엔티티 프레임워크와 느린 벌크 INSERT에 대한 좋은 기사를 소개합니다.

N을 봐주셨으면 합니다.EntityFrameworkCore내선번호EFCore 6.0.8+용 벌크 확장 프레임워크입니다.

Install-Package N.EntityFrameworkCore.Extensions

https://www.nuget.org/packages/N.EntityFrameworkCore.Extensions

nuget 패키지를 설치하면 DbContext 인스턴스에서 BulkInsert() 메서드를 직접 사용할 수 있습니다.BulkDelete, BulkInsert, BulkMerge 등을 지원합니다.

BulkDelete()

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);  

언급URL : https://stackoverflow.com/questions/59954097/ef-core-slow-bulk-insert-80k-rows

반응형