메모리 액세스에 최적화된 테이블의 테이블 및 행 크기

적용 대상:SQL ServerAzure SQL DatabaseAzure SQL Managed Instance

SQL Server 2016(13.x) 이전에는 메모리 최적화 테이블의 행 내 데이터 크기가 8,060바이트를 초과할 수 없었습니다. 그러나 SQL Server 2016(13.x)을 시작하고 Azure SQL Database에서 이제 여러 개의 큰 열(예: 여러 varbinary(8000) 열) 및 LOB 열(예: varbinary(max), varchar(max), nvarchar(max))을 사용하여 메모리 최적화 테이블을 만들고 고유하게 컴파일된 T-SQL 모듈 및 테이블 형식을 사용하여 작업을 수행할 수 있습니다.

8060바이트 행 크기 제한에 맞지 않는 열은 행 외부의 별도 내부 테이블에 배치됩니다. 각 행 외부 열에는 해당하는 내부 테이블이 있으며, 이 테이블에는 단일 비클러스터형 인덱스가 있습니다. 행 외부 열에 사용되는 이러한 내부 테이블에 대한 자세한 내용은 sys.memory_optimized_tables_internal_attributes(Transact-SQL)를 참조하세요.

행과 테이블의 크기를 계산하는 것이 유용한 특정 시나리오가 있습니다.

  • 테이블에서 사용하는 메모리는 얼마인가요?

    • 테이블에서 사용하는 메모리 양을 정확하게 계산할 수 없습니다. 여러 가지 요소들이 사용되는 메모리 양에 영향을 줍니다. 페이지 기반 메모리 할당, 지역성, 캐싱 및 패딩과 같은 요소입니다. 또한 활성 트랜잭션이 연결되어 있거나 가비지 수집을 기다리는 여러 버전의 행도 있습니다.

    • 테이블의 데이터 및 인덱스에 필요한 최소 크기는 아래에서 설명하는 [table size]에 대한 계산으로 제공됩니다.

    • 메모리 사용량에 대한 계산은 아무리 잘해도 근사값만 얻을 수 있으며, 배포 계획에 용량 계획을 포함하는 것이 좋습니다.

  • 행의 데이터 크기이며 8,060 바이트 행 크기 제한에 맞습니까? 이러한 질문에 대답하려면 아래에 설명된 [행 본문 크기]에 대한 계산을 사용합니다.

메모리 최적화 테이블은 행에 대한 포인터를 포함하는 행 및 인덱스의 컬렉션으로 구성됩니다. 다음 그림에서는 인덱스와 행이 있는 테이블을 보여 줍니다. 이 테이블에는 행 머리글과 본문이 있습니다.

Memory optimized table.
인덱스와 행으로 구성된 메모리 액세스에 최적화된 테이블

컴퓨팅 테이블 크기

테이블의 메모리 내 크기(바이트)는 다음과 같이 계산됩니다.

[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 count] * [index key size].

행 크기는 헤더 및 본문을 추가하여 계산됩니다.

[row size] = [row header size] + [actual row body size]  
[row header size] = 24 + 8 * [number of indexes]  

행 본문 크기 계산

행 구조 메모리 최적화 테이블의 행에는 다음과 같은 구성 요소가 있습니다.

  • 행 머리글에는 행 버전 관리를 구현하는 데 필요한 타임스탬프가 포함됩니다. 행 헤더에는 해시 버킷에서 행 체인을 구현하기 위한 인덱스 포인터도 포함되어 있습니다(위에서 설명).

  • 행 본문에는 실제 열 데이터가 포함됩니다. 여기에는 null 허용 열의 null 배열 및 가변 길이 데이터 형식에 대한 오프셋 배열과 같은 일부 보조 정보가 포함됩니다.

다음 그림에서는 두 개의 인덱스가 있는 테이블에 대한 행 구조를 보여 줍니다.

Row structure for a table that has two indexes.

시작 및 종료 타임스탬프는 특정 행 버전의 유효 기간을 나타냅니다. 이 간격에서 시작하는 트랜잭션은 이 행 버전을 볼 수 있습니다. 자세한 내용은 메모리 최적화 테이블이 있는 트랜잭션을 참조 하세요.

