메모리 저장소

*이 콘텐츠는 AI(베타)를 사용해 번역되었으며, 오류가 있을 수 있습니다. 이 페이지를 영어로 보려면 여기를 클릭하세요.

MemoryStoreService는 모든 서버에서 실시간 세션에 접근할 수 있는 빠른 인메모리 데이터 저장소를 제공하는 고처리량 및 저지연 데이터 서비스입니다. 메모리 저장소는 자주 변경되고 내구성이 필요 없는 빠르고 일시적인 데이터에 적합합니다. 이러한 데이터는 접근 속도가 빠르며 최대 수명에 도달하면 사라집니다. 세션 간에 지속해야 하는 데이터는 데이터 저장소를 사용하세요.

데이터 구조

메모리 저장소는 원시 데이터에 직접 접근하는 대신 서버 간에 빠른 처리를 위해 공유되는 세 가지 기본 데이터 구조를 가지고 있습니다: 정렬된 맵, , 해시 맵. 각 데이터 구조는 특정 사용 사례에 적합합니다:

  • 기술 기반 매칭 - 서버 간에 공유되는 에 사용자 정보를 저장하고, 로비 서버를 사용하여 주기적으로 매칭을 실행합니다.
  • 서버 간 거래 및 경매 - 실시간으로 변하는 가격으로 아이템에 입찰할 수 있는 보편적인 거래를 가능하게 하며, 정렬된 맵을 사용하여 키-값 쌍을 관리합니다.
  • 글로벌 리더보드 - 정렬된 맵 내의 공유 리더보드에서 사용자 순위를 저장하고 업데이트합니다.
  • 공유 인벤토리 - 사용자들이 서로 동시에 인벤토리 아이템을 활용할 수 있도록 해시 맵에 인벤토리 아이템과 통계를 저장합니다.
  • 지속 데이터의 캐시 - 데이터 저장소의 지속 데이터를 메모리 저장소 해시 맵에 동기화하고 복사하여 캐시 역할을 하여 게임 성능을 향상시킵니다.

일반적으로 특정 키를 기반으로 데이터에 접근해야 하는 경우 해시 맵을 사용하세요. 데이터가 정렬되어야 하는 경우 정렬된 맵을 사용하세요. 특정 순서로 데이터를 처리해야 하는 경우 큐를 사용하세요.

한계 및 쿼터

확장성과 시스템 성능을 유지하기 위해 메모리 저장소는 메모리 크기, API 요청 및 데이터 구조 크기에 대한 데이터 사용 쿼터를 가지고 있습니다.

메모리 저장소는 만료 시간에 기반한 퇴출 정책을 가지고 있으며, 이를 TTL(생존 시간)이라고도 합니다. 항목은 만료된 후 퇴출되며, 새로운 항목을 위한 메모리 쿼터가 확보됩니다. 메모리 한도에 도달하면 모든 후속 쓰기 요청은 항목이 만료되거나 수동으로 삭제될 때까지 실패합니다.

메모리 크기 쿼터

메모리 쿼터는 게임이 소비할 수 있는 총 메모리 양을 제한합니다. 이는 고정 값이 아니며, 대신 게임의 사용자 수에 따라 64 KB + 1.2 KB * [사용자 수] 공식을 기반으로 시간이 지남에 따라 변경됩니다. 쿼터는 서버 수준이 아닌 게임 수준에 적용됩니다.

사용자가 게임에 참여하면 추가 메모리 쿼터가 즉시 사용 가능해집니다. 사용자가 게임을 떠나면 쿼터는 즉시 줄어들지 않습니다. 쿼터가 더 낮은 값으로 재평가되기까지 8일의 추적 기간이 있습니다.

게임이 메모리 크기 쿼터에 도달하면 메모리 크기를 증가시키는 모든 API 요청은 항상 실패합니다. 메모리 크기를 줄이거나 변경하지 않는 요청은 여전히 성공합니다.

관측 가능성 대시보드를 사용하면 메모리 사용량 차트를 통해 게임의 메모리 크기 쿼터를 실시간으로 확인할 수 있습니다.

API 요청 한도

요청 단위 쿼터는 모든 MemoryStoreService API 호출에 적용됩니다. 이 쿼터는 1000 + 120 * [동시 사용자 수] 요청 단위로 분당 적용됩니다.

