EF 4.1에 엔터티를 삽입하는 것이 ObjectContext에 비해 느린 이유는 무엇입니까?


81

기본적으로 하나의 트랜잭션에 35000 개의 객체를 삽입합니다.

using(var uow = new MyContext()){
  for(int i = 1; i < 35000; i++) {
     var o = new MyObject()...;
     uow.MySet.Add(o);
  }
  uow.SaveChanges();
}

이것은 영원히 걸립니다! 기본 ObjectContext를 사용하면 (를 사용하여 IObjectAdapter) 여전히 느리지 만 약 20 초가 걸립니다. DbSet<>정사각형 시간이 걸리는 선형 검색을 수행하는 것 같습니다 .

이 문제를 본 다른 사람이 있습니까?


3
나는 어떻게 든 대답이 다음과 비슷할 것이라고 믿습니다. stackoverflow.com/questions/5917478/…
— Ladislav Mrnka

답변:


128

Ladislav가 주석에서 이미 언급했듯이 성능을 개선하려면 자동 변경 감지를 비활성화해야합니다.

context.Configuration.AutoDetectChangesEnabled = false;

이 변경 감지는 DbContextAPI 에서 기본적으로 활성화됩니다 .

API DbContext와 다르게 작동 하는 이유 ObjectContext는 자동 변경 감지가 활성화 된 경우 API의 함수보다 DbContextAPI 의 더 많은 함수가 DetectChanges내부적으로 호출 ObjectContext되기 때문입니다.

여기DetectChanges 에서 기본적으로 호출 되는 함수 목록을 찾을 수 있습니다 . 그들은:

  • Add, Attach, Find, Local, 또는 Remove회원에DbSet
  • 의 GetValidationErrors, Entry또는 SaveChanges회원DbContext
  • Entries에 방법DbChangeTracker

특히 귀하가 경험 한 성능 저하의 원인이되는 Add통화 DetectChanges.

이와 대조적으로 ObjectContextAPI는 위에서 언급 한 다른 해당 메서드를 호출 하지 않고 DetectChanges자동으로 호출 합니다 . 이것이의 기본 성능 이 더 빠른 이유 입니다.SaveChangesAddObjectObjectContext

DbContext많은 기능 에서이 기본 자동 변경 감지를 도입 한 이유는 무엇 입니까? 확실하지 않지만, 그것을 비활성화 DetectChanges하고 적절한 지점에서 수동으로 호출하는 것은 고급 으로 간주되고 응용 프로그램에 미묘한 버그를 쉽게 도입 할 수 있으므로 [it]를주의해서 사용하십시오 .


@Ladislav : 당신의 말이 맞아요 제가 삽입 문제를 찾고 있었기 때문에 이것을 찾지 못했습니다 :-(
— Hartmut

설명 해주셔서 감사합니다. 실제로 context.Configuration.AutoDetectChangesEnabled = false를 호출했지만 Seed () 메서드에서 데이터베이스 생성 중에 수행했습니다. 이것이 기본값이 될 것이라고 생각했습니다. 각 인스턴스에 대해 호출해야한다는 사실을 몰랐습니다. 감사!
— Hartmut

3
@Hartmut : 파생 된 DbContext의 생성자 내부에서 변경 감지를 비활성화 할 수 있으며 항상 비활성화 할 수 있습니다. 그러나 개인적으로 "미묘한 버그를 유발할 가능성이있다"는 말이 비활성화되었을 때 저를 불안하게 만듭니다. 기본적으로 변경 감지 기능이 있으며 성능 향상이 분명하고 문제를 일으키지 않는다고 생각되는 코드 블록에서만 비활성화합니다.
— Slauma

동의합니다. 저는 애플리케이션의 성능에 중요한 부분을 테스트하고있었습니다. 프로덕션 코드에서는 대량 삽입 등과 같은 경우로 제한하는 것이 가장 좋습니다.
— Hartmut

이 답변에 감사드립니다. 많은 정보가 있지만 이로 인해 추격전이 발생합니다!
— Fred Wilson

12

EF 4.3 CodeFirst를 사용한 약간의 경험적 테스트 :

AutoDetectChanges = true : 23 초로 1000 개의 개체 제거

AutoDetectChanges = false로 1000 개의 개체 제거 : 11 초

AutoDetectChanges = true : 21 초로 개체 1000 개 삽입

AutoDetectChanges = false로 개체 1000 개 삽입 : 13 초


1
감사합니다 Zax. 질문에 따라 35,000 개의 결과는 무엇입니까? 당신은 성능이 차적으로 떨어질 것으로 tsates 원래의 질문에이를 볼 수 있습니다
— 다니엘 다이슨

9

.netcore 2.0에서는 다음으로 이동되었습니다.

context.ChangeTracker.AutoDetectChangesEnabled = false;


1

여기에서 찾은 답변 외에도. 데이터베이스 수준에서 추가하는 것보다 삽입해야하는 작업이 더 많다는 것을 아는 것이 중요합니다. 데이터베이스는 새 공간을 확장 / 할당해야합니다. 그런 다음 최소한 기본 키 인덱스를 업데이트해야합니다. 인덱스는 업데이트 할 때도 업데이트 될 수 있지만 훨씬 덜 일반적입니다. 외래 키가있는 경우 참조 무결성이 유지되는지 확인하기 위해 해당 인덱스도 읽어야합니다. 트리거는 동일한 방식으로 업데이트에 영향을 미칠 수 있지만 역할도 할 수 있습니다.

모든 데이터베이스 작업은 사용자 항목에서 시작된 일일 삽입 활동에서 의미가 있습니다. 그러나 기존 데이터베이스를 업로드하는 중이거나 많은 삽입을 생성하는 프로세스가있는 경우. 끝까지 연기하여 속도를 높이는 방법을 살펴볼 수 있습니다. 일반적으로 삽입하는 동안 인덱스를 비활성화하는 것이 일반적인 방법입니다. 경우에 따라 수행 할 수있는 매우 복잡한 최적화가 있지만 다소 압도적 일 수 있습니다.

일반적으로 삽입은 업데이트보다 오래 걸립니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.