DAX 클러스터의 총 용량 및 가용성은 노드 유형과 수에 따라 달라집니다. 클러스터에 노드가 많을수록 읽기 용량은 증가하지만, 쓰기 용량은 증가하지 않습니다. 노드 유형이 클수록(최대 r5.8xlarge) 더 많은 쓰기를 처리할 수 있으나, 노드가 너무 적으면 노드 장애 발생 시 가용성에 영향을 미칠 수 있습니다. DAX 클러스터 크기 조정에 대한 자세한 내용은 DAX 클러스터 크기 조정 안내서 섹션을 참조하세요.
다음 섹션에서는 확장 가능하고 비용 효율적인 클러스터를 만들기 위해 노드 유형과 수의 균형을 맞추려면 고려해야 하는 다양한 크기 조정 측면을 설명합니다.
이 섹션의 내용
가용성 계획
DAX 클러스터의 크기를 조정할 때는 먼저 대상 가용성에 초점을 맞춰야 합니다. DAX와 같은 클러스터링된 서비스의 가용성은 클러스터에 있는 총 노드 수의 차원입니다. 단일 노드 클러스터는 장애를 용납하지 않으므로, 가용성은 노드 1개와 같습니다. 10노드 클러스터에서는 단일 노드 손실이 클러스터의 전체 용량에 미치는 영향이 미미합니다. 나머지 노드는 여전히 읽기 요청을 처리할 수 있기 때문에 손실이 생겨도 가용성에 직접적인 영향을 미치지 않습니다. DAX는 쓰기를 재개하기 위해 새 프라이머리 노드를 신속하게 지명합니다.
DAX는 VPC 기반입니다. 서브넷 그룹을 사용하여 노드를 실행할 수 있는 가용 영역
쓰기 처리량 계획
모든 DAX 클러스터에는 라이트-스루 요청을 위한 프라이머리 노드가 있습니다. 클러스터의 노드 유형 크기에 따라 쓰기 용량이 결정됩니다. 읽기 전용 복제본을 추가해도 클러스터의 쓰기 용량은 증가하지 않습니다. 따라서 나중에 노드 유형을 변경할 수 없으므로, 클러스터를 생성할 때 쓰기 용량을 고려해야 합니다.
애플리케이션에서 항목 캐시를 업데이트하려고 DAX를 통해 작성해야 하는 경우 쓰기가 용이하도록 클러스터 리소스 사용을 늘리는 것이 좋습니다. DAX에 대한 쓰기는 캐시 적중 읽기보다 약 25배 더 많은 리소스를 소비합니다. 이 경우 읽기 전용 클러스터보다 더 큰 노드 유형이 필요할 수 있습니다.
라이트-스루 또는 라이트-어라운드가 애플리케이션에 가장 적합한지 여부를 결정하는 방법에 대한 추가 지침은 쓰기 전략 섹션을 참조하세요.
읽기 처리량 계획
DAX 클러스터의 읽기 용량은 워크로드의 캐시 적중률에 따라 달라집니다. 캐시 누락이 발생할 경우 DAX는 DynamoDB에서 데이터를 읽기 때문에 캐시 적중률보다 약 10배 더 많은 클러스터 리소스를 소비합니다. 캐시 적중률을 높이려면 캐시의 TTL 설정을 높여 항목이 캐시에 저장되는 기간을 정의하세요. 하지만 TTL 기간이 길수록 DAX를 통해 업데이트를 작성하지 않는 한 이전 항목 버전을 읽을 가능성이 높아집니다.
클러스터에 충분한 읽기 용량이 있는지 확인하려면 클러스터의 수평적 스케일링에서 설명한 대로 클러스터를 수평으로 스케일링하세요. 노드를 더 추가하면 리소스 풀에 읽기 전용 복제본이 추가되고, 노드를 제거하면 읽기 용량이 줄어듭니다. 클러스터의 노드 수와 크기를 선택할 때는 필요한 최소 읽기 용량과 최대 읽기 용량을 모두 고려해야 합니다. 읽기 요구 사항에 맞게 더 작은 노드 유형으로 클러스터를 수평적으로 스케일링할 수 없는 경우 더 큰 노드 유형을 사용하세요.
데이터세트 크기 계획
사용 가능한 각 노드 유형에는 DAX가 데이터를 캐시할 수 있도록 설정된 메모리 크기가 있습니다. 노드 유형이 너무 작으면 애플리케이션이 요청하는 작업 데이터세트가 메모리에 맞지 않아 캐시 누락이 발생합니다. 큰 노드는 더 큰 캐시를 지원하므로, 캐싱해야 하는 예상 데이터세트보다 큰 노드 유형을 사용하세요. 캐시가 클수록 적중률도 향상됩니다.
반복 읽기가 거의 없는 항목을 캐싱하면 수익이 줄어들 수 있습니다. 자주 액세스하는 항목의 메모리 크기를 계산하고 캐시가 해당 데이터세트를 저장할 수 있을 만큼 충분히 큰지 확인하세요.
대략적인 클러스터 용량 요구 사항 계산
워크로드의 총 용량 요구량을 추정하여 클러스터 노드의 적절한 크기와 수량을 선택할 수 있습니다. 추정하려면 가변 정규화된 초당 요청 수(정규화된 RPS)를 계산하세요. 이 변수는 캐시 적중, 캐시 누락, 쓰기를 포함하여 애플리케이션이 DAX 클러스터를 지원하는 데 필요한 총 작업 단위를 나타냅니다. 정규화된 RPS를 계산하려면 다음 입력을 사용합니다.
-
ReadRPS_CacheHit
- 캐시 적중으로 이어지는 초당 읽기 수를 지정합니다. -
ReadRPS_CacheMiss
- 캐시 누락으로 이어지는 초당 읽기 수를 지정합니다. -
WriteRPS
- DAX를 통과할 초당 쓰기 수를 지정합니다. -
DaxNodeCount
– DAX 클러스터에 있는 노드 수를 지정합니다. -
Size
- 쓰거나 읽는 항목의 크기를 KB 단위로 가장 가까운 KB로 반올림하여 지정합니다. -
10x_ReadMissFactor
- 값 10을 나타냅니다. 캐시 누락이 발생할 경우 DAX는 캐시 적중률보다 약 10배 더 많은 리소스를 사용합니다. -
25x_WriteFactor
- DAX 라이트-스루는 캐시 적중률보다 약 25배 많은 리소스를 사용하기 때문에 값 25를 나타냅니다.
다음 공식을 사용하여 정규화된 RPS를 계산할 수 있습니다.
Normalized RPS = (ReadRPS_CacheHit * Size) + (ReadRPS_CacheMiss * Size * 10x_ReadMissFactor) + (WriteRequestRate * 25x_WriteFactor * Size * DaxNodeCount)
예를 들어, 다음과 같은 입력 값을 고려합니다.
-
ReadRPS_CacheHit
= 50,000 -
ReadRPS_CacheMiss
= 1,000 -
ReadMissFactor
= 1 -
Size
= 2KB -
WriteRPS
= 100 -
WriteFactor
= 1 -
DaxNodeCount
= 3
공식에서 이러한 값을 대체하면 다음과 같이 정규화된 RPS를 계산할 수 있습니다.
Normalized RPS = (50,000 Cache Hits/Sec * 2KB) + (1,000 Cache Misses/Sec * 2KB * 10) + (100 Writes/Sec * 25 * 2KB * 3)
이 예제에서 정규화된 RPS의 계산된 값은 135,000입니다. 하지만 이 정규화된 RPS 값은 클러스터 사용률을 100% 미만으로 유지하거나 노드 손실을 고려하지 않습니다. 추가 용량을 고려하는 것이 좋습니다. 이를 위해서는 목표 사용률과 노드 손실 허용 한도라는 2가지 배수 요소 중 더 큰 값을 결정해야 합니다. 그런 다음 정규화된 RPS에 더 큰 인수를 곱하여 초당 목표 요청 수(목표 RPS)를 구하세요.
-
목표 사용률
성능에 영향을 미치면 캐시 누락이 증가하므로, DAX 클러스터를 100% 사용률로 실행하는 것은 권장하지 않습니다. 클러스터 사용률을 70% 이하로 유지하는 것이 가장 좋습니다. 이를 달성하려면 정규화된 RPS에 1.43을 곱하세요.
-
노드 손실 허용 한도
노드에 장애가 발생하는 경우 애플리케이션은 해당 요청을 나머지 노드에 분산할 수 있어야 합니다. 노드 사용률을 100% 미만으로 유지하려면 장애가 발생한 노드가 다시 온라인 상태가 될 때까지 추가 트래픽을 흡수할 수 있을 만큼 큰 노드 유형을 선택하세요. 노드 수가 적은 클러스터의 경우 한 노드에 장애가 발생할 때 각 노드는 더 큰 트래픽 증가를 허용해야 합니다.
프라이머리 노드에 장애가 발생하면 DAX가 자동으로 읽기 전용 복제본으로 장애 조치하고 이를 새로운 프라이머리 노드로 지정합니다. 복제본 노드에 장애가 발생하면 실패한 노드가 복구될 때까지 DAX 클러스터의 다른 노드가 계속 요청을 수행할 수 있습니다.
예를 들어, 노드 장애가 발생한 3노드 DAX 클러스터의 경우 나머지 두 노드에 50%의 추가 용량이 필요합니다. 이를 위해서는 1.5를 곱해야 합니다. 반대로, 장애가 발생한 노드가 있는 11노드 클러스터의 경우 나머지 노드에 10%의 추가 용량이 필요하거나 배율 1.1을 곱해야 합니다.
다음 공식을 사용하여 목표 RPS를 계산할 수 있습니다.
Target RPS = Normalized RPS * CEILING(TargetUtilization, NodeLossTolerance)
예를 들어, 목표 RPS를 계산하려면 다음 값을 고려하세요.
-
Normalized RPS
= 135,000 -
TargetUtilization
= 1.43최대 70%의 클러스터 사용률을 목표로 하고 있기 때문에
TargetUtilization
을 1.43으로 설정했습니다. -
NodeLossTolerance
= 1.53노드 클러스터를 사용하고 있으므로,
NodeLossTolerance
를 50% 용량으로 설정한다고 가정해 보겠습니다.
공식에서 이러한 값을 대체하면 다음과 같이 목표 RPS를 계산할 수 있습니다.
Target RPS = 135,000 * CEILING(1.43, 1.5)
이 예제에서는 NodeLossTolerance
의 값이 TargetUtilization
보다 크기 때문에 NodeLossTolerance
를 사용하여 목표 RPS 값을 계산합니다. 이를 통해 DAX 클러스터가 지원해야 하는 총 용량인 202,500의 목표 RPS 값을 얻을 수 있습니다. 클러스터에 필요한 노드 수를 결정하려면 목표 RPS를 다음 테이블의 해당 열에 매핑하세요. 목표 RPS가 202,500인 이 예제에서는 3개의 노드가 있는 dax.r5.large 노드 유형이 필요합니다.
노드 유형별 클러스터 처리량 용량 근사치 계산
Target RPS formula을 사용하여 다양한 노드 유형의 클러스터 용량을 추정할 수 있습니다. 다음 테이블에는 노드 유형이 1, 3, 5, 11인 클러스터의 대략적인 용량이 나와 있습니다. 이러한 용량이 있다고 해서 자체 데이터 및 요청 패턴으로 DAX의 부하 테스트를 수행할 필요가 없어지지는 않습니다. 또한, 고정 CPU 용량이 부족하기 때문에 이러한 용량에는 t 유형 인스턴스가 포함되지 않습니다. 다음 테이블의 모든 값 단위는 정규화된 RPS입니다.
노드 유형(메모리) | 노드 1 | 노드 3 | 노드 5 | 노드 11 |
---|---|---|---|---|
dax.r5.24xlarge(768GB) | 100만 | 300만 | 500만 | 11M |
dax.r5.16xlarge(512GB) | 100만 | 300만 | 500만 | 1,100만 |
dax.r5.12xlarge(384GB) | 100만 | 300만 | 500만 | 1,100만 |
dax.r5.8xlarge(256GB) | 100만 | 300만 | 500만 | 1,100만 |
dax.r5.4xlarge(128GB) | 60만 | 180만 | 300만 | 660만 |
dax.r5.2xlarge(64GB) | 30만 | 90만 | 150만 | 330만 |
dax.r5.xlarge(32GB) | 15만 | 45만 | 75만 | 165만 |
dax.r5.large(16GB) | 7만5천 | 22만5천 | 37만5천 | 82만5천 |
각 노드의 최대 한도는 1백만 초당 네트워크 작업 수(NPS)이므로, dax.r5.8xlarge 이상의 노드 유형은 추가 클러스터 용량을 제공하지 않습니다. 8xlarge보다 큰 노드 유형은 클러스터의 총 처리량 용량에 기여하지 않을 수 있습니다. 그러나 이러한 노드 유형은 더 큰 작업 데이터세트를 메모리에 저장하는 데 유용할 수 있습니다.
DAX 클러스터의 쓰기 용량 규모 조정
DAX에 대한 각 쓰기는 모든 노드에서 25개의 정규화된 요청을 소비합니다. 각 노드에 대해 1백만 RPS 한도가 있기 때문에, DAX 클러스터는 읽기 사용량을 고려하지 않고 초당 4만개의 쓰기로 제한됩니다.
사용 사례에서 캐시에 초당 4만회 이상의 쓰기가 필요한 경우 별도의 DAX 클러스터를 사용하고 이들 간에 쓰기를 샤드해야 합니다. DynamoDB와 마찬가지로 캐시에 쓰는 데이터의 파티션 키를 해시할 수 있습니다. 그런 다음 모듈러스를 사용하여 데이터를 쓸 샤드를 결정합니다.
다음 예제에서는 입력 문자열의 해시를 계산합니다. 그런 다음 해시 값의 모듈러스를 10으로 계산합니다.
def hash_modulo(input_string): # Compute the hash of the input string hash_value = hash(input_string) # Compute the modulus of the hash value with 10 bucket_number = hash_value % 10 return bucket_number #Example usage if _name_ == "_main_": input_string = input("Enter a string: ") result = hash_modulo(input_string) print(f"The hash modulo 10 of '{input_string}' is: {result}.")