메모리 액세스에 최적화된 테이블에 대한 쿼리 처리 가이드A Guide to Query Processing for Memory-Optimized Tables

이 항목 적용 대상: 예SQL Server예Azure SQL 데이터베이스없습니다Azure SQL 데이터 웨어하우스 없습니다 병렬 데이터 웨어하우스THIS TOPIC APPLIES TO: yesSQL ServeryesAzure SQL DatabasenoAzure SQL Data Warehouse noParallel Data Warehouse

메모리 내 OLTP는 SQL ServerSQL Server에서 메모리 최적화 테이블과 고유하게 컴파일된 저장 프로시저를 도입합니다.In-Memory OLTP introduces memory-optimized tables and natively compiled stored procedures in SQL ServerSQL Server. 이 문서에서는 메모리 최적화 테이블 및 고유하게 컴파일된 저장 프로시저 모두의 쿼리 처리에 대한 개요를 제공합니다.This article gives an overview of query processing for both memory-optimized tables and natively compiled stored procedures.

이 문서에서는 다음 내용을 포함하여 메모리 최적화 테이블에 대한 쿼리를 컴파일하고 실행하는 방법에 대해 설명합니다.The document explains how queries on memory-optimized tables are compiled and executed, including:

  • 디스크 기반 테이블에 대한 SQL ServerSQL Server 의 쿼리 처리 파이프라인입니다.The query processing pipeline in SQL ServerSQL Server for disk-based tables.

  • 쿼리 최적화: 메모리 최적화 테이블에 대한 통계의 역할 및 잘못된 쿼리 계획의 문제 해결을 위한 지침Query optimization; the role of statistics on memory-optimized tables as well as guidelines for troubleshooting bad query plans.

  • 메모리 최적화 테이블에 액세스하기 위한 해석된 Transact-SQLTransact-SQL 사용입니다.The use of interpreted Transact-SQLTransact-SQL to access memory-optimized tables.

  • 메모리 최적화 테이블 액세스를 위한 쿼리 최적화 관련 고려 사항Considerations about query optimization for memory-optimized table access.

  • 고유하게 컴파일된 저장 프로시저 컴파일 및 처리Natively compiled stored procedure compilation and processing.

  • 최적화 프로그램에서 비용 예측을 위해 사용되는 통계Statistics that are used for cost estimation by the optimizer.

  • 잘못된 쿼리 계획을 수정하는 방법Ways to fix bad query plans.

예제 쿼리Example Query

다음 예는 이 문서에서 설명하는 쿼리 처리 개념을 설명하기 위해 사용됩니다.The following example will be used to illustrate the query processing concepts discussed in this article.

여기에서는 두 가지 테이블인 Customer 및 Order 테이블을 살펴봅니다.We consider two tables, Customer and Order. 다음 Transact-SQLTransact-SQL 스크립트에는 기존의 디스크 기반 형태로 이러한 두 테이블과 관련 인덱스에 대한 정의가 포함됩니다.The following Transact-SQLTransact-SQL script contains the definitions for these two tables and associated indexes, in their (traditional) disk-based form:

CREATE TABLE dbo.[Customer] (  
  CustomerID nchar (5) NOT NULL PRIMARY KEY,  
  ContactName nvarchar (30) NOT NULL   
)  
GO  

CREATE TABLE dbo.[Order] (  
  OrderID int NOT NULL PRIMARY KEY,  
  CustomerID nchar (5) NOT NULL,  
  OrderDate date NOT NULL  
)  
GO  
CREATE INDEX IX_CustomerID ON dbo.[Order](CustomerID)  
GO  
CREATE INDEX IX_OrderDate ON dbo.[Order](OrderDate)  
GO  

이 문서에 표시된 쿼리 계획을 생성하기 위해 두 테이블에는 Northwind 샘플 데이터베이스의 샘플 데이터가 입력되었습니다. 이 데이터베이스는 SQL Server 2000의 Northwind 및 pubs 샘플 데이터베이스에서 다운로드할 수 있습니다.For constructing the query plans shown in this article, the two tables were populated with sample data from the Northwind sample database, which you can download from Northwind and pubs Sample Databases for SQL Server 2000.

다음 쿼리를 살펴보십시오. 이 쿼리는 Customer 및 Order 테이블을 조인하고 주문 ID와 연관된 고객 정보를 반환합니다.Consider the following query, which joins the tables Customer and Order and returns the ID of the order and the associated customer information:

SELECT o.OrderID, c.* FROM dbo.[Customer] c INNER JOIN dbo.[Order] o ON c.CustomerID = o.CustomerID  

