메모리 액세스에 최적화된 테이블에 필요한 메모리 예측Estimate Memory Requirements for Memory-Optimized Tables

이 항목은 다음에 적용됩니다.예SQL Server(2014부터)예Azure SQL Database아니요Azure SQL Data Warehouse 아니요병렬 데이터 웨어하우스 THIS TOPIC APPLIES TO:yesSQL Server (starting with 2014)yesAzure SQL DatabasenoAzure SQL Data Warehouse noParallel Data Warehouse

메모리 액세스에 최적화된 테이블을 사용하려면 모든 행과 인덱스를 메모리 내에 유지하는 데 충분한 메모리가 있어야 합니다.Memory-optimized tables require that sufficient memory exist to keep all of the rows and indexes in memory. 메모리는 한정된 리소스이므로 시스템에서 메모리 사용량을 파악하고 관리해야 합니다.Because memory is a finite resource, it is important that you understand and manage memory usage on your system. 이 섹션의 항목에서는 일반적인 메모리 사용 및 관리 시나리오를 다룹니다.The topics in this section cover common memory use and management scenarios.

새로운 메모리 액세스에 최적화된 테이블을 만들든, 아니면 기존 디스크 기반 테이블을 메모리 내 OLTPIn-Memory OLTP 메모리 액세스에 최적화된 테이블로 마이그레이션하든 간에 충분한 메모리로 서버를 프로비전할 수 있도록 각 테이블에 필요한 메모리를 적절히 예측하는 것이 중요합니다.Whether you are creating a new memory-optimized table or migrating an existing disk-based table to an 메모리 내 OLTPIn-Memory OLTP memory-optimized table, it is important to have a reasonable estimate of each table’s memory needs so you can provision the server with sufficient memory. 이 섹션에서는 메모리 액세스에 최적화된 테이블의 데이터를 저장하는 데 필요한 메모리 양을 예측하는 방법에 대해 설명합니다.This section describes how to estimate the amount of memory that you need to hold data for a memory-optimized table.

디스크 기반 테이블에서 메모리 액세스에 최적화된 테이블로 마이그레이션을 고려하는 경우 이 항목을 진행하기 전에 마이그레이션에 가장 적합한 테이블에 대한 지침에 대해 메모리 내 OLTP에 테이블 또는 저장 프로시저를 이식해야 하는지 확인 항목을 참조하세요.If you are contemplating migrating from disk-based tables to memory-optimized tables, before you proceed in this topic, see the topic Determining if a Table or Stored Procedure Should Be Ported to In-Memory OLTP for guidance on which tables are best to migrate. 메모리 내 OLTP로 마이그레이션 아래의 모든 항목은 디스크 기반 테이블에서 메모리 액세스에 최적화된 테이블로 마이그레이션하는 지침을 제공합니다.All the topics under Migrating to In-Memory OLTP provide guidance on migrating from disk-based to memory-optimized tables.

메모리 요구 사항을 예측하기 위한 기본 지침Basic Guidance for Estimating Memory Requirements

테이블이 메모리에 적합해야 하지만 SQL Server 2016SQL Server 2016부터 메모리 액세스에 최적화된 테이블의 크기에 제한이 없습니다.Starting with SQL Server 2016SQL Server 2016, there is no limit on the size of memory-optimized tables, though the tables do need to fit in memory. SQL Server 2014SQL Server 2014 에서 SCHEMA_AND_DATA 테이블의 경우 지원되는 데이터 크기는 256GB입니다.In SQL Server 2014SQL Server 2014 the supported data size is 256GB for SCHEMA_AND_DATA tables.

메모리 액세스에 최적화된 테이블의 크기는 데이터 크기에 행 머리글에 대한 약간의 오버헤드를 더한 것에 해당합니다.The size of a memory-optimized table corresponds to the size of data plus some overhead for row headers. 디스크 기반 테이블을 메모리 액세스에 최적화되도록 마이그레이션하는 경우 메모리 액세스에 최적화된 테이블의 크기는 대략 원래 디스크 기반 테이블의 힙 또는 클러스터형 인덱스 크기에 해당합니다.When migrating a disk-based table to memory-optimized, the size of the memory-optimized table will roughly correspond to the size of the clustered index or heap of the original disk-based table.