대부분의 API 호출은 하나의 요청 단위를 소비하지만 몇 가지 예외가 있습니다:

  • MemoryStoreSortedMap:GetRangeAsync()

    반환된 항목 수에 따라 단위를 소비합니다. 예를 들어, 이 메서드가 10개의 항목을 반환하면 호출은 10개의 요청 단위로 계산됩니다. 빈 응답을 반환하면 하나의 요청 단위로 계산됩니다.

  • MemoryStoreQueue:ReadAsync()

    반환된 항목 수에 따라 단위를 소비하며, MemoryStoreSortedMap:GetRangeAsync()와 유사하지만 읽는 동안 매 2초마다 추가 단위를 소비합니다. waitTimeout 매개변수로 최대 읽기 시간을 지정하세요.

  • MemoryStoreHashMap:UpdateAsync()

    최소 2개의 단위를 소비합니다.

  • MemoryStoreHashMap:ListItemsAsync()

    [스캔된 파티션 수] + [반환된 항목 수] 단위를 소비합니다.

요청 쿼터는 서버 수준이 아닌 게임 수준에 적용됩니다. 이는 총 요청 속도가 쿼터를 초과하지 않는 한 서버 간에 요청을 할당할 수 있는 유연성을 제공합니다. 쿼터를 초과하면 서비스가 요청을 제한할 때 오류 응답을 받게 됩니다.

관측 가능성 기능을 사용하면 게임의 요청 단위 쿼터를 실시간으로 확인할 수 있습니다.

데이터 구조 크기 한계

단일 정렬된 맵 또는 큐에 대해 다음 크기 및 항목 수 한계가 적용됩니다:

  • 최대 항목 수: 1,000,000
  • 최대 총 크기(정렬된 맵의 키 포함): 100 MB

파티션당 한계

파티션당 한계를 참조하세요.

모범 사례

메모리 사용 패턴을 최적화하고 한계에 도달하지 않도록 하려면 다음 모범 사례를 따르세요:

  • 처리된 항목 제거. MemoryStoreQueue:RemoveAsync() 메서드를 사용하여 큐의 읽은 항목을 지속적으로 정리하고, 정렬된 맵의 경우 MemoryStoreSortedMap:RemoveAsync()를 사용하여 메모리를 확보하고 데이터 구조를 최신 상태로 유지할 수 있습니다.

  • 데이터를 추가할 때 가능한 가장 짧은 시간 프레임으로 만료 시간을 설정하세요. 기본 만료 시간은 MemoryStoreQueue:AddAsync()MemoryStoreSortedMap:SetAsync() 모두 45일이지만, 가능한 짧은 시간을 설정하면 오래된 데이터를 자동으로 정리하여 메모리 사용 쿼터를 채우는 것을 방지할 수 있습니다.

    • 긴 만료 시간으로 많은 데이터를 저장하지 마세요. 이는 메모리 쿼터를 초과할 위험이 있으며, 게임 전체에 문제를 일으킬 수 있습니다.
    • 항상 불필요한 항목을 명시적으로 삭제하거나 짧은 항목 만료 시간을 설정하세요.
    • 일반적으로 메모리를 해제하기 위해 명시적 삭제를 사용하고, 사용되지 않는 항목이 오랜 시간 동안 메모리를 차지하지 않도록 하기 위해 항목 만료를 안전 장치로 사용해야 합니다.
  • 메모리에 필요한 값만 유지하세요.

    예를 들어, 경매장 게임의 경우 가장 높은 입찰가만 유지하면 됩니다. 모든 입찰을 데이터 구조에 유지하는 대신 하나의 키에서 MemoryStoreSortedMap:UpdateAsync()를 사용하여 가장 높은 입찰가를 유지할 수 있습니다.

  • 지수 백오프를 사용하여 API 요청 한도를 초과하지 않도록 하세요.

    예를 들어, DataUpdateConflict 오류가 발생하면 두 초 후에 재시도하고, 그 다음 네 초, 여덟 초 등으로 재시도하는 것이 좋습니다. MemoryStoreService에 지속적으로 요청을 보내어 올바른 응답을 받으려 하지 마세요.

  • 샤딩을 통해 거대한 데이터 구조를 여러 개의 작은 데이터 구조로 나누세요.

    모든 것을 하나의 큰 데이터 구조에 저장하는 것보다 작은 구조에서 데이터를 관리하는 것이 더 쉽습니다. 이 접근 방식은 사용 및 비율 한도를 피하는 데도 도움이 될 수 있습니다. 예를 들어, 키에 접두사를 사용하는 정렬된 맵이 있는 경우 각 접두사를 별도의 정렬된 맵으로 분리하는 것을 고려하세요. 특히 인기 있는 게임의 경우 사용자 ID의 마지막 숫자에 따라 사용자를 여러 맵으로 분리할 수도 있습니다.

  • 해시 맵에서 여러 개의 키 복사본으로 자주 접근하는 키를 샤드하세요.

  • 저장된 값을 압축하세요.

    예를 들어, 저장된 값의 크기를 줄이기 위해 LZW 알고리즘을 사용하는 것을 고려하세요.

  • 확장 서비스에 등록하세요.

    확장 서비스에 온보딩하여 저장소 및 요청 한도 쿼터를 늘릴 수 있습니다.