SQL Server Management StudioSQL Server Management Studio 에서 표시되는 예상 실행 계획은 다음과 같습니다.The estimated execution plan as displayed by SQL Server Management StudioSQL Server Management Studio is as follows

디스크 기반 테이블의 조인을 위한 쿼리 계획.Query plan for join of disk-based tables.
디스크 기반 테이블 조인을 위한 쿼리 계획.Query plan for join of disk-based tables.

이 쿼리 계획 정보:About this query plan:

  • Customer 테이블의 행은 기본 데이터 구조이며 전체 테이블 데이터를 포함하는 클러스터형 인덱스로부터 검색됩니다.The rows from the Customer table are retrieved from the clustered index, which is the primary data structure and has the full table data.

  • Order 테이블의 데이터는 CustomerID 열에 있는 비클러스터형 인덱스를 사용해서 검색됩니다.Data from the Order table is retrieved using the non-clustered index on the CustomerID column. 이 인덱스에는 조인에 사용되는 CustomerID 열과 사용자에게 반환되는 기본 키 열인 OrderID가 모두 포함됩니다.This index contains both the CustomerID column, which is used for the join, and the primary key column OrderID, which is returned to the user. Order 테이블에서 추가 열을 반환하려면 Order 테이블에 대한 클러스터형 인덱스에서 조회가 필요합니다.Returning additional columns from the Order table would require lookups in the clustered index for the Order table.

  • 논리 연산자 Inner Join 은 물리 연산자 Merge Join에 의해 구현됩니다.The logical operator Inner Join is implemented by the physical operator Merge Join. 다른 물리적 조인 유형은 Nested LoopsHash Join입니다.The other physical join types are Nested Loops and Hash Join. Merge Join 연산자는 두 인덱스가 모두 조인 열 CustomerID에 정렬되어 있다는 사실을 활용합니다.The Merge Join operator takes advantage of the fact that both indexes are sorted on the join column CustomerID.

    이제 이 쿼리를 약간 변형하여 OrderID만 아니라 Order 테이블의 모든 행을 반환합니다.Consider a slight variation on this query, which returns all rows from the Order table, not only OrderID:

SELECT o.*, c.* FROM dbo.[Customer] c INNER JOIN dbo.[Order] o ON c.CustomerID = o.CustomerID  

이 쿼리의 예상 계획은 다음과 같습니다.The estimated plan for this query is:

디스크 기반 테이블의 해시 조인을 위한 쿼리 계획.Query plan for a hash join of disk-based tables.
디스크 기반 테이블의 해시 조인을 위한 쿼리 계획.Query plan for a hash join of disk-based tables.

이 쿼리에서 Order 테이블의 행은 클러스터형 인덱스를 사용해서 검색됩니다.In this query, rows from the Order table are retrieved using the clustered index. Hash Match 물리 연산자는 이제 Inner Join에 사용됩니다.The Hash Match physical operator is now used for the Inner Join. Order에 대한 클러스터형 인덱스가 CustomerID로 정렬되지 않았으므로 Merge Join 을 위해 sort 연산자가 필요하지만, 이는 성능에 영향을 줄 수 있습니다.The clustered index on Order is not sorted on CustomerID, and so a Merge Join would require a sort operator, which would affect performance. 이전 예제에서 Merge Join 연산자의 비용(46%)에 대비해서 Hash Match 연산자의 상대적 비용(75%)에 주의하세요.Note the relative cost of the Hash Match operator (75%) compared with the cost of the Merge Join operator in the previous example (46%). 이전 예에서 최적화 프로그램에는 Hash Match 연산자도 고려되었겠지만 Merge Join 연산자가 더 나은 성능을 제공하는 것으로 결정되었습니다.The optimizer would have considered the Hash Match operator also in the previous example, but concluded that the Merge Join operator gave better performance.

SQL ServerSQL Server 디스크 기반 테이블에 대한 쿼리 처리 Query Processing for Disk-Based Tables

다음 다이어그램에서는 임시 쿼리를 위한 SQL ServerSQL Server 의 쿼리 처리 흐름을 간단히 보여줍니다.The following diagram outlines the query processing flow in SQL ServerSQL Server for ad hoc queries:

SQL Server 쿼리 처리 파이프라인.SQL Server query processing pipeline.
SQL Server 쿼리 처리 파이프라인.SQL Server query processing pipeline.