메모리 액세스에 최적화된 테이블의 인덱스는 디스크 기반 테이블의 비클러스터형 인덱스보다 작은 경우가 많습니다.Indexes on memory-optimized tables tend to be smaller than nonclustered indexes on disk-based tables. 비클러스터형 인덱스의 크기는 [primary key size] * [row count]순입니다.The size of nonclustered indexes is in the order of [primary key size] * [row count]. 해시 인덱스의 크기는 [bucket count] * 8 bytes입니다.The size of hash indexes is [bucket count] * 8 bytes.

활성 워크로드가 있는 경우 행 버전 관리 및 다양한 작업을 위해 추가 메모리를 고려해야 합니다.When there is an active workload, additional memory is needed to account for row versioning and various operations. 실제로 필요한 메모리 양은 워크로드에 따라 다르지만 안전을 위해 메모리 액세스에 최적화된 테이블 및 인덱스의 예상 크기의 두 배로 시작하여 실제 필요한 메모리 요구 사항을 관측하는 것이 좋습니다.How much memory is needed in practice depends on the workload, but to be safe the recommendation is to start with two times the expected size of memory-optimized tables and indexes, and observe what are the memory requirements in practice. 행 버전 관리에 대한 오버헤드는 항상 워크로드의 특징에 따라 다릅니다. 특히 장기 실행 트랜잭션에서는 오버헤드가 증가합니다.The overhead for row versioning always depends on the characteristics of the workload - especially long-running transactions increase the overhead. 더 큰 데이터베이스를 사용하는 대부분의 워크로드(예: 100GB 초과)의 경우 오버헤드가 제한됩니다(25% 이하).For most workloads using larger databases (e.g., >100GB), overhead tends to be limited (25% or less).

메모리 요구 사항의 상세 계산Detailed Computation of Memory Requirements

메모리 액세스에 최적화된 테이블의 예 Example memory-optimized table

다음 메모리 액세스에 최적화된 테이블 스키마를 살펴봅니다.Consider the following memory-optimized table schema:

CREATE TABLE t_hk
(  
  col1 int NOT NULL  PRIMARY KEY NONCLUSTERED,  

  col2 int NOT NULL  INDEX t1c2_index   
      HASH WITH (bucket_count = 5000000),  

  col3 int NOT NULL  INDEX t1c3_index   
      HASH WITH (bucket_count = 5000000),  

  col4 int NOT NULL  INDEX t1c4_index   
      HASH WITH (bucket_count = 5000000),  

  col5 int NOT NULL  INDEX t1c5_index NONCLUSTERED,  

  col6 char (50) NOT NULL,  
  col7 char (50) NOT NULL,   
  col8 char (30) NOT NULL,   
  col9 char (50) NOT NULL  

  WITH (memory_optimized = on)  
);
GO  

위의 스키마를 사용하여 이 메모리 액세스에 최적화된 테이블에 필요한 최소 메모리를 결정할 것입니다.Using this schema we will determine the minimum memory needed for this memory-optimized table.

테이블에 대한 메모리 Memory for the table

메모리 액세스에 최적화된 테이블 행은 다음 세 부분으로 구성되어 있습니다.A memory-optimized table row is comprised of three parts:

  • 타임스탬프 Timestamps
    행 머리글/타임스탬프 = 24바이트Row header/timestamps = 24 bytes.

  • 인덱스 포인터 Index pointers
    테이블의 각 해시 인덱스의 경우, 각 행의 인덱스에는 다음 행에 대한 8바이트 주소 포인터가 있습니다.For each hash index in the table, each row has an 8-byte address pointer to the next row in the index. 인덱스가 4개 있으므로 각 행에 인덱스 포인터에 대한 32바이트가 할당됩니다(인덱스당 8바이트 포인터).Since there are 4 indexes, each row will allocate 32 bytes for index pointers (an 8 byte pointer for each index).

  • 데이터 Data
    행의 데이터 부분 크기는 각 데이터 열의 형식 크기를 합하여 결정됩니다.The size of the data portion of the row is determined by summing the type size for each data column. 예제 테이블에는 4바이트 정수 5개, 50바이트 문자 열 3개, 30바이트 문자 열 1개가 있습니다.In our table we have five 4-byte integers, three 50-byte character columns, and one 30-byte character column. 따라서 각 행의 데이터 부분은 4 + 4 + 4 + 4 + 4 + 50 + 50 + 50 + 30 또는 200바이트입니다.Therefore the data portion of each row is 4 + 4 + 4 + 4 + 4 + 50 + 50 + 30 + 50 or 200 bytes.

