메모리 내 OLTP에 대한 예제 데이터베이스

적용 대상:SQL ServerAzure SQL Database

개요

이 샘플에서는 메모리 내 OLTP 기능을 보여 줍니다. 메모리 최적화 테이블과 고유하게 컴파일된 저장 프로시저를 표시하며 메모리 내 OLTP의 성능 이점을 보여 주는 데 사용할 수 있습니다.

참고 항목

SQL Server 2014(12.x)에 대한 이 항목을 보려면 메모리 내 OLTP를 보여 주는 AdventureWorks 확장 기능을 참조하세요.

또한 AdventureWorks2022 데이터베이스의 5개 테이블을 메모리 최적화 테이블로 마이그레이션하며, 판매 주문 처리용 데모 작업을 포함하고 있습니다. 이 데모 워크로드를 사용하여 서버에서 메모리 내 OLTP를 사용할 때의 성능 이점을 확인할 수 있습니다.

샘플에 대한 설명에서는 메모리 최적화 테이블에 대해 아직 지원되지 않는 기능을 설명하기 위해 테이블을 메모리 내 OLTP로 마이그레이션할 때 발생하는 장단분에 대해 설명합니다.

이 샘플의 설명서는 다음과 같이 구성됩니다.

전제 조건

AdventureWorks를 기반으로 메모리 내 OLTP 샘플 설치

