C # Entity Framework : 대량 확장 입력 메모리 문제
현재 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
답변
쿼리와 유사한 조건으로 대량 업데이트를 수행하는 "적절한"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
.
기타 고려 사항
불행히도 이에 대한 배치 작업에 대한 언급이 없습니다.
이와 같은 대규모 업데이트를 수행하기 전에이 열에있는 인덱스를 비활성화하고 나중에 다시 작성하는 것이 좋습니다. 이것은 당신이 그것들이 많은 경우 특히 유용합니다.
Where
EF에서 SQL로 번역 할 수없는 경우 에서 조건에주의를 기울 이면 "일반적인"끔찍한 왕복 "로드-메모리 변경-업데이트"를 의미하는 클라이언트 측에서 수행됩니다.
이것은 실제로 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을 롤링하는 것이 좋지만 내 의견입니다.