C # Entity Framework : 대량 확장 입력 메모리 문제

Aug 17 2020

현재 EF 확장을 사용하고 있습니다. 내가 이해하지 못하는 한 가지는 "성능에 도움이 될 것"

그러나 목록 변수에 백만 개 이상의 레코드를 배치하는 것은 메모리 문제 그 자체입니다. 따라서 모든 것을 메모리에 저장하지 않고 백만 개의 레코드를 업데이트하려면 어떻게 효율적으로 수행 할 수 있습니까?

을 사용하고 for loop일괄 적으로 업데이트 하면 10,000이 표시됩니까? EFExtensions BulkUpdate에이를 지원하는 기본 기능이 있습니까?

예:

var productUpdate = _dbContext.Set<Product>()
    .Where(x => x.ProductType == 'Electronics');  // this creates IQueryable

await productUpdate.ForEachAsync(c => c.ProductBrand = 'ABC Company');

_dbContext.BulkUpdateAsync(productUpdate.ToList());

자원:

https://entityframework-extensions.net/bulk-update

답변

Pac0 Aug 18 2020 at 00:27

쿼리와 유사한 조건으로 대량 업데이트를 수행하는 "적절한"EF 확장 방법을 찾았습니다.

var productUpdate = _dbContext.Set<Product>()
    .Where(x => x.ProductType == 'Electronics')
    .UpdateFromQuery( x => new Product { ProductBrand = "ABC Company" });

이렇게 하면 문서에UPDATE ... SET ... WHERE 따라 엔티티를 먼저로드 할 필요없이 적절한 SQL이 생성됩니다 .

UpdateFromQuery빨리보다 SaveChanges, BulkSaveChanges그리고 BulkUpdate?

UpdateFromQuery같은 SQL에서 문을 직접 실행합니다 UPDATE [TableName] SET [SetColumnsAndValues] WHERE [Key].

다른 작업에는 일반적으로 하나 이상의 데이터베이스 왕복이 필요하므로 성능이 저하됩니다.

이에 작업 구문을 확인하실 수 있습니다 DOTNET 바이올린 예를 들어 자신의 예에서 적응을 BulkUpdate.

기타 고려 사항

  • 불행히도 이에 대한 배치 작업에 대한 언급이 없습니다.

  • 이와 같은 대규모 업데이트를 수행하기 전에이 열에있는 인덱스를 비활성화하고 나중에 다시 작성하는 것이 좋습니다. 이것은 당신이 그것들이 많은 경우 특히 유용합니다.

  • WhereEF에서 SQL로 번역 할 수없는 경우 에서 조건에주의를 기울 이면 "일반적인"끔찍한 왕복 "로드-메모리 변경-업데이트"를 의미하는 클라이언트 측에서 수행됩니다.

2 Flater Aug 17 2020 at 23:43

이것은 실제로 EF가 만들어지지 않은 것입니다. EF의 데이터베이스 상호 작용은 레코드 개체에서 시작하여 거기에서 흐릅니다. EF는 엔터티가 변경 사항을 추적하지 않아서로드되지 않은 경우 부분적인 UPDATE를 생성 할 수 없으며 (즉, 모든 항목을 덮어 쓰지 않음) 마찬가지로 키 대신 조건에 따라 레코드를 삭제할 수 없습니다.

다음과 같은 조건부 업데이트 / 삭제 논리에는 EF에 해당하는 항목 (모든 레코드를로드하지 않음)이 없습니다.

UPDATE People
SET FirstName = 'Bob'
WHERE FirstName = 'Robert'

또는

DELETE FROM People
WHERE FirstName = 'Robert'

EF 접근 방식을 사용하여이 작업을 수행하려면 이러한 모든 엔터티를로드하여 (업데이트 또는 삭제와 함께) 데이터베이스로 다시 보내기 만하면됩니다. 이는 이미 찾은 대역폭과 성능의 낭비입니다.

여기에서 찾은 최상의 솔루션은 EF의 LINQ 친화적 인 메서드를 우회하고 대신 원시 SQL을 직접 실행하는 것입니다. 이것은 EF 컨텍스트를 사용하여 수행 할 수 있습니다.

using (var ctx = new MyContext())
{
    string updateCommand = "UPDATE People SET FirstName = 'Bob' WHERE FirstName = 'Robert'";
    int noOfRowsUpdated = ctx.Database.ExecuteSqlCommand(updateCommand);

    string deleteCommand = "DELETE FROM People WHERE FirstName = 'Robert'";
    int noOfRowsDeleted = ctx.Database.ExecuteSqlCommand(deleteCommand);
}

여기에 더 많은 정보가 있습니다 . 물론 관련된 경우 SQL 주입으로부터 보호하는 것을 잊지 마십시오 .

원시 SQL을 실행하기위한 특정 구문은 EF / EF Core 버전에 따라 다를 수 있지만 모든 버전에서 원시 SQL을 실행할 수 있다는 것을 알고 있습니다.


EF Extensions 또는 BulkUpdate의 성능에 대해 구체적으로 언급 할 수 없으며 그들로부터 구매하지 않을 것입니다.

문서에 따르면 조건부 업데이트 / 삭제 논리를 허용하는 올바른 서명이있는 메서드가없는 것 같습니다.

  • BulkUpdate 이를 최적화 할 수있는 논리적 조건 (UPDATE 명령의 WHERE)을 입력 할 수없는 것 같습니다.
  • BulkDelete여전히 BatchSize한 번에 하나씩 레코드를 처리하고 있으며 조건 (WHERE 절)과 함께 단일 DELETE 쿼리를 사용하지 않는다는 설정이 있습니다.

질문에서 의도 한 코드에 따라 EF Extensions는 실제로 필요한 것을 제공하지 않습니다. EF가 엔터티를로드해야하는 필요성을 우회하므로 데이터베이스에서 원시 SQL을 실행하는 것이 더 성능이 좋고 저렴합니다.

업데이트
내가 정정되었을 수 있으며 여기에 표시된 것처럼 조건부 업데이트 논리에 대한 일부 지원이 있습니다 . 그러나 예제가 여전히 모든 것을 메모리에로드하고 조건부 WHERE 논리의 목적이 이미 메모리에 모두로드되어있는 동안에는 명확하지 않습니다 (그러면 메모리 내 LINQ를 사용하지 않는 이유는 무엇입니까?).

그러나 엔티티를로드하지 않고 작동하더라도 여전히 다음과 같습니다.

  • 더 제한적 (유효한 SQL 인 부울 조건을 허용하는 SQL과 비교하여 동등성 검사 만 허용됨)
  • 상대적으로 복잡합니다 (구문이 마음에 들지 않습니다. 주관적 일 수 있습니다)
  • 더 비싸다 (여전히 유료 도서관)

자체 원시 SQL 쿼리를 롤링하는 것과 비교됩니다. 나는 여전히 여기에서 자신의 원시 SQL을 롤링하는 것이 좋지만 내 의견입니다.