Post

데이터베이스에서 적절한 데이터 타입 선택하기

데이터베이스 데이터 타입 선택에 대한 실무 경험 공유

데이터베이스 테이블을 설계할 때, 어떤 데이터 타입을 선택할지 고민이 되는 경우가 많습니다. 어떤 기준으로 타입을 골라야 할까요?

게임 개발 스타트업에 갓 취직한 신입 백엔드 개발자, 삐약씨🐥를 예로 들어볼게요.

애플리케이션의 요구 사항에 맞게

삐약씨 팀의 기획자가 새로운 이벤트를 기획하였네요. 이 이벤트는 유저가 매일 최대 5개의 미션을 하나씩 클리어할 때마다 보상을 획득할 수 있습니다. 이때, 미션은 1 ~ 30개의 미션 목록 중 랜덤으로 유저에게 배정됩니다. 이 정보를 관리하는 테이블이 다음과 같다면, mission_no 컬럼의 데이터 타입은 어떤 게 좋을까요?

Column nameData type
idint
user_idint
clearDatedatetime
mission_no?

미션이 최대 30개까지만 존재하므로 tinyint가 적절해 보입니다.

TINYINT 데이터 타입의 범위

  • 부호가 있는 경우(SIGNED): -128부터 127까지의 값을 저장할 수 있습니다.
  • 부호가 없는 경우(UNSIGNED): 0부터 255까지의 값을 저장할 수 있습니다.

사양 제한이 없다면 무난히 INT로 가자

미션 클리어 보상으로 유저는 티켓을 1개씩 지급받습니다. 이 티켓은 유저가 게임 플레이를 시작할 때 사용되며, 현재는 이 이벤트에서만 획득 가능한 보상이지만 추후에는 다른 신규 이벤트의 보상으로 지급될 가능성도 있다고 합니다.

유저가 보유하고 있는 티켓 개수를 관리하는 컬럼의 데이터 타입으로는 어떤 게 좋을까요?

삐약씨는 만약 타입으로 SMALLINT를 선택할 경우, 유저가 매일 총 5개의 티켓을 획득하여 티켓 보유 수량이 SMALLINT 범위에 도달하는 데까지 얼마나 걸릴지 계산해 보았습니다.

  • 일일 티켓 획득량: 5개
  • 티켓의 최대 범위 (SMALLINT의 최댓값): 32,767개

티켓 개수가 SMALLINT 범위에 도달하는 데 걸리는 일수를 계산해 보면, 32,767개 ÷ 5개/일 = 약 6,553일이 걸립니다. 이는 약 17년 11개월 정도의 기간에 해당합니다.

이 정도면 꽤 여유 있어 보이는데요, 삐약씨는 저장 공간도 아끼고 이후에 정말 SMALLINT에 도달한다면 범위를 더 늘리면 되겠지라는 생각으로 SMALLINT를 선택하였습니다.

시간이 지나, 삐약씨의 회사는 대박을 쳤습니다.🎉 매일 200만 명이 넘는 유저가 접속하는 게임이 되었고, 어느새 벌써 10주년을 맞이하게 되었지요.

그런데 어느 날, 한 유저에게 문의가 왔습니다.

분명 티켓 보상을 획득했는데, 수량이 증가하지 않고 그대로예요. 버그인가요? 제 티켓 다시 돌려주세요. 😡

삐약씨는 의아했습니다. 10년 동안 이 기능에는 문제가 없었고, 만약 다른 작업으로 인해 해당 기능이 영향을 받아 버그가 발생했다면 문의가 빗발쳐야 하는데, 이 유저 한 명에게만 문의가 왔기 때문이죠.

다른 처리할 문제도 많은 삐약씨는 이 문제를 단순 유저의 네트워크 문제이거나, 유저가 잘못 인지한 것 같다고 판단하고 넘겨버립니다.

그런데 몇 달 뒤, 다른 유저 몇 명에게도 동일한 문의가 옵니다. 무엇이 원인일까요? 🤔

원인은 SMALLINT 데이터 타입의 범위를 초과한 데에서 발생했습니다. 처음에는 도전 과제 이벤트의 보상으로만 지급되던 티켓이 이후 추가된 여러 이벤트의 보상으로도 지급이 되었고, 이로 인해 초반부터 게임을 꾸준히 즐긴 유저들은 SMALLINT 범위까지 다다르게 된 것이죠. 애초에 SMALLINT까지 획득할 거라고 예상을 못 했기에 클라이언트와 서버에서 별도 예외 처리도 하지 않아 원인 파악에도 시간이 걸렸지요.

