저장 프로 시저와 인라인 SQL


27

응용 프로그램의 인라인 SQL보다 실행 경로를 통해 저장 프로 시저가 더 효율적이라는 것을 알고 있습니다. 그러나 눌 렸을 때 나는 왜 그런지 잘 알지 못한다.

이에 대한 기술적 추론을 알고 싶습니다 (나중에 누군가에게 설명 할 수있는 방식으로).

누구든지 좋은 답변을 공식화하도록 도울 수 있습니까?


1
제대로 매개 변수화 된 쿼리 성능 관점에서, 단지 저장 프로 시저 좋은 같습니다. 둘 다 처음 사용하기 전에 컴파일되고, 둘 다 후속 실행에서 캐시 된 실행 계획을 재사용하고, 두 계획 모두 동일한 계획 캐시에 저장되며 둘 다 동일한 이름을 처리합니다. 오늘날 SQL Server에서는 더 이상 저장 프로 시저에 대한 성능 이점이 없습니다.
marc_s

@marc_s 쿼리가 동일하면 사실입니다. 그러나 내 대답에서 지적한 것처럼 동일한 쿼리의 경우에도 성능 문제가 될 수있는 임시 쿼리의 몇 가지 특성이 있습니다 .
Aaron Bertrand

답변:


42

이 감정은 한 시점에서는 사실이지만 현재 버전의 SQL Server에서는 그렇지 않다고 생각합니다. 전체 문제는 예전에는 SQL Server가 배치 수준에서만 최적화 / 컴파일 할 수 있기 때문에 임시 SQL 문을 올바르게 최적화 할 수 없다는 것입니다. 이제 문 수준 최적화가 이루어 졌으므로 응용 프로그램에서 올바로 매개 변수화 된 쿼리를 사용하면 저장 프로 시저에 포함 된 쿼리와 동일한 실행 계획을 활용할 수 있습니다.

나는 여전히 다음과 같은 이유로 DBA 측의 저장 프로 시저를 선호합니다 (그리고 그중 일부는 성능에 큰 영향을 줄 수 있습니다).

  • 동일한 쿼리를 다시 사용하는 여러 앱이있는 경우 저장 프로시 저는 동일한 임시 쿼리를 다른 코드베이스에서 여러 번 흩 뜨리지 않고 해당 논리를 캡슐화합니다. 동일한 쿼리를 재사용하는 응용 프로그램은 그대로 복사되지 않는 한 계획 캐시 팽창에 종속 될 수 있습니다. 대소 문자와 공백의 차이조차도 동일한 계획의 여러 버전이 저장 (폐기) 될 수 있습니다.
  • 응용 프로그램 소스 코드에 액세스하거나 값 비싼 추적을 실행하지 않고 응용 프로그램이 정확히 무엇을 수행하는지 확인하지 않고 쿼리가 수행중인 작업을 검사하고 문제를 해결할 수 있습니다.
  • 또한 응용 프로그램에서 실행할 수있는 쿼리, 액세스 할 수있는 테이블 및 컨텍스트 등을 제어 (및 사전에 알 수 있음) 할 수 있습니다. 개발자가 응용 프로그램에서 임시로 쿼리를 작성하는 경우에는 다음 중 하나를 수행해야합니다. 내가 알지 못했거나 예측할 수없는 테이블에 접근 할 때마다 셔츠 소매를 잡아 당기거나 책임감이 적거나 열성적이거나 보안에 민감한 경우에는 그들이 나를 괴롭히지 않도록 dbo 할 사용자. 일반적으로 이는 개발자가 DBA보다 많거나 DBA가 완고 할 때 수행됩니다. 마지막 요점은 우리의 나쁜 점이며 필요한 쿼리를 제공하는 데 더 나은 방법이 필요합니다.
  • 관련 참고 사항에 따르면 일련의 저장 프로 시저는 내 시스템에서 실행중인 쿼리를 정확하게 인벤토리하는 매우 쉬운 방법입니다. 응용 프로그램이 프로 시저를 우회하고 자체 임시 쿼리를 제출할 수있게 되 자마자이를 찾으려면 전체 비즈니스주기를 다루는 추적을 실행하거나 모든 응용 프로그램 코드를 다시 구문 분석해야합니다. 나는 쿼리처럼 보이는 것을 찾기 위해 액세스 할 수 없습니다. 저장 프로 시저 목록을보고 sys.sql_modules특정 개체에 대한 참조를 위해 단일 소스를 grep 하면 모든 사람의 삶이 훨씬 쉬워집니다.
  • SQL 삽입을 막기 위해 훨씬 더 긴 길이로 갈 수 있습니다. 동적 SQL을 사용하여 입력하고 실행하더라도 발생할 수있는 많은 것을 제어 할 수 있습니다. 인라인 SQL 문을 구성 할 때 개발자가 수행하는 작업을 제어 할 수 없습니다.
  • 응용 프로그램 소스 코드에 대한 액세스 권한, 변경 기능, 효과적으로 수행 할 수있는 응용 프로그램 언어에 대한 지식, 재 컴파일 및 재배치 권한 (번거 로움은 아님)없이 쿼리를 최적화 할 수 있습니다. 앱 등이 있습니다. 이는 앱이 배포 된 경우 특히 문제가됩니다.
  • 저장 프로 시저 내에서 특정 세트 옵션을 강제로 적용하여 개별 쿼리 가 응용 프로그램 에서 느리게 일부 SSMS 에 적용되지 않도록 할 수 있습니까? 문제. 즉, 임시 쿼리를 호출하는 두 개의 서로 다른 응용 프로그램의 경우 하나는 가질 수 SET ANSI_WARNINGS ON있고 다른 하나는 가질 수 있으며 SET ANSI_WARNINGS OFF각각 고유 한 계획 사본이 있습니다. 이들이 얻는 계획은 사용중인 매개 변수, 통계 등에 따라 달라집니다. 각 경우에 쿼리를 처음 호출 할 때마다 계획이 달라져서 성능이 매우 달라질 수 있습니다.
  • 특정 ORM과 달리 데이터 유형 및 매개 변수 사용 방법을 제어 할 수 있습니다 .EF와 같은 일부 이전 버전은 매개 변수 길이에 따라 쿼리를 매개 변수화하므로 매개 변수 N'Smith '와 다른 N' Johnson '나는 두 가지 다른 버전의 계획을 얻을 것입니다. 그들은 이것을 고쳤다. 그들은 이것을 고쳤지만 여전히 다른 것이 무엇입니까?
  • ORM 및 기타 "유용한"프레임 워크 및 라이브러리가 아직 지원할 수없는 작업을 수행 할 수 있습니다.