시나리오 내용:In this scenario:

  1. 사용자가 쿼리를 실행합니다.The user issues a query.

  2. 파서 및 algebrizer가 사용자가 제출한 Transact-SQLTransact-SQL 텍스트 기반의 논리 연산자를 사용해서 쿼리 트리를 생성합니다.The parser and algebrizer construct a query tree with logical operators based on the Transact-SQLTransact-SQL text submitted by the user.

  3. 최적화 프로그램이 물리 연산자가 포함된 최적화된 쿼리 계획을 만듭니다(예: 중첩 루프 조인).The optimizer creates an optimized query plan containing physical operators (for example, nested-loops join). 최적화 후 계획은 계획 캐시에 저장될 수 있습니다.After optimization, the plan may be stored in the plan cache. 계획 캐시에 이미 이 쿼리에 대한 계획이 포함된 경우 이 단계는 무시됩니다.This step is bypassed if the plan cache already contains a plan for this query.

  4. 쿼리 실행 엔진이 쿼리 계획 해석을 처리합니다.The query execution engine processes an interpretation of the query plan.

  5. 각 index seek, index scan 및 table scan 연산자에 대해 실행 엔진이 Access Methods의 해당 인덱스 및 테이블 구조로부터 행을 요청합니다.For each index seek, index scan, and table scan operator, the execution engine requests rows from the respective index and table structures from Access Methods.

  6. Access Methods는 버퍼 풀에 있는 인덱스 및 데이터 페이지로부터 행을 검색하고 필요에 따라 디스크에서 버퍼 풀로 페이지를 로드합니다.Access Methods retrieves the rows from the index and data pages in the buffer pool and loads pages from disk into the buffer pool as needed.

    첫 번째 예제 쿼리에서 실행 엔진은 Access Methods에서 Customer의 클러스터형 인덱스 및 Order의 비클러스터형 인덱스에 있는 행을 요청합니다.For the first example query, the execution engine requests rows in the clustered index on Customer and the non-clustered index on Order from Access Methods. Access Methods는 B-트리 인덱스 구조를 통과하여 요청된 행을 검색합니다.Access Methods traverses the B-tree index structures to retrieve the requested rows. 이 경우에는 계획이 전체 인덱스 검색을 호출하므로 모든 행이 검색됩니다.In this case all rows are retrieved as the plan calls for full index scans.

메모리 액세스에 최적화된 테이블에 대한 해석된 Transact-SQLTransact-SQL 액세스Interpreted Transact-SQLTransact-SQL Access to Memory-Optimized Tables

Transact-SQLTransact-SQL 임시 일괄 처리 및 저장 프로시저는 해석된 Transact-SQLTransact-SQL이라고도 부릅니다. ad hoc batches and stored procedures are also referred to as interpreted Transact-SQLTransact-SQL. 해석되었다는 용어는 쿼리 실행 엔진에서 쿼리 계획의 각 연산자에 대해 쿼리 계획이 해석되었음을 의미합니다.Interpreted refers to the fact that the query plan is interpreted by the query execution engine for each operator in the query plan. 실행 엔진은 연산자 및 해당 매개 변수를 읽고 작업을 수행합니다.The execution engine reads the operator and its parameters and performs the operation.

해석된 Transact-SQLTransact-SQL을 사용하면 메모리 최적화 테이블 및 디스크 기반 테이블에 모두 액세스할 수 있습니다.Interpreted Transact-SQLTransact-SQL can be used to access both memory-optimized and disk-based tables. 다음 그림에서는 메모리 최적화 테이블에 대한 해석된 Transact-SQLTransact-SQL 액세스를 위한 쿼리 처리를 보여 줍니다.The following figure illustrates query processing for interpreted Transact-SQLTransact-SQL access to memory-optimized tables:

해석된 tsql을 위한 쿼리 처리 파이프라인.Query processing pipeline for interpreted tsql.
메모리 최적화 테이블에 대한 해석된 Transact-SQL 액세스를 위한 쿼리 처리 파이프라인Query processing pipeline for interpreted Transact-SQL access to memory-optimized tables.

그림에 표시된 것처럼 쿼리 처리 파이프라인은 대부분 변경되지 않은 상태로 유지됩니다.As illustrated by the figure, the query processing pipeline remains mostly unchanged:

  • 파서 및 algebrizer가 쿼리 트리를 생성합니다.The parser and algebrizer construct the query tree.

  • 최적화 프로그램이 실행 계획을 만듭니다.The optimizer creates the execution plan.

  • 쿼리 실행 엔진이 실행 계획을 해석합니다.The query execution engine interprets the execution plan.

    기존의 쿼리 처리 파이프라인(그림 2)과 기본적으로 다른 점은 메모리 최적화 테이블의 행이 Access Methods를 사용하여 버퍼 풀에서 검색되지 않는다는 것입니다.The main difference with the traditional query processing pipeline (figure 2) is that rows for memory-optimized tables are not retrieved from the buffer pool using Access Methods. 대신 메모리 내 OLTP 엔진을 통해 메모리 내 데이터 구조에서 행이 검색됩니다.Instead, rows are retrieved from the in-memory data structures through the In-Memory OLTP engine. 데이터 구조의 차이점으로 인해 다음 예에 표시된 것처럼 경우에 따라 최적화 프로그램이 서로 다른 계획을 선택하게 됩니다.Differences in data structures cause the optimizer to pick different plans in some cases, as illustrated by the following example.

    다음 Transact-SQLTransact-SQL 스크립트에는 해시 인덱스를 사용하는 Order 및 Customer 테이블의 메모리 최적화 버전이 포함됩니다.The following Transact-SQLTransact-SQL script contains memory-optimized versions of the Order and Customer tables, using hash indexes:

