Entity Framework에 삽입하는 가장 빠른 방법을 찾고 있습니다.
나는 당신이 활성화 된 TransactionScope를 가지고 있고 삽입이 거대한 시나리오 (4000+) 때문에 이것을 묻습니다. 잠재적으로 10 분 이상 지속될 수 있으며 (기본 트랜잭션 시간 초과) 트랜잭션이 불완전하게됩니다.
답변
귀하의 질문에 대한 의견에 대한 귀하의 의견 :
“… 저장 변경 사항 ( 각 레코드마다 ) …”
그것은 당신이 할 수있는 최악의 일입니다! SaveChanges()
각 레코드를 호출 하면 대량 삽입 속도가 매우 느려집니다. 성능을 향상시킬 수있는 몇 가지 간단한 테스트를 수행합니다.
SaveChanges()
모든 기록 후에 한 번 전화하십시오 .SaveChanges()
예를 들어 100 개의 레코드를 호출하십시오 .- 요구
SaveChanges()
예를 들어 100 개의 레코드를 하고 컨텍스트를 삭제하고 새 컨텍스트를 작성하십시오. - 변경 감지 비활성화
대량 인서트의 경우 다음과 같은 패턴으로 작업하고 실험하고 있습니다.
using (TransactionScope scope = new TransactionScope())
{
MyDbContext context = null;
try
{
context = new MyDbContext();
context.Configuration.AutoDetectChangesEnabled = false;
int count = 0;
foreach (var entityToInsert in someCollectionOfEntitiesToInsert)
{
++count;
context = AddToContext(context, entityToInsert, count, 100, true);
}
context.SaveChanges();
}
finally
{
if (context != null)
context.Dispose();
}
scope.Complete();
}
private MyDbContext AddToContext(MyDbContext context,
Entity entity, int count, int commitCount, bool recreateContext)
{
context.Set<Entity>().Add(entity);
if (count % commitCount == 0)
{
context.SaveChanges();
if (recreateContext)
{
context.Dispose();
context = new MyDbContext();
context.Configuration.AutoDetectChangesEnabled = false;
}
}
return context;
}
560.000 엔터티 (9 스칼라 속성, 탐색 속성 없음)를 DB에 삽입하는 테스트 프로그램이 있습니다. 이 코드를 사용하면 3 분 안에 작동합니다.
성능을 위해서는 SaveChanges()
“다수”레코드 ( “다수”약 100 또는 1000) 를 호출하는 것이 중요합니다 . 또한 SaveChanges 후 컨텍스트를 처리하고 새 컨텍스트를 작성하는 성능을 향상시킵니다. 이렇게하면 모든 SaveChanges
엔터티 에서 컨텍스트가 지워지고, 그렇지 않은 경우 엔터티는 여전히 컨텍스트의 컨텍스트에 연결됩니다 Unchanged
. 삽입 단계를 느리게하는 것은 맥락에서 첨부 된 엔티티의 크기가 커지고 있습니다. 따라서 얼마 후에 정리하면 도움이됩니다.
내 560000 엔터티에 대한 몇 가지 측정 값은 다음과 같습니다.
- commitCount = 1, recreateContext = false : 여러 시간 (현재 절차 임)
- commitCount = 100, recreateContext = false : 20 분 이상
- commitCount = 1000, recreateContext = false : 242 초
- commitCount = 10000, recreateContext = false : 202 초
- commitCount = 100000, recreateContext = false : 199 초
- commitCount = 1000000, recreateContext = false : 메모리 부족 예외
- commitCount = 1, recreateContext = true : 10 분 이상
- commitCount = 10, recreateContext = true : 241 초
- commitCount = 100, recreateContext = true : 164 초
- commitCount = 1000, recreateContext = true : 191 초
위의 첫 번째 테스트의 동작은 성능이 매우 비선형이며 시간이 지남에 따라 크게 감소한다는 것입니다. ( “많은 시간”은 추정입니다.이 테스트를 완료 한 적이 없으며 20 분 후에 50.000 개의 엔티티에서 중지되었습니다.)이 비선형 동작은 다른 모든 테스트에서 그다지 중요하지 않습니다.
답변
이 조합은 속도를 충분히 향상시킵니다.
context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ValidateOnSaveEnabled = false;
답변
가장 빠른 방법은 벌크 인서트 확장 을 사용하는 것입니다.
참고 :이 제품은 무료이며 상용 제품입니다
SqlBulkCopy 및 사용자 정의 데이터 리더를 사용하여 최대 성능을 얻습니다. 결과적으로 일반 인서트 또는 AddRange를 사용하는 것보다 20 배 이상 빠릅니다.
사용법은 매우 간단합니다
context.BulkInsert(hugeAmountOfEntities);
답변
System.Data.SqlClient.SqlBulkCopy
이것을 사용하는 것을보아야 합니다. 여기에 설명서 가 있으며 물론 온라인으로 많은 자습서가 있습니다.
죄송합니다. EF에서 원하는 것을 수행 할 수있는 간단한 답변을 찾고 있었지만 대량 작업은 실제로 ORM의 목적이 아닙니다.
답변
나는 Adam Rackis에 동의합니다. SqlBulkCopy
한 데이터 소스에서 다른 데이터 소스로 대량 레코드를 전송하는 가장 빠른 방법입니다. 이것을 사용하여 20K 레코드를 복사했으며 3 초도 걸리지 않았습니다. 아래 예를 살펴보십시오.
public static void InsertIntoMembers(DataTable dataTable)
{
using (var connection = new SqlConnection(@"data source=;persist security info=True;user id=;password=;initial catalog=;MultipleActiveResultSets=True;App=EntityFramework"))
{
SqlTransaction transaction = null;
connection.Open();
try
{
transaction = connection.BeginTransaction();
using (var sqlBulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.TableLock, transaction))
{
sqlBulkCopy.DestinationTableName = "Members";
sqlBulkCopy.ColumnMappings.Add("Firstname", "Firstname");
sqlBulkCopy.ColumnMappings.Add("Lastname", "Lastname");
sqlBulkCopy.ColumnMappings.Add("DOB", "DOB");
sqlBulkCopy.ColumnMappings.Add("Gender", "Gender");
sqlBulkCopy.ColumnMappings.Add("Email", "Email");
sqlBulkCopy.ColumnMappings.Add("Address1", "Address1");
sqlBulkCopy.ColumnMappings.Add("Address2", "Address2");
sqlBulkCopy.ColumnMappings.Add("Address3", "Address3");
sqlBulkCopy.ColumnMappings.Add("Address4", "Address4");
sqlBulkCopy.ColumnMappings.Add("Postcode", "Postcode");
sqlBulkCopy.ColumnMappings.Add("MobileNumber", "MobileNumber");
sqlBulkCopy.ColumnMappings.Add("TelephoneNumber", "TelephoneNumber");
sqlBulkCopy.ColumnMappings.Add("Deleted", "Deleted");
sqlBulkCopy.WriteToServer(dataTable);
}
transaction.Commit();
}
catch (Exception)
{
transaction.Rollback();
}
}
}
답변
EF를 사용하여 대량 삽입을 수행하는 방법에 대해이 기사를 추천합니다.
그는 이러한 영역을 탐구하고 성능을 비교합니다.
- 기본 EF (30,000 개의 레코드 추가 완료까지 57 분)
- ADO.NET 코드로 교체 ( 동일한 30,000에 대해 25 초 )
- 컨텍스트 블로 트-각 작업 단위에 대해 새로운 컨텍스트를 사용하여 활성 컨텍스트 그래프를 작게 유지하십시오 (동일한 30,000 개의 삽입에 33 초 소요)
- 큰 목록-AutoDetectChangesEnabled 끄기 (약 20 초로 줄입니다)
- 배치 (최소 16 초)
- DbTable.AddRange ()-(성능이 12 범위에 있음)
답변
여기에 언급되지 않았으므로 EFCore를 추천하고 싶습니다 .BulkExtensions here
context.BulkInsert(entitiesList); context.BulkInsertAsync(entitiesList);
context.BulkUpdate(entitiesList); context.BulkUpdateAsync(entitiesList);
context.BulkDelete(entitiesList); context.BulkDeleteAsync(entitiesList);
context.BulkInsertOrUpdate(entitiesList); context.BulkInsertOrUpdateAsync(entitiesList); // Upsert
context.BulkInsertOrUpdateOrDelete(entitiesList); context.BulkInsertOrUpdateOrDeleteAsync(entitiesList); // Sync
context.BulkRead(entitiesList); context.BulkReadAsync(entitiesList);