그러나이 질문은 기술적 인 논쟁보다 더 많은 종교적 논쟁을 불러 일으킬 것입니다. 우리는 그런 일이 발생하면 아마 종료 될 것입니다.


2
저장 프로 시저의 또 다른 이유는 무엇입니까? 길고 복잡한 쿼리의 경우 sproc가 아닌 한 매번 쿼리를 서버에 푸시해야합니다. 기본적으로 "exec sprocname"과 몇 가지 매개 변수를 푸시합니다. 이것은 느리거나 바쁜 네트워크에서 차이를 만들 수 있습니다.
David Crowell

0

나는 제출자를 존중하지만 "종교적인 이유"가 아니라 제공된 답변에 겸손하게 동의하지 않습니다. 다시 말해, Microsoft가 제공 한 기능이 없기 때문에 저장 프로 시저를 사용하기위한 지침의 필요성이 줄어 듭니다.

원시 텍스트 SQL 쿼리 사용을 선호하는 개발자에게 제공되는 모든 지침은 많은주의 사항으로 채워 져야합니다. 그래서 가장 신중한 조언은 저장 프로 시저 사용을 장려하고 개발자 팀이 실습에 참여하지 못하게하는 것입니다 SQL SPROC (저장 프로 시저) 외부에서 코드에 SQL 문을 포함하거나 원시 텍스트 기반의 SQL 기반 원시 요청을 제출합니다.

제출자가 추측 한대로 SPROC를 사용하는 이유에 대한 간단한 대답은 SPROC가 구문 분석, 최적화 및 컴파일된다고 생각합니다. 따라서 쿼리의 정적 표현을 저장했기 때문에 쿼리 / 실행 계획이 캐시되며 일반적으로 매개 변수에 의해서만 변경되므로 일반적으로 모핑 될 수있는 복사 / 붙여 넣기 된 SQL 문의 경우에는 사실이 아닙니다. 페이지 간 및 구성 요소 / 계층에서 데이터베이스 이름까지 서로 다른 테이블을 C2C로 지정할 수있는 정도까지 분산되어 있습니다. 이 유형의 동적 임시 허용SQL 제출은 매우 엄격한 규칙에 따라 DB 엔진이 임시 명령문에 대한 쿼리 계획을 재사용 할 가능성을 크게 줄입니다. 여기서는 동적 애드혹 쿼리 (질문의 정신)와 효율적인 System SPROC sp_executesql의 사용을 구분하고 있습니다.

보다 구체적으로 다음과 같은 구성 요소가 있습니다.

  • 사용자 컨텍스트를 유지하지 않고 DB 엔진에서 재사용 할 수있는 직렬 및 병렬 쿼리 계획.
  • 다른 데이터 매개 변수를 가진 새 사용자가 쿼리 계획을 재사용 할 수있는 실행 컨텍스트.
  • 우리가 추구하는 효율성을 창출하기 위해 DB 엔진이 쿼리하는 프로 시저 캐시.

"임시 명령문"이라고하는 웹 페이지에서 SQL 문이 발행되면 엔진은 요청을 처리 할 기존 실행 계획을 찾습니다. 이 텍스트는 사용자가 제출 한 텍스트이므로 유효한 경우 수집, 파싱, 컴파일 및 실행됩니다. 현재 쿼리 비용은 0입니다. 쿼리 비용은 DB 엔진이 알고리즘을 사용하여 캐시에서 제거 할 실행 계획을 결정하는 데 사용됩니다.