CREATE TABLE dbo.[Customer] (  
  CustomerID nchar (5) NOT NULL PRIMARY KEY NONCLUSTERED,  
  ContactName nvarchar (30) NOT NULL   
) WITH (MEMORY_OPTIMIZED=ON)  
GO  

CREATE TABLE dbo.[Order] (  
  OrderID int NOT NULL PRIMARY KEY NONCLUSTERED,  
  CustomerID nchar (5) NOT NULL INDEX IX_CustomerID HASH(CustomerID) WITH (BUCKET_COUNT=100000),  
  OrderDate date NOT NULL INDEX IX_OrderDate HASH(OrderDate) WITH (BUCKET_COUNT=100000)  
) WITH (MEMORY_OPTIMIZED=ON)  
GO  

다음과 같이 메모리 최적화 테이블에서 동일한 쿼리를 실행한다고 가정해보세요.Consider the same query executed on memory-optimized tables:

SELECT o.OrderID, c.* FROM dbo.[Customer] c INNER JOIN dbo.[Order] o ON c.CustomerID = o.CustomerID  

예상 계획은 다음과 같습니다.The estimated plan is as follows:

메모리 최적화 테이블의 조인을 위한 쿼리 계획.Query plan for join of memory optimized tables.
메모리 최적화 테이블의 조인을 위한 쿼리 계획Query plan for join of memory-optimized tables.

디스크 기반 테이블에서 동일한 쿼리를 계획에 사용해서 다음과 같은 차이점을 관찰합니다(그림 1).Observe the following differences with the plan for the same query on disk-based tables (figure 1):

  • 이 계획에는 Customer 테이블에 대해 클러스터형 인덱스 검색이 아닌 테이블 검색이 포함됩니다.This plan contains a table scan rather than a clustered index scan for the table Customer:

    • 테이블 정의에는 클러스터형 인덱스가 포함되지 않습니다.The definition of the table does not contain a clustered index.

    • 클러스터형 인덱스는 메모리 최적화 테이블에서 지원되지 않습니다.Clustered indexes are not supported with memory-optimized tables. 대신 메모리 최적화 모든 테이블에 적어도 하나 이상의 비클러스터형 인덱스가 있어야 하고 메모리 최적화 테이블의 모든 인덱스는 인덱스에 행을 저장하거나 클러스터형 인덱스를 참조할 필요 없이 테이블의 모든 열에 효율적으로 액세스할 수 있습니다.Instead, every memory-optimized table must have at least one nonclustered index and all indexes on memory-optimized tables can efficiently access all columns in the table without having to store them in the index or refer to a clustered index.

  • 이 계획에는 Hash Match 이 아니라 Merge Join가 포함됩니다.This plan contains a Hash Match rather than a Merge Join. Order 및 Customer 테이블에 모두 있는 인덱스는 해시 인덱스이므로 정렬되지 않습니다.The indexes on both the Order and the Customer table are hash indexes, and are thus not ordered. Merge Join 을 사용하기 위해서는 성능 저하가 발생하는 sort 연산자가 필요합니다.A Merge Join would require sort operators that would decrease performance.

고유하게 컴파일된 저장 프로시저Natively Compiled Stored Procedures

고유하게 컴파일된 저장 프로시저는 쿼리 실행 엔진에서 해석되지 않고 기계어 코드로 컴파일된 Transact-SQLTransact-SQL 저장 프로시저입니다.Natively compiled stored procedures are Transact-SQLTransact-SQL stored procedures compiled to machine code, rather than interpreted by the query execution engine. 다음 스크립트는 예제 쿼리(예제 쿼리 섹션 참조)를 실행하는 기본적으로 컴파일되는 저장 프로시저를 만듭니다.The following script creates a natively compiled stored procedure that runs the example query (from the Example Query section).

