메모리 내 OLTP에 대한 예제 데이터베이스Sample Database for In-Memory OLTP

개요Overview

이 샘플에서는 메모리 내 OLTP 기능을 소개합니다.This sample showcases the In-Memory OLTP feature. 이 예제는 메모리 액세스에 최적화된 테이블과 고유하게 컴파일된 저장 프로시저를 보여 주며, 메모리 내 OLTP의 성능 이점을 설명하는 데 사용할 수 있습니다.It shows memory-optimized tables and natively-compiled stored procedures, and can be used to demonstrate performance benefits of In-Memory OLTP.

참고

SQL Server 2014SQL Server 2014에 대한 이 항목을 보려면 [메모리 내 OLTP를 보여주기 위한 AdventureWorks 확장](https://msdn.microsoft.com/library/dn511655(v=sql.120).aspx)을 참조하세요.To view this topic for SQL Server 2014SQL Server 2014, see Extensions to AdventureWorks to Demonstrate In-Memory OLTP.

또한 AdventureWorks 데이터베이스의 5개 테이블을 메모리 액세스에 최적화된 테이블로 마이그레이션하며, 판매 주문 처리용 데모 작업을 포함하고 있습니다.The sample migrates 5 tables in the AdventureWorks database to memory-optimized, and it includes a demo workload for sales order processing. 이 데모 작업을 사용하여 서버에서 메모리 내 OLTP를 사용하는 경우의 성능 이점을 확인할 수 있습니다.You can use this demo workload to see the performance benefit of using In-Memory OLTP on your server.

샘플에 대한 설명에서는 테이블을 메모리 내 OLTP로 마이그레이션할 때 메모리 액세스에 최적화된 테이블에 아직 지원되지 않는 기능을 보완하기 위해 수행한 절충 작업에 대해 설명합니다.In the description of the sample we discuss the tradeoffs that were made in migrating the tables to In-Memory OLTP to account for the features that are not (yet) supported for memory-optimized tables.

이 샘플에 대한 설명서는 다음과 같이 구성되어 있습니다.The documentation of this sample is structured as follows:

Prerequisites Prerequisites

Installing the In-Memory OLTP sample based on AdventureWorks Installing the In-Memory OLTP sample based on AdventureWorks

다음 단계를 수행하여 예제를 설치합니다.Follow these steps to install the sample:

  1. https://www.microsoft.com/download/details.aspx?id=49502 에서 로컬 폴더(예: 'c:\temp')에 AdventureWorks2016CTP3.bak 및 SQLServer2016CTP3Samples.zip을 다운로드합니다.Download AdventureWorks2016CTP3.bak and SQLServer2016CTP3Samples.zip from: https://www.microsoft.com/download/details.aspx?id=49502 to a local folder, for example 'c:\temp'.

  2. Transact-SQLTransact-SQL 또는 SQL Server Management StudioSQL Server Management Studio를 사용하여 데이터베이스 백업을 복원합니다.Restore the database backup using Transact-SQLTransact-SQL or SQL Server Management StudioSQL Server Management Studio:

    1. 다음 예와 같은 데이터 파일의 대상 폴더와 파일 이름을 식별합니다.Identify the target folder and filename for the data file, for example

      'h:\DATA\AdventureWorks2016CTP3_Data.mdf''h:\DATA\AdventureWorks2016CTP3_Data.mdf'

    2. 다음 예와 같은 로그 파일의 대상 폴더와 파일 이름을 식별합니다.Identify the target folder and filename for the log file, for example

      'i:\DATA\AdventureWorks2016CTP3_log.ldf''i:\DATA\AdventureWorks2016CTP3_log.ldf'

      1. 로그 파일은 데이터 파일과 다른 드라이브에 배치되어야 합니다. 최상의 성능을 위해 SSD 또는 PCIe 저장소와 같이 대기 시간이 적은 드라이브가 이상적입니다.The log file should be placed on a different drive than the data file, ideally a low latency drive such as an SSD or PCIe storage, for maximum performance.

      T-SQL 스크립트 예:Example T-SQL script:

    RESTORE DATABASE [AdventureWorks2016CTP3]   
      FROM DISK = N'C:\temp\AdventureWorks2016CTP3.bak'   
        WITH FILE = 1,    
      MOVE N'AdventureWorks2016_Data' TO N'h:\DATA\AdventureWorks2016CTP3_Data.mdf',    
      MOVE N'AdventureWorks2016_Log' TO N'i:\DATA\AdventureWorks2016CTP3_log.ldf',  
      MOVE N'AdventureWorks2016CTP3_mod' TO N'h:\data\AdventureWorks2016CTP3_mod'  
     GO  
    
  3. 샘플 스크립트와 워크로드를 보려면 로컬 폴더에 SQLServer2016CTP3Samples.zip 파일의 압축을 풉니다.To view the sample scripts and workload, unpack the file SQLServer2016CTP3Samples.zip to a local folder. 워크로드 실행에 대한 지침은 In-Memory OLTP\readme.txt 파일을 참조하세요.Consult the file In-Memory OLTP\readme.txt for instructions on running the workload.

Description of the sample tables and procedures Description of the sample tables and procedures

예제에서는 AdventureWorks의 기존 테이블을 기반으로 제품과 판매 주문에 대한 새 테이블을 만듭니다.The sample creates new tables for products and sales orders, based on existing tables in AdventureWorks. 새 테이블의 스키마는 아래에 설명된 대로 몇 가지 차이점이 있지만 기존 테이블과 유사합니다.The schema of the new tables is similar to the existing tables, with a few differences, as explained below.

새로운 메모리 액세스에 최적화된 테이블에는 ‘_inmem’ 접미사가 붙습니다.The new memory-optimized tables carry the suffix ‘_inmem’. 예제에는 '_ondisk' 접미사가 붙는 해당 테이블도 포함되어 있습니다. 이러한 테이블을 사용하여 시스템에서 메모리 액세스에 최적화된 테이블과 디스크 기반 테이블의 성능을 일 대 일로 비교할 수 있습니다.The sample also includes corresponding tables carrying the suffix ‘_ondisk’ – these tables can be used to make a one-to-one comparison between the performance of memory-optimized tables and disk-based tables on your system..

성능 비교를 위한 작업에서 사용되는 메모리 액세스에 최적화된 테이블은 완전한 내구성을 가지며 모두 기록됩니다.Note that the memory-optimized tables used in the workload for performance comparison are fully durable and fully logged. 이러한 테이블은 성능 이점을 얻기 위해 내구성이나 안정성을 희생하지 않습니다.They do not sacrifice durability or reliability to attain the performance gain.

이 예제의 대상 작업은 판매 주문 처리이므로 제품 및 할인에 대한 정보도 고려하며,The target workload for this sample is sales order processing, where we consider also information about products and discounts. 이를 위해 SalesOrderHeader, SalesOrderDetail, Product, SpecialOffer 및 SpecialOfferProduct 테이블이 사용됩니다.To this end, the table SalesOrderHeader, SalesOrderDetail, Product, SpecialOffer, and SpecialOfferProduct.

두 가지 새로운 저장 프로시저 Sales.usp_InsertSalesOrder_inmem 및 Sales.usp_UpdateSalesOrderShipInfo_inmem은 판매 주문을 삽입하고 지정된 판매 주문의 배송 정보를 업데이트하는 데 사용됩니다.Two new stored procedures, Sales.usp_InsertSalesOrder_inmem and Sales.usp_UpdateSalesOrderShipInfo_inmem, are used to insert sales orders and to update the shipping information of a given sales order.

새로운 스키마 'Demo'에는 데모 작업을 실행하기 위한 도우미 테이블과 저장 프로시저가 포함되어 있습니다.The new schema 'Demo' contains helper tables and stored procedures to execute a demo workload.

구체적으로 설명하자면 메모리 내 OLTP 샘플에서는 다음 개체를 AdventureWorks에 추가합니다.Concretely, the In-Memory OLTP sample adds the following objects to AdventureWorks:

예제에서 추가된 테이블Tables added by the sample

새로운 테이블The New Tables

Sales.SalesOrderHeader_inmemSales.SalesOrderHeader_inmem

  • 판매 주문에 대한 머리글 정보.Header information about sales orders. 각 판매 주문에 해당하는 행이 이 테이블에 하나씩 있습니다.Each sales order has one row in this table.

    Sales.SalesOrderDetail_inmemSales.SalesOrderDetail_inmem

  • 판매 주문에 대한 세부 정보.Details of sales orders. 판매 주문의 각 품목에 해당하는 행이 이 테이블에 하나씩 있습니다.Each line item of a sales order has one row in this table.

    Sales.SpecialOffer_inmemSales.SpecialOffer_inmem

  • 각 특별 행사와 관련된 할인율을 비롯한 특별 행사에 대한 정보.Information about special offers, including the discount percentage associated with each special offer.

    Sales.SpecialOfferProduct_inmemSales.SpecialOfferProduct_inmem

  • 특별 행상 및 제품 간의 참조 테이블.Reference table between special offers and products. 각 특별 행사에는 제품이 0개 이상 있을 수 있으며 각 제품은 0개 이상의 특별 행사에 포함될 수 있습니다.Each special offer can feature zero or more products, and each product can be featured in zero or more special offers.

    Production.Product_inmemProduction.Product_inmem

  • 가격을 비롯한 제품에 대한 정보Information about products, including their list price.

    Demo.DemoSalesOrderDetailSeedDemo.DemoSalesOrderDetailSeed

  • 예제 판매 주문을 생성하기 위해 데모 작업에서 사용됩니다.Used in the demo workload to construct sample sales orders.

    테이블의 디스크 기반 변형:Disk-based variations of the tables:

  • Sales.SalesOrderHeader_ondiskSales.SalesOrderHeader_ondisk

  • Sales.SalesOrderDetail_ondiskSales.SalesOrderDetail_ondisk

  • Sales.SpecialOffer_ondiskSales.SpecialOffer_ondisk

  • Sales.SpecialOfferProduct_ondiskSales.SpecialOfferProduct_ondisk

  • Production.Product_ondiskProduction.Product_ondisk

원래 디스크 기반 테이블과 새로운 메모리 액세스에 최적화된 테이블 간의 차이점Differences between original disk-based and the and new memory-optimized tables

대부분의 경우 이 예제에서 도입한 새로운 테이블은 원래 테이블과 동일한 열 및 동일한 데이터 형식을 사용하지만For the most part, the new tables introduced by this sample use the same columns and the same data types as the original tables. 몇 가지 차이점이 있습니다.However, there are a few differences. 이러한 차이점과 변경에 대한 이유는 다음과 같습니다.We list the differences below, along with a rationale for the changes.

Sales.SalesOrderHeader_inmemSales.SalesOrderHeader_inmem

  • 기본 제약 조건 은 메모리 액세스에 최적화된 테이블에 지원되며 대부분의 기본 제약 조건은 있는 그대로 마이그레이션되었습니다.Default constraints are supported for memory-optimized tables, and most default constraints we migrated as is. 그러나 원래 테이블 Sales.SalesOrderHeader에는 OrderDate 및 ModifiedDate 열에 대한 현재 날짜를 검색하는 두 가지 기본 제약 조건이 포함되어 있습니다.However, the original table Sales.SalesOrderHeader contains two default constraints that retrieve the current date, for the columns OrderDate and ModifiedDate. 동시 작업과 처리량이 많은 주문 처리 작업에서 전역 리소스는 경합 지점이 될 수 있습니다.In a high throughput order processing workload with a lot of concurrency, any global resource can become a point of contention. 시스템 시간은 이러한 전역 리소스이며 판매 주문을 삽입하는 메모리 내 OLTP 작업을 실행할 때 병목 현상을 발생시킬 수 있다는 사실이 관찰되었습니다. 이는 판매 주문 정보뿐만 아니라 판매 주문 머리글의 여러 열에 대해 시스템 시간을 검색해야 하는 경우 특히 해당하는 사실입니다.System time is such a global resource, and we have observed that it can become a bottleneck when running an In-Memory OLTP workload that inserts sales orders, in particular if the system time needs to be retrieved for multiple columns in the sales order header, as well as the sales order details. 이러한 문제는 이 예제에서 삽입되는 각 판매 주문에 대해 시스템 시간을 한 번만 검색하고 Sales.usp_InsertSalesOrder_inmem 저장 프로시저에서 SalesOrderHeader_inmem 및 SalesOrderDetail_inmem의 datetime 열에 이 값을 사용하여 해결되었습니다.The problem is addressed in this sample by retrieving the system time only once for each sales order that is inserted, and use that value for the datetime columns in SalesOrderHeader_inmem and SalesOrderDetail_inmem, in the stored procedure Sales.usp_InsertSalesOrder_inmem.

  • 별칭 UDT - 원래 테이블은 PurchaseOrderNumber 및 AccountNumber 열에 대해 두 가지 별칭 UDT(사용자 정의 데이터 형식)인 dbo.OrderNumber 및 dbo.AccountNumber를 각각 사용합니다.Alias UDTs - The original table uses two alias user-defined data types (UDTs) dbo.OrderNumber and dbo.AccountNumber, for the columns PurchaseOrderNumber and AccountNumber, respectively. SQL Server 2016SQL Server 2016 에서는 메모리 액세스에 최적화된 테이블에 대한 별칭 UDT를 지원하지 않으므로 새 테이블은 시스템 데이터 형식 nvarchar(25) 및 nvarchar(15)을 각각 사용합니다. does not support alias UDT for memory-optimized tables, thus the new tables use system data types nvarchar(25) and nvarchar(15), respectively.

  • 인덱스 키의 Null 허용 열 - 원래 테이블에서 SalesPersonID 열은 Null을 허용하지만 새 테이블에서 이 열은 Null을 허용하지 않으며 값(-1)을 사용하는 기본 제약 조건을 갖습니다.Nullable columns in index keys - In the original table, the column SalesPersonID is nullable, while in the new tables the column is not nullable and has a default constraint with value (-1). 이는 메모리 액세스에 최적화된 테이블에 대한 인덱스의 경우 인덱스 키에 Null 허용 열이 있을 수 없기 때문이며, 이 경우 -1은 NULL의 대리 값입니다.This is because indexes on memory-optimized tables cannot have nullable columns in the index key; -1 is a surrogate for NULL in this case.

  • 계산 열 - SQL Server 2016SQL Server 2016 에서는 메모리 액세스에 최적화된 테이블에서 계산 열을 지원하지 않기 때문에 계산 열 SalesOrderNumber 및 TotalDue가 생략되었습니다.Computed columns - The computed columns SalesOrderNumber and TotalDue are omitted, as SQL Server 2016SQL Server 2016 does not support computed columns in memory-optimized tables. 새로운 뷰 Sales.vSalesOrderHeader_extended_inmem이 SalesOrderNumber 및 TotalDue 열을 반영하므로The new view Sales.vSalesOrderHeader_extended_inmem reflects the columns SalesOrderNumber and TotalDue. 이러한 열이 필요한 경우 이 뷰를 사용할 수 있습니다.Therefore, you can use this view if these columns are needed.

    • Applies to: SQL Server vNextSQL Server vNext CTP 1.1.Applies to: SQL Server vNextSQL Server vNext CTP 1.1.
      SQL Server vNextSQL Server vNext CTP 1.1부터 계산 열이 메모리 액세스에 최적화된 테이블 및 인덱스에서 지원됩니다.Beginning with SQL Server vNextSQL Server vNext CTP 1.1, computed columns are supported in memory-optimized tables and indexes.
  • 외래 키 제약 조건SQL Server 2016SQL Server 2016에서 메모리 액세스에 최적화된 테이블에 지원되지만 참조된 테이블도 메모리 액세스에 최적화된 경우에만 지원됩니다.Foreign key constraints are supported for memory-optimized tables in SQL Server 2016SQL Server 2016, but only if the referenced tables are also memory-optimized. 메모리 액세스에 최적화된 테이블로 마이그레이션된 테이블을 참조하는 외래 키는 마이그레이션된 테이블에 유지되지만 다른 외래 키는 생략됩니다.Foreign keys that reference tables that are also migrated to memory-optimized are kept in the migrated tables, while other foreign keys are omitted. 또한 SalesOrderHeader_inmem은 예제 작업에서 핫 테이블이며, FOREIGN KEY 제약 조건을 지정하려면 모든 DML 작업에 대한 추가 처리가 필요합니다. 이는 이러한 제약 조건에서 참조된 다른 모든 테이블에서 조회가 필요하기 때문입니다.In addition, SalesOrderHeader_inmem is a hot table in the example workload, and foreign keys constraints require additional processing for all DML operations, as it requires lookups in all the other tables referenced in these constraints. 따라서 응용 프로그램에서 Sales.SalesOrderHeader_inmem 테이블에 대한 참조 무결성을 보장하며 참조 무결성은 행이 삽입될 때 확인되지 않는다고 가정합니다.Therefore, the assumption is that the app ensures referential integrity for the Sales.SalesOrderHeader_inmem table, and referential integrity is not validated when rows are inserted.

  • Rowguid - rowguid 열이 생략되었습니다.Rowguid - The rowguid column is omitted. uniqueidentifier가 메모리 액세스에 최적화된 테이블에 지원되지만 ROWGUIDCOL 옵션은 SQL Server 2016SQL Server 2016에서 지원되지 않습니다.While uniqueidentifier is support for memory-optimized tables, the option ROWGUIDCOL is not supported in SQL Server 2016SQL Server 2016. 이러한 종류의 열은 일반적으로 병합 복제나 filestream 열이 있는 테이블에 사용됩니다.Columns of this kind are typically used for either merge replication or tables that have filestream columns. 이 예제에는 둘 다 포함되어 있지 않습니다.This sample includes neither.

    Sales.SalesOrderDetailSales.SalesOrderDetail

  • 기본 제약 조건 - SalesOrderHeader와 유사하게 시스템 날짜/시간을 필요로 하는 기본 제약 조건은 마이그레이션되지 않으며, 대신 판매 주문을 삽입하는 저장 프로시저가 첫 번째 삽입 시 현재 시스템 날짜/시간을 삽입하는 작업을 처리합니다.Default constraints – similar to SalesOrderHeader, the default constraint requiring the system date/time is not migrated, instead the stored procedure inserting sales orders takes care of inserting the current system date/time on first insert.

  • 계산 열 - 계산 열이 SQL Server 2016SQL Server 2016에서 메모리 액세스에 최적화된 테이블에 지원되지 않기 때문에 계산 열 LineTotal이 마이그레이션되지 않았습니다.Computed columns – the computed column LineTotal was not migrated as computed columns are not supported with memory-optimized tables in SQL Server 2016SQL Server 2016. 이 열에 액세스하려면 Sales.vSalesOrderDetail_extended_inmem 뷰를 사용합니다.To access this column use the view Sales.vSalesOrderDetail_extended_inmem.

  • Rowguid - rowguid 열이 생략되었습니다.Rowguid - The rowguid column is omitted. 자세한 내용은 SalesOrderHeader 테이블에 대한 설명을 참조하세요.For details see the description for the table SalesOrderHeader.

    Production.ProductProduction.Product

  • 별칭 UDT - 원래 테이블은 시스템 데이터 형식 bit와 동일한 사용자 정의 데이터 형식 dbo.Flag를 사용합니다.Alias UDTs – the original table uses the user-defined data type dbo.Flag, which is equivalent to the system data type bit. 마이그레이션된 테이블은 bit 데이터 형식을 대신 사용합니다.The migrated table uses the bit data type instead.

  • Rowguid - rowguid 열이 생략되었습니다.Rowguid - The rowguid column is omitted. 자세한 내용은 SalesOrderHeader 테이블에 대한 설명을 참조하세요.For details see the description for the table SalesOrderHeader.

    Sales.SpecialOfferSales.SpecialOffer

  • Rowguid - rowguid 열이 생략되었습니다.Rowguid - The rowguid column is omitted. 자세한 내용은 SalesOrderHeader 테이블에 대한 설명을 참조하세요.For details see the description for the table SalesOrderHeader.

    Sales.SpecialOfferProductSales.SpecialOfferProduct

  • Rowguid - rowguid 열이 생략되었습니다.Rowguid - The rowguid column is omitted. 자세한 내용은 SalesOrderHeader 테이블에 대한 설명을 참조하세요.For details see the description for the table SalesOrderHeader.

메모리 액세스에 최적화된 테이블의 인덱스에 대한 고려 사항Considerations for indexes on memory-optimized tables

메모리 액세스에 최적화된 테이블의 기준 인덱스는 포인트 조회(같음 조건자의 인덱스 검색), 범위 검색(같지 않음 조건자의 인덱스 검색), 전체 인덱스 검색 및 정렬된 검색을 지원하는 비클러스터형 인덱스입니다.The baseline index for memory-optimized tables is the NONCLUSTERED index, which supports point lookups (index seek on equality predicate), range scans (index seek in inequality predicate), full index scans, and ordered scans. 또한 비클러스터형 인덱스는 인덱스 키의 선행 열에 대한 검색을 지원합니다.In addition, NONCLUSTERED indexes support searching on leading columns of the index key. 메모리 액세스에 최적화된 비클러스터형 인덱스는 역방향 검색만 제외하고 디스크 기반 비클러스터형 인덱스가 지원하는 모든 작업을 지원합니다.In fact memory-optimized NONCLUSTERED indexes support all the operations supported by disk-based NONCLUSTERED indexes, with the only exception being backward scans. 따라서 비클러스터형 인덱스의 사용은 인덱스에 선택할 수 있는 안전한 방법입니다.Therefore, using NONCLUSTERED indexes is a safe choice for your indexes.

해시 인덱스를 사용하여 작업을 추가로 최적화할 수 있습니다.HASH indexes are can be used to further optimize the workload. 해시 인덱스는 포인트 조회와 행 삽입에 대해 특히 최적화됩니다.They are particularly optimized for point lookups and row inserts. 그러나 해시 인덱스가 범위 검색, 정렬된 검색 또는 선행 인덱스 키 열에 대한 검색을 지원하지 않는다는 점을 고려해야 합니다.However, one must consider that they do not support range scans, ordered scans, or search on leading index key columns. 따라서 이러한 인덱스를 사용할 때는 주의를 기울여야 합니다.Therefore, care needs to be taken when using these indexes. 또한 만들 때 bucket_count를 지정해야 합니다.In addition, it is necessary to specify the bucket_count at create time. bucket_count는 일반적으로 인덱스 키 값의 수와 그 두 배 사이에서 설정되어야 하지만 더 많이 추정해도 대개 문제가 되지 않습니다.It should usually be set at between one and two times the number of index key values, but overestimating is usually not a problem.

인덱스 지침올바른 bucket_count 선택지침에 대한 자세한 내용은 온라인 설명서를 참조하세요.See Books Online for more details about index guidelines and guidelines for choosing the right bucket_count.

마이그레이션된 테이블의 인덱스는 데모 판매 주문 처리 작업에 맞게 조정되었습니다.The indexes on the migrated tables have been tuned for the demo sales order processing workload. 작업은 Sales.SalesOrderHeader_inmem 및 Sales.SalesOrderDetail_inmem 테이블의 삽입 및 포인트 조회에 의존하며 Production.Product_inmem 및 Sales.SpecialOffer_inmem 테이블에서 기본 키 열에 대한 포인트 조회에도 의존합니다.The workload relies on inserts and point lookups in the tables Sales.SalesOrderHeader_inmem and Sales.SalesOrderDetail_inmem, and it also relies on point lookups on the primary key columns in the tables Production.Product_inmem and Sales.SpecialOffer_inmem.

Sales.SalesOrderHeader_inmem에는 세 가지 인덱스가 있는데 정렬된 검색이나 범위 검색이 작업에 필요하지 않기 때문에 모두 성능상의 이유로 해시 인덱스입니다.Sales.SalesOrderHeader_inmem has three indexes, which are all HASH indexes for performance reasons, and because no ordered or range scans are needed for the workload.

  • (SalesOrderID)의 해시 인덱스: 예상된 판매 주문 수가 1,000만 개이기 때문에 bucket_count의 크기는 1,000만(1,600만으로 반올림됨)으로 설정됩니다.HASH index on (SalesOrderID): bucket_count is sized at 10 million (rounded up to 16 million), because the expected number of sales orders is 10 million

  • (SalesPersonID)의 해시 인덱스: bucket_count가 100만입니다.HASH index on (SalesPersonID): bucket_count is 1 million. 제공된 데이터 집합에는 많은 영업 사원이 없지만, 이후 성장을 허용할 수 있으며 bucket_count의 크기가 과도하게 설정된 경우 포인트 조회에 대한 성능 저하가 발생하지 않습니다.The data set provided does not have a lot of sales persons, but this allows for future growth, plus you don’t pay a performance penalty for point lookups if the bucket_count is oversized.

  • (CustomerID)의 해시 인덱스: bucket_count가 100만입니다.HASH index on (CustomerID): bucket_count is 1 million. 제공된 데이터 집합에는 많은 고객이 없지만 이후 성장을 허용할 수 있습니다.The data set provided does not have a lot of customers, but this allows for future growth.

    Sales.SalesOrderDetail_inmem에는 세 가지 인덱스가 있는데 정렬된 검색이나 범위 검색이 작업에 필요하지 않기 때문에 모두 성능상의 이유로 해시 인덱스입니다.Sales.SalesOrderDetail_inmem has three indexes, which are all HASH indexes for performance reasons, and because no ordered or range scans are needed for the workload.

  • (SalesOrderID, SalesOrderDetailID)의 해시 인덱스: 기본 키 인덱스이며 (SalesOrderID, SalesOrderDetailID)의 조회가 빈번하지는 않지만 키에 해시 인덱스를 사용하면 행 삽입이 빨라집니다.HASH index on (SalesOrderID, SalesOrderDetailID): this is the primary key index, and even though lookups on (SalesOrderID, SalesOrderDetailID) will be infrequent, using a hash index for the key speeds up row inserts. bucket_count 크기 5,000만(6,700만으로 반올림됨): 예상 판매 주문 수가 1,000만 개이므로 이 크기에는 주문당 평균 5개 품목이 있을 수 있습니다.The bucket_count is sized at 50 million (rounded up to 67 million): the expected number of sales orders is 10 million, and this is sized to have an average of 5 items per order

  • (SalesOrderID)의 해시 인덱스: 단일 주문에 해당하는 모든 품목을 찾으려고 합니다.HASH index on (SalesOrderID): lookups by sales order are frequent: you will want to find all the line items corresponding to a single order. 예상된 판매 주문 수가 1,000만 개이기 때문에 bucket_count의 크기는 1,000만(1,600만으로 반올림됨)으로 설정됩니다.bucket_count is sized at 10 million (rounded up to 16 million), because the expected number of sales orders is 10 million

  • (ProductID)의 해시 인덱스: bucket_count가 100만입니다.HASH index on (ProductID): bucket_count is 1 million. 제공된 데이터 집합에는 많은 제품이 없지만 이후 성장을 허용할 수 있습니다.The data set provided does not have a lot of product, but this allows for future growth.

    Production.Product_inmem에는 세 가지 인덱스가 있습니다.Production.Product_inmem has three indexes

  • (ProductID)의 해시 인덱스: ProductID의 조회가 데모 작업의 중요 경로에 있으므로 해시 인덱스가 사용됩니다.HASH index on (ProductID): lookups on ProductID are in the critical path for the demo workload, therefore this is a hash index

  • (Name)의 비클러스터형 인덱스: 제품 이름의 정렬된 검색을 허용합니다.NONCLUSTERED index on (Name): this will allow ordered scans of product names

  • (ProductNumber)의 비클러스터형 인덱스: 제품 번호의 정렬된 검색을 허용합니다.NONCLUSTERED index on (ProductNumber): this will allow ordered scans of product numbers

    Sales.SpecialOffer_inmem에는 (SpecialOfferID)의 해시 인덱스가 하나 있습니다. 특별 행사의 포인트 조회는 데모 작업의 중요한 부분에 있습니다.Sales.SpecialOffer_inmem has one HASH index on (SpecialOfferID): point lookups of special offers are in the critical part of the demo workload. bucket_count의 크기는 이후 성장을 허용하기 위해 100만으로 설정됩니다.The bucket_count is sized at 1 million to allow for future growth.

    Sales.SpecialOfferProduct_inmem은 데모 작업에서 참조되지 않으므로 작업을 최적화하기 위해 이 테이블에 해시 인덱스를 사용할 명확한 이유가 없습니다. (SpecialOfferID, ProductID) 및 (ProductID)의 인덱스는 비클러스터형 인덱스입니다.Sales.SpecialOfferProduct_inmem is not referenced in the demo workload, and thus there is no apparent need to use hash indexes on this table to optimize the workload – the indexes on (SpecialOfferID, ProductID) and (ProductID) are NONCLUSTERED.

    위에서 일부 bucket_counts의 크기는 과도하게 설정되었지만 SalesOrderHeader_inmem 및 SalesOrderDetail_inmem의 인덱스에 대한 bucket_counts의 크기는 1,000만 개의 판매 주문에 대해서만 설정되었습니다.Notice that in the above some of the bucket_counts are over-sized, but not the bucket_counts for the indexes on SalesOrderHeader_inmem and SalesOrderDetail_inmem: they are sized for just 10 million sales orders. 이는 사용할 수 있는 메모리가 적은 시스템에 예제를 설치할 수 있도록 하기 위해서입니다. 하지만 이러한 경우에 데모 작업은 메모리 부족으로 실패합니다.This was done to allow installing the sample on systems with low memory availability, although in those cases the demo workload will fail with out-of-memory. 1,000만 개가 훨씬 넘는 판매 주문으로 확장하려는 경우 bucket_counts를 그에 맞게 늘리면 됩니다.If you do want to scale well beyond 10 million sales orders, feel free to increase the bucket counts accordingly.

메모리 사용률에 대한 고려 사항Considerations for memory utilization

데모 작업을 실행하기 전후에 샘플 데이터베이스의 메모리 사용률은 메모리 액세스에 최적화된 테이블의 메모리 사용률섹션에 설명되어 있습니다.Memory utilization in the sample database, both before and after running the demo workload, is discussed in the Section Memory utilization for the memory-optimized tables.

예제에서 추가된 저장 프로시저Stored Procedures added by the sample

판매 주문을 삽입하고 배송 정보를 업데이트하기 위한 두 가지 주요 저장 프로시저는 다음과 같습니다.The two key stored procedures for inserting sales order and updating shipping details are as follows:

  • Sales.usp_InsertSalesOrder_inmemSales.usp_InsertSalesOrder_inmem

    • 데이터베이스에 새로운 판매 주문을 삽입하고 해당 판매 주문의 SalesOrderID를 출력합니다.Inserts a new sales order in the database and outputs the SalesOrderID for that sales order. 입력 매개 변수로 판매 주문 머리글의 정보뿐만 아니라 주문의 품목을 사용합니다.As input parameters it takes details for the sales order header, as well as the line items in the order.

    • 출력 매개 변수:Output parameter:

      • @SalesOrderID int – 방금 삽입된 판매 주문의 SalesOrderID@SalesOrderID int – the SalesOrderID for the sales order that was just inserted
    • 입력 매개 변수(필수):Input parameters (required):

      • @DueDate datetime2@DueDate datetime2

      • @CustomerID int@CustomerID int

      • @BillToAddressID [int]@BillToAddressID [int]

      • @ShipToAddressID [int]@ShipToAddressID [int]

      • @ShipMethodID [int]@ShipMethodID [int]

      • @SalesOrderDetails Sales.SalesOrderDetailType_inmem – 주문의 품목이 포함된 TVP@SalesOrderDetails Sales.SalesOrderDetailType_inmem – TVP that contains the line items of the order

    • 입력 매개 변수(선택적):Input parameters (optional):

      • @Status [tinyint]@Status [tinyint]

      • @OnlineOrderFlag [bit]@OnlineOrderFlag [bit]

      • @PurchaseOrderNumber [nvarchar](25)@PurchaseOrderNumber [nvarchar](25)

      • @AccountNumber [nvarchar](15)@AccountNumber [nvarchar](15)

      • @SalesPersonID [int]@SalesPersonID [int]

      • @TerritoryID [int]@TerritoryID [int]

      • @CreditCardID [int]@CreditCardID [int]

      • @CreditCardApprovalCode [varchar](15)@CreditCardApprovalCode [varchar](15)

      • @CurrencyRateID [int]@CurrencyRateID [int]

      • @Comment nvarchar(128)@Comment nvarchar(128)

  • Sales.usp_UpdateSalesOrderShipInfo_inmemSales.usp_UpdateSalesOrderShipInfo_inmem

    • 지정된 판매 주문에 대한 배송 정보를 업데이트합니다.Update the shipping information for a given sales order. 또한 판매 주문의 모든 품목에 대한 배송 정보도 업데이트합니다.This will also update the shipping information for all line items of the sales order.

    • 이 저장 프로시저는 동일한 주문을 업데이트하는 동시 트랜잭션과의 예기치 않은 잠재적 충돌을 처리하는 재시도 논리가 포함된 고유하게 컴파일된 저장 프로시저 Sales.usp_UpdateSalesOrderShipInfo_native의 래퍼 프로시저입니다.This is a wrapper procedure for the natively compiled stored procedures Sales.usp_UpdateSalesOrderShipInfo_native with retry logic to deal with (unexpected) potential conflicts with concurrent transactions updating the same order. 재시도 논리에 대한 자세한 내용은 여기에서 온라인 설명서 항목을 참조하세요.For more information about retry logic see the Books Online topic here.

  • Sales.usp_UpdateSalesOrderShipInfo_nativeSales.usp_UpdateSalesOrderShipInfo_native

    • 배송 정보 업데이트를 실제로 처리하는 고유하게 컴파일된 저장 프로시저입니다.This is the natively compiled stored procedure that actually processes the update to the shipping information. 래퍼 저장 프로시저 Sales.usp_UpdateSalesOrderShipInfo_inmem에서 호출되기 위한 수단입니다.It is means to be called from the wrapper stored procedure Sales.usp_UpdateSalesOrderShipInfo_inmem. 클라이언트가 오류를 처리하고 재시도 논리를 구현할 수 있는 경우 래퍼 저장 프로시저를 사용하지 않고 이 프로시저를 직접 호출할 수 있습니다.If the client can deal with failures and implements retry logic, you can call this procedure directly, rather than using the wrapper stored procedure.

    다음 저장 프로시저는 데모 작업에 사용됩니다.The following stored procedure is used for the demo workload.

  • Demo.usp_DemoResetDemo.usp_DemoReset

    • SalesOrderHeader 및 SalesOrderDetail 테이블을 비우고 초기값을 다시 설정하여 데모를 다시 설정합니다.Resets the demo by emptying and reseeding the SalesOrderHeader and SalesOrderDetail tables.

    다음 저장 프로시저는 도메인 및 참조 무결성을 보장하면서 메모리 액세스에 최적화된 테이블에서 삽입하고 삭제하는 데 사용됩니다.The following stored procedures are used for inserting in and deleting from memory-optimized tables while guaranteeing domain and referential integrity.

  • Production.usp_InsertProduct_inmemProduction.usp_InsertProduct_inmem

  • Production.usp_DeleteProduct_inmemProduction.usp_DeleteProduct_inmem

  • Sales.usp_InsertSpecialOffer_inmemSales.usp_InsertSpecialOffer_inmem

  • Sales.usp_DeleteSpecialOffer_inmemSales.usp_DeleteSpecialOffer_inmem

  • Sales.usp_InsertSpecialOfferProduct_inmemSales.usp_InsertSpecialOfferProduct_inmem

    마지막으로 다음 저장 프로시저는 도메인 및 참조 무결성을 확인하는 데 사용됩니다.Finally the following stored procedure is used to verify domain and referential integrity.

  1. dbo.usp_ValidateIntegritydbo.usp_ValidateIntegrity

    • 선택적 매개 변수: @object_id – 무결성을 확인할 개체의 ID.Optional parameter: @object_id – ID of the object to validate integrity for

    • 이 프로시저는 확인되어야 하는 무결성 규칙에 대해 dbo.DomainIntegrity, dbo.ReferentialIntegrity 및 dbo.UniqueIntegrity 테이블에 의존합니다. 예제에서는 이러한 테이블을 AdventureWorks 데이터베이스의 원래 테이블에 존재하는 CHECK, FOREIGN KEY 및 UNIQUE 제약 조건을 기반으로 채웁니다.This procedure relies on the tables dbo.DomainIntegrity, dbo.ReferentialIntegrity, and dbo.UniqueIntegrity for the integrity rules that need to be verified – the sample populates these tables based on the check, foreign key, and unique constraints that exist for the original tables in the AdventureWorks database.

    • 이 프로시저는 도우미 프로시저 dbo.usp_GenerateCKCheck, dbo.usp_GenerateFKCheck 및 dbo.GenerateUQCheck에 의존하여 무결성 검사를 수행하는 데 필요한 T-SQL을 생성합니다.It relies on the helper procedures dbo.usp_GenerateCKCheck, dbo.usp_GenerateFKCheck, and dbo.GenerateUQCheck to generate the T-SQL needed for performing the integrity checks.

Performance Measurements using the Demo Workload Performance Measurements using the Demo Workload

Ostress는 Microsoft CSS SQL Server 지원 팀에서 개발한 명령줄 도구입니다.Ostress is a command-line tool that was developed by the Microsoft CSS SQL Server support team. 이 도구는 쿼리를 실행하거나 저장 프로시저를 병렬로 실행하는 데 사용할 수 있습니다.This tool can be used to execute queries or run stored procedures in parallel. 지정된 T-SQL 문을 병렬로 실행할 스레드 수를 구성할 수 있으며 해당 스레드에서 문이 실행될 횟수를 지정할 수 있습니다. ostress는 스레드를 시작하고 모든 스레드에서 문을 병렬로 실행합니다.You can configure the number of threads to run a given T-SQL statement in parallel, and you can specify how many times the statement should be executed on this thread; ostress will spin up the threads and execute the statement on all threads in parallel. 모든 스레드의 실행이 완료된 후 ostress는 모든 스레드의 실행이 완료되는 데 걸린 시간을 보고합니다.After execution finishes for all threads, ostress will report the time taken for all threads to finish execution.

ostress 설치Installing ostress

ostress는 RML 유틸리티의 일부로 설치되며 ostress의 독립 실행형 설치는 없습니다.Ostress is installed as part of the RML Utilities; there is no standalone installation for ostress.

설치 단계:Installation steps:

  1. 다음 페이지에서 RML 유틸리티의 x64 설치 패키지를 다운로드하고 실행합니다. http://blogs.msdn.com/b/psssql/archive/2013/10/29/cumulative-update-2-to-the-rml-utilities-for-microsoft-sql-server-released.aspxDownload and run the x64 installation package for the RML utilities from the following page: http://blogs.msdn.com/b/psssql/archive/2013/10/29/cumulative-update-2-to-the-rml-utilities-for-microsoft-sql-server-released.aspx

  2. 특정 파일이 사용 중이라는 대화 상자가 나타나면 'Continue'를 클릭합니다.If there is a dialog box saying certain files are in use, click ‘Continue’

ostress 실행Running ostress

ostress는 명령줄 프롬프트에서 실행됩니다.Ostress is run from the command-line prompt. RML 유틸리티의 일부로 설치되는 "RML Cmd Prompt"에서 도구를 실행하는 것이 가장 간편합니다.It is most convenient to run the tool from the "RML Cmd Prompt", which is installed as part of the RML Utilities.

RML Cmd Prompt를 열려면 다음 지침을 따르세요.To open the RML Cmd Prompt follow these instructions:

Windows Server 2012 [R2]와 Windows 8 및 8.1에서 Windows 키를 클릭하여 시작 메뉴를 열고 'rml'을 입력합니다.In Windows Server 2012 [R2] and in Windows 8 and 8.1, open the start menu by clicking the Windows key, and type ‘rml’. 검색 결과의 목록에 있는 “RML Cmd Prompt”를 클릭합니다.Click on “RML Cmd Prompt”, which will be in the list of search results.

명령 프롬프트가 RML 유틸리티 설치 폴더에 있는지 확인합니다.Ensure that the command prompt is located in the RML Utilities installation folder.

ostress의 명령줄 옵션은 명령줄 옵션 없이 ostress.exe를 실행하기만 하면 볼 수 있습니다.The command-line options for ostress can be seen when simply running ostress.exe without any command-line options. 이 예제와 함께 ostress를 실행하기 위해 고려할 기본 옵션은 다음과 같습니다.The main options to consider for running ostress with this sample are:

  • -S 연결할 Microsoft SQL Server 인스턴스 이름-S name of Microsoft SQL Server instance to connect to

  • -E Windows 인증을 사용하여 연결(기본값). SQL Server 인증을 사용하는 경우 –U 및 –P 옵션을 사용하여 사용자 이름과 암호를 각각 지정합니다.-E use Windows authentication to connect (default); if you use SQL Server authentication, use the options –U and –P to specify the username and password, respectively

  • -d 데이터베이스의 이름. 이 예의 경우 AdventureWorks2014입니다.-d name of the database, for this example AdventureWorks2014

  • -Q 실행될 T-SQL 문-Q the T-SQL statement to be executed

  • -n 각 입력 파일/쿼리를 처리하는 연결의 수-n number of connections processing each input file/query

  • -r 각 연결에서 각 입력 파일/쿼리를 실행할 반복 횟수-r the number of iterations for each connection to execute each input file/query

데모 작업Demo Workload

데모 작업에서 사용되는 기본 저장 프로시저는 Sales.usp_InsertSalesOrder_inmem/ondisk입니다.The main stored procedure used in the demo workload is Sales.usp_InsertSalesOrder_inmem/ondisk. 아래의 스크립트는 예제 데이터로 TVP(테이블 반환 매개 변수)를 생성하고 프로시저를 호출하여 품목이 5개인 판매 주문을 삽입합니다.The script in the below constructs a table-valued parameter (TVP) with sample data, and calls the procedure to insert a sales order with 5 line items.

ostress 도구는 저장 프로시저 호출을 병렬로 실행하여 판매 주문을 동시에 삽입하는 클라이언트를 시뮬레이트하는 데 사용됩니다.The ostress tool is used to execute the stored procedure calls in parallel, to simulate clients inserting sales orders concurrently.

ostress 실행이 끝날 때마다 Demo.usp_DemoReset을 실행하여 데모를 다시 설정합니다.Reset the demo after each stress run executing Demo.usp_DemoReset. 이 프로시저는 메모리 액세스에 최적화된 테이블의 행을 삭제하고, 디스크 기반 테이블을 자르며, 데이터베이스 검사점을 실행합니다.This procedure deletes the rows in the memory-optimized tables, truncates the disk-based tables, and executes a database checkpoint.

다음 스크립트는 판매 주문 처리 작업을 시뮬레이트하기 위해 동시에 실행됩니다.The following script is executed concurrently to simulate a sales order processing workload:

DECLARE   
      @i int = 0,   
      @od Sales.SalesOrderDetailType_inmem,   
      @SalesOrderID int,   
      @DueDate datetime2 = sysdatetime(),   
      @CustomerID int = rand() * 8000,   
      @BillToAddressID int = rand() * 10000,   
      @ShipToAddressID int = rand() * 10000,   
      @ShipMethodID int = (rand() * 5) + 1;   

INSERT INTO @od   
SELECT OrderQty, ProductID, SpecialOfferID   
FROM Demo.DemoSalesOrderDetailSeed   
WHERE OrderID= cast((rand()*106) + 1 as int);   

WHILE (@i < 20)   
BEGIN;   
      EXEC Sales.usp_InsertSalesOrder_inmem @SalesOrderID OUTPUT, @DueDate, @CustomerID, @BillToAddressID, @ShipToAddressID, @ShipMethodID, @od;   
      SET @i += 1   
END  

이 스크립트를 사용하면 생성된 각 예제 주문이 WHILE 루프에서 실행되는 20개의 저장 프로시저를 통해 20회 삽입됩니다.With this script, each sample order that is constructed is inserted 20 times, through 20 stored procedures executed in a WHILE loop. 루프는 데이터베이스가 예제 주문을 생성하는 데 사용된다는 사실을 보완하는 데 사용됩니다.The loop is used to account for the fact that the database is used to construct the sample order. 일반적인 프로덕션 환경에서는 중간 계층 응용 프로그램이 삽입될 판매 주문을 생성합니다.In typical production environments, the mid-tier application will construct the sales order to be inserted.

위의 스크립트는 판매 주문을 메모리 액세스에 최적화된 테이블에 삽입합니다.The above script inserts sales orders into memory-optimized tables. 판매 주문을 디스크 기반 테이블에 삽입하는 스크립트는 두 ‘_inmem’을 ‘_ondisk’로 바꿔서 파생됩니다.The script to insert sales orders into disk-based tables is derived by replacing the two occurrences of ‘_inmem’ with ‘_ondisk’.

ostress 도구를 사용하여 여러 동시 연결을 통해 스크립트를 실행할 것입니다.We will use the ostress tool to execute the scripts using several concurrent connections. ‘-n’ 매개 변수를 사용하여 연결 수를 제어하고 ‘r’ 매개 변수를 사용하여 스크립트가 각 연결에서 실행되는 횟수를 제어하려고 합니다.We will use the parameter ‘-n’ to control the number of connections, and the parameter ‘r’ to control how many times the script is executed on each connection.

작업 실행Running the Workload

최대 규모로 테스트하기 위해 100개의 연결을 사용하여 1,000만 개의 판매 주문을 삽입합니다.To test at scale we insert 10 million sales orders, using 100 connections. 이 테스트는 일반 서버(예: 물리적 코어 8개, 논리적 코어 16개)와 로그용 기본 SSD 저장소에서 문제 없이 실행됩니다.This test performs reasonably on a modest server (e.g., 8 physical, 16 logical cores), and basic SSD storage for the log. 테스트가 사용자의 하드웨어에서 제대로 실행되지 않는 경우 느리게 실행되는 테스트 문제 해결섹션을 참조하세요. 이 테스트에 대한 스트레스 수준을 줄이려면 '-n' 매개 변수를 변경하여 연결 수를 줄입니다.If the test does not perform well on your hardware, take look at the Section Troubleshooting slow-running tests.If you want to reduce the level of stress for this test, lower the number of connections by changing the parameter ‘-n’. 예를 들어 연결 수를 40으로 줄이려면 ‘-n100’ 매개 변수를 ‘-n40’으로 변경합니다.For example to lower the connection count to 40, change the parameter ‘-n100’ to ‘-n40’.

작업에 대한 성능 측정으로 작업을 실행한 후 ostress.exe에서 보고하는 경과 시간을 사용합니다.As a performance measure for the workload we use the elapsed time as reported by ostress.exe after running the workload.

아래 지침과 측정값에서는 1000만 개의 판매 주문을 삽입하는 작업을 사용합니다.The below instructions and measurements use a workload that inserts 10 million sales orders. 100만 개의 판매 주문을 삽입하는 축소된 워크로드를 실행하는 지침은 SQLServer2016CTP3Samples.zip 보관 파일의 일부인 'In-Memory OLTP\readme.txt'에 있는 지침을 참조하세요.For instructions to run a scaled-down workload inserting 1 million sales orders, see the instructions in 'In-Memory OLTP\readme.txt' that is part of the SQLServer2016CTP3Samples.zip archive.

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

메모리 액세스에 최적화된 테이블에서 작업을 실행하는 것부터 시작합니다.We will start by running the workload on memory-optimized tables. 다음 명령은 각각 5,000회의 반복을 위해 실행되는 100개의 스레드를 엽니다.The following command opens 100 threads, each running for 5,000 iterations. 각 반복에서는 별도의 트랜잭션에서 20개의 판매 주문을 삽입합니다.Each iteration inserts 20 sales orders in separate transactions. 데이터베이스가 삽입될 데이터를 생성하는 데 사용된다는 사실을 보완하기 위해 반복당 20개의 삽입이 있습니다.There are 20 inserts per iteration to compensate for the fact that the database is used to generate the data to be inserted. 이에 따라 총 20 * 5,000 * 100 = 10,000,000개의 판매 주문 삽입이 생성됩니다.This yield a total of 20 * 5,000 * 100 = 10,000,000 sales order inserts.

RML Cmd Prompt를 열고 다음 명령을 실행합니다.Open the RML Cmd Prompt, and execute the following command:

복사 단추를 클릭하여 명령을 복사하고 RML 유틸리티 명령 프롬프트에 붙여 넣습니다.Click the Copy button to copy the command, and paste it into the RML Utilities command prompt.

ostress.exe –n100 –r5000 -S. -E -dAdventureWorks2016CTP3 -q -Q"DECLARE @i int = 0, @od Sales.SalesOrderDetailType_inmem, @SalesOrderID int, @DueDate datetime2 = sysdatetime(), @CustomerID int = rand() * 8000, @BillToAddressID int = rand() * 10000, @ShipToAddressID int = rand() * 10000, @ShipMethodID int = (rand() * 5) + 1; INSERT INTO @od SELECT OrderQty, ProductID, SpecialOfferID FROM Demo.DemoSalesOrderDetailSeed WHERE OrderID= cast((rand()*106) + 1 as int); while (@i < 20) begin; EXEC Sales.usp_InsertSalesOrder_inmem @SalesOrderID OUTPUT, @DueDate, @CustomerID, @BillToAddressID, @ShipToAddressID, @ShipMethodID, @od; set @i += 1 end"  

물리적 코어가 총 8개(논리적 코어 총 16개)인 테스트 서버에서 이 작업은 2분 5초가 소요되었고,On one test server with a total number of 8 physical (16 logical) cores, this took 2 minutes and 5 seconds. 물리적 코어가 24개(논리적 코어 48개)인 두 번째 테스트 서버에서는 1분 0초가 소요되었습니다.On a second test server with 24 physical (48 logical) cores, this took 1 minute and 0 seconds.

작업 관리자 등을 사용하여 작업이 실행되는 동안 CPU 사용률을 관찰합니다.Observe the CPU utilization while the workload is running, for example using task manager. CPU 사용률이 100%에 가깝게 나타나는 것을 확인할 수 있습니다.You will see that CPU utilization is close to 100%. 그렇지 않은 경우에는 로그 IO 병목 상태가 발생한 것입니다. 느리게 실행되는 테스트 문제 해결을 참조하세요.If this is not the case, you have a log IO bottleneck see also Troubleshooting slow-running tests.

디스크 기반 테이블Disk-based tables

다음 명령은 디스크 기반 테이블에서 작업을 실행합니다.The following command will run the workload on disk-based tables. 이 작업은 실행되는 데 시간이 걸릴 수 있는데 그 이유는 대부분 시스템의 래치 경합 때문입니다.Note that this workload may take a while to execute, which is largely due to latch contention in the system. 메모리 액세스에 최적화된 테이블은 래치를 사용하지 않으므로 이 문제의 영향을 받지 않습니다.Memory-optimized table are latch-free and thus do not suffer from this problem.

RML Cmd Prompt를 열고 다음 명령을 실행합니다.Open the RML Cmd Prompt, and execute the following command:

복사 단추를 클릭하여 명령을 복사하고 RML 유틸리티 명령 프롬프트에 붙여 넣습니다.Click the Copy button to copy the command, and paste it into the RML Utilities command prompt.

ostress.exe –n100 –r5000 -S. -E -dAdventureWorks2016CTP3 -q -Q"DECLARE @i int = 0, @od Sales.SalesOrderDetailType_ondisk, @SalesOrderID int, @DueDate datetime2 = sysdatetime(), @CustomerID int = rand() * 8000, @BillToAddressID int = rand() * 10000, @ShipToAddressID int = rand() * 10000, @ShipMethodID int = (rand() * 5) + 1; INSERT INTO @od SELECT OrderQty, ProductID, SpecialOfferID FROM Demo.DemoSalesOrderDetailSeed WHERE OrderID= cast((rand()*106) + 1 as int); while (@i < 20) begin; EXEC Sales.usp_InsertSalesOrder_ondisk @SalesOrderID OUTPUT, @DueDate, @CustomerID, @BillToAddressID, @ShipToAddressID, @ShipMethodID, @od; set @i += 1 end"  

물리적 코어가 총 8개(논리적 코어 총 16개)인 테스트 서버에서 이 작업은 41분 25초가 소요되었고,On one test server with a total number of 8 physical (16 logical) cores, this took 41 minutes and 25 seconds. 물리적 코어가 24개(논리적 코어 48개)인 두 번째 테스트 서버에서는 52분 16초가 소요되었습니다.On a second test server with 24 physical (48 logical) cores, this took 52 minutes and 16 seconds.

이 테스트에서 메모리 액세스에 최적화된 테이블과 디스크 기반 테이블 간 성능 차이의 주요 요인은 디스크 기반 테이블을 사용하는 경우 SQL Server에서 CPU를 완전히 활용할 수 없다는 사실입니다.The main factor in the performance difference between memory-optimized tables and disk-based tables in this test is the fact that when using disk-based tables, SQL Server cannot not fully utilize the CPU. 그 이유는 래치 경합 때문입니다. 즉, 동시 트랜잭션이 동일한 데이터 페이지에 쓰려고 합니다. 래치는 한 번에 한 트랜잭션만 페이지에 쓸 수 있도록 하는 데 사용됩니다.The reason is latch contention: concurrent transactions are attempting to write to the same data page; latches are used to ensure only one transaction at a time can write to a page. 메모리 내 OLTP 엔진은 래치를 사용하지 않으며 데이터 행이 페이지에 구성되지 않습니다.The In-Memory OLTP engine is latch-free, and data rows are not organized in pages. 따라서 동시 트랜잭션이 서로의 삽입을 차단하지 않기 때문에 SQL Server에서 CPU를 완전히 활용할 수 있습니다.Thus, concurrent transactions do not block each other’s inserts, thus enabling SQL Server to fully utilize the CPU.

작업 관리자 등을 사용하여 작업이 실행되는 동안 CPU 사용률을 관찰할 수 있습니다.You can observe the CPU utilization while the workload is running, for example using task manager. 디스크 기반 테이블을 사용하는 경우 CPU 사용률이 100%에 크게 못 미치는 것을 확인할 수 있습니다.You will see with disk-based tables the CPU utilization is far from 100%. 논리적 프로세서가 16개인 테스트 구성에서 사용률은 24% 정도입니다.On a test configuration with 16 logical processors, the utilization would hover around 24%.

필요에 따라 성능 모니터를 사용하여 성능 카운터 ‘\SQL Server:Latches\Latch Waits/sec’를 통해 초당 래치 대기 수를 볼 수 있습니다.Optionally, you can view the number of latch waits per second using Performance Monitor, with the performance counter ‘\SQL Server:Latches\Latch Waits/sec’.

데모 다시 설정Resetting the demo

데모를 다시 설정하려면 RML Cmd Prompt를 열고 다음 명령을 실행합니다.To reset the demo, open the RML Cmd Prompt, and execute the following command:

ostress.exe -S. -E -dAdventureWorks2016CTP3 -Q"EXEC Demo.usp_DemoReset"  

하드웨어에 따라 이 명령이 실행되는 데 몇 분 정도 걸릴 수 있습니다.Depending on the hardware this may take a few minutes to run.

데모 실행이 끝날 때마다 다시 설정하는 것이 좋습니다.We recommend a reset after every demo run. 이 작업이 삽입만 수행하기 때문에 각 실행에서 더 많은 메모리가 사용되므로 메모리 부족을 방지하려면 다시 설정해야 합니다.Because this workload is insert-only, each run will consume more memory, and thus a reset is required to prevent running out of memory. 실행 후 사용되는 메모리 양은 작업 실행 후 메모리 사용률섹션에 설명되어 있습니다.The amount of memory consumed after a run is discussed in Section Memory utilization after running the workload.

Troubleshooting slow-running tests Troubleshooting slow-running tests

테스트 결과는 일반적으로 하드웨어와 테스트 실행에서 사용되는 동시성 수준에 따라 달라집니다.Test results will typically vary with hardware, and also the level of concurrency used in the test run. 결과가 예상과 다른 경우 확인할 몇 가지 사항은 다음과 같습니다.A couple of things to look for if the results are not as expected:

  • 동시 트랜잭션 수: 단일 스레드에서 작업을 실행할 때 메모리 내 OLTP를 사용한 성능 이점은 두 배보다 적을 수 있습니다.Number of concurrent transactions: When running the workload on a single thread, performance gain with In-Memory OLTP will likely be less than 2X. 래치 경합은 동시성 수준이 높은 경우에만 큰 문제가 됩니다.Latch contention is only a big problem if there is a high level of concurrency.

  • SQL Server에서 사용할 수 있는 적은 코어 수: 즉, 동시에 실행되는 트랜잭션이 SQL에서 사용할 수 있는 코어 수만큼만 있을 수 있으므로 시스템에서 동시성 수준이 낮습니다.Low number of cores available to SQL Server: This means there will be a low level of concurrency in the system, as there can only be as many concurrently executing transactions as there are cores available to SQL.

    • 증상: 디스크 기반 테이블에서 작업을 실행할 때 CPU 사용률이 높은 경우 경합이 많다는 의미이며 동시성 부족을 나타냅니다.Symptom: if the CPU utilization is high when running the workload on disk-based tables, this means there is not a lot of contention, pointing to a lack of concurrency.
  • 로그 드라이브의 속도: 로그 드라이브가 시스템의 트랜잭션 처리량 수준을 유지할 수 없는 경우 작업이 로그 IO에서 병목 상태가 됩니다.Speed of the log drive: If the log drive cannot keep up with the level of transaction throughput in the system, the workload becomes bottlenecked on log IO. 메모리 내 OLTP를 사용하는 경우 로깅이 보다 효율적이지만 로그 IO가 병목 상태인 경우 잠재적인 성능 이점이 제한됩니다.Although logging is more efficient with In-Memory OLTP, if log IO is a bottleneck, the potential performance gain is limited.

    • 증상: 메모리 액세스에 최적화된 테이블에서 작업을 실행할 때 CPU 사용률이 100%에 가깝지 않거나 변동이 심한 경우 로그 IO 병목 상태가 있을 수 있습니다.Symptom: if the CPU utilization is not close to 100% or is very spiky when running the workload on memory-optimized tables, it is possible there is a log IO bottleneck. 이는 리소스 모니터를 열고 로그 드라이브의 큐 길이를 살펴보고 확인할 수 있습니다.This can be confirmed by opening Resource Monitor and looking at the queue length for the log drive.

샘플의 메모리 및 디스크 공간 사용률 Memory and Disk Space Utilization in the Sample

아래에서는 예제 데이터베이스의 메모리 및 디스크 공간 사용률 측면에서 기대하는 것에 대해 설명합니다.In the below we describe what to expect in terms of memory and disk space utilization for the sample database. 또한 논리적 코어가 16개인 테스트 서버에서 관찰한 결과도 보여 줍니다.We also show the results we have seen in on a test server with 16 logical cores.

Memory utilization for the memory-optimized tables Memory utilization for the memory-optimized tables

데이터베이스의 전체 사용률Overall utilization of the database

다음 쿼리를 사용하여 시스템에서 메모리 내 OLTP의 총 메모리 사용률을 얻을 수 있습니다.The following query can be used to obtain the total memory utilization for In-Memory OLTP in the system.

SELECT type  
   , name  
, pages_kb/1024 AS pages_MB   
FROM sys.dm_os_memory_clerks WHERE type LIKE '%xtp%'  

데이터베이스를 만든 후의 스냅숏:Snapshot after the database has just been created:

typetype namename pages_MBpages_MB
MEMORYCLERK_XTPMEMORYCLERK_XTP 기본값Default 9494
MEMORYCLERK_XTPMEMORYCLERK_XTP DB_ID_5DB_ID_5 877877
MEMORYCLERK_XTPMEMORYCLERK_XTP 기본값Default 00
MEMORYCLERK_XTPMEMORYCLERK_XTP 기본값Default 00

기본 메모리 클럭은 시스템 차원의 메모리 구조를 포함하고 있으며 비교적 작습니다.The default memory clerks contain system-wide memory structures and are relatively small. 사용자 데이터베이스(이 경우 ID 5인 데이터베이스)의 메모리 클럭은 약 900MB입니다.The memory clerk for the user database, in this case database with ID 5, is about 900MB.

테이블당 메모리 사용률Memory utilization per table

다음 쿼리를 사용하여 개별 테이블과 인덱스의 메모리 사용률을 세부적으로 확인할 수 있습니다.The following query can be used to drill down into the memory utilization of the individual tables and their indexes:

SELECT object_name(t.object_id) AS [Table Name]  
     , memory_allocated_for_table_kb  
 , memory_allocated_for_indexes_kb  
FROM sys.dm_db_xtp_table_memory_stats dms JOIN sys.tables t   
ON dms.object_id=t.object_id  
WHERE t.type='U'  

예제를 처음 설치한 경우 이 쿼리의 결과는 다음과 같습니다.The following shows the results of this query for a fresh installation of the sample:

테이블 이름Table Name memory_allocated_for_table_kbmemory_allocated_for_table_kb memory_allocated_for_indexes_kbmemory_allocated_for_indexes_kb
SpecialOfferProduct_inmemSpecialOfferProduct_inmem 6464 38403840
DemoSalesOrderHeaderSeedDemoSalesOrderHeaderSeed 19841984 55045504
SalesOrderDetail_inmemSalesOrderDetail_inmem 1531615316 663552663552
DemoSalesOrderDetailSeedDemoSalesOrderDetailSeed 6464 1043210432
SpecialOffer_inmemSpecialOffer_inmem 33 81928192
SalesOrderHeader_inmemSalesOrderHeader_inmem 71687168 147456147456
Product_inmemProduct_inmem 124124 1235212352

보시다시피 테이블은 상당히 작습니다. SalesOrderHeader_inmem은 약 7MB이고, SalesOrderDetail_inmem은 약 15MB입니다.As you can see the tables are fairly small: SalesOrderHeader_inmem is about 7MB, and SalesOrderDetail_inmem is about 15MB in size.

여기에서 눈에 띄는 것은 테이블 데이터의 크기와 비교할 때 인덱스에 할당된 메모리의 크기입니다.What is striking here is the size of the memory allocated for indexes, compared to the size of the table data. 이는 예제에서 해시 인덱스의 크기가 더 큰 데이터 크기에 대해 설정되었기 때문입니다.That is because the hash indexes in the sample are pre-sized for a larger data size. 해시 인덱스의 크기는 고정되어 있으므로 테이블의 데이터 크기에 따라 커지지 않습니다.Note that hash indexes have a fixed size, and thus their size will not grow with the size of data in the table.

Memory utilization after running the workload Memory utilization after running the workload

1,000만 개의 판매 주문을 삽입한 후 총 메모리 사용률은 다음과 유사합니다.After insert 10 million sales orders, the all-up memory utilization looks similar to the following:

SELECT type  
, name  
, pages_kb/1024 AS pages_MB   
FROM sys.dm_os_memory_clerks WHERE type LIKE '%xtp%'  
typetype namename pages_MBpages_MB
MEMORYCLERK_XTPMEMORYCLERK_XTP 기본값Default 146146
MEMORYCLERK_XTPMEMORYCLERK_XTP DB_ID_5DB_ID_5 73747374
MEMORYCLERK_XTPMEMORYCLERK_XTP 기본값Default 00
MEMORYCLERK_XTPMEMORYCLERK_XTP 기본값Default 00

보시다시피 SQL Server는 샘플 데이터베이스에서 메모리 액세스에 최적화된 테이블과 인덱스에 8GB보다 조금 작은 크기를 사용하고 있습니다.As you can see, SQL Server is using a bit under 8GB for the memory-optimized tables and indexes in the sample database.

예제를 한 번 실행한 후 테이블당 자세한 메모리 사용률을 살펴보면 다음과 같습니다.Looking at the detailed memory usage per table after one example run:

SELECT object_name(t.object_id) AS [Table Name]  
     , memory_allocated_for_table_kb  
 , memory_allocated_for_indexes_kb  
FROM sys.dm_db_xtp_table_memory_stats dms JOIN sys.tables t   
ON dms.object_id=t.object_id  
WHERE t.type='U'  
테이블 이름Table Name memory_allocated_for_table_kbmemory_allocated_for_table_kb memory_allocated_for_indexes_kbmemory_allocated_for_indexes_kb
SalesOrderDetail_inmemSalesOrderDetail_inmem 51137615113761 663552663552
DemoSalesOrderDetailSeedDemoSalesOrderDetailSeed 6464 1036810368
SpecialOffer_inmemSpecialOffer_inmem 22 81928192
SalesOrderHeader_inmemSalesOrderHeader_inmem 15756791575679 147456147456
Product_inmemProduct_inmem 111111 1203212032
SpecialOfferProduct_inmemSpecialOfferProduct_inmem 6464 37123712
DemoSalesOrderHeaderSeedDemoSalesOrderHeaderSeed 19841984 55045504

총 6.5GB 정도의 데이터를 확인할 수 있습니다.We can see a total of about 6.5GB of data. SalesOrderHeader_inmem 및 SalesOrderDetail_inmem 테이블의 인덱스 크기는 판매 주문을 삽입하기 전의 인덱스 크기와 동일합니다.Notice that the size of the indexes on the table SalesOrderHeader_inmem and SalesOrderDetail_inmem is is the same as the size of the indexes before inserting the sales orders. 인덱스 크기는 두 테이블 모두 해시 인덱스를 사용하고 해시 인덱스가 고정되어 있기 때문에 변경되지 않았습니다.The index size did not change because both tables are using hash indexes, and hash indexes are static.

데모를 다시 설정한 후After demo reset

저장 프로시저 Demo.usp_DemoReset을 사용하여 데모를 다시 설정할 수 있습니다.The stored procedure Demo.usp_DemoReset can be used to reset the demo. 이 프로시저는 SalesOrderHeader_inmem 및 SalesOrderDetail_inmem 테이블의 데이터를 삭제하고 원래 테이블 SalesOrderHeader 및 SalesOrderDetail의 데이터로 초기값을 다시 설정합니다.It deletes the data in the tables SalesOrderHeader_inmem and SalesOrderDetail_inmem, and re-seeds the data from the original tables SalesOrderHeader and SalesOrderDetail.

테이블의 행이 삭제되었더라도 메모리가 즉시 회수되지는 않습니다.Now, even though the rows in the tables have been deleted, this does not mean that memory is reclaimed immediately. SQL Server는 필요에 따라 백그라운드에서 메모리 액세스에 최적화된 테이블의 삭제된 행에서 메모리를 회수합니다.SQL Server reclaims memory from deleted rows in memory-optimized tables in the background, as needed. 데모가 다시 설정된 직후에는 시스템에 트랜잭션 작업이 없으므로 삭제된 행의 메모리가 아직 회수되지 않은 것을 확인할 수 있습니다.You will see that immediately after demo reset, with no transactional workload on the system, memory from deleted rows is not yet reclaimed:

SELECT type  
, name  
, pages_kb/1024 AS pages_MB   
FROM sys.dm_os_memory_clerks WHERE type LIKE '%xtp%'  
typetype namename pages_MBpages_MB
MEMORYCLERK_XTPMEMORYCLERK_XTP 기본값Default 22612261
MEMORYCLERK_XTPMEMORYCLERK_XTP DB_ID_5DB_ID_5 73967396
MEMORYCLERK_XTPMEMORYCLERK_XTP 기본값Default 00
MEMORYCLERK_XTPMEMORYCLERK_XTP 기본값Default 00

이는 예상된 결과입니다. 트랜잭션 작업이 실행 중일 때 메모리가 회수됩니다.This is expected: memory will be reclaimed when the transactional workload is running.

데모 작업의 두 번째 실행을 시작하는 경우 이전에 삭제된 행이 정리됨에 따라 메모리 사용률이 처음에는 줄어드는 것을 확인할 수 있습니다.If you start a second run of the demo workload you will see the memory utilization decrease initially, as the previously deleted rows are cleaned up. 특정 시점에서 메모리 크기가 다시 증가하고 작업이 완료될 때까지 증가합니다.At some point the memory size will increase again, until the workload finishes. 데모를 다시 설정하고 1,000만 개의 행을 삽입한 후 메모리 사용률은 처음 실행한 후의 사용률과 매우 유사합니다.After inserting 10 million rows after demo reset, the memory utilization will be very similar to the utilization after the first run. 예를 들어For example:

SELECT type  
, name  
, pages_kb/1024 AS pages_MB   
FROM sys.dm_os_memory_clerks WHERE type LIKE '%xtp%'  
typetype namename pages_MBpages_MB
MEMORYCLERK_XTPMEMORYCLERK_XTP 기본값Default 18631863
MEMORYCLERK_XTPMEMORYCLERK_XTP DB_ID_5DB_ID_5 73907390
MEMORYCLERK_XTPMEMORYCLERK_XTP 기본값Default 00
MEMORYCLERK_XTPMEMORYCLERK_XTP 기본값Default 00

메모리 액세스에 최적화된 테이블의 디스크 사용률Disk utilization for memory-optimized tables

지정된 시점에서 데이터베이스의 검사점 파일에 대한 전체 디스크 크기는 다음 쿼리를 사용하여 확인할 수 있습니다.The overall on-disk size for the checkpoint files of a database at a given time can be found using the query:

SELECT SUM(df.size) * 8 / 1024 AS [On-disk size in MB]  
FROM sys.filegroups f JOIN sys.database_files df   
   ON f.data_space_id=df.data_space_id  
WHERE f.type=N'FX'  

초기 상태Initial state

예제 파일 그룹과 예제 메모리 액세스에 최적화된 테이블이 처음에 만들어질 때 많은 검사점 파일이 미리 만들어지고 시스템이 이러한 파일을 채우기 시작합니다. 미리 만들어지는 검사점 파일의 수는 시스템의 논리적 프로세서 수에 따라 달라집니다.When the sample filegroup and sample memory-optimized tables are created initially, a number of checkpoint files are pre-created and the system starts filling the files – the number of checkpoint files pre-created depends on the number of logical processors in the system. 예제는 처음에는 매우 작으므로 미리 만들어진 파일은 처음 만들어진 후 거의 비어 있습니다.As the sample is initially very small, the pre-created files will be mostly empty after initial create.

논리적 프로세서가 16개인 컴퓨터에서 예제의 초기 디스크 크기는 다음과 같습니다.The following shows the initial on-disk size for the sample on a machine with 16 logical processors:

SELECT SUM(df.size) * 8 / 1024 AS [On-disk size in MB]  
FROM sys.filegroups f JOIN sys.database_files df   
   ON f.data_space_id=df.data_space_id  
WHERE f.type=N'FX'  

||
|-|
|On-disk size in MBOn-disk size in MB|
|23122312|

보시다시피 검사점 파일의 디스크 크기(2.3GB)와 실제 데이터 크기(30MB에 가까움)에는 큰 차이가 있습니다.As you can see, there is a big discrepancy between the on-disk size of the checkpoint files, which is 2.3GB, and the actual data size, which is closer to 30MB.

디스크 공간 사용의 출처를 자세히 살펴보려면 다음 쿼리를 사용할 수 있습니다.Looking closer at where the disk-space utilization comes from, you can use the following query. 이 쿼리에서 반환되는 디스크 크기는 상태가 5(REQUIRED FOR BACKUP/HA), 6(IN TRANSITION TO TOMBSTONE) 또는 7(TOMBSTONE)인 파일에 대해 대략적인 값입니다.The size on disk returned by this query is approximate for files with state in 5 (REQUIRED FOR BACKUP/HA), 6 (IN TRANSITION TO TOMBSTONE), or 7 (TOMBSTONE).

SELECT state_desc  
 , file_type_desc  
 , COUNT(*) AS [count]  
 , SUM(CASE  
   WHEN state = 5 AND file_type=0 THEN 128*1024*1024  
   WHEN state = 5 AND file_type=1 THEN 8*1024*1024  
   WHEN state IN (6,7) THEN 68*1024*1024  
   ELSE file_size_in_bytes  
    END) / 1024 / 1024 AS [on-disk size MB]   
FROM sys.dm_db_xtp_checkpoint_files  
GROUP BY state, state_desc, file_type, file_type_desc  
ORDER BY state, file_type  

예제의 초기 상태에 대한 결과는 논리적 프로세서가 16개인 서버의 경우와 유사합니다.For the initial state of the sample, the result will look something like for a server with 16 logical processors:

state_descstate_desc file_type_descfile_type_desc countcount on-disk size MBon-disk size MB
PRECREATEDPRECREATED DATADATA 1616 20482048
PRECREATEDPRECREATED DELTADELTA 1616 128128
UNDER CONSTRUCTIONUNDER CONSTRUCTION DATADATA 11 128128
UNDER CONSTRUCTIONUNDER CONSTRUCTION DELTADELTA 11 88

보시다시피 대부분의 공간이 미리 만들어진 데이터 및 델타 파일에서 사용됩니다.As you can see, most of the space is used by precreated data and delta files. SQL Server는 논리적 프로세서당 하나의 (데이터, 델타) 파일 쌍을 미리 만들었습니다.SQL Server pre-created one pair of (data, delta) files per logical processor. 또한 데이터 파일의 크기는 128MB로, 델타 파일의 크기는 8MB로 미리 지정되므로 이러한 파일에 더욱 효율적으로 데이터를 삽입할 수 있습니다.In addition, data files are pre-sized at 128MB, and delta files at 8MB, in order to make inserting data into these files more efficient.

메모리 액세스에 최적화된 테이블의 실제 데이터는 단일 데이터 파일에 있습니다.The actual data in the memory-optimized tables is in the single data file.

작업을 실행한 후After running the workload

1,000만 개의 판매 주문을 삽입하는 단일 테스트 실행 후 전체 디스크 크기는 다음과 같습니다(16코어 테스트 서버의 경우).After a single test run that inserts 10 million sales orders, the overall on-disk size looks something like this (for a 16-core test server):

SELECT SUM(df.size) * 8 / 1024 AS [On-disk size in MB]  
FROM sys.filegroups f JOIN sys.database_files df   
   ON f.data_space_id=df.data_space_id  
WHERE f.type=N'FX'  

||
|-|
|On-disk size in MBOn-disk size in MB|
|88288828|

디스크 크기는 데이터의 메모리 내 크기와 유사하게 9GB에 가깝습니다.The on-disk size is close to 9GB, which comes close to the in-memory size of the data.

다양한 상태에 있는 검사점 파일의 크기를 좀더 자세히 살펴보면 다음과 같습니다.Looking more closely at the sizes of the checkpoint files across the various states:

SELECT state_desc  
 , file_type_desc  
 , COUNT(*) AS [count]  
 , SUM(CASE  
   WHEN state = 5 AND file_type=0 THEN 128*1024*1024  
   WHEN state = 5 AND file_type=1 THEN 8*1024*1024  
   WHEN state IN (6,7) THEN 68*1024*1024  
   ELSE file_size_in_bytes  
    END) / 1024 / 1024 AS [on-disk size MB]   
FROM sys.dm_db_xtp_checkpoint_files  
GROUP BY state, state_desc, file_type, file_type_desc  
ORDER BY state, file_type  
state_descstate_desc file_type_descfile_type_desc countcount on-disk size MBon-disk size MB
PRECREATEDPRECREATED DATADATA 1616 20482048
PRECREATEDPRECREATED DELTADELTA 1616 128128
UNDER CONSTRUCTIONUNDER CONSTRUCTION DATADATA 11 128128
UNDER CONSTRUCTIONUNDER CONSTRUCTION DELTADELTA 11 88

검사점이 닫힐 때 사용할 준비가 된 미리 만들어진 파일 쌍이 16개 있습니다.We still have 16 pairs of pre-created files, ready to go as checkpoints are closed.

현재 검사점이 닫힐 때까지 사용되는, 작성 중인 쌍이 하나 있습니다.There is one pair under construction, which is used until the current checkpoint is closed. 활성 검사점 파일과 함께 메모리의 6.5GB 데이터에 대한 약 6.5GB의 디스크를 사용합니다.Along with the active checkpoint files this gives about 6.5GB of disk utilization for 6.5GB of data in memory. 인덱스가 디스크에 유지되지 않으므로 이 경우 디스크의 전체 크기는 메모리의 크기보다 작습니다.Recall that indexes are not persisted on disk, and thus the overall size on disk is smaller than the size in memory in this case.

데모를 다시 설정한 후After demo reset

데모를 다시 설정한 후에는 데이터베이스 검사점이 없고 시스템에 트랜잭션 작업이 없는 경우 디스크 공간이 즉시 회수되지 않습니다.After demo reset, disk space is not reclaimed immediately if there is no transactional workload on the system, and there are not database checkpoints. 검사점 파일이 다양한 상태를 이동하여 결국 삭제되려면 검사점 파일의 병합을 시작하고 가비지 수집을 시작하기 위해 많은 검사점 및 로그 잘림 이벤트가 발생해야 합니다.For checkpoint files to be moved through their various stages and eventually be discarded, a number of checkpoints and log truncation events need to happen, to initiate merge of checkpoint files, as well as to initiate garbage collection. 이러한 이벤트는 시스템에 트랜잭션 작업이 있으면(전체 복구 모델을 사용하는 경우에는 정기 로그 백업을 수행하면) 자동으로 발생하지만, 데모 시나리오와 같이 시스템이 유휴 상태일 때는 발생하지 않습니다.These will happen automatically if you have a transactional workload in the system [and take regular log backups, in case you are using the FULL recovery model], but not when the system is idle, as in a demo scenario.

예제에서는 데모를 다시 설정한 후 다음과 유사하게 나타날 수 있습니다.In the example, after demo reset, you may see something like

SELECT SUM(df.size) * 8 / 1024 AS [On-disk size in MB]  
FROM sys.filegroups f JOIN sys.database_files df   
   ON f.data_space_id=df.data_space_id  
WHERE f.type=N'FX'  

||
|-|
|On-disk size in MBOn-disk size in MB|
|1183911839|

거의 12GB로, 데모를 다시 설정하기 전의 9GB보다 훨씬 큽니다.At nearly 12GB, this is significantly more than the 9GB we had before the demo reset. 이는 다음에서 확인할 수 있듯이 일부 검사점 파일 병합이 시작되었지만 일부 병합 대상이 아직 설치되지 않았으며 일부 병합 원본 파일이 아직 정리되지 않았기 때문입니다.This is because some checkpoint file merges have been started, but some of the merge targets have not yet been installed, and some of the merge source files have not yet been cleaned up, as can be seen from the following:

SELECT state_desc  
 , file_type_desc  
 , COUNT(*) AS [count]  
 , SUM(CASE  
   WHEN state = 5 AND file_type=0 THEN 128*1024*1024  
   WHEN state = 5 AND file_type=1 THEN 8*1024*1024  
   WHEN state IN (6,7) THEN 68*1024*1024  
   ELSE file_size_in_bytes  
    END) / 1024 / 1024 AS [on-disk size MB]   
FROM sys.dm_db_xtp_checkpoint_files  
GROUP BY state, state_desc, file_type, file_type_desc  
ORDER BY state, file_type  
state_descstate_desc file_type_descfile_type_desc countcount on-disk size MBon-disk size MB
PRECREATEDPRECREATED DATADATA 1616 20482048
PRECREATEDPRECREATED DELTADELTA 1616 128128
ACTIVEACTIVE DATADATA 3838 51525152
ACTIVEACTIVE DELTADELTA 3838 13311331
MERGE TARGETMERGE TARGET DATADATA 77 896896
MERGE TARGETMERGE TARGET DELTADELTA 77 5656
MERGED SOURCEMERGED SOURCE DATADATA 1313 17721772
MERGED SOURCEMERGED SOURCE DELTADELTA 1313 455455

트랜잭션 작업이 시스템에서 발생하면 병합 대상이 설치되고 병합된 원본이 정리됩니다.Merge targets are installed and merged source are cleaned up as transactional activity happens in the system.

데모를 다시 설정한 후 1,000만 개의 판매 주문을 삽입하는 데모 작업을 두 번째로 실행하면 작업을 처음 실행하는 동안 생성된 파일이 정리된 것을 확인할 수 있습니다.After a second run of the demo workload, inserting 10 million sales orders after the demo reset, you will see that the files constructed during the first run of the workload have been cleaned up. 작업이 실행되는 동안 위의 쿼리를 몇 차례 실행하는 경우 검사점 파일이 다양한 상태를 거치는 것을 확인할 수 있습니다.If you run the above query several times while the workload is running, you can see the checkpoint files make their way through the various stages.

1,000만 개의 판매 주문을 삽입하는 작업을 두 번째로 실행한 후 디스크 사용률이 처음 실행한 후와 매우 유사한 것을 확인할 수 있습니다. 하지만 시스템이 특성상 동적이므로 반드시 같지는 않습니다.After the second run of the workload insert 10 million sales orders you will see disk utilization very similar to, though not necessarily the same as after the first run, as the system is dynamic in nature. 예를 들어For example:

SELECT state_desc  
 , file_type_desc  
 , COUNT(*) AS [count]  
 , SUM(CASE  
   WHEN state = 5 AND file_type=0 THEN 128*1024*1024  
   WHEN state = 5 AND file_type=1 THEN 8*1024*1024  
   WHEN state IN (6,7) THEN 68*1024*1024  
   ELSE file_size_in_bytes  
    END) / 1024 / 1024 AS [on-disk size MB]   
FROM sys.dm_db_xtp_checkpoint_files  
GROUP BY state, state_desc, file_type, file_type_desc  
ORDER BY state, file_type  
state_descstate_desc file_type_descfile_type_desc countcount on-disk size MBon-disk size MB
PRECREATEDPRECREATED DATADATA 1616 20482048
PRECREATEDPRECREATED DELTADELTA 1616 128128
UNDER CONSTRUCTIONUNDER CONSTRUCTION DATADATA 22 268268
UNDER CONSTRUCTIONUNDER CONSTRUCTION DELTADELTA 22 1616
ACTIVEACTIVE DATADATA 4141 56085608
ACTIVEACTIVE DELTADELTA 4141 328328

이 경우에는 'under construction' 상태의 검사점 파일 쌍이 두 개 있습니다. 즉, 작업의 높은 동시성 수준 때문에 여러 파일 쌍이 ‘under construction’ 상태로 이동했습니다.In this case, there are two checkpoint file pairs in the ‘under construction’ state, which means multiple file pairs were moved to the ‘under construction’ state, likely due to the high level of concurrency in the workload. 여러 동시 스레드에서 같은 시간에 새로운 파일 쌍을 필요로 했으므로 파일 쌍이 'precreated'에서 ‘under construction’으로 이동했습니다.Multiple concurrent threads required a new file pair at the same time, and thus moved a pair from ‘precreated’ to ‘under construction’.

관련 항목:See Also

메모리 내 OLTP(메모리 내 최적화)In-Memory OLTP (In-Memory Optimization)