← 문제로

13. 게임 데이터 모델링: RDBMS vs NoSQL, 정규화/비정규화, 핫·콜드 분리

난이도 중
내 답안
모범답안

모범답안 — 게임 데이터 모델링: RDBMS vs NoSQL, 정규화/비정규화, 핫·콜드 분리

난이도: 중상

1. RDBMS vs NoSQL

  • 데이터 모델: RDBMS 는 행/열의 관계형 테이블 + 외래키로 정형화한다. NoSQL 은 종류별로 다르다 — 키-값(Redis/DynamoDB), 문서(MongoDB, JSON 덩어리), 와이드컬럼(Cassandra/HBase), 그래프 등.
  • 스키마: RDBMS 는 스키마-온-라이트(쓸 때 구조·제약 강제). 다수 NoSQL 은 스키마-온-리드(저장은 자유, 읽는 쪽이 해석) — 유연하지만 무결성은 애플리케이션 책임.
  • 일관성·트랜잭션: RDBMS 는 ACID·다중 행/테이블 트랜잭션·조인이 강점. 전통 NoSQL 은 단일 키/문서 단위의 원자성에 강하고, 다중 키 트랜잭션·조인은 약하거나 비쌌다(최근 제품들은 부분 지원).
  • 확장: RDBMS 는 기본이 수직 확장(스케일업) + 읽기 복제. 수평 샤딩은 가능하나 운영이 무겁다. NoSQL 은 수평 확장(샤딩·파티셔닝) 을 처음부터 전제로 설계돼 대규모 쓰기·데이터에 유리.
  • CAP 관점: 분산 NoSQL 은 보통 가용성·분할 내성을 위해 결국 일관성 (eventual) 을 택하는 경우가 많다. 강한 일관성이 필수면 RDBMS(또는 CP 계열).

2. 정규화 vs 비정규화

  • 정규화: 중복 제거·갱신 이상 방지·무결성. 한 사실을 한 곳에만 둔다. 쓰기· 무결성은 좋지만 읽을 때 여러 테이블을 조인해야 해 핫패스에서 비쌀 수 있다.
  • 비정규화: 함께 읽는 데이터를 미리 합쳐(중복 허용) 저장 → 조인 제거, 읽기 빠름. 대신 같은 사실이 여러 곳에 있어 갱신 시 모두 맞춰야(쓰기 복잡· 불일치 위험) 한다.
  • 게임 예: 길드 멤버 목록 화면에 "닉네임·레벨·직업·길드기여도"를 보여줄 때, 정규화하면 character·guild_member·stats 조인. 자주 읽히는 화면이면 길드 문서에 요약 필드를 비정규화해 한 번에 읽는다. 랭킹 점수, 보스 기여도 합계처럼 집계는 미리 계산해 저장(읽기 시 재계산 회피).

3. 접근 패턴이 스키마를 결정한다 (NoSQL 설계)

  • RDBMS 는 "데이터를 어떻게 정규화하나"부터, NoSQL 은 "이 데이터를 어떻게 읽고 쓰나(쿼리 패턴)"부터 설계한다.
  • 예: "캐릭터 로그인 시 인벤토리 전체를 한 번에 로드"가 지배적 패턴이라면 → 캐릭터 1개 = 문서/값 1개로 인벤토리·장비·통화·퀘스트 진행을 통째로 담아 단일 키 읽기 1번으로 끝낸다. 저장도 캐릭터 단위 원자 쓰기.
    • 관계형 정규화면 inventory_item 테이블에서 WHERE char_id=? 로 N행을 읽고 조립 → 조인·다중 행 비용. 대신 "특정 아이템을 가진 모든 캐릭터 검색" 같은 교차 질의는 관계형이 압도적으로 쉽다.
  • 트레이드오프: 문서 모델은 "한 엔터티를 통째로" 다루는 패턴에 최적, 엔터티를 가로지르는 임의 질의·집계에는 약하다. 그래서 주 접근 패턴에 맞춰 모델을 고르고, 부차 질의는 보조 인덱스·검색엔진·분석 저장소로 보낸다.

4. 데이터별 저장소 제안

  • 계정/인증/결제: RDBMS(강한 일관성·트랜잭션·감사). 돈·과금은 ACID· 무결성·정합성이 최우선. 멱등 결제 처리도 트랜잭션으로.
  • 캐릭터 세이브·인벤토리: 문서/KV(캐릭터 단위 통짜 로드) 또는 RDBMS + 캐시. 핵심은 "캐릭터 단위 원자 저장/로드". 읽기 많고 캐릭터 경계로 샤딩 쉬움.
  • 우편: 캐릭터별 리스트 → KV/문서(받은편지함 = 키). 대량 발송은 비동기 큐.
  • 길드: 관계형(가입/권한/자금 무결성) + 자주 읽는 요약은 비정규화 문서.
  • 실시간 랭킹: Redis Sorted Set(점수 정렬·범위 조회 O(log N)) — 앞 문제 참고. 원본은 DB, 표시용은 인메모리.
  • 세션 상태: 인메모리/Redis(휘발·초저지연·TTL). 영속 불필요, 빠른 접근만.
  • 행동 로그/텔레메트리: append-only 대용량 저장(와이드컬럼/오브젝트 스토리지/데이터레이크) + 배치 분석(컬럼형 OLAP). 쓰기 폭주·수명 짧음·임의 갱신 없음 → 관계형 OLTP 에 넣으면 안 됨.

5. 핫 vs 콜드 데이터 분리

  • 문제: 활성 캐릭터(핫)와 과거 로그·휴면 계정(콜드)을 같은 고급 저장소에 두면, 콜드 데이터가 용량·인덱스·백업·비용을 잡아먹고 핫 데이터의 캐시 적중률·성능을 떨어뜨린다.
  • 전략:
    • 캐시 계층: 핫 데이터를 Redis/로컬 캐시로 앞단에 둬 DB 부하·지연 감소 (캐시 일관성·TTL·스탬피드 주의 — 앞 문제들 참고).
    • 티어드 스토리지: 자주 쓰는 데이터는 빠른(비싼) 스토리지, 오래된 데이터는 저렴한 스토리지로. 접근 빈도 기반 자동 이동.
    • 아카이빙/콜드 분리: 휴면 계정·오래된 로그는 별도 아카이브 테이블/오브젝트 스토리지로 옮기고, 활성 OLTP 테이블을 작게 유지(인덱스·백업·복구 빨라짐).
    • 수명 정책(TTL/파티셔닝): 로그는 날짜 파티션 + 보존기간 후 자동 폐기.
  • 효과: 핫 경로는 작고 빠르게, 콜드는 싸게 보관. 저장소를 "한 종류로 통일"하기 보다 데이터 성격에 맞는 다중 저장소(polyglot persistence) 를 쓰는 것이 정석.

핵심 한 줄

"하나의 DB 로 다 한다"는 환상을 버리고, 접근 패턴·일관성 요구·규모·수명 으로 데이터를 분류해 돈/무결성은 RDBMS, 엔터티 통짜 로드는 문서/KV, 랭킹·세션은 인메모리, 로그는 대용량 append 저장소로 나눠라. 정규화/비정규화도 읽기·쓰기 패턴으로 결정한다.