CREATE PROCEDURE usp_SampleJoin  
WITH NATIVE_COMPILATION, SCHEMABINDING, EXECUTE AS OWNER  
AS BEGIN ATOMIC WITH   
(  TRANSACTION ISOLATION LEVEL = SNAPSHOT,  
  LANGUAGE = 'english')  

  SELECT o.OrderID, c.CustomerID, c.ContactName   
FROM dbo.[Order] o INNER JOIN dbo.[Customer] c   
  ON c.CustomerID = o.CustomerID  

END  

고유하게 컴파일된 저장 프로시저는 프로시저를 만들 때 컴파일되지만 해석된 저장 프로시저는 처음 실행할 때 컴파일됩니다.Natively compiled stored procedures are compiled at create time, whereas interpreted stored procedures are compiled at first execution time. 컴파일 중 특히 구문 분석과 대수 부분은 생성 시에 발생합니다.(A portion of the compilation, particularly parsing and algebrization, take place at create. 하지만 해석된 저장 프로시저의 경우 처음 실행할 때 쿼리 계획의 최적화가 발생합니다. 다시 컴파일 논리도 비슷합니다.However, for interpreted stored procedures, optimization of the query plans takes place at first execution.) The recompilation logic is similar. 고유하게 컴파일된 저장 프로시저는 서버를 다시 시작한 경우 프로시저를 처음 실행할 때 다시 컴파일됩니다.Natively compiled stored procedures are recompiled on first execution of the procedure if the server is restarted. 해석된 저장 프로시저는 계획 캐시에 계획이 더 이상 없는 경우에 다시 컴파일됩니다.Interpreted stored procedures are recompiled if the plan is no longer in the plan cache. 다음 표에서는 고유하게 컴파일된 저장 프로시저와 해석된 저장 프로시저 모두의 컴파일 및 다시 컴파일 사례를 요약해서 보여줍니다.The following table summarizes compilation and recompilation cases for both natively compiled and interpreted stored procedures:

네이티브 컴파일Natively compiled 메모리 액세스에 최적화된 테이블에 대한 해석된Interpreted
초기 컴파일Initial compilation 생성 시At create time. 처음 실행 시At first execution.
자동 다시 컴파일Automatic recompilation 데이터베이스나 서버를 다시 시작한 후 프로시저를 처음 실행할 때Upon first execution of the procedure after a database or server restart. 서버를 다시 시작할 때On server restart. 또는 일반적으로 스키마 또는 통계 변경이나 메모리 압력에 따라 계획 캐시에서 계획이 제거될 때Or, eviction from the plan cache, usually based on schema or stats changes, or memory pressure.
수동 다시 컴파일Manual recompilation sp_recompile을 사용합니다.Use sp_recompile. sp_recompile을 사용합니다.Use sp_recompile. 캐시에서 계획을 수동으로 제거할 수 있습니다(예: DBCC FREEPROCCACHE 사용).You can manually evict the plan from the cache, for example through DBCC FREEPROCCACHE. 또한 WITH RECOMPILE로 저장 프로시저를 만들면 실행할 때마다 저장 프로시저가 다시 컴파일됩니다.You can also create the stored procedure WITH RECOMPILE and the stored procedure will be recompiled at every execution.

컴파일 및 쿼리 처리Compilation and Query Processing

다음 다이어그램은 고유하게 컴파일된 저장 프로시저의 컴파일 프로세스를 보여줍니다.The following diagram illustrates the compilation process for natively compiled stored procedures:

저장 프로시저의 네이티브 컴파일.Native compilation of stored procedures.
저장 프로시저의 고유 컴파일Native compilation of stored procedures.

