응용 프로그램의 인라인 SQL보다 실행 경로를 통해 저장 프로 시저가 더 효율적이라는 것을 알고 있습니다. 그러나 눌 렸을 때 나는 왜 그런지 잘 알지 못한다.
이에 대한 기술적 추론을 알고 싶습니다 (나중에 누군가에게 설명 할 수있는 방식으로).
누구든지 좋은 답변을 공식화하도록 도울 수 있습니까?
응용 프로그램의 인라인 SQL보다 실행 경로를 통해 저장 프로 시저가 더 효율적이라는 것을 알고 있습니다. 그러나 눌 렸을 때 나는 왜 그런지 잘 알지 못한다.
이에 대한 기술적 추론을 알고 싶습니다 (나중에 누군가에게 설명 할 수있는 방식으로).
누구든지 좋은 답변을 공식화하도록 도울 수 있습니까?
답변:
이 감정은 한 시점에서는 사실이지만 현재 버전의 SQL Server에서는 그렇지 않다고 생각합니다. 전체 문제는 예전에는 SQL Server가 배치 수준에서만 최적화 / 컴파일 할 수 있기 때문에 임시 SQL 문을 올바르게 최적화 할 수 없다는 것입니다. 이제 문 수준 최적화가 이루어 졌으므로 응용 프로그램에서 올바로 매개 변수화 된 쿼리를 사용하면 저장 프로 시저에 포함 된 쿼리와 동일한 실행 계획을 활용할 수 있습니다.
나는 여전히 다음과 같은 이유로 DBA 측의 저장 프로 시저를 선호합니다 (그리고 그중 일부는 성능에 큰 영향을 줄 수 있습니다).
sys.sql_modules
특정 개체에 대한 참조를 위해 단일 소스를 grep 하면 모든 사람의 삶이 훨씬 쉬워집니다.SET ANSI_WARNINGS ON
있고 다른 하나는 가질 수 있으며 SET ANSI_WARNINGS OFF
각각 고유 한 계획 사본이 있습니다. 이들이 얻는 계획은 사용중인 매개 변수, 통계 등에 따라 달라집니다. 각 경우에 쿼리를 처음 호출 할 때마다 계획이 달라져서 성능이 매우 달라질 수 있습니다.그러나이 질문은 기술적 인 논쟁보다 더 많은 종교적 논쟁을 불러 일으킬 것입니다. 우리는 그런 일이 발생하면 아마 종료 될 것입니다.
나는 제출자를 존중하지만 "종교적인 이유"가 아니라 제공된 답변에 겸손하게 동의하지 않습니다. 다시 말해, Microsoft가 제공 한 기능이 없기 때문에 저장 프로 시저를 사용하기위한 지침의 필요성이 줄어 듭니다.
원시 텍스트 SQL 쿼리 사용을 선호하는 개발자에게 제공되는 모든 지침은 많은주의 사항으로 채워 져야합니다. 그래서 가장 신중한 조언은 저장 프로 시저 사용을 장려하고 개발자 팀이 실습에 참여하지 못하게하는 것입니다 SQL SPROC (저장 프로 시저) 외부에서 코드에 SQL 문을 포함하거나 원시 텍스트 기반의 SQL 기반 원시 요청을 제출합니다.
제출자가 추측 한대로 SPROC를 사용하는 이유에 대한 간단한 대답은 SPROC가 구문 분석, 최적화 및 컴파일된다고 생각합니다. 따라서 쿼리의 정적 표현을 저장했기 때문에 쿼리 / 실행 계획이 캐시되며 일반적으로 매개 변수에 의해서만 변경되므로 일반적으로 모핑 될 수있는 복사 / 붙여 넣기 된 SQL 문의 경우에는 사실이 아닙니다. 페이지 간 및 구성 요소 / 계층에서 데이터베이스 이름까지 서로 다른 테이블을 C2C로 지정할 수있는 정도까지 분산되어 있습니다. 이 유형의 동적 임시 허용SQL 제출은 매우 엄격한 규칙에 따라 DB 엔진이 임시 명령문에 대한 쿼리 계획을 재사용 할 가능성을 크게 줄입니다. 여기서는 동적 애드혹 쿼리 (질문의 정신)와 효율적인 System SPROC sp_executesql의 사용을 구분하고 있습니다.
보다 구체적으로 다음과 같은 구성 요소가 있습니다.
"임시 명령문"이라고하는 웹 페이지에서 SQL 문이 발행되면 엔진은 요청을 처리 할 기존 실행 계획을 찾습니다. 이 텍스트는 사용자가 제출 한 텍스트이므로 유효한 경우 수집, 파싱, 컴파일 및 실행됩니다. 현재 쿼리 비용은 0입니다. 쿼리 비용은 DB 엔진이 알고리즘을 사용하여 캐시에서 제거 할 실행 계획을 결정하는 데 사용됩니다.
임시 쿼리는 기본적으로 원래 쿼리 비용 값 0을받습니다. 다른 사용자 프로세스 (또는 동일한 프로세스)에 의해 정확히 동일한 임시 쿼리 텍스트를 후속 실행하면 현재 쿼리 비용이 원래 컴파일 비용으로 재설정됩니다. 애드혹 쿼리 컴파일 비용이 0이므로 재사용 가능성에 적합하지 않습니다. 분명히 0은 가장 낮은 값의 정수이지만 왜 제거됩니까?
메모리 압력이 발생하고 자주 사용하는 사이트가있는 경우 DB 엔진은 정리 알고리즘을 사용하여 프로 시저 캐시에서 사용중인 메모리를 회수 할 수있는 방법을 결정합니다. 현재 쿼리 비용을 사용하여 제거 할 계획을 결정합니다. 짐작할 수 있듯이 비용이 0 인 계획은 캐시에서 가장 먼저 제거됩니다. 0은 본질적으로 "이 계획에 대한 현재 사용자 나 참조가 없음"을 의미하기 때문입니다.
따라서 메모리 압박이 발생하면 이러한 계획이 먼저 제거 될 가능성이 높습니다.
따라서 "필요한 것 이외의"메모리가 많은 서버를 구축 한 경우 작업량이 처리하기에 "충분한"메모리 만있는 사용량이 많은 서버만큼 자주이 문제가 발생하지 않을 수 있습니다. (아직 알고리즘은 아니지만 서버 메모리 용량과 활용률은 다소 주관적 / 상대적입니다.)
이제 하나 이상의 요점에 대해 사실 틀린 경우 수정 될 수 있습니다.
마지막으로 저자는 다음과 같이 썼습니다.
"이제 문 수준 최적화 기능을 갖추고 있으므로 응용 프로그램에서 나오는 매개 변수가있는 쿼리는 저장 프로 시저에 포함 된 쿼리와 동일한 실행 계획을 활용할 수 있습니다."
필자는 저자가 "임시 작업량 최적화"옵션을 언급하고 있다고 생각합니다.
그렇다면이 옵션을 사용하면 전체 쿼리 계획을 즉시 프로 시저 캐시로 보내지 않아도되는 2 단계 프로세스가 가능합니다. 더 작은 쿼리 스텁 만 보냅니다. 쿼리 스텁이 여전히 프로 시저 캐시에있는 동안 정확한 쿼리 호출이 서버로 다시 전송되면 전체 쿼리 실행 계획이 그 때 프로 시저 캐시에 저장됩니다. 이 메모리 압력 사고시, 메모리에 저장 할 수있다 퇴거 알고리즘은 캐시 된 큰 쿼리 계획보다 덜 자주 스텁을 퇴거 할 수 있습니다. 다시 이것은 서버 메모리 및 사용률에 따라 다릅니다.
그러나이 옵션은 기본적으로 해제되어 있으므로 설정해야합니다.
마지막으로 개발자가 페이지, 구성 요소 및 기타 장소에 SQL을 포함시키는 이유는 유연하고 동적 SQL 쿼리를 데이터베이스 엔진에 제출하기를 원하기 때문입니다. 따라서 실제 사용 사례에서는 SQL Server에 임시 쿼리를 제출할 때 원하는 캐싱 / 효율과 같이 동일한 텍스트 인 Call-over-call을 제출할 가능성이 거의 없습니다.
자세한 내용은 다음을 참조하십시오.
https://technet.microsoft.com/en-us/library/ms181055(v=sql.105).aspx
http://sqlmag.com/database-performance-tuning/don-t-fear-dynamic-sql
최고,
헨리
TLDR : 인라인 SQL이 매개 변수화되어있는 한 두 가지 사이에 성능 차이가 눈에 띄지 않습니다.
이것이 저장 프로 시저를 천천히 단계적으로 제거한 이유입니다.
프로덕션 데이터베이스를 공유하는 프로덕션과 유사한 환경 인 '베타'애플리케이션 환경을 실행합니다. db 코드는 응용 프로그램 수준에 있으며 db 구조 변경은 드물기 때문에 사람들이 QA 이외의 새로운 기능을 확인하고 프로덕션 배포 창 외부에서 배포를 수행 할 수 있지만 여전히 프로덕션 기능과 중요하지 않은 수정 사항을 제공 할 수 있습니다. 애플리케이션 코드의 절반이 DB에있는 경우에는 불가능합니다.
우리는 데이터베이스 레벨 (문어 + dacpacs)에서 devops를 연습합니다. 그러나 비즈니스 계층 이상은 기본적으로 제거 및 교체 할 수 있지만 그 반대의 경우도 마찬가지이지만 데이터베이스로 가야하는 점진적이고 파괴적인 변경 사항에는 해당되지 않습니다. 따라서 DB 배포를 더 가볍고 덜 자주 유지하는 것이 좋습니다.
선택적 매개 변수에 대해 동일한 코드의 거의 정확한 사본을 피하기 위해 종종 'var @ null 또는 @ var = table.field'패턴을 사용합니다. 저장 프로 시저를 사용하면 의도가 다르더라도 동일한 실행 계획을 얻을 수 있으므로 성능 문제가 발생하거나 '재 컴파일'힌트로 캐시 계획을 제거 할 수 있습니다. 그러나 SQL 끝에 "서명"주석을 추가하는 간단한 코드를 사용하면 어떤 변수가 null인지에 따라 다른 계획을 강제 실행할 수 있습니다 (모든 변수 조합에 대해 다른 계획으로 해석되지 않아야 함) null이 아님).
SQL로 즉시 변경하면 결과를 크게 변경할 수 있습니다. 예를 들어, 두 개의 CTE "Raw"및 "ReportReady"로 끝나는 문을 가질 수 있습니다. 두 CTE를 모두 사용해야한다고 말하는 것은 없습니다.
...
{from {(format)} "선택
이를 통해 간소화 된 API 호출과 복잡한 로직을 복제하지 않도록보다 세부적으로보고해야하는 보고서에 대해 동일한 비즈니스 로직 방법을 사용할 수 있습니다.
procs를 사용해야하는 유효한 이유가 있습니다.
보안-앱이 통과해야하는 또 다른 계층이 있습니다. 응용 프로그램 서비스 계정이 테이블을 터치 할 수 없지만 프로세스에 대해 '실행'권한 만있는 경우 추가 보호 기능이 있습니다. 이것은 비용이 들기 때문에 주어진 것이 아니지만 가능성이 있습니다.
재사용-비 DB 관련 비즈니스 규칙을 우회하지 않도록하기 위해 비즈니스 계층에서 재사용이 크게 발생해야한다고 말하지만, 여전히 업무용, 저수준 "모든 곳에서 사용"유형의 유틸리티 프로세서 및 기능이 있습니다.
실제로 proc를 지원하지 않거나 쉽게 완화되는 IMO 인 몇 가지 주장이 있습니다.
재사용 – 나는 이것을 위에서 "플러스"라고 언급했지만, 비즈니스 계층에서 재사용이 크게 이루어져야한다고 여기에서 언급하고 싶었다. 비즈니스 계층이 다른 비 DB 서비스도 검사 할 때 레코드를 삽입하는 절차를 "재사용"으로 간주해서는 안됩니다.
캐시 계획 팽창-이것이 문제가 될 유일한 방법은 매개 변수화가 아닌 값을 연결하는 경우입니다. proc 당 하나 이상의 계획을 거의 얻지 못한다는 사실은 쿼리에 '또는'이있을 때 실제로 당신을 아프게합니다.
명령문 크기-proc name에 대한 추가 kb의 SQL 문은 일반적으로 다시 오는 데이터와 관련하여 무시할 수 있습니다. 엔터티가 괜찮다면 나도 괜찮습니다.
정확한 쿼리보기-코드에서 쿼리를 쉽게 찾을 수 있도록하는 것은 호출 위치를 코드에 주석으로 추가하는 것만 큼 간단합니다. C # 코드에서 ssms로 코드를 복사 할 수있게 만드는 것은 창의적인 보간 및 주석 사용법만큼 쉽습니다.
//Usage /*{SSMSOnly_}*/Pure Sql To run in SSMS/*{_SSMSOnly}*/
const string SSMSOnly_ = "*//*<SSMSOnly>/*";
const string _SSMSOnly = "*/</SSMSOnly>";
//Usage /*{NetOnly_}{InterpolationVariable}{_NetOnly}*/
const string NetOnly_ = "*/";
const string _NetOnly = "/*";
SQL 주입-쿼리를 매개 변수화하십시오. 끝난. proc이 대신 동적 SQL을 사용하는 경우 실제로 취소 할 수 있습니다.
배포 우회-데이터베이스 수준에서도 devops를 연습하므로 우리에게는 옵션이 아닙니다.
"응용 프로그램이 느리고 SSMS가 빠름"-이것은 양쪽에 영향을주는 계획 캐싱 문제입니다. set 옵션은 단지 ONE SET OFF 변수에 대한 문제를 해결하기 위해 새로운 계획이 컴파일되도록합니다. 이것은 왜 다른 결과를 보는지에 대한 답입니다. 설정 옵션 자체는 매개 변수 스니핑 문제를 해결하지 않습니다.
인라인 SQL 실행 계획이 캐시되지 않습니다. 단순히 거짓입니다. proc 이름과 마찬가지로 매개 변수화 된 명령문은 빠르게 해시 된 후 해당 해시에서 계획을 검색합니다. 100 % 동일합니다.
분명히 ORM에서 생성되지 않은 원시 인라인 SQL에 대해 이야기하고 있습니다. 우리는 마이크로 ORM 인 Dapper 만 사용합니다.