다음은 메모리 액세스에 최적화된 테이블에 있는 5,000,000(5백만)개 행에 대한 크기 계산입니다.The following is a size computation for 5,000,000 (5 million) rows in a memory-optimized table. 데이터 행에서 사용하는 총 메모리는 다음과 같이 예상됩니다.The total memory used by data rows is estimated as follows:

테이블의 행에 대한 메모리Memory for the table’s rows

위의 계산에서 메모리 액세스에 최적화된 테이블의 각 행 크기는 24 + 32 + 200 또는 256바이트입니다.From the above calculations, the size of each row in the memory-optimized table is 24 + 32 + 200, or 256 bytes. 행이 5백만 개이므로 테이블은 5,000,000 * 256바이트 또는 1,280,000,000바이트, 즉 1.28GB 정도를 사용합니다.Since we have 5 million rows, the table will consume 5,000,000 * 256 bytes, or 1,280,000,000 bytes – approximately 1.28 GB.

인덱스에 대한 메모리 Memory for indexes

각 해시 인덱스에 대한 메모리Memory for each hash index

각 해시 인덱스는 8바이트 주소 포인터의 해시 배열입니다.Each hash index is a hash array of 8-byte address pointers. 배열의 크기는 해당 인덱스에 대한 고유한 인덱스 값의 수에 따라 최적으로 결정됩니다. 예를 들어 고유한 Col2 값의 수는 t1c2_index의 배열 크기를 계산하기 위한 좋은 출발점입니다.The size of the array is best determined by the number of unique index values for that index – e.g., the number of unique Col2 values is a good starting point for the array size for the t1c2_index. 해시 배열이 너무 크면 메모리가 낭비되고,A hash array that is too big wastes memory. 해시 배열이 너무 작으면 동일한 인덱스로 해시되는 인덱스 값에 의한 충돌이 너무 많이 발생하므로 성능이 저하됩니다.A hash array that is too small slows performance since there will be too many collisions by index values that hash to the same index.

해시 인덱스는 같은지 여부를 확인하는 다음과 같은 조회를 매우 빠르게 수행합니다.Hash indexes achieve very fast equality lookups such as:

SELECT * FROM t_hk  
   WHERE Col2 = 3;

비클러스터형 인덱스는 다음과 같은 범위 조회의 경우 더욱 빠릅니다.Nonclustered indexes are faster for range lookups such as:

SELECT * FROM t_hk  
   WHERE Col2 >= 3;

디스크 기반 테이블을 마이그레이션하는 경우 다음을 사용하여 t1c2_index 인덱스에 대한 고유 값의 수를 확인할 수 있습니다.If you are migrating a disk-based table you can use the following to determine the number of unique values for the index t1c2_index.

SELECT COUNT(DISTINCT [Col2])  
  FROM t_hk;

새 테이블을 만드는 경우 배열 크기를 예측하거나 배포 전에 테스트에서 데이터를 수집해야 합니다.If you are creating a new table, you’ll need to estimate the array size or gather data from your testing prior to deployment.

해시 인덱스가 메모리 내 OLTPIn-Memory OLTP 메모리 액세스에 최적화된 테이블에서 작동하는 방식에 대한 자세한 내용은 해시 인덱스를 참조하세요.For information on how hash indexes work in 메모리 내 OLTPIn-Memory OLTP memory-optimized tables, see Hash Indexes.

해시 인덱스 배열 크기 설정Setting the hash index array size

해시 배열 크기는 (bucket_count= value) 에 의해 설정됩니다. 여기서 value 는 0보다 큰 정수 값입니다.The hash array size is set by (bucket_count= value) where value is an integer value greater than zero. value 가 2의 제곱이 아니면 실제 bucket_count는 가장 가까운 다음 2의 제곱으로 반올림됩니다.If value is not a power of 2, the actual bucket_count is rounded up to the next closest power of 2. 예제 테이블, (bucket_count = 5000000)에서 5,000,000은 2의 제곱이 아니므로 실제 버킷 수는 8,388,608(2^23)로 반올림됩니다.In our example table, (bucket_count = 5000000), since 5,000,000 is not a power of 2, the actual bucket count rounds up to 8,388,608 (2^23). 해시 배열에 필요한 메모리를 계산할 때는 5,000,000이 아니라 이 수를 사용해야 합니다.You must use this number, not 5,000,000 when calculating memory needed by the hash array.