프로세스에 대한 설명은 다음과 같습니다.The process is described as,

  1. 사용자가 에 대해 CREATE PROCEDURE SQL ServerSQL Server문을 실행합니다.The user issues a CREATE PROCEDURE statement to SQL ServerSQL Server.

  2. 파서 및 algebrizer가 프로시저에 대한 처리 흐름과 저장 프로시저에 있는 Transact-SQLTransact-SQL 쿼리에 대한 쿼리 트리를 만듭니다.The parser and algebrizer create the processing flow for the procedure, as well as query trees for the Transact-SQLTransact-SQL queries in the stored procedure.

  3. 최적화 프로그램이 저장 프로시저에 있는 모든 쿼리에 대한 최적화된 쿼리 실행 계획을 만듭니다.The optimizer creates optimized query execution plans for all the queries in the stored procedure.

  4. 메모리 내 OLTP 컴파일러가 포함된 최적화된 쿼리 계획에 처리 흐름을 사용해서 저장 프로시저 실행을 위한 기계어 코드가 포함된 DLL을 생성합니다.The In-Memory OLTP compiler takes the processing flow with the embedded optimized query plans and generates a DLL that contains the machine code for executing the stored procedure.

  5. 생성된 DLL이 메모리에 로드됩니다.The generated DLL is loaded into memory.

    고유하게 컴파일된 저장 프로시저의 호출은 DLL의 함수 호출과 같습니다.Invocation of a natively compiled stored procedure translates to calling a function in the DLL.

    고유하게 컴파일된 저장 프로시저의 실행.Execution of natively compiled stored procedures.
    고유하게 컴파일된 저장 프로시저의 실행Execution of natively compiled stored procedures.

    고유하게 컴파일된 저장 프로시저의 호출에 대한 설명은 다음과 같습니다.Invocation of a natively compiled stored procedure is described as follows:

  6. 사용자가 EXECusp_myproc 문을 실행합니다.The user issues an EXECusp_myproc statement.

  7. 파서가 이름 및 저장 프로시저 매개 변수를 추출합니다.The parser extracts the name and stored procedure parameters.

    sp_prep_exec등을 사용해서 문이 준비되었으면 파서가 실행 시에 프로시저 이름과 매개 변수를 추출할 필요가 없습니다.If the statement was prepared, for example using sp_prep_exec, the parser does not need to extract the procedure name and parameters at execution time.

  8. 메모리 내 OLTP 런타임이 저장 프로시저에 대한 DLL 진입점을 찾습니다.The In-Memory OLTP runtime locates the DLL entry point for the stored procedure.

  9. DLL의 기계어 코드가 실행되고 결과가 클라이언트에 반환됩니다.The machine code in the DLL is executed and the results of are returned to the client.

    매개 변수 스니핑Parameter sniffing

    생성 시에 컴파일되는 고유하게 컴파일된 저장 프로시저와 달리 해석된 Transact-SQLTransact-SQL 저장 프로시저는 첫 실행 시 컴파일됩니다.Interpreted Transact-SQLTransact-SQL stored procedures are compiled at first execution, in contrast to natively compiled stored procedures, which are compiled at create time. 호출 시에 해석된 저장 프로시저가 컴파일될 때 이 호출을 위해 제공된 매개 변수의 값은 최적화 프로그램이 실행 계획을 생성할 때 사용됩니다.When interpreted stored procedures are compiled at invocation, the values of the parameters supplied for this invocation are used by the optimizer when generating the execution plan. 이렇게 컴파일 중 매개 변수의 사용을 매개 변수 스니핑이라고 부릅니다.This use of parameters during compilation is called parameter sniffing.

    고유하게 컴파일된 저장 프로시저를 컴파일하는 데에는 매개 변수 스니핑이 사용되지 않습니다.Parameter sniffing is not used for compiling natively compiled stored procedures. 이 저장 프로시저에 대한 모든 매개 변수는 UNKNOWN 값을 갖는 것으로 간주됩니다.All parameters to the stored procedure are considered to have UNKNOWN values. 해석된 저장 프로시저처럼 고유하게 컴파일된 저장 프로시저도 OPTIMIZE FOR 힌트를 지원합니다.Like interpreted stored procedures, natively compiled stored procedures also support the OPTIMIZE FOR hint. 자세한 내용은 쿼리 힌트(Transact-SQL)를 참조하세요.For more information, see Query Hints (Transact-SQL).

고유하게 컴파일된 저장 프로시저에 대한 쿼리 실행 검색Retrieving a Query Execution Plan for Natively Compiled Stored Procedures

고유하게 컴파일된 저장 프로시저에 대한 쿼리 실행 계획은 에서 예상 실행 계획 Management StudioManagement Studio을 사용하거나 Transact-SQLTransact-SQL에서 SHOWPLAN_XML 옵션을 사용하여 검색할 수 있습니다.The query execution plan for a natively compiled stored procedure can be retrieved using Estimated Execution Plan in Management StudioManagement Studio, or using the SHOWPLAN_XML option in Transact-SQLTransact-SQL. 예를 들어For example:

SET SHOWPLAN_XML ON  
GO  
EXEC dbo.usp_myproc  
GO  
SET SHOWPLAN_XML OFF  
GO  

