Entity Framework 쿼리는 느리지 만 SqlQuery의 동일한 SQL은 빠름


95

.NET Framework 버전 4에서 Entity Framework Code-First를 사용하는 매우 간단한 쿼리와 관련된 정말 이상한 성능이 표시됩니다. LINQ2Entities 쿼리는 다음과 같습니다.

 context.MyTables.Where(m => m.SomeStringProp == stringVar);

실행하는 데 3000 밀리 초 이상이 걸립니다. 생성 된 SQL은 매우 간단합니다.

 SELECT [Extent1].[ID], [Extent1].[SomeStringProp], [Extent1].[SomeOtherProp],
 ...
 FROM [MyTable] as [Extent1]
 WHERE [Extent1].[SomeStringProp] = '1234567890'

이 쿼리는 Management Studio를 통해 실행될 때 거의 즉시 실행됩니다. SqlQuery 함수를 사용하도록 C # 코드를 변경하면 5-10 밀리 초 안에 실행됩니다.

 context.MyTables.SqlQuery("SELECT [Extent1].[ID] ... WHERE [Extent1].[SomeStringProp] = @param", stringVar);

따라서 정확히 동일한 SQL, 결과 엔터티는 두 경우 모두 변경 추적되지만 둘 사이의 성능 차이는 크게 다릅니다. 무엇을 제공합니까?


2
초기화 지연이 발생할 것으로 예상합니다. 아마도 뷰 컴파일 일 것입니다. MSDN 참조 :Performance Considerations for Entity Framework 5
Nicholas Butler

미리 생성 된 뷰를 시도했지만 도움이되지 않는 것 같습니다. 또한 느린 쿼리 전에 다른 EF 쿼리를 실행하여 초기화 항목을 배제했습니다. 새 쿼리는 빠르게 실행되었으며 느린 쿼리는 첫 번째 쿼리 중에 컨텍스트 워밍업이 발생 했음에도 불구하고 여전히 느리게 실행되었습니다.
Brian Sullivan

1
@marc_s-아니요, SqlQuery는 완전히 구체화되고 변경 추적 된 엔터티 인스턴스를 반환합니다. msdn.microsoft.com/en-us/library/…
Brian Sullivan

EF 쿼리에 대해 생성 된 SQL이 실제로 매개 변수 값을 인라인하거나 매개 변수를 사용합니까? 이는 개별 쿼리에 대한 쿼리 속도에 영향을주지 않지만 시간이 지남에 따라 서버에서 쿼리 계획이 팽창 할 수 있습니다.
Jim Wooley

동일한 쿼리를 두 번 / 여러 번 실행 해 보셨습니까? 두 번째로 실행할 때 얼마나 걸렸습니까? .NET Framework 4.5에서 이것을 시도해 보았습니까?-도움이 될 수있는 .NET Framework 4.5의 일부 EF 관련 성능 향상이 있습니다.
Pawel

답변:


97

그것을 발견. 그것은 SQL 데이터 유형의 문제로 밝혀졌습니다. SomeStringProp데이터베이스 의 열은 varchar 이었지만 EF는 .NET 문자열 유형이 nvarchar라고 가정합니다. 쿼리 중에 DB가 비교를 수행하는 결과 번역 프로세스는 시간이 오래 걸립니다. 나는 EF Prof가 나를 약간 잘못 이끌고 있다고 생각하며, 실행되는 쿼리의 더 정확한 표현은 다음과 같습니다.

 SELECT [Extent1].[ID], [Extent1].[SomeStringProp], [Extent1].[SomeOtherProp],
 ...
 FROM [MyTable] as [Extent1]
 WHERE [Extent1].[SomeStringProp] = N'1234567890'

따라서 결과 수정은 올바른 SQL 데이터 유형을 나타내는 코드 우선 모델에 주석을 추가하는 것입니다.

public class MyTable
{
    ...

    [Column(TypeName="varchar")]
    public string SomeStringProp { get; set; }

    ...
}

1
좋은 조사입니다. 귀하의 쿼리는 여기에 설명 된대로 "암시 적 변환"이 발생했습니다. brentozar.com/archive/2012/07/…
Jaime

몇 시간의 디버깅 시간을 절약했습니다. 이것이 바로 문제였습니다.
Cody