인덱스 포인터는 체인에서 해시 버킷에 속한 그 다음 행을 가리킵니다. 다음 그림에서는 두 개의 열(이름, 도시)과 두 개의 인덱스가 있는 테이블의 구조를 보여 줍니다. 하나는 열 이름에, 다른 하나는 열 도시에 있습니다.

Structure of a table with two columns and indexes.

이 그림에서 John과 Jane의 이름은 첫 번째 버킷에 해시됩니다. 수잔은 두 번째 버킷으로 해시됩니다. 베이징과 보고타는 첫 번째 양동이로 해시됩니다. 파리와 프라하는 두 번째 버킷에 해시됩니다.

따라서 이름에 대한 해시 인덱스의 체인은 다음과 같습니다.

  • 첫 번째 버킷: (존, 베이징); (존, 파리); (제인, 프라하)

  • 두 번째 버킷: (Susan, 보고타)

도시 인덱스의 체인은 다음과 같습니다.

  • 첫 번째 버킷: (존, 베이징), (수잔, 보고타)

  • 두 번째 버킷: (존, 파리), (제인, 프라하)

종료 타임스탬프 ∞(무한대)는 해당 행이 현재 유효한 버전의 행임을 나타냅니다. 이 행 버전이 작성된 이후로 행이 업데이트되거나 삭제되지 않았습니다.

200보다 큰 시간 동안 테이블에는 다음 행이 포함됩니다.

입력 City
John 베이징
Jane 프라하

그러나 시작 시간이 100인 활성 트랜잭션에는 다음과 같은 테이블 버전이 표시됩니다.

입력 City
John 파리
Jane 프라하
Susan 보고타 주

[행 본문 크기]의 계산은 다음 표에 설명되어 있습니다.

행 본문 크기는 계산된 크기 및 실제 크기의 두 가지 방식으로 계산할 수 있습니다.

  • 계산된 행 본문 크기표시된 계산 크기는 8,060바이트의 행 크기 제한을 초과하는지 확인하는 데 사용됩니다.

  • 실제 행 본문 크기로 표시된 실제 크기는 메모리 및 검사점 파일에서 행 본문의 실제 스토리지 크기입니다.

계산된 행 본문 크기와실제 행 본문 크기는 모두 비슷하게 계산됩니다. 유일한 차이점은 다음 표의 하단에 표시된 것처럼 (n)varchar(i) 및 varbinary(i) 열의 크기에 대한 계산입니다. 계산된 행 본문 크기는 선언된 크기인 i 를 열 크기로 사용하고, 실제 행 본문 크기는 데이터의 실제 크기를 사용합니다.

다음 표에서는 실제 행 본문 크기 = SUM(단순 형식의 크기) + 2 + 2 * 깊이 형식 열 수로 지정된 행 본문 크기의 계산에 대해 설명합니다.

섹션 크기 주석
단순 형식 열 SUM([단순 형식의 크기]) 개별 형식의 크기(바이트)는 다음과 같습니다.

비트: 1

Tinyint: 1

Smallint: 2

Int: 4

Real: 4

Smalldatetime: 4

스몰머니: 4

비긴트: 8

날짜/시간: 8

Datetime2: 8

Float: 8

: 8

숫자(전체 자릿수 <=18): 8

Time: 8

Numeric(precision>18): 16

Uniqueidentifier: 16
단순 열 안쪽 여백 가능한 값은 다음과 같습니다.

1 딥 형식 열이 있고 단순 열의 총 데이터 크기가 홀수인 경우

0이 아닌 경우
딥 형식은 형식(var)이진 및 (n)(var)char입니다.
전체 형식 열의 오프셋 배열 가능한 값은 다음과 같습니다.

딥 형식 열이 없는 경우 0

그렇지 않으면 2 + 2 * [number of deep type columns]입니다.
딥 형식은 형식(var)이진 및 (n)(var)char입니다.
NULL 배열 [null 허용 열 수] / 8, 전체 바이트로 반올림. 배열에는 nullable 열당 1비트가 있습니다. 전체 바이트로 반올림됩니다.
NULL 배열 패딩 가능한 값은 다음과 같습니다.

딥 형식 열이 있고 NULL 배열의 크기가 홀수 바이트인 경우 1입니다.

0이 아닌 경우
딥 형식은 형식(var)이진 및 (n)(var)char입니다.
패딩 딥 형식 열이 없는 경우: 0