결국 삐약씨는 게임 점검을 걸어야 할지 DBA 분과 상의하고, 문제 되고 있는 다른 컬럼은 없는지 전수조사를 하게 됩니다.

DBA란 데이터베이스 관리를 전문적으로 하는 직업을 뜻합니다.

그냥 ALTER TABLE로 바로 해결하면 안되나?

SMALLINTint로 변경하기 위해 DDL 작업은 필요합니다. 하지만 데이터 규모가 작으면 모를까 크다면 그 작업이 절대 단순하지는 않습니다.

특정 테이블에 ALTER 명령을 실행하면, 읽기 작업은 계속 진행되지만 쓰기 작업은 제한될 수 있습니다. 특히 테이블의 크기가 크거나 작업이 복잡할 경우, 전체 테이블에 대한 락이 발생할 수 있으며, 이로 인해 락이 걸리는 시간이 길어질 수 있습니다.

만약 락이 걸린 사이에 유저가 아이템을 획득(=쓰기)하려고 시도하면, 데이터베이스가 테이블에 걸린 락이 해제될 때까지 해당 요청을 처리하지 못하게 됩니다. 그동안 유저는 응답을 받을 수 없으며, 서버 또한 데이터베이스의 응답을 기다리게 되어 대기 상태가 됩니다. 이러한 대기 상태가 길어지면 서버 자원이 소모되고, 동시에 많은 유저가 대기 상태에 놓일 경우 서버 부하가 급격히 증가하게 됩니다. 이로 인해 유저가 게임 플레이를 제대로 할 수 없게 되어 고객센터에 문의가 빗발치게 되겠지요.

DDL 명령어를 포함한 SQL 파일을 작성하여 DBA께 전달했고, 실제 서비스 환경에서 실행된 적이 있었습니다. 원래라면 사전에 공유를 해야 했지만 그러지 못한 것이 문제였습니다. 이로 인해 서버 응답 속도가 눈에 띄게 감소하고, 실제 게임 플레이도 많이 느려졌습니다. 이번에는 점검 없이 문제를 해결할 수 있었지만, 앞으로는 반드시 미리 공유를 해야 한다고 DBA께 당부 받은 적이 있습니다. 그때 제가 할 수 있는 거라곤 서버 에러 로그 카운트가 줄어드는지 확인하면서 부디 잘 해결되길 기도하는 수밖에 없었는데, 그때 생각하면 아직도 등골이 서늘하네요.💦

순차 증가 타입은 Int? Bigint?

우아한 기술 블로그 - 개발자 머피의 법칙: Primary Key는 int 가 아니라 long 으로에서는 다음과 같은 조언을 하고 있습니다.

너무도 명명백백하게 어떤 상황에서도 Integer 범위를 넘길 수 없는 경우가 아니라면 순차 증가 기본키는 Long으로 매핑하는게 이득인 것 같습니다.

Integer로 설정된 Primary Key는 서비스가 성공적으로 성장하면 범위를 초과하여 장애를 유발할 수 있으며, 나중에 IntegerLong으로 변경하는 것은 매우 비용이 많이 들 뿐 아니라 연계 시스템의 하나라도 Integer로 남아있으면 전면 장애로 이어질 수 있다고 하네요.

반면, 회사 내부에서 사용하는 그룹웨어나 인트라넷처럼 외부 접속이 제한된 시스템의 경우, 사용자 수와 데이터 생성량이 비교적 작고 통제된 환경에서 운영됩니다. 이런 경우 int 범위(2,147,483,647까지)가 충분히 큰 범위로 작용할 수 있습니다.

저는 SI 업체에 다닐 때 대부분 내부망에서만 사용하는 시스템을 개발했는데, 사용자가 평균 10명 이하였기 때문에 주로 int를 선택했고 bigint를 고려할 필요는 거의 없었습니다. 그러나 좀 더 규모가 큰 회사로 이직한 후에는 int로만 설정하는 것이 습관이 되어버려 초반에 항상 주의해서 데이터 타입을 결정해야 했던 것이 기억에 남습니다.

마무리

데이터베이스 설계 시 초기에는 단순한 요구 사항만을 고려할 수 있지만, 시간이 지나고 시스템이 성장하면서 예기치 않은 확장성과 유지 보수 문제가 발생할 수 있습니다. 따라서 초기 설계 단계에서부터 이러한 가능성을 충분히 고려하고, 필요 시 여유 있게 데이터 타입을 설정하는 것이 장기적인 관점에서 더욱 안정적인 시스템을 구축하는 데 도움이 될 것입니다.

물론, 사람마다 주관이 다를 수 있는 점을 참고해 주세요.

This post is licensed under CC BY 4.0 by the author.