메모리 액세스에 최적화된 테이블의 테이블 및 행 크기
메모리 최적화 테이블은 행의 컬렉션과 행에 대한 포인터를 포함하는 인덱스로 구성됩니다. 메모리 최적화 테이블에서 행은 8,060바이트를 초과할 수 없습니다. 메모리 최적화 테이블의 크기를 알면 컴퓨터 메모리가 충분한지 이해하는 데 도움이 됩니다.
테이블과 행 크기를 계산하는 이유는 두 가지입니다.
테이블에서 사용하는 메모리의 양
테이블에서 사용되는 메모리의 양은 정확하게 계산할 수 없습니다. 여러 가지 요소들이 사용되는 메모리 양에 영향을 줍니다. 이러한 요소에는 페이지 기반 메모리 할당, 지역성, 캐시 및 패딩 등이 포함됩니다. 또한 연관된 활성 트랜잭션을 포함하는 행이나 가비지 수집을 대기 중인 행 등 여러 가지 버전의 행이 존재할 수 있습니다.
테이블의 데이터 및 인덱스에 필요한 최소 크기는 아래에서 설명하는 [table size]에 대한 계산으로 제공됩니다.
메모리 사용량에 대한 계산은 아무리 잘해도 근사값만 얻을 수 있으며, 배포 계획에 용량 계획을 포함하는 것이 좋습니다.
행의 데이터 크기 및 8,060바이트 행 크기 제한에 맞는지 여부. 이 질문에 답변하려면 아래 설명된 [row body size] 계산을 사용합니다.
다음 그림에서는 인덱스 및 행을 포함하며 차례로 행 헤더와 본문을 가지는 테이블을 보여줍니다.
인덱스와 행으로 구성된 메모리 액세스에 최적화된 테이블
테이블의 메모리 내 크기(바이트)는 다음과 같이 계산됩니다.
[table size] = [size of index 1] + ... + [size of index n] + ([row size] * [row count])
해시 인덱스의 크기는 테이블을 만들 때 고정되며 실제 버킷 수에 따라 달라집니다. 인덱스 사양으로 지정된 bucket_count는 [실제 버킷 수]를 얻기 위해 가장 가까운 2의 제곱으로 반올림됩니다. 예를 들어, 지정된 bucket_count가 100000인 경우 인덱스의 [실제 버킷 수]는 131072입니다.
[hash index size] = 8 * [actual bucket count]
행 크기는 헤더 및 본문을 추가하여 계산됩니다.
[row size] = [row header size] + [actual row body size]
[row header size] = 24 + 8 * [number of indices]
행 본문 크기
[row body size] 계산은 다음 표에서 설명합니다.
행 본문 크기는 계산된 크기 및 실제 크기의 두 가지 방식으로 계산할 수 있습니다.
[computed row body size]로 표시되는 계산 크기는 행 크기 제한인 8,060바이트를 초과하는지 여부를 확인하기 위해 사용됩니다.
[actual row body size]로 표시되는 실제 크기는 메모리 및 검사점 파일에서 행 본문의 실제 스토리지 크기입니다.
[computed row body size] 및 [actual row body size]는 모두 비슷하게 계산됩니다. 유일한 차이점은 다음 표의 하단에 표시된 것처럼 (n)varchar(i) 및 varbinary(i) 열의 크기에 대한 계산입니다. 계산된 행 본문 크기는 선언된 크기인 i 를 열 크기로 사용하고, 실제 행 본문 크기는 데이터의 실제 크기를 사용합니다.
다음 표에서는 [actual row body size] = SUM([size of shallow types]) + 2 + 2 * [number of deep type columns]와 같이 행 본문 크기의 계산에 대해 설명합니다.
섹션 | 크기 | 주석 |
---|---|---|
단순 형식 열 | SUM([size of shallow types]) 개별 형식의 크기는 다음과 같습니다. 비트 | 1 Tinyint | 1 Smallint | 2 Int | 4 Real | 4 Smalldatetime | 4 Smallmoney | 4 Bigint | 8 Datetime | 8 Datetime2 | 8 Float 8 Money 8 숫자(전체 자릿수 <=18) | 8 시간 | 8 Numeric(precision>18) | 16 Uniqueidentifier | 16 |
|
단순 열 패딩 | 가능한 값은 다음과 같습니다. 전체 형식 열이 있고 단순 열의 총 데이터 크기가 홀수인 경우 1입니다. 그렇지 않으면 0입니다. |
전체 형식은 (var)binary 및 (n)(var)char 형식입니다. |
전체 형식 열의 오프셋 배열 | 가능한 값은 다음과 같습니다. 전체 형식 열이 없으면 0입니다. 그렇지 않으면 2 + 2 * [number of deep type columns]입니다. |
전체 형식은 (var)binary 및 (n)(var)char 형식입니다. |
NULL 배열 | [number of nullable columns]/8, 전체 바이트로 반올림 | null 허용 열당 1비트가 배열에 포함됩니다. 전체 바이트로 반올림됩니다. |
NULL 배열 패딩 | 가능한 값은 다음과 같습니다. 전체 형식 열이 있고 NULL 배열의 크기가 홀수 바이트인 경우 1입니다. 그렇지 않으면 0입니다. |
전체 형식은 (var)binary 및 (n)(var)char 형식입니다. |
안쪽 여백 | 전체 형식 열이 없으면 0 전체 형식 열이 있는 경우, 단순 열에 필요한 최대 맞춤에 따라 0-7바이트의 패딩이 추가됩니다. 각 단순 열에는 위에 설명한 대로 해당 크기와 동일한 맞춤이 필요하지만, GUID 열은 16바이트가 아니라 1바이트의 맞춤이 필요하고, 숫자 열에는 항상 16이 아닌 8바이트의 맞춤이 필요합니다. 모든 단순 열 사이에 가장 높은 맞춤 요구 사항이 사용되며, 지금까지의 총 크기(전체 형식 열 없음)가 필요한 맞춤의 배수가 되도록 0-7바이트의 패딩이 추가됩니다. |
전체 형식은 (var)binary 및 (n)(var)char 형식입니다. |
고정 길이 전체 형식 열 | SUM([size of fixed length deep type columns]) 각 열의 크기는 다음과 같습니다. char(i) 및 binary(i)의 경우 i nchar(i)의 경우 2 * i |
고정 길이 전체 형식 열은 char(i), nchar(i) 또는 binary(i) 유형의 열입니다. |
가변 길이 전체 형식 열 [computed size] | SUM([computed size of variable length deep type columns]) 각 열에 대해 계산된 크기는 다음과 같습니다. varchar(i) 및 varbinary(i)의 경우 i nvarchar(i)의 경우 2 * i |
이 행은 [computed row body size]에만 적용되었습니다. 가변 길이 전체 형식 열은 varchar(i), nvarchar(i) 또는 varbinary(i) 유형의 열입니다. 계산된 크기는 열의 최대 길이(i)에 의해 결정됩니다. |
가변 길이 전체 형식 열 [actual size] | SUM([actual size of variable length deep type columns]) 각 열의 실제 크기는 다음과 같습니다. n, 여기서 n은 varchar(i)에 대해 열에 저장된 문자 수입니다. 2 * n, 여기서 n은 nvarchar(i)에 대해 열에 저장된 문자 수입니다. n, 여기서 n은 varbinary(i)에 대해 열에 저장된 바이트 수입니다. |
이 행은 [actual row body size]에만 적용되었습니다. 실제 크기는 행의 열에 저장된 데이터에 의해 결정됩니다. |
행 구조
메모리 최적화 테이블의 행에는 다음과 같은 구성 요소가 있습니다.
행 머리글에는 행 버전 관리를 구현하는 데 필요한 타임스탬프가 포함됩니다. 행 머리글에는 위에서 설명한 해시 버킷의 행 체인을 구현하는 인덱스 포인터도 포함됩니다.
행 본문에는 실제 열 데이터가 포함됩니다. 여기에는 Null 허용 열에 대한 Null 배열과 가변 길이 데이터 형식에 대한 오프셋 배열 같은 일부 보조 정보가 포함됩니다.
다음 그림에서는 두 개의 인덱스가 있는 테이블에 대한 행 구조를 보여 줍니다.
시작 및 종료 타임스탬프는 특정 행 버전의 유효 기간을 나타냅니다. 이 기간에 시작되는 트랜잭션은 이 행 버전을 참조할 수 있습니다. 자세한 내용은 메모리 액세스에 최적화된 테이블의 트랜잭션을 참조하세요.
인덱스 포인터는 체인에서 해시 버킷에 속한 그 다음 행을 가리킵니다. 다음 그림에서는 두 개의 열(이름, 도시)이 있는 테이블의 구조를 보여 줍니다. 여기에는 두 개의 인덱스가 있는데, 하나의 이름 열에 대한 것이고 다른 하나는 도시 열에 대한 것입니다.
이 그림에서는 John과 Jane이라는 이름이 첫 번째 버킷에 해시됩니다. Susan은 두 번째 버킷에 해시됩니다. 베이징과 보고타는 첫 번째 버킷에 해시됩니다. 파리와 프라하는 두 번째 버킷에 해시됩니다.
따라서 이름에 대한 해시 인덱스의 체인은 다음과 같습니다.
첫 번째 버킷: (John, 베이징), (John, 파리), (Jane, 프라하)
두 번째 버킷: (Susan, 보고타)
도시에 대한 인덱스의 체인은 다음과 같습니다.
첫 번째 버킷: (John, 베이징), (Susan, 보고타)
두 번째 버킷: (John, 파리), (Jane, 프라하)
종료 타임스탬프 ∞(무한대)는 해당 행이 현재 유효한 버전의 행임을 나타냅니다. 이 행 버전이 기록되었기 때문에 행은 업데이트되거나 삭제되지 않았습니다.
200보다 큰 시간의 경우 테이블에 다음 행이 포함됩니다.
Name | City |
---|---|
John | 베이징 |
Jane | 프라하 |
그러나 시작 시간이 100인 활성 트랜잭션은 다음 버전의 테이블을 참조합니다.
Name | City |
---|---|
John | 파리 |
Jane | 프라하 |
Susan | 보고타 |
예제: 테이블 및 행 크기 계산
해시 인덱스의 경우 실제 버킷 수는 가장 가까운 2의 제곱으로 반올림됩니다. 예를 들어, 지정된 bucket_count가 100000인 경우 인덱스의 실제 버킷 수는 131072입니다.
다음 정의의 Orders 테이블을 살펴보십시오.
CREATE TABLE dbo.Orders (
OrderID int NOT NULL
PRIMARY KEY NONCLUSTERED,
CustomerID int NOT NULL
INDEX IX_CustomerID HASH WITH (BUCKET_COUNT=10000),
OrderDate datetime NOT NULL,
OrderDescription nvarchar(1000)
) WITH (MEMORY_OPTIMIZED=ON)
GO
이 테이블에는 1개의 해시 인덱스와 1개의 비클러스터형 인덱스(기본 키)가 있습니다. 또한 3개의 고정 길이 열과 1개의 가변 길이 열이 포함되며, 이러한 열 중 하나는 Null 허용으로 구성됩니다(OrderDescription). Orders 테이블에 8379개의 행이 있고 OrderDescription 열에 있는 값의 평균 길이가 78자라고 가정해 보겠습니다.
테이블 크기를 결정하려면 먼저 인덱스 크기를 결정해야 합니다. 두 인덱스의 bucket_count는 10000으로 지정되었습니다. 가장 가까운 2의 제곱인 16384로 반올림됩니다. 따라서 Orders 테이블에서 인덱스의 총 크기는 다음과 같습니다.
8 * 16384 = 131072 bytes
남은 것은 테이블 데이터 크기입니다.
[row size] * [row count] = [row size] * 8379
예제 테이블에는 8379개 행이 있습니다. 이제 다음과 같이 됩니다.
[row size] = [row header size] + [actual row body size]
[row header size] = 24 + 8 * [number of indices] = 24 + 8 * 1 = 32 bytes
그러면 이제 [actual row body size]를 계산해보겠습니다.
단순 형식 열:
SUM([size of shallow types]) = 4 [int] + 4 [int] + 8 [datetime] = 16
총 단순 열 크기는 짝수이므로 단순 열 패딩은 0입니다.
전체 형식 열의 오프셋 배열:
2 + 2 * [number of deep type columns] = 2 + 2 * 1 = 4
NULL 배열은 1입니다.
NULL 배열 크기가 홀수이고 전체 형식 열이 있으므로 NULL 배열 패딩은 1입니다.
안쪽 여백
가장 큰 맞춤 요구 사항은 8입니다.
지금까지 크기는 16 + 0 + 4 + 1 + 1 = 22입니다.
8의 가장 가까운 배수는 24입니다.
총 패딩은 24 - 22 = 2바이트입니다.
고정 길이 전체 형식 열은 없습니다(고정 길이 전체 형식 열은 0임).
전체 형식 열의 실제 크기는 2 * 78 = 156입니다. 단일 전체 형식 열 OrderDescription의 형식은 nvarchar입니다.
[actual row body size] = 24 + 156 = 180 bytes
계산을 완료하면 다음과 같습니다.
[row size] = 32 + 180 = 212 bytes
[table size] = 8 * 16384 + 212 * 8379 = 131072 + 1776348 = 1907420
메모리에서 총 테이블 크기는 약 2MB입니다. 여기에는 메모리 할당으로 발생하는 잠재적인 오버헤드와 이 테이블에 액세스하는 트랜잭션에 필요한 행 버전 관리가 고려되지 않았습니다.
이 테이블 및 해당 인덱스에 할당되어 사용되는 실제 메모리는 다음 쿼리를 통해 얻을 수 있습니다.
select * from sys.dm_db_xtp_table_memory_stats
where object_id = object_id('dbo.Orders')