////
Search

24장 - 실시간 게임 순위표

생성일
2024/08/26 13:17
태그
Interview

1. 요구사항 정리

온라인 게임의 리더보드 설계
순위표에 상위 10명의 플레이어를 표시한다.
특정 사용자의 순위를 표시한다.
어떤 사용자보다 4순위 위와 아래에 있는 사용자를 표시한다. (보너스 문제)

1.1. 비기능 요구사항

점수 업데이트는 실시간으로 순위표에 반영한다.
일반적인 확장성, 가용성 및 안정성 요구사항

1.2. 개략적 규모추정

DAU = 500만
초당 평균 사용자 = 50명
평균 값으로 추정하되 최대 부하는 평균의 5배로 가정
최대 부하량 = 5,000,000 / 10^5 X 5 = 250
Plain Text
복사
사용자 점수 획득 QPS = 500
한 사용자가 하루 평균 10개의 게임을 플레이한다고 가정
최대 QPS = 500 X 5 = 2,500
Plain Text
복사
각 사용자가 하루에 한 번 게임을 연다고 가정
상위 10명 순위표는 사용자가 처음 게임을 열 때만 표시한다고 가정
상위 10명 순위표 가져오기 QPS = 50
Plain Text
복사

2. 청사진 그리기

클라이언트가 순위표 서비스와 직접 통신하면 보안 문제(중간자 공격)가 있어서 게임 서비스에서 갱신한다.
게임 데이터를 다양하게 사용하기 위해서 메시지 큐를 사용할 수 있다.

2.1. 데이터베이스

RDB를 사용하면 지속적으로 변화하는 랭킹 시스템에 신속하게 대응하지 못한다.
기본적으로 랭킹 데이터는 중요하지 않은 데이터에 속한다.
캐시를 쓰는게 좋지 않겠는가?
그러니 우리는 레디스를 쓸꺼다.
랭킹 시스템에 적합한 정렬 집합 자료형을 사용한다.
내부적으로 해시테이블과 스킵 리스트를 가져서 사용자의 점수를 기준으로 미리 정렬해놓을 수 있다.
연결 리스트에 연산을 하면 O(n)만큼의 시간 복잡도를 가진다.
더 빠르게 하기 위해서 이진 검색 알고리즘과 비슷하게 노드를 구성해서 만들자.
1차 색인과 2차 색인을 나누어 중간 값을 통해서 더 적은 연산으로 결과에 도달할 수 잇다.
그런데 데이터가 작으면 효과가 크게 체감되지 않는다.
레디스 정렬 집합 사용시?
ZADD = 새로운 사용자의 집합 삽입 ( O(log(n) )
ZINCRBY = 특정 사용자의 점수를 값만큼 증가시킴 ( O(log(n) )
ZRANGE/ZREVANGE = 특정 범위에 드는 자들을 가져옴 ( O(log(n) + M )
n = 정렬 집합의 크기
m = 가져 올 항목 수
ZRANK/ZREVRANK = 오름/내림차순 정렬 시 특정 사용자의 위치를 가져옴 ( O(log(n) )
저장소 요구사항
ID가 24자 문자열이고 점수가 16비트 정수라고 한다면 순위표 한 항목당 26바이트가 필요
최악을 가정하면 26바이트 X 2,500만 = 650M의 저장공간이 필요
레디스 사용시 걱정해야할 부분은 영속성인데 레디스도 영속성을 지원하긴 한다.
그런데 레디스 디스크도 장애가 날 수 있어서 사본 서버를 만들어서 방지해두자~

3. 실제 설계

3.1. 클라우드 사용?

자체 IDC이용
클라우드 사용
특별히 설명할 사항은 없는것 같다.

3.2. 레디스 규모 확장

5억 DAU 사용시 65GB의 저장공간 / 250,000QPS의 질의 처리가 가능해야함
1대로는 역부족

3.2.1. 데이터 샤딩 방안

고정 파티션을 통한 방안
말 그대로 고정된 파티션으로 나누는 방식
적절히 조정해서 고르게 분포됐다고 가정
어디에 어느 사용자가 있는지 2차 캐시를 사용하면 보다 빠르게 갱신이 가능
상위 사용자는 가장 점수가 높은 샤드만 찾으면 됨
해시 파티션을 통한 방안
각 키가 특정한 해시 슬록에 속하도록 하는 샤딩 기법
총 16384개의 해시 슬롯이 있으며, CRC16(key) % 16384의 연산을 통해 어느 슬롯에 속할지 계산함
k개의 결과를 반환하는 경우 각 샤드에서 읽고 정렬해야 해서 지연이 발생
특정 사용자의 순위를 결정할 간단한 방법이 없음
⇒ 결론 : 고정파티션 사용
NoSQL을 대안으로 사용할 수 있다.
쓰기 연산 최적화, 파티션 내 항목 점수를 효율적으로 정렬 가능