딥 형식 열이 있는 경우 얕은 열에 필요한 가장 큰 맞춤에 따라 0-7바이트의 안쪽 여백이 추가됩니다. GUID 열에는 1바이트(16이 아님)의 맞춤이 필요하고 숫자 열에는 항상 8바이트(16이 아님)의 맞춤이 필요하다는 점을 제외하고 각 단순 열에는 위에 설명된 것과 같은 크기로 맞춤이 필요합니다. 모든 단순 열 중에서 가장 큰 맞춤 요구 사항이 사용되며, 0-7바이트의 안쪽 여백이 추가되어 지금까지 전체 크기(딥 형식 열 제외)가 필요한 맞춤의 배수가 됩니다.
딥 형식은 형식(var)이진 및 (n)(var)char입니다.
고정 길이 딥 형식 열 SUM(고정 길이 딥 형식 열의 크기)

각 열의 크기는 다음과 같습니다.

char(i) 및 binary(i)용 i입니다.

2 * i for nchar(i)
고정 길이 딥 형식 열은 char(i), nchar(i) 또는 binary(i) 형식의 열입니다.
가변 길이 심층 형식 열 계산 크기 SUM(가변 길이 심층 형식 열의 계산 크기)

각 열의 계산 크기는 다음과 같습니다.

varchar(i) 및 varbinary(i)용 i

2 * i for nvarchar(i)
이 행은 계산된 행 본문 크기에만 적용됩니다.

가변 길이 딥 형식 열은 varchar(i), nvarchar(i) 또는 varbinary(i) 형식의 열입니다. 계산된 크기는 열의 최대 길이(i)에 따라 결정됩니다.
가변 길이 딥 형식 열 실제 크기 SUM(가변 길이 심층 형식 열의 실제 크기)

각 열의 실제 크기는 다음과 같습니다.

n, 여기서 n은 varchar(i)의 경우 열에 저장된 문자 수입니다.

2 * n, 여기서 n은 nvarchar(i)에 대해 열에 저장된 문자 수입니다.

n, 여기서 n은 varbinary(i)에 대해 열에 저장된 바이트 수입니다.
이 행은 실제 행 본문 크기에만 적용됩니다.

실제 크기는 행의 열에 저장된 데이터에 의해 결정됩니다.

예제: 테이블 및 행 크기 계산

해시 인덱스의 경우 실제 버킷 수는 가장 가까운 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  

이 테이블에는 해시 인덱스 하나와 비클러스터형 인덱스(기본 키)가 있습니다. 또한 고정 길이 열 3개와 가변 길이 열 1개가 있으며 열 중 하나는 NULLable(OrderDescription)입니다. 테이블에 8379개의 행이 있고 열에 있는 OrderDescription 값의 평균 길이가 78자라고 가정 Orders 해 보겠습니다.

테이블 크기를 결정하려면 먼저 인덱스 크기를 결정해야 합니다. 두 인덱스의 bucket_count 10000으로 지정됩니다. 16384: 2의 가장 가까운 전력으로 반올림됩니다. 따라서 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  

다음으로 [실제 행 본문 크기]를 계산해 보겠습니다.

  • 단순 형식 열:

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

행 끄기 열 제한 사항

메모리 최적화 테이블에 행 외부 열을 사용할 경우의 특정 제한 사항 및 유의 사항은 다음과 같습니다.

  • 메모리 최적화 테이블에 대한 columnstore 인덱스가 있는 경우 모든 열이 행 내부에 맞아야 합니다.
  • 모든 인덱스 키 열은 행 내부에 저장되어야 합니다. 인덱스 키 열이 행 내부에 맞지 않을 경우 인덱스를 추가하지 못합니다.
  • 행 열이 없는 메모리 최적화 테이블을 변경하는 경우 주의 해야 합니다.
  • LOB의 경우 크기 제한은 디스크 기반 테이블의 크기 제한(LOB 값에 대한 2GB 제한)을 미러링합니다.
  • 최적의 성능을 위해 대부분의 열이 8060바이트 내에 맞는 것이 좋습니다.

What's new for In-Memory OLTP in SQL Server 2016 since CTP3(CTP3 이후 SQL Server 2016의 메모리 내 OLTP에 대한 새로운 기능) 블로그 게시물에서 이러한 몇 가지 복잡한 사항을 자세히 설명합니다.

참고 항목

메모리 최적화 테이블