쿼리 최적화 프로그램에서 생성되는 실행 계획은 트리의 노드 및 리프에 대한 쿼리 연산자가 포함된 트리로 구성됩니다.The execution plan generated by the query optimizer consists of a tree with query operators on the nodes and leaves of the tree. 트리 구조에 따라 연산자 사이의 상호 작용(한 연산자에서 다른 연산자로의 행 흐름)이 결정됩니다.The structure of the tree determines the interaction (the flow of rows from one operator to another) between the operators. SQL Server Management StudioSQL Server Management Studio의 그래픽 보기에서 흐름은 오른쪽에서 왼쪽의 방향으로 진행됩니다.In the graphical view of SQL Server Management StudioSQL Server Management Studio, the flow is from right to left. 예를 들어, 그림 1의 쿼리 계획에는 merge join 연산자에 행을 제공하는 2개의 index scan 연산자가 포함됩니다.For example, the query plan in figure 1 contains two index scan operators, which supplies rows to a merge join operator. merge join 연산자는 select 연산자에 행을 제공합니다.The merge join operator supplies rows to a select operator. select 연산자가 마지막으로 클라이언트에 행을 반환합니다.The select operator, finally, returns the rows to the client.

고유하게 컴파일된 저장 프로시저의 쿼리 연산자Query Operators in Natively Compiled Stored Procedures

다음 표에서는 고유하게 컴파일 저장 프로시저 내부에서 지원되는 쿼리 연산자를 요약해서 보여줍니다.The following table summarizes the query operators supported inside natively compiled stored procedures:

연산자Operator 예제 쿼리Sample query 참고Notes
SELECTSELECT SELECT OrderID FROM dbo.[Order]
INSERTINSERT INSERT dbo.Customer VALUES ('abc', 'def')
UPDATEUPDATE UPDATE dbo.Customer SET ContactName='ghi' WHERE CustomerID='abc'
DELETEDELETE DELETE dbo.Customer WHERE CustomerID='abc'
Compute ScalarCompute Scalar SELECT OrderID+1 FROM dbo.[Order] 이 연산자는 내장 함수 및 형식 변환에 모두 사용됩니다.This operator is used both for intrinsic functions and type conversions. 고유하게 컴파일된 저장 프로시저 내에서 모든 함수 및 형식 변환이 지원되는 것은 아닙니다.Not all functions and type conversions are supported inside natively compiled stored procedures.
중첩 루프 조인Nested Loops Join SELECT o.OrderID, c.CustomerID FROM dbo.[Order] o INNER JOIN dbo.[Customer] c Nested Loops는 고유하게 컴파일된 저장 프로시저에서 지원되는 유일한 조인 연산자입니다.Nested Loops is the only join operator supported in natively compiled stored procedures. 해석된 Transact-SQLTransact-SQL 로 실행된 것과 동일한 쿼리에 대한 계획에 해시 또는 병합 조인이 포함되더라도 조인을 포함하는 모든 계획에는 Nested Loops 연산자가 사용됩니다.All plans that contain joins will use the Nested Loops operator, even if the plan for same query executed as interpreted Transact-SQLTransact-SQL contains a hash or merge join.
정렬Sort SELECT ContactName FROM dbo.Customer ORDER BY ContactName
위쪽Top SELECT TOP 10 ContactName FROM dbo.Customer
Top-sortTop-sort SELECT TOP 10 ContactName FROM dbo.Customer ORDER BY ContactName TOP 식(반환할 행의 수)은 8,000행을 초과할 수 없습니다.The TOP expression (the number of rows to be returned) cannot exceed 8,000 rows. 쿼리에 조인 및 집계 연산자도 있을 경우 이보다 적습니다.Fewer if there are also join and aggregation operators in the query. 일반적으로 조인과 집계는 기본 테이블의 행 개수와 비교할 때 정렬될 행 수를 줄입니다.Joins and aggregation do typically reduce the number of rows to be sorted, compared with the row count of the base tables.
Stream AggregateStream Aggregate SELECT count(CustomerID) FROM dbo.Customer Hash Match 연산자는 집계가 지원되지 않습니다.Note that the Hash Match operator is not supported for aggregation. 따라서 해석된 Transact-SQLTransact-SQL 의 동일 쿼리에 대한 계획에 Hash Match 연산자가 사용되더라도 고유하게 컴파일된 저장 프로시저의 모든 집계에는 Stream Aggregate 연산자가 사용됩니다.Therefore, all aggregation in natively compiled stored procedures uses the Stream Aggregate operator, even if the plan for the same query in interpreted Transact-SQLTransact-SQL uses the Hash Match operator.

열 통계 및 조인Column Statistics and Joins

