MemoryStoreService는 모든 서버에서 접근할 수 있는 빠른 메모리 내 데이터 저장소를 제공하는 고처리량 및 저지연 데이터 서비스입니다. 메모리 저장소는 자주 변경되며 내구성이 필요 없는 빠르고 임시 데이터에 적합합니다. 이러한 데이터는 접근 속도가 더 빠르고 최대 수명을 초과하면 사라집니다. 세션 간에 지속되어야 하는 데이터는 데이터 저장소를 사용하세요.
데이터 구조
원시 데이터에 직접 접근하는 대신, 메모리 저장소는 빠른 처리를 위해 서버 간에 공유되는 세 가지 기본 데이터 구조를 가지고 있습니다: 정렬된 맵, 큐, 해시 맵. 각 데이터 구조는 특정 용례에 적합합니다:
- 기술 기반 매치메이킹 - 사용자 정보를 공유 큐에 저장하여 서버 간에 사용하고, 로비 서버를 사용해 주기적으로 매치메이킹을 실행합니다.
- 서버 간 거래 및 경매 - 아이템에 대한 실시간 변동 가격으로 사용자가 입찰할 수 있도록 서로 다른 서버 간에 보편적인 거래를 가능하게 하며, 정렬된 맵의 키-값 쌍을 사용합니다.
- 글로벌 리더보드 - 공유 리더보드 내에서 사용자 순위를 저장하고 업데이트합니다 정렬된 맵에서.
- 공유 인벤토리 - 사용자가 서로 동시에 인벤토리 아이템을 활용할 수 있는 공유 해시 맵에 인벤토리 아이템 및 통계를 저장합니다.
- 영구 데이터의 캐시 - 데이터 저장소의 지속적인 데이터를 메모리 저장소 해시 맵에 동기화하고 복사하여 캐시로 사용하고 게임 성능을 향상시킵니다.
일반적으로, 특정 키를 기반으로 데이터를 접근해야 한다면 해시 맵을 사용하세요. 데이터의 순서가 필요하다면 정렬된 맵을 사용하세요. 특정 순서로 데이터를 처리해야 할 경우에는 큐를 사용하세요.
제한 사항 및 쿼터
확장성과 시스템 성능을 유지하기 위해 메모리 저장소는 메모리 크기, API 요청 및 데이터 구조 크기에 대한 데이터 사용 쿼터를 가지고 있습니다.
메모리 저장소는 수명, 즉 TTL(최대 사용 기간) 기반의 만료 시간 정책을 갖습니다. 아이템은 만료 후 퇴출되며, 새로운 항목을 위한 메모리 쿼터가 확보됩니다. 메모리 한도에 도달하면 모든 후속 쓰기 요청은 아이템이 만료되거나 수동으로 삭제될 때까지 실패합니다.
메모리 크기 쿼터
메모리 쿼터는 게임이 소비할 수 있는 메모리의 총량을 제한합니다. 이는 고정 값이 아니며, 대신 64KB + 1.2KB * [사용자 수] 공식에 따라 게임의 사용자 수에 따라 시간이 지남에 따라 변경됩니다. 쿼터는 서버 레벨이 아닌 게임 레벨에 적용됩니다.
사용자가 게임에 접속하면 추가 메모리 쿼터가 즉시 제공됩니다. 사용자가 게임에서 나가면 쿼터는 즉시 감소하지 않습니다. 쿼터가 더 낮은 값으로 재평가되기 전에 8일의 추적 기간이 있습니다.
게임이 메모리 크기 쿼터에 도달한 후에는 메모리 크기를 증가시키는 API 요청은 항상 실패합니다. 메모리 크기를 줄이거나 변경하지 않는 요청은 여전히 성공합니다.
가시성 대시보드를 사용하면 메모리 사용량 차트를 통해 게임의 메모리 크기 쿼터를 실시간으로 확인할 수 있습니다.
API 요청 제한
모든 MemoryStoreService API 호출에는 요청 단위 쿼터가 적용됩니다. 이 쿼터는 1000 + 120 * [동시 사용자 수] 요청 단위당 분입니다.
대부분의 API 호출은 한 요청 단위를 소비하지만, 몇 가지 예외가 있습니다:
MemoryStoreSortedMap:GetRangeAsync()
반환된 항목 수에 따라 단위를 소비합니다. 예를 들어, 이 메서드가 10개의 항목을 반환하면 호출 수는 10 요청 단위로 계산됩니다. 빈 응답을 반환하면 하나의 요청 단위로 계산됩니다.
MemoryStoreSortedMap:GetRangeAsync()와 같이 반환된 항목 수에 따라 단위를 소비하지만, 읽는 동안 2초마다 추가 단위를 소비합니다. 최대 읽기 시간을 waitTimeout 매개변수로 지정합니다.
MemoryStoreHashMap:UpdateAsync()
최소 2개 단위를 소비합니다.
MemoryStoreHashMap:ListItemsAsync()
[스캔된 파티션 수] + [반환된 아이템 수] 단위를 소비합니다.
요청 쿼터는 서버 레벨이 아닌 게임 레벨에 적용됩니다. 이로 인해 총 요청 속도가 쿼터를 초과하지 않는 한 요청을 서버 간에 할당하는 유연성이 제공됩니다. 쿼터를 초과하면 서비스가 요청을 제한할 때 오류 응답이 발생합니다.
가시성 기능을 사용하면 게임의 요청 단위 쿼터를 실시간으로 확인할 수 있습니다.
데이터 구조 크기 제한
단일 정렬된 맵 또는 큐에 대해 다음 크기 및 아이템 수 제한이 적용됩니다:
- 최대 아이템 수: 1,000,000
- 최대 총 크기(정렬된 맵키 포함): 100MB
각 파티션 한도
각 파티션 한도를 참조하세요.
모범 사례
메모리 사용 패턴을 최적화하고 제한 사항을 초과하지 않도록 하려면 다음 모범 사례를 따르세요:
처리된 항목 제거. MemoryStoreQueue:RemoveAsync() 메서드를 사용하여 큐의 읽기 항목을 일관되게 정리하고 MemoryStoreSortedMap:RemoveAsync()를 정렬된 맵에서 사용하여 메모리를 확보하고 데이터 구조를 최신 상태로 유지합니다.
데이터 추가 시 최대한 짧은 수명 설정. 기본 만료 시간은 MemoryStoreQueue:AddAsync() 및 MemoryStoreSortedMap:SetAsync()의 경우 45일입니다. 가능하면 최단 수명을 설정하면 자동으로 오래된 데이터를 정리 이전의 메모리 사용량 쿼터를 방지할 수 있습니다.
- 긴 만료로 대량의 데이터를 저장하지 마세요. 이렇게 하면 메모리 쿼터를 초과할 위험이 있으며 전체 게임이 고장을 일으킬 수 있습니다.
- 필요 없는 항목은 항상 명시적으로 삭제하거나 짧은 항목 만료를 설정하세요.
- 일반적으로 메모리를 방출하기 위해 명시적 삭제를 사용하고, 사용하지 않는 항목이 장기간 메모리를 차지하지 않도록 하기 위해 아이템 만료를 안전 장치로 사용해야 합니다.
메모리에서 필요한 값만 유지하세요.
예를 들어, 경매 집 게임의 경우 최상 입찰만 유지하면 충분합니다. 모든 입찰을 데이터 구조에 유지하기보다 하나의 키에 대해서 MemoryStoreSortedMap:UpdateAsync()를 사용하여 최상 입찰만 유지할 수 있습니다.
지수 백오프를 사용하여 API 요청 제한을 초과하지 않도록 도움을 줍니다.
예를 들어, DataUpdateConflict가 발생하면 2초 후에 재시도하고, 그 다음 4초, 8초 등의 순서로 재시도할 수 있습니다. MemoryStoreService에 지속적으로 요청을 보내 올바른 응답을 받는 것은 피하세요.
샤딩을 통해 거대한 데이터 구조를 여러 개의 작은 데이터 구조로 분할하세요.
데이터가 한large data structure에 모든 데이터를 저장하는 것보다 작은 구조에서 관리하는 것이 더 쉽습니다. 이 방법은 사용 및 속도 제한을 회피하는 데도 도움이 됩니다. 예를 들어, 키에 접두사를 사용하는 정렬된 맵이 있다면 각 접두사를 고유의 정렬된 맵으로 분리하는 것을 고려하세요. 매우 인기 있는 게임에서는 사용자 ID의 마지막 숫자를 기준으로 여러 맵으로 나누는 방법도 고려할 수 있습니다.
해시 맵에서 부하를 분산시키기 위해 키의 여러 복사본으로 샤드하도록 합니다.
저장된 값을 압축하세요.
예를 들어, 저장된 값의 크기를 줄이기 위해 LZW 알고리즘을 사용하는 것을 고려하세요.
확장 서비스에 등록하세요.
확장 서비스에 온보딩하여 저장소 및 요청 한도를 늘릴 수 있습니다.
가시성
가시성 대시보드는 메모리 저장소 사용을 모니터링하고 문제를 해결하기 위한 통찰력 및 분석을 제공합니다. 메모리 사용량 및 API 요청의 다양한 측면에 대한 실시간 업데이트 차트를 통해, 게임의 메모리 사용 패턴을 추적하고 현재 할당된 쿼터를 확인하며 API 상태를 모니터링하고 성능 최적화를 위한 잠재적인 문제를 식별할 수 있습니다.
다음 표는 가시성 대시보드의 상태별 요청 수 및 API x 상태별 요청 차트에서 사용할 수 있는 API 응답의 모든 상태 코드를 나열하고 설명합니다. 이러한 오류를 해결하는 방법에 대한 자세한 내용은 문제 해결을 참조하세요. 오류와 관련된 특정 쿼터 또는 제한에 대한 내용은 제한 사항 및 쿼터를 참조하세요.
| 상태 코드 | 설명 |
|---|---|
| 성공 | 성공. |
| DataStructureMemoryOverLimit | 데이터 구조 수준 메모리 크기 제한(100MB)을 초과했습니다. |
| DataUpdateConflict | 동시 업데이트로 인한 충돌입니다. |
| AccessDenied | 게임 데이터에 접근할 수 있는 권한이 없습니다. 이 요청은 요청 단위를 소모하지 않거나 쿼트를 사용하지 않습니다. |
| InternalError | 내부 오류입니다. |
| InvalidRequest | 요청에 필수 정보가 없거나 형식이 잘못되었습니다. |
| DataStructureItemsOverLimit | 데이터 구조 수준 아이템 수 제한(1M)을 초과했습니다. |
| NoItemFound | MemoryStoreQueue:ReadAsync() 또는 MemoryStoreSortedMap:UpdateAsync()에서 항목을 찾을 수 없습니다. ReadAsync()는 2초마다 폴링하며 큐의 항목을 찾을 때까지 이 상태 코드를 반환합니다. |
| DataStructureRequestsOverLimit | 데이터 구조 수준 요청 단위 제한(1분당 100,000 요청 단위)을 초과했습니다. |
| PartitionRequestsOverLimit | 파티션 요청 단위 제한을 초과했습니다. |
| TotalRequestsOverLimit | 유니버스 수준 요청 단위 제한을 초과했습니다. |
| TotalMemoryOverLimit | 유니버스 수준 메모리 쿼터를 초과했습니다. |
| ItemValueSizeTooLarge | 값 크기가 제한(32KB)을 초과했습니다. |
다음 표는 현재 가시성 대시보드에서 사용할 수 없는 클라이언트 측 상태 코드 목록입니다.
| 상태 코드 | 설명 |
|---|---|
| InternalError | 내부 오류입니다. |
| UnpublishedPlace | 메모리 저장소 서비스를 사용하려면 이 장소를 게시해야 합니다. |
| InvalidClientAccess | 메모리 저장소 서비스는 서버에서 호출해야 합니다. |
| InvalidExpirationTime | '만료' 필드는 0과 3,888,000 사이여야 합니다. |
| InvalidRequest | 값을 JSON으로 변환할 수 없습니다. |
| InvalidRequest | sortKey를 유효한 숫자 또는 문자열로 변환할 수 없습니다. |
| TransformCallbackFailed | 변환 콜백 함수 호출에 실패했습니다. |
| RequestThrottled | 최근 메모리 저장소 요청이 하나 이상의 제한에 도달했습니다. |
| UpdateConflict | 최대 재시도 횟수를 초과했습니다. |
문제 해결
다음 표는 각 응답 상태 코드에 대한 추천 솔루션을 나열하고 설명합니다:
| 오류 | 문제 해결 옵션 |
|---|---|
| DataStructureRequestsOverLimit / PartitionRequestsOverLimit |
|
| TotalRequestsOverLimit | |
| DataStructureItemsOverLimit |
|
| DataStructureMemoryOverLimit | |
| TotalMemoryOverLimit | |
| DataUpdateConflict |
|
| 내부 오류 |
|
| 잘못된 요청 |
|
| 아이템 값 크기가 너무 큽니다. |
|
스튜디오에서 테스트 및 디버깅
MemoryStoreService의 데이터는 스튜디오와 프로덕션 간에 격리되어 있으므로, 스튜디오에서 데이터를 변경해도 프로덕션 동작에 영향을 주지 않습니다. 따라서 스튜디오에서의 API 호출은 프로덕션 데이터를 액세스하지 않으며, 메모리 저장소와 새로운 기능을 프로덕션 전에 안전하게 테스트할 수 있습니다.
스튜디오 테스트는 프로덕션과 동일한 제한 사항 및 쿼터를 가지고 있습니다. 사용자 수에 따라 계산되는 쿼터의 경우, 스튜디오 테스트에서 유일한 사용자이기 때문에 결과 쿼터가 매우 작을 수 있습니다. 스튜디오에서 테스트할 때는 액세스 및 권한을 확인하기 위해 수행되는 몇 가지 추가 검사가 있으므로 프로덕션에서 사용되는 것보다 약간 높은 대기 시간 및 오류율을 경험할 수 있습니다.
실시간 게임 또는 스튜디오에서 메모리 저장소를 디버깅하는 방법에 대한 자세한 내용은 개발자 콘솔을 사용하세요.