[Book Review] 대규모 시스템 설계 기초
가상 면접 사례로 배우는 대규모 시스템 설계 기초 독후감.
책 내용을 요약하는 포스팅은 아닙니다. 읽으면서 들었던 생각들을 주저리 정리하는 글에 가까움. (미래에 부끄러워서 지울수 있음)
개인적으로 이 책은 신입이나 취준생 보다는 조금 더 연차가 있을때 읽는게 좋다고 생각한다.
저연차 때는 시스템 설계보다는 개발 피쳐 하나를 깔끔하게 처리하는게 더 중요하다고 생각하기 때문에,
시스템 설계 보다는 CS, 주 사용 언어, 툴, 코딩, 그리고 프로젝트 내에 이미 잘 짜여진 선배들 코드를 공부를 하는게 더 맞다고 생각한다. 실제로 그것만 공부해도 시간이 모자라고.
게임 업계의 중니어 정도의 연차가 되면서 조금 더 거시적인 관점에서 프로그램을 보고싶기도 하고 다른 도메인의 구조가 궁금해서 보게된 책. 웹 백엔드 개발자를 타겟으로 작성한 듯 하나, 게임 서버 개발자인 내가 보기에도 재밌는 내용들이 있었다.
중반부 까지는 실제로 개발했던 내용들과 겹치는 부분도 많아서 재미있게 읽었는데, 책만 읽고 덮어버리면 머릿속에 잘 남질 않아서 그 부분에 대해서 내 관점에서 느낀점을 써보려고 한다.
스케일링
서비스를 운영하다보면 DB에 쌓이는 데이터는 계속해서 늘어간다. 게임 캐릭터나 아이템은 계속해서 늘어나기만 한다. 게임사에서 유저가 소중하게 키운 캐릭터나 아이템을 지우는 일은 서비스 종료까지는 없을 것이다.
게임 서버는 빠른 응답이 필요하면서도 매 초마다 많은 DB 작업이 발생한다.
웹서비스에서 로그인하거나 결제하는데 3초 걸린다고 화내는 사람이 있을까? 게임은 아이템 줍고 가방에 들어오는데 3초씩 걸린다고하면 운영자체가 불가능하다. 몬스터는 계속해서 쏟아지고 몬스터가 주는 아이템도 계속해서 쏟아진다. 네트워크 부하가 큰 환경이다.
또한 게임에서 데이터 일관성은 필수다. 아이템을 거래할 때도, 사냥을 해서 돈을 얻을 때도, 던전 공략 순위를 매길때도, 공성전을 할때도.. 얻은 장비를 강화할 때도, 길드에 가입해서 컨텐츠를 즐길 때도
DB의 강력한 일관성, 데이터 무결성은 포기할 수 없는 조건이기 때문에 RDB를 메인으로 사용하고 이를 최적화하는데 힘을 쏟는다.
그렇다면 게임 도메인의 빡빡한 RDB는 어떻게 스케일링을 할까? 내가 경험해본 바로 정리하자면, 정책에 따라 달라진다.
- 서버를 분리하여 서비스하는 경우: 대부분의 MMORPG 게임이나 나라별로 서비스를 분리하는 게임이 취하는 방식. 같이 플레이하는 유저 끼리 같은 DB에 값을 저장한다. 제일 빠르고 단순한 방법이다. DB를 새로 추가하고 새 DB에는 새 유저만 받거나 기존 유저를 마이그레이션 해주는 방식으로 스케일링 하면된다. 오래전부터 취하던 방식이라 유저들도 익숙하기도 하고…
- 한 서버로 서비스하는 경우: 글로벌 서비스를 운영하는 경우 취하는 방식, DB를 유저 ID 기준으로 샤딩한다. 이 책에 나와있는 대로 ID를 기준으로 해시 값을 사용해도 되고 단순하게 Modular 샤딩을 해도 된다. 게임에서 DB확장이 웹서비스만큼 큰 규모로 발생하지는 않는다. 채팅이나 로그를 RDB에 저장하지는 않기 때문에, 대부분 Modular 샤딩으로 충분하다. 책에 나온 안정해시를 사용해야할 만큼 DB 동적 스케일링이 필요한 경우가 잘 없기 때문에… 대부분 점검걸고 해결한다.
물론 로그같은 경우는 RDB를 사용해야할 이유가 없으므로 NOSQL을 사용한다. 무조건 RDB! 는 아니다.
유니크 ID 생성
게임에서도 유니크 ID는 반드시 필요하다. 유저 id나 아이템 id, 길드 id등 항상 고유한 id로 구분을 해야한다. 로그를 쌓을 때도 마찬가지다.
책에서는 스노우 플레이크 ID 기법(타임 스탬프와 데이터센터 ID, 서버ID, Counter를 사용하는)이라는게 나오는데 이게 분산 서버를 기준으로 책에서 설명하고 있다.
하지만 게임은 보통 Stateful하게 한 서버에서 요청이 처리되기 때문에 서버ID를 인덱스로 사용해도 큰 의미가 없다 bit만 잡아먹는다. 멀티스레드 환경에서 돌아가는 서버인 경우가 많기 때문에 오히려 스레드id를 인덱스로 사용하는 편이 좋다.
처리율 제한 장치
게임에서도 처리율 제한 장치가 필요한 경우가 있다.
예를 들어, DDOS를 막기 위해 짧은 시간내로 여러가지 요청이 온 경우에 비정상 유저로 판단해서 제재를 하는 경우. 그리고 게임을 런칭하거나 점검 이후에 오픈하는 경우 대기열이 필요하다.
대기열의 경우 순서를 보장하면서 서버가 감당 가능할 정도로 초당 처리량을 제한 하는 기능이 필요하다. 요즘엔 서버가 좋아지기도 했고 대기열이 필요할만큼 게임이 대박나기가 어려워서 잘 활용은 안하는것 같은데. 다행히 속해있는 프로젝트가 잘돼서 대규모 트래픽을 경험하게 되었다.
대기열은 계속해서 변화하므로 쓰기 연산이 많고, 순서가 보장되는 자료구조를 적당히 선택해서 구현했었다. 이런 기능을 구현할 때는 당연히 트래픽을 대규모로 밀어넣는 부하테스트도 진행하는데. 책에서 나온 버킷이나 윈도우 알고리즘을 사용할 필요는 없었다.
대기열 구현할 때 어떤 알고리즘을 사용하는지 보다는 어떻게 해야 레디스 접근을 최소화할지, 캐싱을 어떻게하고, 불필요한 연산을 줄이고, 빠르게 줄어드는 대기열을 어떻게 잘 동기화시키는지가 더 중요했다.
특별한 알고리즘 없이도 적당한 스펙의 레디스로도 잘 돌아가더라(?). 부하를 줄이는 용도의 대기열이였기 때문에 수강신청이나 티켓팅 처럼 대기열이 절대 터지면 안되는 조건이라 HA를 위한 복잡한 다중화가 필요한 케이스도 아니였어서..
책에서 게임 서버 개발자로써 재밌게 봤던 일부분만 독후감으로 정리했다. 중후반 부는 페이스북이나 유튜브를 예시로 간략하게 어떻게 설계해야되는지에 대한 얘기가 주를 이뤘는데 다른 도메인의 서비스가 어떻게 설계되는지 개략적인 감을 잡기에 좋은 책이였다.
다만 내 지식이 부족한 탓인지 후반부 부터는 반복되는 느낌이 강했다.
- Stateless 서버에게 오는 요청을 로드밸런서/DNS/분산시스템(주키퍼)를 통해 분산한다.
- DB는 앞에 캐시를 둔다.
- 용량이 큰 파일들은 CDN을 활용한다.
- 저장소가 필요한 경우 Azure, Amazon 같은 외부 서비스를 활용한다.
- 처리량과 고가용성을 위해 분산과 마스터/슬레이브 구조로 레디스를 사용한다.
- 메세지큐나 Pub/Sub큐 등 큐를 활용해 서버 결합도를 줄이고 확장성을 늘린다. (단, 서버 복잡도 증가, 실패시 처리 등이 어려워짐)
요약하자면 위 조건들이 반복되는 느낌(?). 정리하고보니 당연한 것 같기도하다. 위 내용에서 수용 가능한 만큼의 장/단점을 취하는 것이 시스템 설계인 것 같다.
DB에서 CAP 이론처럼 모든게 완벽한 솔루션은 없을테니 상황에 맞는 방식을 택하는 것.
재미있는 책이였고, 저연차에게 추천하는 책이라 굳이 리뷰까지 작성했다. 읽어주셔서 감사합니다.