SQL ServerSQL Server 는 index scan 및 index seek와 같은 특정 작업의 비용을 예측할 수 있도록 인덱스 키 열에 값 통계를 유지 관리합니다. maintains statistics on values in index key columns to help estimate the cost of certain operations, such as index scan and index seeks. (인덱스가 아닌 키 열을 사용자가 명시적으로 만들거나 쿼리 최적화 프로그램에서 조건자가 있는 쿼리에 대한 응답으로 만드는 경우 SQL ServerSQL Server에서 그에 대한 통계도 만듭니다.) 비용 예측의 기본 메트릭은 단일 연산자에서 처리되는 행 수입니다.( SQL ServerSQL Server also creates statistics on non-index key columns if you explicitly create them or if the query optimizer creates them in response to a query with a predicate.) The main metric in cost estimation is the number of rows processed by a single operator. 디스크 기반 테이블의 경우에는 특정 연산자에서 액세스되는 페이지 수가 비용 예측에서 중요한 요소입니다.Note that for disk-based tables, the number of pages accessed by a particular operator is significant in cost estimation. 하지만 메모리 최적화 테이블의 경우에는 페이지 수가 항상 0이므로 중요하지 않습니다. 이 문서에서는 행 수에 대해 중점적으로 설명합니다.However, as page count is not important for memory-optimized tables (it is always zero), this discussion focuses on row count. 예측은 계획에서 index seek 및 scan 연산자로 시작해서 조인 연산자와 같은 다른 연산자를 포함하도록 확장됩니다.The estimation starts with the index seek and scan operators in the plan, and is then extended to include the other operators, like the join operator. 조인 연산자에서 처리될 것으로 예상되는 행 수는 기본 index, seek 및 scan 연산자의 예측을 기반으로 결정됩니다.The estimated number of rows to be processed by a join operator is based on the estimation for the underlying index, seek, and scan operators. 메모리 최적화 테이블에 대한 해석된 Transact-SQLTransact-SQL 액세스의 경우 실제 실행 계획을 관찰하면 계획에서 연산자에 대한 예측 및 실제 행 수 간의 차이를 볼 수 있습니다.For interpreted Transact-SQLTransact-SQL access to memory-optimized tables, you can observe the actual execution plan to see the difference between the estimated and actual row counts for the operators in the plan.

그림 1 예의 경우,For the example in figure 1,

  • Customer에 대한 클러스터형 인덱스 검색의 예측은 91이고, 실제도 91입니다.The clustered index scan on Customer has estimated 91; actual 91.

  • CustomerID에 대한 비클러스터형 인덱스 검색의 예측은 830이고 실제도 830입니다.The nonclustered index scan on CustomerID has estimated 830; actual 830.

  • Merge Join 연산자의 예측은 815이고 실제는 830입니다.The Merge Join operator has estimated 815; actual 830.

    인덱스 검색에 대한 예측은 정확합니다.The estimates for the index scans are accurate. SQL ServerSQL Server 는 디스크 기반 테이블에 대한 행 수를 유지 관리합니다. maintains the row count for disk-based tables. 전체 테이블 및 인덱스 검색에 대한 예측은 항상 정확합니다.Estimates for full table and index scans are always accurate. 조인에 대한 예측도 상당히 정확한 편입니다.The estimate for the join is fairly accurate, too.

    이러한 예측이 변경될 경우 각각의 계획 대안에 대한 비용 고려도 함께 변경됩니다.If these estimates change, the cost considerations for different plan alternatives change as well. 예를 들어, 조인의 어느 한쪽에 대한 예측 행 수가 1개 또는 몇 개 정도뿐이라면 중첩 루프 조인을 사용하는 것이 비용이 낮습니다.For example, if one of the sides of the join has an estimated row count of 1 or just a few rows, using a nested loops joins is less expensive.

    다음은 쿼리에 대한 계획입니다.The following is the plan for the query:

SELECT o.OrderID, c.* FROM dbo.[Customer] c INNER JOIN dbo.[Order] o ON c.CustomerID = o.CustomerID  

Customer 테이블에서 행을 하나만 남겨두고 모두 삭제한 후에는 다음과 같이 됩니다.After deleting all rows but one in the table Customer:

열 통계 및 조인Column statistics and joins.

이 쿼리 계획에 대한 설명은 다음과 같습니다.Regarding this query plan:

  • Hash Match가 Nested Loops 물리적 조인 연산자로 바뀌었습니다.The Hash Match has been replaced with a Nested Loops physical join operator.

  • IX_CustomerID에 대한 전체 인덱스 검색은 index seek로 바뀌었습니다.The full index scan on IX_CustomerID has been replaced with an index seek. 그 결과 전체 인덱스 검색에 필요한 830개 행 대신 5개 행이 검색되었습니다.This resulted in scanning 5 rows, instead of the 830 rows required for the full index scan.

참고 항목See Also

메모리 액세스에 최적화된 테이블Memory-Optimized Tables