따라서 이 예제에서 각 해시 배열에 필요한 메모리는 다음과 같습니다.Thus, in our example, the memory needed for each hash array is:

8,388,608 * 8 = 2^23 * 8 = 2^23 * 2^3 = 2^26 = 67,108,864 or approximately 64 MB.8,388,608 * 8 = 2^23 * 8 = 2^23 * 2^3 = 2^26 = 67,108,864 or approximately 64 MB.

해시 인덱스가 3개 있으므로 해시 인덱스에 필요한 메모리는 3 * 64MB = 192MB입니다.Since we have three hash indexes, the memory needed for the hash indexes is 3 * 64MB = 192MB.

비클러스터형 인덱스에 대한 메모리Memory for non-clustered indexes

비클러스터형 인덱스는 인덱스 값과 이후 노드에 대한 포인터가 포함된 내부 노드가 있는 BTrees로 구현됩니다.Non-clustered indexes are implemented as BTrees with the inner nodes containing the index value and pointers to subsequent nodes. 리프 노드에는 인덱스 값과 메모리의 테이블 행에 대한 포인터가 포함됩니다.Leaf nodes contain the index value and a pointer to the table row in memory.

해시 인덱스와 달리 비클러스터형 인덱스에는 고정된 버킷 크기가 없습니다.Unlike hash indexes, non-clustered indexes do not have a fixed bucket size. 인덱스는 데이터와 함께 동적으로 증가하고 감소합니다.The index grows and shrinks dynamically with the data.

비클러스터형 인덱스에 필요한 메모리는 다음과 같이 계산할 수 있습니다.Memory needed by non-clustered indexes can be computed as follows:

  • 리프가 아닌 노드에 할당된 메모리 Memory allocated to non-leaf nodes
    일반적인 구성에서 리프가 아닌 노드에 할당된 메모리는 인덱스에 사용되는 전체 메모리의 작은 비율을 차지합니다.For a typical configuration, the memory allocated to non-leaf nodes is a small percentage of the overall memory taken by the index. 이 비율은 매우 작으므로 무시해도 괜찮습니다.This is so small it can safely be ignored.

  • 리프 노드에 대한 메모리 Memory for leaf nodes
    리프 노드에는 테이블의 고유 키마다 행이 하나씩 있으며 이 행은 해당 고유 키가 있는 데이터 행을 가리킵니다.The leaf nodes have one row for each unique key in the table that points to the data rows with that unique key. 동일한 키가 있는 행이 여러 개인 경우(즉, 고유하지 않은 비클러스터형 인덱스가 있는 경우), 인덱스 리프 노드에는 행이 하나만 있으며 이 행은 다른 행과 서로 연결된 행 중 하나를 가리킵니다.If you have multiple rows with the same key (i.e., you have a non-unique non-clustered index), there is only one row in the index leaf node that points to one of the rows with the other rows linked to each other. 따라서 필요한 총 메모리는 대략적으로 다음과 같이 계산될 수 있습니다.Thus, the total memory required can be approximated by:

    • memoryForNonClusteredIndex = (pointerSize + sum(keyColumnDataTypeSizes)) * rowsWithUniqueKeysmemoryForNonClusteredIndex = (pointerSize + sum(keyColumnDataTypeSizes)) * rowsWithUniqueKeys

    비클러스터형 인덱스는 다음 쿼리로 예시된 범위 조회에 사용될 때 가장 효과적입니다.Non-clustered indexes are best when used for range lookups, as exemplified by the following query:

SELECT * FRON t_hk  
   WHERE c2 > 5;  

행 버전 관리에 대한 메모리 Memory for row versioning