관측 가능성

관측 가능성 대시보드는 메모리 저장소 사용을 모니터링하고 문제를 해결하기 위한 통찰력과 분석을 제공합니다. 메모리 사용량 및 API 요청의 다양한 측면에 대한 실시간 업데이트 차트를 통해 게임의 메모리 사용 패턴을 추적하고, 현재 할당된 쿼터를 확인하며, API 상태를 모니터링하고, 성능 최적화를 위한 잠재적 문제를 식별할 수 있습니다.

다음 표는 관측 가능성 대시보드의 상태별 요청 수API x 상태별 요청 차트에서 사용할 수 있는 모든 API 응답 상태 코드를 나열하고 설명합니다. 이러한 오류를 해결하는 방법에 대한 자세한 내용은 문제 해결을 참조하세요. 오류와 관련된 특정 쿼터 또는 한도에 대한 내용은 한계 및 쿼터를 참조하세요.

상태 코드설명
성공성공.
DataStructureMemoryOverLimit데이터 구조 수준 메모리 크기 한도(100 MB)를 초과했습니다.
DataUpdateConflict동시 업데이트로 인한 충돌입니다.
AccessDenied게임 데이터에 접근할 수 있는 권한이 없습니다. 이 요청은 요청 단위를 소비하지 않거나 쿼터를 사용하지 않습니다.
InternalError내부 오류입니다.
InvalidRequest요청에 필수 정보가 없거나 잘못된 정보가 포함되어 있습니다.
DataStructureItemsOverLimit데이터 구조 수준 항목 수 한도(1M)를 초과했습니다.
NoItemFoundMemoryStoreQueue:ReadAsync() 또는 MemoryStoreSortedMap:UpdateAsync()에서 항목을 찾을 수 없습니다. ReadAsync()는 매 2초마다 폴링하며 큐에서 항목을 찾을 때까지 이 상태 코드를 반환합니다.
DataStructureRequestsOverLimit데이터 구조 수준 요청 단위 한도(분당 100,000 요청 단위)를 초과했습니다.
PartitionRequestsOverLimit파티션 요청 단위 한도를 초과했습니다.
TotalRequestsOverLimit우주 수준 요청 단위 한도를 초과했습니다.
TotalMemoryOverLimit우주 수준 메모리 쿼터를 초과했습니다.
ItemValueSizeTooLarge값 크기가 한도를 초과했습니다(32 KB).

다음 표는 클라이언트 측 상태 코드를 나열하며, 현재 관측 가능성 대시보드에서 사용할 수 없습니다.

상태 코드설명
InternalError내부 오류입니다.
UnpublishedPlaceMemoryStoreService를 사용하려면 이 장소를 게시해야 합니다.
InvalidClientAccessMemoryStoreService는 서버에서 호출해야 합니다.
InvalidExpirationTime'expiration' 시간 필드는 0과 3,888,000 사이여야 합니다.
InvalidRequest값을 json으로 변환할 수 없습니다.
InvalidRequestsortKey를 유효한 숫자 또는 문자열로 변환할 수 없습니다.
TransformCallbackFailed변환 콜백 함수 호출에 실패했습니다.
RequestThrottled최근 MemoryStores 요청이 하나 이상의 한도에 도달했습니다.
UpdateConflict최대 재시도 횟수를 초과했습니다.

문제 해결

다음 표는 각 응답 상태 코드에 대한 권장 솔루션을 나열하고 설명합니다:

오류문제 해결 옵션
DataStructureRequestsOverLimit / PartitionRequestsOverLimit
  • 정보를 다른 변수에 저장하고 일정 시간 간격(예: 30초) 후에 다시 확인하여 로컬 캐시를 추가하세요.
  • 상태별 요청 수 차트를 사용하여 성공 응답이 NoItemFound보다 더 많이 수신되고 있는지 확인하세요. 실패한 요청으로 MemoryStoreService를 호출하는 횟수를 제한하세요.
  • 요청 간에 짧은 지연을 구현하세요.
  • 모범 사례를 따르세요. 여기에는:
    • 상당한 양의 DataStructureRequestsOverLimit/PartitionRequestsOverLimit 응답을 받는 경우 데이터 구조를 샤딩하세요.
    • 해시 맵 호출에서 PartitionRequestsOverLimit 응답을 받는 경우 해시 맵 키를 샤딩하세요.
    • 특정 데이터 구조나 해시 맵 키에 대한 호출을 줄이거나 배치하여 PartitionRequestsOverLimit 응답을 보세요.
    • 보낼 요청의 합리적인 비율을 찾기 위해 지수 백오프를 구현하세요.
TotalRequestsOverLimit
DataStructureItemsOverLimit
  • 메모리 크기를 줄이기 위해 모범 사례를 적용하세요.
DataStructureMemoryOverLimit
TotalMemoryOverLimit
DataUpdateConflict
  • 동일한 키를 동시에 업데이트하는 여러 요청을 피하기 위해 요청 간에 짧은 지연을 구현하세요.
  • 정렬된 맵의 경우, MemoryStoreSortedMap:UpdateAsync() 메서드에서 특정 시도 횟수 후 요청을 중단하기 위해 콜백 함수를 사용하세요. 다음 코드 샘플을 참조하세요:
  • 요청 중단 예제
    local MemoryStoreService = game:GetService("MemoryStoreService")
    local map = MemoryStoreService:GetSortedMap("AuctionItems")
    function placeBid(itemKey, bidAmount)
    map:UpdateAsync(itemKey, function(item)
    item = item or { highestBid = 0 }
    if item.highestBid < bidAmount then
    item.highestBid = bidAmount
    return item
    end
    print("항목은 "..item.highestBid.."입니다.")
    return nil
    end, 1000)
    end
    placeBid("MyItem", 50)
    placeBid("MyItem", 40)
    print("완료")
  • MemoryStoreService를 효율적으로 호출하여 충돌을 피하고 있는지 확인하세요. 이상적으로는 요청을 과도하게 보내지 않아야 합니다.
  • 큐의 경우 MemoryStoreQueue:RemoveAsync() 메서드를 사용하여 읽은 후 항목을 지속적으로 제거하고, 정렬된 맵의 경우 MemoryStoreSortedMap:RemoveAsync()를 사용하세요.
내부 오류
InvalidRequest
  • 요청에 올바르고 유효한 매개변수를 포함하고 있는지 확인하세요. 잘못된 매개변수의 예는 다음과 같습니다:
    • 빈 문자열
    • 길이 제한을 초과하는 문자열
ItemValueSizeTooLarge
  • 항목 값을 여러 키로 샤딩하거나 나누세요.
    • 그룹화된 키를 정리하기 위해 키에 prefix를 추가하여 알파벳 순으로 정렬하세요.
  • 저장된 값을 인코딩하거나 압축하세요.

스튜디오에서 테스트 및 디버그

MemoryStoreService의 데이터는 스튜디오와 프로덕션 간에 격리되어 있으므로 스튜디오에서 데이터를 변경해도 프로덕션 동작에 영향을 미치지 않습니다. 즉, 스튜디오에서의 API 호출은 프로덕션 데이터를 접근하지 않으므로 메모리 저장소와 새로운 기능을 안전하게 테스트할 수 있습니다.

스튜디오 테스트는 프로덕션과 동일한 한계 및 쿼터를 가지고 있습니다. 사용자 수에 따라 계산된 쿼터의 경우, 스튜디오 테스트의 경우 유일한 사용자이므로 결과 쿼터가 매우 작을 수 있습니다. 스튜디오에서 테스트할 때는 접근 및 권한을 확인하기 위해 수행되는 추가 검사로 인해 프로덕션에서의 사용에 비해 약간 높은 지연 및 오류율을 경험할 수 있습니다.

실시간 게임에서 메모리 저장소를 디버그하는 방법이나 스튜디오에서 테스트할 때는 개발자 콘솔을 사용하세요.

©2026 Roblox Corporation. Roblox 및 Roblox 로고, 'Powering Imagination'은 미국 및 기타 국가 내 당사의 등록 및 미등록 상표입니다.