벤티의 개발 로그

[Database] 파티셔닝 or 인덱스? 본문

카테고리 없음

[Database] 파티셔닝 or 인덱스?

sonsh75 2026. 3. 11. 23:05

무사히 마무리했던 첫 업무

입사 후 처음으로 개발했던 서비스가 출석체크로 전환되면서 많은 것이 달라졌다.

 

우선 이용자 수가 달라졌다. 하루에 수백 명만 이용하던 서비스는 이제 수천 명이 이용하고 있다.

이용 건수도 달라졌다. 하루에 수천 건이던 이용 건수는 이제 수만 건으로 늘었다.

이렇게 긍정적으로 바뀌는 것만 있었으면 좋았겠지만, 그렇지 않은 것도 있었다.

 

느릿느릿...

 

일단 내가 구현한 서비스를 간단히 소개하자면 다음과 같다.

 

1. 사용자가 캐릭터를 선택한 다음

2. 일정 비율 이상으로 스크래치를 긁으면

3. 사용자에게 랜덤으로 리워드를 지급한다.

 

위는 사용자 관점에서 볼 때 화면에서 일어나는 일이고, 코드에서 일어나는 일은 다음과 같다.

 

1. 사용자가 스크래치를 일정한 비율로 긁은 직후, 서버로 리워드 계산 요청을 보낸다.

2. 서버에서 일정한 확률이 적용된 리워드를 계산한다.

3. DB에 리워드 로그를 저장하고, 프론트로 반환한다.

4. Postback URL로 요청을 보내는 것이다.

5. 사용자에게 본인이 받을 리워드를 보여준다.

 

여기서 문제가 발생했다. 어느 순간 스크래치를 긁은 후 리워드가 화면에 보여지기까지의 시간이 눈에 띄게 느려졌다.

 

Stacked Logs

 

하루에 수천 건만 쌓이던 로그들은 이제 수만 건이 쌓이고 있었다. 그렇다 보니 중복 지급/참여 방지를 위해 조회해야 할 로그의 건수는 점점 늘어나고 있었다. 아쉽게도 처음에는 로그를 위한 코드만 작성할 생각을 했지, 로그를 테이블로 어떻게 관리해야 할 것인지에 관한 생각은 전혀 하지 않았기 때문에 수백만 건의 로그가 모두 하나의 테이블에서 관리하고 있었다.

 

서버 로그를 확인해 보니 프론트와 서버가 요청을 주고받는 시간이 1초 미만에서 1초 후반대로 늘어나 있었다. 이 작은 차이가 프론트에서 '화면의 버벅거림'을 유발하고 있다는 생각이 들었다.

 

이 시간을 줄일 수 있는 방법에 대해서 여러 가지 생각을 해봤는데, '파티셔닝'과 '인덱스'가 최선의 방법이라는 생각이 들었다. 결국 서버의 비즈니스 로직 코드를 아무리 리팩토링해도 응답 시간이 눈에 띌 만큼 감소할 것 같지 않았기 때문이다. 그래서 두 방법의 장단점을 조사했다.

 

파티셔닝

파티셔닝이란 말 그대로 테이블을 '물리적으로' 분리하는 것을 의미한다. 내 경우에는 하나로 뭉쳐진 로그 테이블을 월이나 일정 건수 같은 기준을 두고 테이블을 새로 생성해가며 관리하면 될 것 같았다.

 

파티셔닝의 가장 큰 장점은 데이터의 조회나 백업 시 매우 유리하다는 것이다. 예를 들어 매월 새로운 테이블을 만들어 관리할 경우, 다른 테이블은 신경 쓰지 않고 그 테이블 하나만 집중해서 분석할 수 있는 것이다. 이렇게 보면 '운영' 측면에서는 나쁘지 않아보였다.

 

하지만 결과적으로 파티셔닝을 사용하지 않았다. 이유는 아래와 같다.

 

우선, 파티셔닝을 사용할 경우 물리적인 공간이 선형적으로 증가한다는 특징이 있다. 테이블을 생성하는 기준을 어떻게 세웠든 간에, 시간이 흐를수록 더 많은 로그가 저장될 것이고, 당연히 더 많은 테이블이 만들어질 것이기 때문이다.

 

그리고 결정적으로 서비스의 목적과 맞지 않다고 생각했다. 아무래도 '출석 체크' 서비스이므로, 하루가 지나면 사용자에게 그 전날 기록이 사실상 무용지물이 되었다. 물론 운영하는 입장에는 데이터 분석을 위해 로그를 관리하는 것은 중요하지만, 최초의 목적은 '사용자에게 빠르게 리워드를 보여주자'였기 때문에 데이터 조회 범위를 줄이는 것이 더 중요하다고 판단했다.

 

인덱스

인덱스란 테이블 데이터에 대한 검색 효율을 높이기 위한 자료 구조다. MySQL에서는 주로 B-Tree라는 자료구조를 사용한다. (학부 때 C++로 B+-Tree를 직접 구현했던 기억이 선하다.)

 

인덱스의 가장 큰 장점은 탐색하고 싶은 범위에 해당하는 데이터'만' 조회할 수 있기 때문에 검색 속도도 빨라지고, I/O도 효율적으로 할 수 있다는 것이다. 예를 들어 파티셔닝을 한 달 단위로 진행한다고 하면, 3월 1일에 조회할 데이터 수보다 3월 31일에 조회할 데이터 수가 더 많을 것이다. 또한, 3월 31일에 중복 검사를 위해 데이터를 조회할 경우 3월 1일 데이터부터 모두 확인해야 한다는 단점이 있다.

 

하지만 기존 테이블 속성 중 사용자 ID나 날짜 데이터에 인덱스를 생성하면, 중복 지급/참여 방지를 위해 검사할 데이터를 1일 치로 한정할 수 있다.

 

현재 6개월간 500만 건 이상의 로그가 쌓였기 때문에 대략 계산하면 한 달에 80만 건 이상의 로그가 쌓일 것이다. 만약 파티셔닝을 사용할 경우, 3월 31일에 출석 체크 1건을 처리하기 위해 조회할 데이터의 수는 무려 80만 건이다. 하지만 인덱스를 사용할 경우, 조회할 데이터가 2~3만 건으로 크게 줄어든다.

 

결론

출석 체크를 한 번 실행할 때 조회할 데이터의 수, 시간 복잡도, 그리고 '출석 체크'라는 서비스의 목적을 고려하여 사용자 ID와 날짜 데이터에 인덱스를 적용했다.

 

다행히 서버 로그를 확인한 결과 API 응답 시간도 예전처럼 1초 미만으로 줄었고, 버벅거림도 훨씬 줄어들었다.

 

다만, 파티셔닝과 인덱스를 모두 사용하는 경우가 많다고 한다. 생각해 보면 이왕에 범위를 좁힌다고 하면, 더 효율적으로 좁히는 것이 당연히 낫기 때문인 것 같다. 역시 뭐든지 trade-off를 고려해서 적절한 수준으로 적용하는 것이 제일 중요한 것 같다.