1
제 경우에는 varchar모든 것을 사용하는 레거시 데이터베이스와 함께 EDMX를 사용 하고 있으며 실제로 이것이 문제였습니다. 모든 문자열 열에 대해 varchar를 고려하도록 EDMX를 만들 수 있는지 궁금합니다.
Alisson

1
좋은 찾는 사람. 하지만 @Jaime은 데이터베이스에서 EF 모델을 업데이트 한 후 모든 것 (예 : db 모델의 데이터 주석)이 지워 지므로 데이터베이스 우선 접근 방식을 위해해야 ​​할 일입니다.
Nauman Khan

잠시 동안 홈 페이지로 설정하여 잠시 동안 이러한 훌륭한 답변을 찾는 흥분을 다시 느낄 수 있습니다. 감사합니다!!!
OJisBad

44

EF에서 만든 쿼리 속도를 늦추는 이유는 nullable이 아닌 스칼라와 nullable 스칼라를 비교하는 것입니다.

long? userId = 10; // nullable scalar

db.Table<Document>().Where(x => x.User.Id == userId).ToList() // or userId.Value
                                ^^^^^^^^^    ^^^^^^
                                Type: long   Type: long?

이 쿼리에는 35 초가 걸렸습니다. 그러나 다음과 같은 작은 리팩토링 :

long? userId = 10;
long userIdValue = userId.Value; // I've done that only for the presentation pursposes

db.Table<Document>().Where(x => x.User.Id == userIdValue).ToList()
                                ^^^^^^^^^    ^^^^^^^^^^^
                                Type: long   Type: long

놀라운 결과를 제공합니다. 완료하는 데 50ms 밖에 걸리지 않았습니다. EF의 버그 일 가능성이 있습니다.


13
이것은 그렇게 이상해
다니엘 카르데나스

1
OMG. IUserId.Id 인터페이스를 사용할 때도이 문제가 발생할 수 있지만 먼저 Id를 정수로 매핑하면 작동합니다. 이제 100.000 줄 응용 프로그램에서 모든 쿼리를 확인해야합니까?
Dirk Boer

이 버그가보고 되었습니까? 여전히 최신 버전 6.2.0
Dirk Boer 19

2
EF Core에도 동일한 문제가 있습니다. 찾아 주셔서 감사합니다!
Yannickv

또 다른 제안은 LINQ 식에 넣기 전에 변수를 처리하는 것입니다. 그렇지 않으면 생성 된 SQL이 훨씬 길고 느려집니다. 나는 나를 괴롭히는 LINQ 표현식 내부에 Trim () 및 ToLower ()가있을 때 경험했습니다.
samheihey


4

나는 같은 문제가 있었지만 (SQL 관리자에서 실행할 때 쿼리가 빠르다) EF에서 실행하면 시간 초과가 만료됩니다.

보기에서 생성 된 엔티티에 잘못된 엔티티 키가있는 것으로 나타났습니다. 따라서 엔터티에는 동일한 키가있는 중복 행이 있으며 배경에서 그룹화를 수행해야했습니다.


3

나는 또한 복잡한 ef 쿼리로 이것을 발견했습니다. 6 초 ef 쿼리를 생성 한 서브 초 SQL 쿼리로 줄인 저에게 한 가지 수정 사항은 지연로드를 끄는 것입니다.

이 설정 (ef 6)을 찾으려면 .edmx 파일로 이동하여 속성-> 코드 생성-> 지연로드 활성화를 확인하십시오. false로 설정하십시오.

저에게 성능이 크게 향상되었습니다.


4
멋지지만 포스터 질문과는 아무 관련이 없습니다.
Jace Rhea

2

나도이 문제가 있었다. 제 경우의 범인은 SQL-Server 매개 변수 스니핑 이었습니다.

내 문제가 실제로 매개 변수 스니핑 때문이라는 첫 번째 단서는 "set arithabort off"또는 "set arithabort on"을 사용하여 쿼리를 실행하면 Management Studio에서 실행 시간이 크게 달라진다는 것입니다. ADO.NET은 기본적으로 "set arithabort off"를 사용하고 Management Studio는 기본적으로 "set arithabort on"을 사용하기 때문입니다. 쿼리 계획 캐시는이 매개 변수에 따라 다른 계획을 유지합니다.

여기에서 찾을 수있는 솔루션을 사용하여 쿼리에 대한 쿼리 계획 캐싱을 비활성화 했습니다 .

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