임시 쿼리는 기본적으로 원래 쿼리 비용 값 0을받습니다. 다른 사용자 프로세스 (또는 동일한 프로세스)에 의해 정확히 동일한 임시 쿼리 텍스트를 후속 실행하면 현재 쿼리 비용이 원래 컴파일 비용으로 재설정됩니다. 애드혹 쿼리 컴파일 비용이 0이므로 재사용 가능성에 적합하지 않습니다. 분명히 0은 가장 낮은 값의 정수이지만 왜 제거됩니까?

메모리 압력이 발생하고 자주 사용하는 사이트가있는 경우 DB 엔진은 정리 알고리즘을 사용하여 프로 시저 캐시에서 사용중인 메모리를 회수 할 수있는 방법을 결정합니다. 현재 쿼리 비용을 사용하여 제거 할 계획을 결정합니다. 짐작할 수 있듯이 비용이 0 인 계획은 캐시에서 가장 먼저 제거됩니다. 0은 본질적으로 "이 계획에 대한 현재 사용자 나 참조가 없음"을 의미하기 때문입니다.

  • 참고 : 특별 실행 계획-현재 비용은 계획의 원래 컴파일 비용에 따라 각 사용자 프로세스에 따라 증가합니다. 그러나 계획의 최대 비용은 원래 컴파일 비용보다 클 수 없습니다 (임시 쿼리의 경우 ... 제로). 따라서이 값은 "증가"됩니다. 제로 (zero)는 본질적으로 가장 낮은 비용 계획을 유지한다는 의미입니다.

따라서 메모리 압박이 발생하면 이러한 계획이 먼저 제거 될 가능성이 높습니다.

따라서 "필요한 것 이외의"메모리가 많은 서버를 구축 한 경우 작업량이 처리하기에 "충분한"메모리 만있는 사용량이 많은 서버만큼 자주이 문제가 발생하지 않을 수 있습니다. (아직 알고리즘은 아니지만 서버 메모리 용량과 활용률은 다소 주관적 / 상대적입니다.)

이제 하나 이상의 요점에 대해 사실 틀린 경우 수정 될 수 있습니다.

마지막으로 저자는 다음과 같이 썼습니다.

"이제 문 수준 최적화 기능을 갖추고 있으므로 응용 프로그램에서 나오는 매개 변수가있는 쿼리는 저장 프로 시저에 포함 된 쿼리와 동일한 실행 계획을 활용할 수 있습니다."

필자는 저자가 "임시 작업량 최적화"옵션을 언급하고 있다고 생각합니다.

그렇다면이 옵션을 사용하면 전체 쿼리 계획을 즉시 프로 시저 캐시로 보내지 않아도되는 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

최고,
헨리


4
나는 당신의 게시물의 여러 단락을주의 깊게 두세 번 읽었으며, 여전히 당신이 어떤 생각을 전달하려고하는지 전혀 모른다. 어떤 경우에는 문장의 끝에 문장이 말하기 시작한 것과 정확히 반대의 말을하는 것처럼 보입니다. 이 제출물을주의 깊게 교정하고 편집해야합니다.
Pieter Geerkens

Pieter 피드백에 감사드립니다. 이 경우 요점을보다 명확하게하기 위해 문장을 짧게해야 할 수도 있습니다. 원래 생각과 반대되는 모습을 보이는 예를 제공해 줄 수 있습니까? 매우 감사.
Henry

아니요, Ad Hoc 워크로드에 대한 최적화를 의미하는 것이 아니라 명령문 수준의 최적화를 의미했습니다. 예를 들어 SQL Server 2000에서는 저장 프로 시저가 전체적으로 컴파일되므로 앱에서 프로 시저의 항목과 일치하는 자체 임시 쿼리에 대한 계획을 재사용 할 방법이 없었습니다. 나는 Pieter에 동의한다고 말할 것입니다-당신이 말하는 많은 것들이 따르기가 어렵습니다. "저는 Microsoft가 제공 한 기능이 없기 때문에 저장 프로 시저를 사용하기위한 지침의 필요성을 줄여줍니다." 불필요하게 복잡하고 이해하기에는 너무 많은 구문 분석이 필요합니다. 이모.
Aaron Bertrand

1
"ad hoc"sql에 대한 혐오감은 sql이 어떻게 실행간에 변경되는지에 대한 아이디어를 기반으로하는 것 같습니다.
b_levitt

0

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 only"규칙이 있으면 대부분의 SQL에서 CRUD가되는 많은 양의 중복성이 생깁니다. 모든 매개 변수를 바인드하고 모든 매개 변수를 proc 서명에 나열합니다. 다른 프로젝트의 다른 파일에있는 경우) 해당 간단한 매개 변수를 열에 매핑합니다. 이것은 매우 분리 된 개발 경험을 만듭니다.

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 만 사용합니다.

https://weblogs.asp.net/fbouma/38178

https://stackoverflow.com/a/15277/852208

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