메모리 내 OLTP에 대한 예제 데이터베이스
적용 대상: SQL Server Azure SQL 데이터베이스
개요
이 샘플에서는 메모리 내 OLTP 기능을 보여 줍니다. 이 샘플은 메모리 최적화 테이블과 고유하게 컴파일된 저장 프로시저를 보여주며, 메모리 내 OLTP의 성능 이점을 설명하는 데 사용할 수 있습니다.
참고 항목
SQL Server 2014 (12.x)에 대한 이 항목을 보려면 메모리 내 OLTP를 보여주기 위한 AdventureWorks 확장을 참조하세요.
또한 AdventureWorks2022
데이터베이스의 5개 테이블을 메모리 최적화 테이블로 마이그레이션하며, 판매 주문 처리용 데모 작업을 포함하고 있습니다. 이 데모 워크로드를 사용하여 서버에서 메모리 내 OLTP를 사용할 때의 성능 이점을 확인할 수 있습니다.
샘플에 대한 설명에서는 메모리 최적화 테이블에 대해 아직 지원되지 않는 기능을 설명하기 위해 테이블을 메모리 내 OLTP로 마이그레이션할 때 발생하는 절충점에 대해 설명합니다.
이 샘플의 설명서는 다음과 같이 구성되어 있습니다.
샘플을 설치하고 데모 워크로드를 실행하기 위한 필수 조건
예제 테이블 및 프로시저에 대한 설명 – 메모리 내 OLTP 샘플에서
AdventureWorks2022
에 추가한 테이블 및 프로시저에 대한 설명과 원래AdventureWorks2022
테이블을 메모리 최적화 테이블로 마이그레이션하기 위한 고려 사항이 포함되어 있습니다.데모 워크로드를 사용한 성능 측정을 수행하기 위한 지침 – 데모 작업 자체를 실행하기 위한 지침뿐 아니라 작업을 추진하는 데 사용되는 도구인 ostress를 설치하고 실행하기 위한 지침이 포함되어 있습니다.
필수 조건
-
SQL Server 2016(13.x)
성능 테스트의 경우 프로덕션 환경과 유사한 사양을 가진 서버입니다. 이 특정 샘플의 경우 SQL Server에 사용할 수 있는 메모리가 16GB 이상 있어야 합니다. 메모리 내 OLTP용 하드웨어에 대한 일반적인 지침은 다음 블로그 게시물 SQL Server 2014의 메모리 내 OLTP에 대한 하드웨어 고려 사항을 참조하세요.
AdventureWorks 기반의 메모리 내 OLTP 샘플 지침
다음 단계에 따라 샘플을 설치합니다.
https://github.com/microsoft/sql-server-samples/releases/tag/adventureworks에서
AdventureWorks2016_EXT.bak
및SQLServer2016Samples.zip
를 로컬 폴더로 다운로드(예:C:\Temp
).Transact-SQL 또는 SQL Server Management Studio 사용하여 데이터베이스 백업 복구.
데이터 파일의 대상 폴더 및 파일 이름을 식별(예:
'h:\DATA\AdventureWorks2022_Data.mdf'
로그 파일의 대상 폴더 및 파일 이름을 식별(예:
'i:\DATA\AdventureWorks2022_log.ldf'
- 로그 파일은 데이터 파일과 다른 드라이브에 배치해야 하며, 성능을 최대화하려면 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
샘플 스크립트와 워크로드를 보려면 로컬 폴더에 SQLServer2016Samples.zip 파일의 압축을 풉니다. 워크로드 실행에 대한 지침은 메모리 내 OLTP\readme.txt 파일을 참조하세요.
샘플 테이블 및 프로시저에 대한 설명
예제에서는 AdventureWorks2022
의 기존 테이블을 기반으로 제품과 판매 주문에 대한 새 테이블을 만듭니다. 새 테이블의 스키마는 아래에 설명된 대로 몇 가지 차이점이 있지만 기존 테이블과 유사합니다.
새 메모리 최적화 테이블에는 접미사 '_inmem'이 있습니다. 예제에는 '_ondisk' 접미사가 붙는 해당 테이블도 포함되어 있습니다. 이러한 테이블을 사용하여 시스템에서 메모리 최적화 테이블과 디스크 기반 테이블의 성능을 일 대 일로 비교할 수 있습니다.
성능 비교를 위한 작업에서 사용되는 메모리 최적화 테이블은 완전한 내구성을 가지며 모두 기록됩니다. 이러한 테이블은 성능 이점을 얻기 위해 내구성이나 안정성을 희생하지 않습니다.
이 샘플의 대상 워크로드는 제품 및 할인에 대한 정보도 고려하는 판매 주문 처리입니다. 이를 위해 SalesOrderHeader
, SalesOrderDetail
, Product
, SpecialOffer
및 SpecialOfferProduct
테이블을 사용합니다.
두 가지 새로운 저장 프로시저 Sales.usp_InsertSalesOrder_inmem
및 Sales.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
에는OrderDate
및ModifiedDate
열에 대한 현재 날짜를 검색하는 두 가지 기본 제약 조건이 포함되어 있습니다. 동시 작업과 처리량이 많은 주문 처리 작업에서 전역 리소스는 경합 지점이 될 수 있습니다. 시스템 시간은 글로벌 리소스이며, 특히 판매 주문 헤더의 여러 열과 판매 주문 세부 정보에 대해 시스템 시간을 검색해야 하는 경우 판매 주문을 삽입하는 메모리 내 OLTP 워크로드를 실행할 때 병목 현상이 발생할 수 있음을 확인했습니다. 이러한 문제는 이 예제에서 삽입되는 각 판매 주문에 대해 시스템 시간을 한 번만 검색하고 저장 프로시저Sales.usp_InsertSalesOrder_inmem
에서SalesOrderHeader_inmem
및SalesOrderDetail_inmem
의 datetime 열에 이 값을 사용하여 해결되었습니다.별칭 UDT(사용자 정의 데이터 형식) - 원래 테이블은
PurchaseOrderNumber
및AccountNumber
열에 대해 각각dbo.OrderNumber
및dbo.AccountNumber
의 두 가지 별칭 UDT를 사용합니다. SQL Server 2016(13.x)은 메모리 최적화 테이블에 별칭 UDT를 지원하지 않으므로 새 테이블은 각각 nvarchar(25) 및 nvarchar(15) 시스템 데이터 형식을 사용합니다.인덱스 키의 Null 허용 열 - 원래 테이블에서
SalesPersonID
열은 Null을 허용하지만 새 테이블에서 이 열은 Null을 허용하지 않으며 값(-1)을 사용하는 기본 제약 조건을 갖습니다. 메모리 최적화 테이블의 인덱스에는 인덱스 키에 null 허용 열이 있을 수 없기 때문입니다. -1은 이 경우 NULL의 대용값입니다.계산 열 - SQL Server 2016 (13.x)에서는 메모리 최적화 테이블에서 계산 열을 지원하지 않기 때문에 계산 열
SalesOrderNumber
및TotalDue
가 생략되었습니다. 새로운Sales.vSalesOrderHeader_extended_inmem
뷰는SalesOrderNumber
및TotalDue
열을 반영합니다. 따라서 이러한 열이 필요한 경우 이 보기를 사용할 수 있습니다.- 적용 대상: SQL Server 2017(14.x) CTP 1.1.
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가 메모리 최적화 테이블에 지원되지만 ROWGUIDCOL 옵션은 SQL Server 2016 (13.x)에서 지원되지 않습니다. 이러한 종류의 열은 일반적으로 병합 복제 또는 파일 스트림 열이 있는 테이블에 사용됩니다. 이 예제에는 둘 다 포함되어 있지 않습니다.
Sales.SalesOrderDetail
기본 제약 조건 -
SalesOrderHeader
와 유사하게 시스템 날짜/시간을 필요로 하는 기본 제약 조건은 마이그레이션되지 않으며, 대신 판매 주문을 삽입하는 저장 프로시저가 첫 번째 삽입 시 현재 시스템 날짜/시간을 삽입하는 작업을 처리합니다.계산 열 - SQL Server 2016 (13.x)에서 계산 열이 메모리 최적화 테이블에 지원되지 않기 때문에 계산 열
LineTotal
이 마이그레이션되지 않았습니다. 이 열에 액세스하려면Sales.vSalesOrderDetail_extended_inmem
뷰를 사용합니다.Rowguid -
rowguid
열이 생략되었습니다. 자세한 내용은SalesOrderHeader
테이블에 대한 설명을 참조하세요.
Production.Product
별칭 UDT - 원래 테이블은 시스템 데이터 형식 bit와 동일한 사용자 정의 데이터 형식
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_inmem
및 Sales
.SalesOrderDetail_inmem
테이블의 삽입 및 포인트 조회에 의존하고, Production.Product_inmem
및 Sales.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_inmem
및 SalesOrderDetail_inmem
의 인덱스에 대한 버킷 수의 크기는 1,000만 개의 판매 주문에 대해서만 설정되었습니다. 이는 사용할 수 있는 메모리가 적은 시스템에 예제를 설치할 수 있도록 하기 위해서입니다. 하지만 이러한 경우에 데모 작업은 메모리 부족으로 실패합니다. 1,000만 개 이상의 판매 주문을 초과하여 확장하려면 버킷 수를 적절하게 늘릴 수 있습니다.
메모리 사용률 고려 사항
데모 워크로드를 실행하기 전과 후에 샘플 데이터베이스의 메모리 사용률은 메모리 최적화 테이블에 대한 메모리 사용률 센션에서 설명합니다.
예제에서 추가된 저장 프로시저
판매 주문을 삽입하고 배송 세부 정보를 업데이트하기 위한 두 가지 주요 저장 프로시저는 다음과 같습니다.
Sales.usp_InsertSalesOrder_inmem
데이터베이스에 새로운 판매 주문을 삽입하고 해당 판매 주문의
SalesOrderID
를 출력합니다. 입력 매개 변수로는 주문의 품목뿐만 아니라 판매 주문 헤더에 대한 세부 정보를 사용합니다.출력 매개 변수:
- @SalesOrderID int – 방금 삽입된 판매 주문의
SalesOrderID
- @SalesOrderID int – 방금 삽입된 판매 주문의
입력 매개 변수(필수):
@DueDate datetime2
@CustomerID int
@BillToAddressID [int]
@ShipToAddressID [int]
@ShipMethodID [int]
@SalesOrderDetails
Sales.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
SalesOrderHeader
및SalesOrderDetail
테이블을 비우고 다시 시드하여 데모를 다시 설정합니다.
다음 저장 프로시저는 도메인 및 참조 무결성을 보장하면서 메모리 최적화 테이블에서 삽입 및 삭제하는 데 사용됩니다.
Production.usp_InsertProduct_inmem
Production.usp_DeleteProduct_inmem
Sales.usp_InsertSpecialOffer_inmem
Sales.usp_DeleteSpecialOffer_inmem
Sales.usp_InsertSpecialOfferProduct_inmem
마지막으로 다음 저장 프로시저를 사용하여 도메인 및 참조 무결성을 확인합니다.
dbo.usp_ValidateIntegrity
선택적 매개 변수: @object_id – 무결성을 확인할 개체의 ID
이 프로시저는 확인되어야 하는 무결성 규칙에 대해
dbo.DomainIntegrity
,dbo.ReferentialIntegrity
및dbo.UniqueIntegrity
테이블에 의존합니다. 예제에서는 이러한 테이블을AdventureWorks2022
데이터베이스의 원래 테이블에 존재하는 CHECK, FOREIGN KEY 및 UNIQUE 제약 조건을 기반으로 채웁니다.이 프로시저는 도우미 프로시저
dbo.usp_GenerateCKCheck
,dbo.usp_GenerateFKCheck
및dbo.GenerateUQCheck
에 의존하여 무결성 검사를 수행하는 데 필요한 T-SQL을 생성합니다.
데모 작업을 사용한 성능 측정
Ostress는 Microsoft CSS SQL Server 지원 팀에서 개발한 명령줄 도구입니다. 이 도구를 사용하여 쿼리를 실행하거나 저장 프로시저를 병렬로 실행할 수 있습니다. 지정된 T-SQL 문을 병렬로 실행하도록 스레드 수를 구성할 수 있으며 이 스레드에서 문을 실행할 횟수를 지정할 수 있습니다. ostress는 스레드를 스핀업하고 모든 스레드에서 문을 병렬로 실행합니다. 모든 스레드의 실행이 완료된 후 ostress는 모든 스레드의 실행이 완료되는 데 걸린 시간을 보고합니다.
ostress 설치
Ostress는 리포트 생성 언어(RML) 유틸리티의 일부로 설치됩니다. ostress에 대한 독립 실행형 설치가 없습니다.
설치 단계:
다음 페이지 SQL Server용 RML 다운로드에서 RML 유틸리티용 x64 설치 패키지를 다운로드하고 실행합니다.
특정 파일이 사용 중이라는 대화 상자가 나타나면 '계속’을 선택합니다.
ostress 실행
Ostress는 명령줄 프롬프트에서 실행됩니다. RML 유틸리티의 일부로 설치된 "RML Cmd 프롬프트"에서 도구를 실행하는 것이 가장 편리합니다.
RML Cmd Prompt를 열려면 다음 지침을 따르세요.
Windows에서 Windows 키를 선택하고 rml
을 입력하여 시작 메뉴를 엽니다. 검색 결과의 목록에서 "RML Cmd Prompt"를 선택합니다.
명령 프롬프트가 RML 유틸리티 설치 폴더에 있는지 확인합니다.
명령줄 옵션 없이 단순히 ostress.exe를 실행할 때 ostress에 대한 명령줄 옵션을 볼 수 있습니다. 이 예제와 함께 ostress를 실행하기 위해 고려할 기본 옵션은 다음과 같습니다.
연결할 Microsoft SQL Server 인스턴스의 -S 이름
-E Windows 인증을 사용하여 연결(기본값). SQL Server 인증을 사용하는 경우 –U 및 –P 옵션을 사용하여 사용자 이름과 비밀번호를 각각 지정합니다.
-d 데이터베이스의 이름. 이 예의 경우
AdventureWorks2022
입니다.-Q 실행할 T-SQL 문
-n 각 입력 파일/쿼리를 처리하는 연결 수
-r 각 입력 파일/쿼리를 실행할 각 연결에 대한 반복 수입니다.
데모 워크로드
데모 작업에 사용되는 기본 저장 프로시저는 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"
하드웨어에 따라 이 명령이 실행되는 데 몇 분 정도 걸릴 수 있습니다.
데모 실행이 끝날 때마다 다시 설정하는 것이 좋습니다. 이 워크로드는 삽입 전용이므로 각 실행은 더 많은 메모리를 소비하며, 이에 따라 메모리 부족이 발생하지 않도록 다시 설정해야 합니다. 실행 후 사용된 메모리 양은 워크로드를 실행한 후 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 | name | 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 | 5,504 |
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 | name | 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 | 5,504 |
총 6.5GB의 데이터를 볼 수 있습니다. SalesOrderHeader_inmem
및 SalesOrderDetail_inmem
테이블의 인덱스 크기는 판매 주문을 삽입하기 전의 인덱스 크기와 동일합니다. 두 테이블 모두 해시 인덱스를 사용하고 해시 인덱스가 정적이므로 인덱스 크기가 변경되지 않았습니다.
데모 초기화 후
저장 프로시저 Demo.usp_DemoReset
을 사용하여 데모를 다시 설정할 수 있습니다. 이 저장 프로시저는 테이블 SalesOrderHeader_inmem
및 SalesOrderDetail_inmem
의 데이터를 삭제하고 원래 테이블 SalesOrderHeader
및 SalesOrderDetail
로부터 데이터를 다시 시드합니다.
테이블의 행이 삭제되었더라도 메모리가 즉시 회수되지는 않습니다. SQL Server는 필요에 따라 백그라운드에서 메모리 최적화 테이블의 삭제된 행에서 메모리를 회수합니다. 데모가 다시 설정된 직후에는 시스템에 트랜잭션 작업이 없으므로 삭제된 행의 메모리가 아직 회수되지 않은 것을 확인할 수 있습니다.
SELECT type
, name
, pages_kb/1024 AS pages_MB
FROM sys.dm_os_memory_clerks WHERE type LIKE '%xtp%';
type | name | 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 | name | 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 | count | on-disk size MB |
---|---|---|---|
PRECREATED | 데이터 | 16 | 2048 |
PRECREATED | DELTA | 16 | 128 |
UNDER CONSTRUCTION | 데이터 | 1 | 128 |
UNDER CONSTRUCTION | DELTA | 1 | 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 | count | on-disk size MB |
---|---|---|---|
PRECREATED | 데이터 | 16 | 2048 |
PRECREATED | DELTA | 16 | 128 |
UNDER CONSTRUCTION | 데이터 | 1 | 128 |
UNDER CONSTRUCTION | DELTA | 1 | 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 | count | on-disk size MB |
---|---|---|---|
PRECREATED | 데이터 | 16 | 2048 |
PRECREATED | DELTA | 16 | 128 |
활성 | 데이터 | 38 | 5152 |
활성 | DELTA | 38 | 1331 |
MERGE TARGET | 데이터 | 7 | 896 |
MERGE TARGET | DELTA | 7 | 56 |
MERGED SOURCE | 데이터 | 13 | 1,772 |
MERGED SOURCE | DELTA | 13 | 4:55 |
병합 대상이 설치되고 시스템에서 트랜잭션 작업이 수행되면 병합된 원본이 정리됩니다.
데모 워크로드를 두 번째로 실행한 후 데모 재설정 후 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 | count | on-disk size MB |
---|---|---|---|
PRECREATED | 데이터 | 16 | 2048 |
PRECREATED | DELTA | 16 | 128 |
UNDER CONSTRUCTION | 데이터 | 2 | 268 |
UNDER CONSTRUCTION | DELTA | 2 | 16 |
활성 | 데이터 | 41 | 5608 |
활성 | DELTA | 41 | 328 |
이 경우에는 'under construction' 상태의 검사점 파일 쌍이 두 개 있습니다. 즉, 작업의 높은 동시성 수준 때문에 여러 파일 쌍이 ‘under construction’ 상태로 이동했습니다. 여러 동시 스레드에서 같은 시간에 새로운 파일 쌍을 필요로 했으므로 파일 쌍이 'precreated'에서 ‘under construction’으로 이동했습니다.