다음 단계에 따라 샘플을 설치합니다.

  1. 다운로드 AdventureWorks2016_EXT.bakSQLServer2016Samples.zip 원본: https://github.com/microsoft/sql-server-samples/releases/tag/adventureworks 로컬 폴더(예 C:\Temp: .)

  2. Transact-SQL 또는 SQL Server Management Studio를 사용하여 데이터베이스 백업을 복원합니다.

    1. 데이터 파일의 대상 폴더 및 파일 이름 식별(예:

      'h:\DATA\AdventureWorks2022_Data.mdf'

    2. 로그 파일의 대상 폴더 및 파일 이름 식별(예:

      'i:\DATA\AdventureWorks2022_log.ldf'

      1. 로그 파일은 데이터 파일과 다른 드라이브에 배치해야 하며, 성능을 최대화하려면 SSD 또는 PCIe 스토리지와 같은 대기 시간이 짧은 드라이브가 가장 좋습니다.

    T-SQL 스크립트 예제:

    RESTORE DATABASE [AdventureWorks2022]   
      FROM DISK = N'C:\temp\AdventureWorks2022.bak'   
        WITH FILE = 1,    
      MOVE N'AdventureWorks2022_Data' TO N'h:\DATA\AdventureWorks2022_Data.mdf',    
      MOVE N'AdventureWorks2022_Log' TO N'i:\DATA\AdventureWorks2022_log.ldf',  
      MOVE N'AdventureWorks2022_mod' TO N'h:\data\AdventureWorks2022_mod'  
     GO  
    
  3. 샘플 스크립트와 워크로드를 보려면 로컬 폴더에 SQLServer2016Samples.zip 파일의 압축을 풉니다. 워크로드 실행에 대한 지침은 메모리 내 OLTP\readme.txt 파일을 참조하세요.

샘플 테이블 및 프로시저에 대한 설명

예제에서는 AdventureWorks2022의 기존 테이블을 기반으로 제품과 판매 주문에 대한 새 테이블을 만듭니다. 새 테이블의 스키마는 아래에 설명된 대로 몇 가지 차이점이 있지만 기존 테이블과 유사합니다.

새 메모리 최적화 테이블에는 접미사 '_inmem'이 있습니다. 예제에는 '_ondisk' 접미사가 붙는 해당 테이블도 포함되어 있습니다. 이러한 테이블을 사용하여 시스템에서 메모리 최적화 테이블과 디스크 기반 테이블의 성능을 일 대 일로 비교할 수 있습니다.

성능 비교를 위한 작업에서 사용되는 메모리 최적화 테이블은 완전한 내구성을 가지며 모두 기록됩니다. 이러한 테이블은 성능 이점을 얻기 위해 내구성이나 안정성을 희생하지 않습니다.

이 샘플의 대상 워크로드는 제품 및 할인에 대한 정보도 고려하는 판매 주문 처리입니다. 이를 위해 SalesOrderHeader, SalesOrderDetail, Product, SpecialOfferSpecialOfferProduct 테이블을 사용합니다.

두 가지 새로운 저장 프로시저 Sales.usp_InsertSalesOrder_inmemSales.usp_UpdateSalesOrderShipInfo_inmem은 판매 주문을 삽입하고 지정된 판매 주문의 배송 정보를 업데이트하는 데 사용됩니다.

새로운 스키마 Demo에는 데모 작업을 실행하기 위한 도우미 테이블과 저장 프로시저가 포함되어 있습니다.

구체적으로 설명하자면 메모리 내 OLTP 샘플에서는 다음 개체를 AdventureWorks2022에 추가합니다.

예제에서 추가된 테이블

새로운 테이블

Sales.SalesOrderHeader_inmem

  • 판매 주문에 대한 헤더 정보입니다. 각 판매 주문에는 이 테이블에 하나의 행이 있습니다.

Sales.SalesOrderDetail_inmem

  • 판매 주문에 대한 세부 정보. 판매 주문의 각 품목에 해당하는 행이 이 테이블에 하나씩 있습니다.

Sales.SpecialOffer_inmem

  • 각 특별 제안과 관련된 할인 비율을 포함하여 특별 제품에 대한 정보입니다.

Sales.SpecialOfferProduct_inmem

  • 특별 제품과 제품 간의 참조 테이블입니다. 각 특별 제품에는 0개 이상의 제품이 제공될 수 있으며, 각 제품은 0개 이상의 특별 제품으로 추천할 수 있습니다.

Production.Product_inmem

  • 가격을 비롯한 제품에 대한 정보

Demo.DemoSalesOrderDetailSeed

  • 데모 워크로드에서 샘플 판매 주문을 생성하는 데 사용됩니다.

테이블의 디스크 기반 변형:

  • Sales.SalesOrderHeader_ondisk

  • Sales.SalesOrderDetail_ondisk

  • Sales.SpecialOffer_ondisk

  • Sales.SpecialOfferProduct_ondisk

  • Production.Product_ondisk

원래 디스크 기반 테이블과 새로운 메모리 최적화 테이블 간의 차이점

대부분의 경우 이 샘플에서 도입된 새 테이블은 원래 테이블과 동일한 열과 동일한 데이터 형식을 사용합니다. 그러나 몇 가지 차이점이 있습니다. 변경 근거와 함께 아래 차이점을 나열합니다.

Sales.SalesOrderHeader_inmem

  • 기본 제약 조건은 메모리 최적화 테이블과 마이그레이션한 대부분의 기본 제약 조건에 대해 지원됩니다. 그러나 원래 테이블 Sales.SalesOrderHeader에는 OrderDateModifiedDate 열에 대한 현재 날짜를 검색하는 두 가지 기본 제약 조건이 포함되어 있습니다. 동시성이 많은 높은 처리량 순서 처리 워크로드에서 전역 리소스는 경합 지점이 될 수 있습니다. 시스템 시간은 글로벌 리소스이며, 특히 판매 주문 헤더의 여러 열과 판매 주문 세부 정보에 대해 시스템 시간을 검색해야 하는 경우 판매 주문을 삽입하는 메모리 내 OLTP 워크로드를 실행할 때 병목 현상이 발생할 수 있음을 확인했습니다. 이러한 문제는 이 예제에서 삽입되는 각 판매 주문에 대해 시스템 시간을 한 번만 검색하고 저장 프로시저 Sales.usp_InsertSalesOrder_inmem에서 SalesOrderHeader_inmemSalesOrderDetail_inmem의 datetime 열에 이 값을 사용하여 해결되었습니다.

  • 별칭 UDT(사용자 정의 데이터 형식) - 원래 테이블은 각각 열과 열에 대해 두 개의 별칭 UDT와 dbo.AccountNumberAccountNumberUDT dbo.OrderNumberPurchaseOrderNumber 사용합니다. SQL Server 2016(13.x)은 메모리 최적화 테이블에 별칭 UDT를 지원하지 않으므로 새 테이블은 각각 nvarchar(25) 및 nvarchar(15) 시스템 데이터 형식을 사용합니다.

  • 인덱스 키 의 Null 허용 열 - 원래 테이블에서 열 SalesPersonID 은 null을 허용하지만 새 테이블에서는 열이 null을 허용하지 않으며 값이 있는 기본 제약 조건(-1)이 있습니다. 메모리 최적화 테이블의 인덱스에는 인덱스 키에 null 허용 열이 있을 수 없기 때문입니다. -1은 이 경우 NULL의 서로게이트입니다.

  • 계산 열 - SQL SalesOrderNumberTotalDue Server 2016(13.x)이 메모리 최적화 테이블의 계산 열을 지원하지 않으므로 계산 열은 생략됩니다. 새로운 Sales.vSalesOrderHeader_extended_inmem 뷰는 SalesOrderNumberTotalDue 열을 반영합니다. 따라서 이러한 열이 필요한 경우 이 보기를 사용할 수 있습니다.

    • 적용 대상: SQL Server 2017(14.x) CTP 1.1.
      SQL Server 2017(14.x) CTP 1.1부터 계산 열은 메모리 최적화 테이블 및 인덱스에서 지원됩니다.
  • 외래 키 제약 조건은 SQL Server 2016(13.x)의 메모리 최적화 테이블에 대해 지원되지만 참조된 테이블도 메모리 최적화인 경우에만 지원됩니다. 메모리 최적화로 마이그레이션된 테이블을 참조하는 외장 키는 마이그레이션된 테이블에 유지되지만 다른 외세 키는 생략됩니다. 또한 SalesOrderHeader_inmem은 예제 작업에서 핫 테이블이며, FOREIGN KEY 제약 조건을 지정하려면 모든 DML 작업에 대한 추가 처리가 필요합니다. 이는 이러한 제약 조건에서 참조된 다른 모든 테이블에서 조회가 필요하기 때문입니다. 따라서 앱에서 Sales.SalesOrderHeader_inmem 테이블의 참조 무결성을 보장하며 참조 무결성은 행이 삽입될 때 확인되지 않는다고 가정합니다.

  • Rowguid - rowguid 열이 생략됩니다. uniqueidentifier는 메모리 최적화 테이블에 대해 지원되지만 SQL Server 2016(13.x)에서는 ROWGUIDCOL 옵션이 지원되지 않습니다. 이러한 종류의 열은 일반적으로 병합 복제 또는 파일 스트림 열이 있는 테이블에 사용됩니다. 이 예제에는 둘 다 포함되어 있지 않습니다.

Sales.SalesOrderDetail

  • 기본 제약 조건 - 시스템 SalesOrderHeader날짜/시간이 필요한 기본 제약 조건은 마이그레이션되지 않고 판매 주문을 삽입하는 저장 프로시저는 첫 번째 삽입 시 현재 시스템 날짜/시간을 삽입하는 작업을 처리합니다.

  • 계산 열 - 계산 열 LineTotal 이 SQL Server 2016(13.x)의 메모리 최적화 테이블에서 지원되지 않으므로 계산 열이 마이그레이션되지 않았습니다. 이 열에 액세스하려면 Sales.vSalesOrderDetail_extended_inmem 뷰를 사용합니다.

  • Rowguid - rowguid 열이 생략됩니다. 자세한 내용은 SalesOrderHeader 테이블에 대한 설명을 참조하세요.

Production.Product

  • 별칭 UDT - 원래 테이블은 시스템 데이터 형식 dbo.Flag비트와 동일한 사용자 정의 데이터 형식을 사용합니다. 마이그레이션된 테이블은 비트 데이터 형식을 대신 사용합니다.

  • Rowguid - rowguid 열이 생략됩니다. 자세한 내용은 SalesOrderHeader 테이블에 대한 설명을 참조하세요.

Sales.SpecialOffer

  • Rowguid - rowguid 열이 생략됩니다. 자세한 내용은 SalesOrderHeader 테이블에 대한 설명을 참조하세요.

Sales.SpecialOfferProduct

  • Rowguid - rowguid 열이 생략됩니다. 자세한 내용은 SalesOrderHeader 테이블에 대한 설명을 참조하세요.

메모리 최적화 테이블의 인덱스에 대한 고려 사항

메모리 최적화 테이블의 기준 인덱스는 지점 조회(같음 조건자에서 인덱스 검색), 범위 검색(같지 않음 조건자의 인덱스 검색), 전체 인덱스 검색 및 정렬된 검사를 지원하는 NONCLUSTERED 인덱스입니다. 또한 NONCLUSTERED 인덱스는 인덱스 키의 선행 열에 대한 검색을 지원합니다. 실제로 메모리 최적화 NONCLUSTERED 인덱스는 디스크 기반 NONCLUSTERED 인덱스가 지원하는 모든 작업을 지원하며, 유일한 예외는 뒤로 검색입니다. 따라서 NONCLUSTERED 인덱스를 사용하는 것이 인덱스에 안전한 선택입니다.

해시 인덱스를 사용하여 작업을 추가로 최적화할 수 있습니다. 해시 인덱스는 포인트 조회와 행 삽입에 대해 최적화됩니다. 그러나 범위 검색, 정렬된 검사 또는 선행 인덱스 키 열 검색을 지원하지 않는다는 점을 고려해야 합니다. 따라서 이러한 인덱스를 사용할 때는 주의해야 합니다. 또한 만들 때 bucket_count를 지정해야 합니다. 일반적으로 인덱스 키 값의 1~2배로 설정해야 하지만 일반적으로 과대평가는 문제가 되지 않습니다.

자세한 내용은 페이지를 참조하십시오.

마이그레이션된 테이블의 인덱스는 데모 판매 주문 처리 워크로드에 맞게 조정되었습니다. 워크로드는 Sales.SalesOrderHeader_inmemSales.SalesOrderDetail_inmem 테이블의 삽입 및 포인트 조회에 의존하고, Production.Product_inmemSales.SpecialOffer_inmem 테이블의 기본 키 열에 대한 포인트 조회에도 의존합니다.

Sales.SalesOrderHeader_inmem에는 세 가지 인덱스가 있는데 정렬된 검색이나 범위 검색이 작업에 필요하지 않기 때문에 모두 성능상의 이유로 해시 인덱스입니다.

  • (SalesOrderID)의 해시 인덱스: 예상된 판매 주문 수가 1,000만 개이기 때문에 bucket_count의 크기는 1,000만(1,600만으로 반올림됨)으로 설정됩니다.

  • (SalesPersonID)의 해시 인덱스: bucket_count가 100만입니다. 제공된 데이터 집합에는 판매 담당자가 많지 않습니다. 그러나 이 큰 bucket_count 미래의 성장을 허용합니다. 또한 bucket_count 큰 경우 포인트 조회에 대한 성능 페널티를 지불하지 않습니다.

  • (CustomerID)의 해시 인덱스: bucket_count가 100만입니다. 제공된 데이터 세트에는 많은 고객이 없지만 향후 성장을 가능하게 합니다.

Sales.SalesOrderDetail_inmem에는 세 가지 인덱스가 있는데 정렬된 검색이나 범위 검색이 작업에 필요하지 않기 때문에 모두 성능상의 이유로 해시 인덱스입니다.

  • (SalesOrderID, SalesOrderDetailID)의 해시 인덱스: 기본 키 인덱스이며 (SalesOrderID, SalesOrderDetailID)에 대한 조회가 빈번하지는 않지만 키에 해시 인덱스를 사용하면 행 삽입이 빨라집니다. bucket_count 크기는 5,000만 개(최대 6,700만 개까지 반올림됨): 예상 판매 주문 수는 1,000만 개이며, 이는 주문당 평균 5개의 품목을 가질 수 있도록 크기가 조정됩니다.

  • (SalesOrderID)의 해시 인덱스: 판매 주문에 의한 조회가 빈번합니다. 단일 주문에 해당하는 모든 품목을 찾으려고 합니다. 예상 판매 주문 수는 1,000만 개이기 때문에 bucket_count 크기는 1,000만 개(최대 1,600만 개까지 반올림됨)입니다.

  • (ProductID)의 해시 인덱스: bucket_count가 100만입니다. 제공된 데이터 집합에는 많은 제품이 없지만 이후 성장을 허용할 수 있습니다.

Production.Product_inmem에는 세 개의 인덱스가 있습니다.

  • (ProductID)의 해시 인덱스: ProductID의 조회가 데모 작업의 중요한 부분에 있으므로 해시 인덱스가 사용됩니다.

  • (Name)의 비클러스터형 인덱스: 제품 이름의 정렬된 검색을 허용합니다.

  • (ProductNumber)의 비클러스터형 인덱스: 제품 번호의 정렬된 검색을 허용합니다.

Sales.SpecialOffer_inmem에는 (SpecialOfferID)의 해시 인덱스가 하나 있습니다. 특별 행사의 포인트 조회는 데모 작업의 중요한 부분에 있습니다. bucket_count의 크기는 이후 성장을 허용하기 위해 100만으로 설정됩니다.

Sales.SpecialOfferProduct_inmem은 데모 작업에서 참조되지 않으므로 작업을 최적화하기 위해 이 테이블에 해시 인덱스를 사용할 명확한 이유가 없습니다. (SpecialOfferID, ProductID) 및 (ProductID)의 인덱스는 비클러스터형 인덱스입니다.

위에서 일부 버킷 수의 크기는 과도하게 설정되었지만 SalesOrderHeader_inmemSalesOrderDetail_inmem의 인덱스에 대한 버킷 수의 크기는 1,000만 개의 판매 주문에 대해서만 설정되었습니다. 이는 사용할 수 있는 메모리가 적은 시스템에 예제를 설치할 수 있도록 하기 위해서입니다. 하지만 이러한 경우에 데모 작업은 메모리 부족으로 실패합니다. 1,000만 개 이상의 판매 주문을 초과하여 확장하려면 버킷 수를 적절하게 늘릴 수 있습니다.

메모리 사용률 고려 사항

데모 워크로드를 실행하기 전과 후에 샘플 데이터베이스의 메모리 사용률은 메모리 최적화 테이블에 대한 섹션 메모리 사용률에서 설명합니다.

예제에서 추가된 저장 프로시저

판매 주문을 삽입하고 배송 세부 정보를 업데이트하기 위한 두 가지 주요 저장 프로시저는 다음과 같습니다.

  • Sales.usp_InsertSalesOrder_inmem

    • 데이터베이스에 새로운 판매 주문을 삽입하고 해당 판매 주문의 SalesOrderID를 출력합니다. 입력 매개 변수로는 주문의 품목뿐만 아니라 판매 주문 헤더에 대한 세부 정보를 사용합니다.

    • 출력 매개 변수:

      • @SalesOrderID int – 방금 삽입된 판매 주문의 SalesOrderID
    • 입력 매개 변수(필수):

      • @DueDate Datetime2

      • @CustomerID int

      • @BillToAddressID [int]

      • @ShipToAddressID [int]

      • @ShipMethodID [int]

      • @SalesOrderDetailsSales.SalesOrderDetailType_inmem - 주문의 품목을 포함하는 TVP(테이블 반환 매개 변수)

    • 입력 매개 변수(선택 사항):

      • @Status [tinyint]

      • @OnlineOrderFlag [bit]

      • @PurchaseOrderNumber [nvarchar] (25)

      • @AccountNumber [nvarchar] (15)

      • @SalesPersonID [int]

      • @TerritoryID [int]

      • @CreditCardID [int]

      • @CreditCardApprovalCode [varchar] (15)

      • @CurrencyRateID [int]

      • @Comment nvarchar(128)

  • Sales.usp_UpdateSalesOrderShipInfo_inmem

    • 지정된 판매 주문에 대한 배송 정보를 업데이트합니다. 그러면 판매 주문의 모든 품목에 대한 배송 정보도 업데이트됩니다.

    • 이 저장 프로시저는 동일한 주문을 업데이트하는 동시 트랜잭션과의 예기치 않은 잠재적 충돌을 처리하는 재시도 논리가 포함된 고유하게 컴파일된 저장 프로시저 Sales.usp_UpdateSalesOrderShipInfo_native의 래퍼 프로시저입니다. 자세한 내용은 재시도 논리를 참조하세요.

  • Sales.usp_UpdateSalesOrderShipInfo_native

    • 배송 정보에 대한 업데이트를 실제로 처리하는 고유하게 컴파일된 저장 프로시저입니다. 래퍼 저장 프로시저 Sales.usp_UpdateSalesOrderShipInfo_inmem에서 호출되기 위한 수단입니다. 클라이언트가 오류를 처리하고 재시도 논리를 구현하는 경우 래퍼 저장 프로시저를 사용하는 대신 이 프로시저를 직접 호출할 수 있습니다.

데모 워크로드에는 다음 저장 프로시저가 사용됩니다.

  • Demo.usp_DemoReset

    • SalesOrderHeaderSalesOrderDetail 테이블을 비우고 다시 시드하여 데모를 다시 설정합니다.

다음 저장 프로시저는 도메인 및 참조 무결성을 보장하면서 메모리 최적화 테이블에서 삽입 및 삭제하는 데 사용됩니다.

  • Production.usp_InsertProduct_inmem

  • Production.usp_DeleteProduct_inmem

  • Sales.usp_InsertSpecialOffer_inmem

  • Sales.usp_DeleteSpecialOffer_inmem

  • Sales.usp_InsertSpecialOfferProduct_inmem

마지막으로 다음 저장 프로시저를 사용하여 도메인 및 참조 무결성을 확인합니다.

  1. dbo.usp_ValidateIntegrity

    • 선택적 매개 변수: @object_id - 무결성의 유효성을 검사할 개체의 ID입니다.

    • 이 프로시저는 확인되어야 하는 무결성 규칙에 대해 dbo.DomainIntegrity, dbo.ReferentialIntegritydbo.UniqueIntegrity 테이블에 의존합니다. 예제에서는 이러한 테이블을 AdventureWorks2022 데이터베이스의 원래 테이블에 존재하는 CHECK, FOREIGN KEY 및 UNIQUE 제약 조건을 기반으로 채웁니다.

    • 이 프로시저는 도우미 프로시저 dbo.usp_GenerateCKCheck, dbo.usp_GenerateFKCheckdbo.GenerateUQCheck에 의존하여 무결성 검사를 수행하는 데 필요한 T-SQL을 생성합니다.

데모 작업을 사용한 성능 측정

Ostress는 Microsoft CSS SQL Server 지원 팀에서 개발한 명령줄 도구입니다. 이 도구를 사용하여 쿼리를 실행하거나 저장 프로시저를 병렬로 실행할 수 있습니다. 지정된 T-SQL 문을 병렬로 실행하도록 스레드 수를 구성할 수 있으며 이 스레드에서 문을 실행할 횟수를 지정할 수 있습니다. ostress는 스레드를 스핀업하고 모든 스레드에서 문을 병렬로 실행합니다. 모든 스레드의 실행이 완료된 후 ostress는 모든 스레드의 실행이 완료되는 데 걸린 시간을 보고합니다.

ostress 설치

Ostress는 RML(Report Markup Language) 유틸리티의 일부로 설치됩니다. ostress에 대한 독립 실행형 설치가 없습니다.

설치 단계:

  1. 다음 페이지에서 RML 유틸리티용 x64 설치 패키지를 다운로드하고 실행합니다 . SQL Server용 RML 다운로드

  2. 특정 파일이 사용 중이라는 대화 상자가 나타나면 '계속’을 선택합니다.

ostress 실행

Ostress는 명령줄 프롬프트에서 실행됩니다. RML 유틸리티의 일부로 설치된 "RML Cmd 프롬프트"에서 도구를 실행하는 것이 가장 편리합니다.

RML Cmd Prompt를 열려면 다음 지침을 따르세요.

Windows에서 Windows 키를 선택하고 rml을 입력하여 시작 메뉴를 엽니다. 검색 결과 목록에 있는 "RML Cmd 프롬프트"를 선택합니다.

명령 프롬프트가 RML 유틸리티 설치 폴더에 있는지 확인합니다.

명령줄 옵션 없이 ostress.exe를 실행하기만 하면 ostress에 대한 명령줄 옵션을 볼 수 있습니다. 이 예제와 함께 ostress를 실행하기 위해 고려할 기본 옵션은 다음과 같습니다.

  • 연결할 Microsoft SQL Server 인스턴스의 -S 이름

  • -E Windows 인증을 사용하여 연결(기본값); SQL Server 인증을 사용하는 경우 -you 및 -P 옵션을 사용하여 사용자 이름과 암호를 각각 지정합니다.

  • -d 데이터베이스의 이름. 이 예의 경우 AdventureWorks2022입니다.

  • -Q 실행할 T-SQL 문

  • -n개 입력 파일/쿼리를 처리하는 연결 수

  • -각 입력 파일/쿼리를 실행할 각 연결에 대한 반복 수입니다.

데모 워크로드

데모 작업에 사용되는 기본 저장 프로시저는 Sales.usp_InsertSalesOrder_inmem/ondisk입니다. 아래의 스크립트는 샘플 데이터를 사용하여 TVP(테이블 반환 매개 변수)를 생성하고 5개의 품목이 있는 판매 주문을 삽입하는 절차를 호출합니다.

ostress 도구는 판매 주문을 동시에 삽입하는 클라이언트를 시뮬레이션하기 위해 저장 프로시저 호출을 병렬로 실행하는 데 사용됩니다.

스트레스 실행이 끝날 때마다 Demo.usp_DemoReset을 실행하여 데모를 다시 설정합니다. 이 절차는 메모리 최적화 테이블의 행을 삭제하고 디스크 기반 테이블을 잘라내고 데이터베이스 검사점을 실행합니다.

다음 스크립트는 동시에 실행되어 판매 주문 처리 워크로드를 시뮬레이션합니다.

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번 삽입됩니다. 루프는 데이터베이스가 샘플 순서를 생성하는 데 사용된다는 사실을 고려하는 데 사용됩니다. 일반적인 프로덕션 환경에서 중간 계층 애플리케이션은 삽입할 판매 주문을 생성합니다.

위의 스크립트는 메모리 최적화 테이블에 판매 주문을 삽입합니다. 판매 주문을 디스크 기반 테이블에 삽입하는 스크립트는 두 ‘_inmem’을 ‘_ondisk’로 바꿔서 파생됩니다.

ostress 도구를 사용하여 여러 동시 연결을 사용하여 스크립트를 실행합니다. '-n' 매개 변수를 사용하여 연결 수를 제어하고 매개 변수 'r'을 사용하여 각 연결에서 스크립트가 실행되는 횟수를 제어합니다.

워크로드 실행

대규모로 테스트하기 위해 100개의 연결을 사용하여 1,000만 개의 판매 주문을 삽입합니다. 이 테스트는 일반 서버(예: 물리적 코어 8개, 논리적 코어 16개)와 로그용 기본 SSD 스토리지에서 문제 없이 실행됩니다. 테스트가 사용자의 하드웨어에서 제대로 실행되지 않는 경우 느리게 실행되는 테스트 문제 해결 섹션을 참조하세요. 이 테스트에 대한 스트레스 수준을 줄이려면 '-n' 매개 변수를 변경하여 연결 수를 줄입니다. 예를 들어 연결 수를 40으로 낮추려면 '-n100' 매개 변수를 '-n40'으로 변경합니다.

작업에 대한 성능 측정으로 작업을 실행한 후 ostress.exe에서 보고하는 경과 시간을 사용합니다.

아래 지침과 측정값에서는 1000만 개의 판매 주문을 삽입하는 작업을 사용합니다. 100만 개의 판매 주문을 삽입하는 축소된 워크로드를 실행하는 지침은 SQLServer2016Samples.zip 보관 파일의 일부인 'In-Memory OLTP\readme.txt'에 있는 지침을 참조하세요.

메모리 최적화 테이블

메모리 최적화 테이블에서 작업을 실행하는 것부터 시작합니다. 다음 명령은 각각 5,000회의 반복을 위해 실행되는 100개의 스레드를 엽니다. 각 반복은 별도의 트랜잭션에 20개의 판매 주문을 삽입합니다. 데이터베이스가 삽입될 데이터를 생성하는 데 사용된다는 사실을 보완하기 위해 반복당 20개의 삽입이 있습니다. 이렇게 하면 총 20 * 5,000 * 100 = 10,000,000개의 판매 주문 삽입이 생성됩니다.

RML Cmd 프롬프트를 열고 다음 명령을 실행합니다.

복사 단추를 선택하여 명령을 복사하고 RML 유틸리티 명령 프롬프트에 붙여넣습니다.

ostress.exe -n100 -r5000 -S. -E -dAdventureWorks2022 -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초가 걸렸습니다. 물리적 코어가 24개(논리적 코어 48개)인 두 번째 테스트 서버에서는 1분 0초가 소요되었습니다.

작업 관리자를 사용하는 등 워크로드가 실행되는 동안 CPU 사용률을 관찰합니다. CPU 사용률이 100%에 가까운 것을 볼 수 있습니다. 그렇지 않은 경우 로그 IO 병목 현상이 발생합니다. 느린 실행 테스트 문제 해결도 표시됩니다.

디스크 기반 테이블

다음 명령은 디스크 기반 테이블에서 워크로드를 실행합니다. 이 작업은 실행되는 데 시간이 걸릴 수 있는데 그 이유는 주로 시스템의 래치 경합 때문입니다. 메모리 최적화 테이블은 래치가 없으므로 이 문제를 겪지 않습니다.

RML Cmd 프롬프트를 열고 다음 명령을 실행합니다.

복사 단추를 선택하여 명령을 복사하고 RML 유틸리티 명령 프롬프트에 붙여넣습니다.

ostress.exe -n100 -r5000 -S. -E -dAdventureWorks2022 -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초가 소요되었고, 24개의 물리적(48개의 논리) 코어가 있는 두 번째 테스트 서버에서는 52분 16초가 걸렸습니다.

이 테스트에서 메모리 최적화 테이블과 디스크 기반 테이블 간의 성능 차이의 주요 요인은 디스크 기반 테이블을 사용할 때 SQL Server가 CPU를 완전히 활용할 수 없다는 사실입니다. 그 이유는 래치 경합입니다. 동시 트랜잭션이 동일한 데이터 페이지에 쓰려고 합니다. 래치는 한 번에 하나의 트랜잭션만 페이지에 쓸 수 있도록 하는 데 사용됩니다. 메모리 내 OLTP 엔진은 래치를 사용하지 않으며 데이터 행은 페이지로 구성되지 않습니다. 따라서 동시 트랜잭션은 서로의 삽입을 차단하지 않으므로 SQL Server가 CPU를 완전히 활용할 수 있습니다.

작업 관리자 등을 사용하여 작업이 실행되는 동안 CPU 사용률을 관찰할 수 있습니다. 디스크 기반 테이블에서 CPU 사용률이 100%와는 거리가 먼 것을 볼 수 있습니다. 논리적 프로세서가 16개인 테스트 구성에서 사용률은 24% 정도입니다.

필요에 따라 성능 모니터를 사용하여 성능 카운터 \SQL Server:Latches\Latch Waits/sec를 통해 초당 래치 대기 수를 볼 수 있습니다.

데모 다시 설정

데모를 다시 설정하려면 RML Cmd 프롬프트를 열고 다음 명령을 실행합니다.

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

하드웨어에 따라 실행하는 데 몇 분 정도 걸릴 수 있습니다.

데모 실행이 끝날 때마다 다시 설정하는 것이 좋습니다. 이 워크로드는 삽입 전용이므로 각 실행은 더 많은 메모리를 소비하므로 메모리 부족이 발생하지 않도록 다시 설정해야 합니다. 실행 후 사용된 메모리 양은 워크로드를 실행한 후 Section Memory 사용률에 대해 설명합니다.

느리게 실행되는 테스트 문제 해결

테스트 결과는 일반적으로 하드웨어와 테스트 실행에 사용되는 동시성 수준에 따라 달라집니다. 결과가 예상대로 되지 않는 경우 몇 가지 사항을 찾아야 합니다.

  • 동시 트랜잭션 수: 단일 스레드에서 작업을 실행할 때 메모리 내 OLTP를 사용한 성능 이점은 두 배보다 적을 수 있습니다. 래치 경합은 동시성 수준이 높은 경우에만 상당한 문제가 됩니다.

  • SQL Server에서 사용할 수 있는 코어 수가 적습니다. 즉, SQL에서 사용할 수 있는 코어 수만큼 동시 실행 트랜잭션만 있을 수 있으므로 시스템에 낮은 수준의 동시성이 있습니다.

    • 증상: 디스크 기반 테이블에서 워크로드를 실행할 때 CPU 사용률이 높은 경우 동시성 부족을 가리키는 경합이 많지 않음을 의미합니다.
  • 로그 드라이브의 속도: 로그 드라이브가 시스템의 트랜잭션 처리량 수준을 따라갈 수 없는 경우 워크로드는 로그 IO에서 병목 상태가 됩니다. 로깅은 메모리 내 OLTP를 사용하는 것이 더 효율적이지만 로그 IO가 병목 상태인 경우 잠재적인 성능 향상은 제한됩니다.

    • 증상: 메모리 최적화 테이블에서 워크로드를 실행할 때 CPU 사용률이 100%에 가깝지 않거나 매우 뾰족한 경우 로그 IO 병목 현상이 발생할 수 있습니다. 리소스 모니터를 열고 로그 드라이브의 큐 길이를 확인하여 확인할 수 있습니다.

샘플의 메모리 및 디스크 공간 사용률

아래에서는 샘플 데이터베이스에 대한 메모리 및 디스크 공간 사용률 측면에서 예상되는 사항에 대해 설명합니다. 또한 16개의 논리 코어가 있는 테스트 서버에서 본 결과도 보여 줍니다.

메모리 최적화 테이블에 대한 메모리 사용률

데이터베이스의 전체 사용률

다음 쿼리를 사용하여 시스템의 메모리 내 OLTP에 대한 총 메모리 사용률을 가져올 수 있습니다.

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

데이터베이스를 방금 만든 후의 스냅샷:

type 이름 pages_MB
MEMORYCLERK_XTP 기본 94
MEMORYCLERK_XTP DB_ID_5 877
MEMORYCLERK_XTP 기본 0
MEMORYCLERK_XTP 기본 0

기본 메모리 클럭은 시스템 차원의 메모리 구조를 포함하고 있으며 비교적 작습니다. 사용자 데이터베이스(여기서는 ID가 5인 데이터베이스)의 메모리 클럭은 약 900MB입니다(database_id는 인스턴스마다 다를 수 있음).

테이블당 메모리 사용률

다음 쿼리를 사용하여 개별 테이블 및 해당 인덱스의 메모리 사용률을 드릴다운할 수 있습니다.

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';  

다음 테이블에서는 예제를 처음 설치한 경우 이 쿼리의 결과를 보여 줍니다.

테이블 이름 memory_allocated_for_table_kb memory_allocated_for_indexes_kb
SpecialOfferProduct_inmem 64 3840
DemoSalesOrderHeaderSeed 1984 5504
SalesOrderDetail_inmem 15316 663552
DemoSalesOrderDetailSeed 64 10432
SpecialOffer_inmem 3 8192
SalesOrderHeader_inmem 7168 147456
Product_inmem 124 12352

보다시피 테이블이 상당히 작습니다. SalesOrderHeader_inmem은 크기가 약 7MB이고, SalesOrderDetail_inmem은 크기가 약 15MB입니다.

여기서 눈에 띄는 점은 테이블 데이터의 크기와 비교하여 인덱스에 할당된 메모리의 크기입니다. 이는 샘플의 해시 인덱스가 더 큰 데이터 크기에 대해 미리 크기가 조정되기 때문입니다. 해시 인덱스의 크기는 고정되어 있으므로 해당 크기는 테이블의 데이터 크기에 따라 증가하지 않습니다.

워크로드를 실행한 후 메모리 사용률

1,000만 개의 판매 주문을 삽입한 후 전체 메모리 사용률은 다음과 유사합니다.

SELECT type  
, name  
, pages_kb/1024 AS pages_MB   
FROM sys.dm_os_memory_clerks WHERE type LIKE '%xtp%'  
type 이름 pages_MB
MEMORYCLERK_XTP 기본 146
MEMORYCLERK_XTP DB_ID_5 7374
MEMORYCLERK_XTP 기본 0
MEMORYCLERK_XTP 기본 0

보다시피 SQL Server는 예제 데이터베이스에서 메모리 최적화 테이블과 인덱스에 8GB보다 조금 작은 크기를 사용하고 있습니다.

다음 예제를 실행한 후 테이블당 자세한 메모리 사용량을 확인합니다.

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'  
테이블 이름 memory_allocated_for_table_kb memory_allocated_for_indexes_kb
SalesOrderDetail_inmem 5113761 663552
DemoSalesOrderDetailSeed 64 10368
SpecialOffer_inmem 2 8192
SalesOrderHeader_inmem 1575679 147456
Product_inmem 111 12032
SpecialOfferProduct_inmem 64 3712
DemoSalesOrderHeaderSeed 1984 5504

총 6.5GB의 데이터를 볼 수 있습니다. SalesOrderHeader_inmemSalesOrderDetail_inmem 테이블의 인덱스 크기는 판매 주문을 삽입하기 전의 인덱스 크기와 동일합니다. 두 테이블 모두 해시 인덱스를 사용하고 해시 인덱스가 정적이므로 인덱스 크기가 변경되지 않았습니다.

데모 재설정 후

저장 프로시저 Demo.usp_DemoReset을 사용하여 데모를 다시 설정할 수 있습니다. 이 저장 프로시저는 테이블 SalesOrderHeader_inmemSalesOrderDetail_inmem의 데이터를 삭제하고 원래 테이블 SalesOrderHeaderSalesOrderDetail로부터 데이터를 다시 시드합니다.

테이블의 행이 삭제되었더라도 메모리가 즉시 회수되지는 않습니다. SQL Server는 필요에 따라 백그라운드에서 메모리 최적화 테이블의 삭제된 행에서 메모리를 회수합니다. 데모가 다시 설정된 직후에는 시스템에 트랜잭션 작업이 없으므로 삭제된 행의 메모리가 아직 회수되지 않은 것을 확인할 수 있습니다.

SELECT type  
, name  
, pages_kb/1024 AS pages_MB   
FROM sys.dm_os_memory_clerks WHERE type LIKE '%xtp%';
type 이름 pages_MB
MEMORYCLERK_XTP 기본 2261
MEMORYCLERK_XTP DB_ID_5 7396
MEMORYCLERK_XTP 기본 0
MEMORYCLERK_XTP 기본 0

트랜잭션 워크로드가 실행 중일 때 메모리가 회수됩니다.

데모 워크로드의 두 번째 실행을 시작하면 이전에 삭제된 행이 정리되므로 처음에 메모리 사용률이 감소하는 것을 볼 수 있습니다. 워크로드가 완료될 때까지 메모리 크기가 다시 증가합니다. 데모 재설정 후 1,000만 개의 행을 삽입한 후 메모리 사용률은 첫 번째 실행 후의 사용률과 매우 유사합니다. 예:

SELECT type  
, name  
, pages_kb/1024 AS pages_MB   
FROM sys.dm_os_memory_clerks WHERE type LIKE '%xtp%';
type 이름 pages_MB
MEMORYCLERK_XTP 기본 1863
MEMORYCLERK_XTP DB_ID_5 7390
MEMORYCLERK_XTP 기본 0
MEMORYCLERK_XTP 기본 0

메모리 최적화 테이블에 대한 디스크 사용률

지정된 시간에 데이터베이스의 검사점 파일에 대한 전체 디스크 크기는 쿼리를 사용하여 찾을 수 있습니다.

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';  
  

초기 상태

샘플 파일 그룹 및 샘플 메모리 최적화 테이블이 처음에 만들어지면 많은 검사점 파일이 미리 만들어지고 시스템이 파일 채우기를 시작합니다. 미리 생성된 검사점 파일 수는 시스템의 논리 프로세서 수에 따라 달라집니다. 샘플은 처음에는 매우 작기 때문에 미리 만든 파일은 초기 생성 후 대부분 비어 있습니다.

다음 코드는 16개의 논리 프로세서가 있는 컴퓨터의 샘플에 대한 초기 디스크 크기입니다.

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 MB
2312

볼 수 있듯이 검사점 파일의 디스크 크기(2.3GB)와 30MB에 가까운 실제 데이터 크기 사이에는 큰 차이가 있습니다.

디스크 공간 사용률의 위치를 자세히 살펴보면 다음 쿼리를 사용할 수 있습니다. 이 쿼리에서 반환된 디스크의 크기는 상태가 5(BACKUP/HA에 필요), 6(TOMBSTONE으로 전환) 또는 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개인 서버에 대해 다음과 같이 표시됩니다.

state_desc file_type_desc on-disk size MB
미리 생성됨 데이터 16 2048
미리 생성됨 DELTA 16 128
UNDER CONSTRUCTION 데이터 6 128
UNDER CONSTRUCTION DELTA 6 8

보듯이 대부분의 공간은 미리 생성된 데이터 및 델타 파일에서 사용됩니다. SQL Server는 논리 프로세서당 한 쌍의(데이터, 델타) 파일을 미리 만들었습니다. 또한 데이터 파일의 크기는 128MB로, 델타 파일의 크기는 8MB로 미리 지정되므로 이러한 파일에 더욱 효율적으로 데이터를 삽입할 수 있습니다.

메모리 최적화 테이블의 실제 데이터는 단일 데이터 파일에 있습니다.

작업을 실행한 후

1,000만 개의 판매 주문을 삽입하는 단일 테스트 실행 후 전체 디스크 크기는 다음과 같습니다(16코어 테스트 서버의 경우).

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 MB
8828

디스크 크기는 데이터의 메모리 내 크기와 유사하게 9GB에 가깝습니다.

다양한 상태에서 검사점 파일의 크기를 더 자세히 살펴봅니다.

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_desc file_type_desc on-disk size MB
미리 생성됨 데이터 16 2048
미리 생성됨 DELTA 16 128
UNDER CONSTRUCTION 데이터 6 128
UNDER CONSTRUCTION DELTA 6 8

검사점이 닫히면 사용할 준비가 된 16쌍의 미리 만들어진 파일이 있습니다.

현재 검사점이 닫힐 때까지 사용되는, 작성 중인 쌍이 하나 있습니다. 활성 검사점 파일과 함께 메모리의 6.5GB 데이터에 대해 약 6.5GB의 디스크를 사용합니다. 인덱스는 디스크에 유지되지 않으므로 디스크의 전체 크기가 이 경우 메모리 크기보다 작습니다.

데모 재설정 후

데모 재설정 후 시스템에 트랜잭션 워크로드가 없고 데이터베이스 검사점이 없는 경우 디스크 공간이 즉시 회수되지 않습니다. 검사점 파일을 다양한 단계를 통해 이동하고 결국 삭제하려면 검사점 파일 병합을 시작하고 가비지 수집을 시작하려면 여러 검사점 및 로그 잘림 이벤트가 발생해야 합니다. 이러한 이벤트는 시스템에 트랜잭션 작업이 있으면(전체 복구 모델을 사용하는 경우에는 정기 로그 백업을 수행하면) 자동으로 발생하지만, 데모 시나리오와 같이 시스템이 유휴 상태일 때는 발생하지 않습니다.

예제에서는 데모를 다시 설정한 후 다음과 유사하게 나타날 수 있습니다.

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 MB
11839

거의 12GB에서 데모를 다시 설정하기 전의 9GB보다 훨씬 더 많은 값입니다. 일부 검사점 파일 병합이 시작되었지만 일부 병합 대상은 아직 설치되지 않았으며 병합 원본 파일 중 일부는 아직 정리되지 않았기 때문입니다.

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_desc file_type_desc on-disk size MB
미리 생성됨 데이터 16 2048
미리 생성됨 DELTA 16 128
활성 데이터 38 5152
활성 DELTA 38 1331
MERGE TARGET 데이터 7 896
MERGE TARGET DELTA 7 56
병합된 원본 데이터 13 1772
병합된 원본 DELTA 13 455

병합 대상이 설치되고 시스템에서 트랜잭션 작업이 수행되면 병합된 원본이 정리됩니다.

데모 워크로드를 두 번째로 실행한 후 데모 재설정 후 1,000만 개의 판매 주문을 삽입하면 워크로드의 첫 번째 실행 중에 생성된 파일이 정리된 것을 볼 수 있습니다. 작업이 실행되는 동안 위의 쿼리를 몇 차례 실행하는 경우 검사점 파일이 다양한 상태를 거치는 것을 확인할 수 있습니다.

워크로드의 두 번째 실행 후 1,000만 개의 판매 주문을 삽입하면 시스템이 동적이므로 첫 번째 실행 후와 동일하지는 않지만 디스크 사용률이 매우 유사합니다. 예:

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_desc file_type_desc on-disk size MB
미리 생성됨 데이터 16 2048
미리 생성됨 DELTA 16 128
UNDER CONSTRUCTION 데이터 2 268
UNDER CONSTRUCTION DELTA 2 16
활성 데이터 41 5608
활성 DELTA 41 328

이 경우에는 'under construction' 상태의 검사점 파일 쌍이 두 개 있습니다. 즉, 작업의 높은 동시성 수준 때문에 여러 파일 쌍이 ‘under construction’ 상태로 이동했습니다. 여러 동시 스레드는 동시에 새 파일 쌍이 필요하므로 쌍을 'precreated'에서 'under construction'으로 이동했습니다.

다음 단계