잠금을 방지하기 위해 메모리 내 OLTP는 행을 업데이트하거나 삭제할 때 낙관적 동시성을 사용합니다.To avoid locks, In-Memory OLTP uses optimistic concurrency when updating or deleting rows. 즉, 행이 업데이트될 때 행의 추가 버전이 만들어집니다.This means that when a row is updated, an additional version of the row is created. 또한 삭제가 논리적으로 수행되어 기존 행이 삭제된 상태로 표시되지만 바로 제거되지는 않습니다.In addition, deletes are logical - the existing row is marked as deleted, but not removed immediately. 시스템에서는 해당 버전을 사용할 수 있는 모든 트랜잭션 실행이 완료될 때까지 이전 행 버전(삭제된 행 포함)을 사용할 수 있는 상태로 보관합니다.The system keeps old row versions (including deleted rows) available until all transactions that could possibly use the version have finished execution.

언제든지 가비지 수집 주기에 따라 메모리가 해제되기를 기다리는 많은 추가 행이 메모리에 있을 수 있기 때문에 이러한 추가 행을 수용하는 데 충분한 메모리가 있어야 합니다.Because there may be a number of additional rows in memory at any time waiting for the garbage collection cycle to release their memory, you must have sufficient memory to accommodate these additional rows.

추가 행 수는 행 업데이트 및 삭제의 초당 최대 수를 계산한 다음 가장 긴 트랜잭션에 소요되는 초 단위 시간(최소값 1)과 곱하여 예측할 수 있습니다.The number of additional rows can be estimated by computing the peak number of row updates and deletions per second, then multiplying that by the number of seconds the longest transaction takes (minimum of 1).

그런 다음 이 값을 행 크기와 곱하면 행 버전 관리에 필요한 바이트 수를 얻을 수 있습니다.That value is then multiplied by the row size to get the number of bytes you need for row versioning.

rowVersions = durationOfLongestTransctoinInSeconds * peakNumberOfRowUpdatesOrDeletesPerSecond

유효하지 않은 행에 필요한 메모리는 유효하지 않은 행 수와 메모리 액세스에 최적화된 테이블 행의 크기(위의 테이블에 대한 메모리 참조)를 곱하여 예측됩니다.Memory needs for stale rows is then estimated by multiplying the number of stale rows by the size of a memory-optimized table row (See Memory for the table above).

memoryForRowVersions = rowVersions * rowSize

테이블 변수에 대한 메모리 Memory for table variables

테이블 변수에 사용되는 메모리는 테이블 변수가 범위를 벗어날 때만 해제됩니다.Memory used for a table variable is released only when the table variable goes out of scope. 업데이트의 일부로 삭제된 행을 포함하여 테이블 변수에서 삭제된 행은 가비지 수집의 대상이 아닙니다.Deleted rows, including rows deleted as part of an update, from a table variable are not subject to garbage collection. 테이블 변수가 범위를 벗어날 때까지는 메모리가 해제되지 않습니다.No memory is released until the table variable exits scope.

프로시저 범위와 반대로 많은 트랜잭션에서 사용되는 큰 SQL 일괄 처리에서 정의된 테이블 변수는 다량의 메모리를 사용할 수 있습니다.Table variables defined in a large SQL batch, as opposed to a procedure scope, which are used in many transactions, can consume a lot of memory. 테이블 변수는 가비지 수집되지 않기 때문에 테이블 변수의 삭제된 행은 다량의 메모리를 사용할 수 있으며 읽기 작업이 삭제된 행을 통과하여 검색해야 하므로 성능이 저하될 수 있습니다.Because they are not garbage collected, deleted rows in a table variable can consume a lot memory and degrade performance since read operations need to scan past the deleted rows.

증가에 대한 메모리 Memory for growth

위의 계산에서는 현재 상태의 테이블에 필요한 메모리를 예측합니다.The above calculations estimate your memory needs for the table as it currently exists. 이 메모리 외에도 테이블의 증가를 예측하고 이러한 증가를 수용하는 데 충분한 메모리를 제공해야 합니다.In addition to this memory, you need to estimate the growth of the table and provide sufficient memory to accommodate that growth. 예를 들어 10% 증가를 예상하는 경우 위의 결과에 1.1을 곱하여 테이블에 필요한 총 메모리를 얻어야 합니다.For example, if you anticipate 10% growth then you need to multiple the results from above by 1.1 to get the total memory needed for your table.

관련 항목:See Also

메모리 내 OLTP로 마이그레이션Migrating to In-Memory OLTP