[실전 DB 01] 데이터베이스가 느려지고 비싸지는 진짜 이유

개인 프로젝트로 만든 커뮤니티 사이트를 운영하다 어느 날 데이터베이스 요금 경고를 받았다. 트래픽은 하루 백 명 남짓인데 데이터베이스 읽기량이 한도의 네 배를 넘고 있었다. 방문자는 적은데 왜 읽기가 폭증했을까. 원인을 파보니 데이터베이스에 대한 근본적인 오해가 있었다. 이번 시리즈는 그 오해를 풀고 데이터베이스를 빠르고 저렴하게 다루는 법을, 실제로 겪으며 배운 대로 정리한다.


앞서 서버리스 시리즈에서 데이터베이스를 잠깐 다뤘는데, 이 시리즈는 성능과 비용 최적화에 훨씬 깊이 들어간다. 데이터베이스가 사이트의 심장이자, 잘못 다루면 가장 큰 비용이 나는 곳이기 때문이다.

1. 읽기량은 반환한 행이 아니라 스캔한 행이다

가장 중요한 오해부터 풀자. 많은 사람이 데이터베이스 읽기량을 쿼리가 돌려준 결과 행 수라고 생각한다. 그런데 실제로는 결과가 아니라 쿼리가 훑은 행, 즉 스캔한 행이 읽기량이다. 조건에 맞는 하나를 찾으려고 전체를 훑었다면, 돌려준 건 하나라도 읽기량은 전체 행 수만큼이다.


예를 들어 글이 오천 개 있는 테이블에서 특정 글 하나를 찾는다고 하자. 만약 조건에 맞는 인덱스가 없으면 데이터베이스는 오천 개를 하나씩 훑어 조건을 확인한다. 결과는 한 행인데 스캔은 오천 행이다. 이게 읽기량 오천으로 잡힌다. 결과의 크기가 아니라 찾는 과정의 크기가 비용인 것이다.


이 차이를 모르면 비용이 왜 튀는지 이해할 수 없다. 결과가 몇 개 안 되니 가볍겠지 생각하지만, 그걸 찾으려고 얼마나 훑었느냐가 진짜 비용이다. 데이터베이스 최적화의 출발점은 이 관점의 전환이다. 얼마나 돌려주느냐가 아니라 얼마나 훑느냐를 봐야 한다.


이 관점의 전환이 왜 중요한지 조금 더 풀어보자. 우리는 결과가 작으면 비용도 작으리라 직관적으로 믿는다. 그런데 데이터베이스는 결과를 만들기 위해 훑은 과정 전체가 비용이다. 작은 결과 뒤에 거대한 스캔이 숨어 있을 수 있다. 이 숨은 스캔을 보는 눈이 최적화의 첫 자질이다.


조금 더 배경을 주면, 이 오해는 데이터베이스를 처음 다루는 거의 모든 사람이 겪는다. 결과 행 수와 스캔 행 수를 헷갈리는 것이다. 이걸 바로잡지 않으면 아무리 최적화해도 왜 효과가 나는지 이해하지 못한다. 반대로 이 하나만 제대로 잡으면, 이후의 모든 최적화가 왜 통하는지가 자연스럽게 보인다.


한 가지 더, 이 시리즈는 특정 데이터베이스에 국한된 이야기가 아니다. 스캔이 비싸고 인덱스가 싸다는 원리는 어느 관계형 데이터베이스에도 통한다. 그래서 여기서 배운 감각은 다른 데이터베이스를 다룰 때도 그대로 쓰인다. 도구가 바뀌어도 원리는 남는다는 점에서, 데이터베이스 최적화는 오래 가는 기술이다.

2. 방문자가 적어도 읽기는 폭증할 수 있다

내 사이트에서 방문자는 적은데 읽기가 폭증한 이유가 여기 있었다. 방문자 한 명이 목록 페이지 하나를 열 때, 그 뒤에서 데이터베이스는 수천 행을 스캔하고 있었다. 인덱스 없는 비효율적인 쿼리가 매 요청마다 큰 테이블을 통째로 훑었기 때문이다. 방문자 수와 읽기량은 직접 비례하지 않는다.


여기에 봇 트래픽이 더해지면 상황이 심각해진다. 검색엔진 봇들은 사람보다 훨씬 많은 페이지를 기계적으로 긁는다. 그 하나하나가 비효율적인 쿼리를 실행시키면, 방문자는 백 명이어도 실제 쿼리는 수만 번 실행된다. 비효율적인 쿼리 한 개가 봇 트래픽과 만나 비용 폭탄이 되는 것이다.


그래서 데이터베이스 비용은 방문자 수가 아니라 쿼리의 효율로 결정된다. 방문자가 적어도 쿼리가 비효율적이면 비용이 크고, 방문자가 많아도 쿼리가 효율적이면 비용이 작다. 이걸 이해하면 최적화의 방향이 분명해진다. 방문자를 줄이는 게 아니라 쿼리를 효율적으로 만드는 것이다.


방문자와 읽기량이 비례하지 않는다는 게 처음엔 이상하게 들린다. 하지만 생각해보면 당연하다. 한 방문자가 목록을 여러 번 새로고침하고, 봇은 사람보다 수십 배 많은 페이지를 긁는다. 게다가 각 요청이 비효율적이면 그 비효율이 요청마다 곱해진다. 방문자 수는 빙산의 일각이고, 진짜 부담은 쿼리 효율에 숨어 있다.

3. 무엇이 읽기를 폭증시키나

내가 겪은 읽기 폭증의 주범들은 대체로 정해져 있다. 첫째는 인덱스 없이 큰 테이블을 정렬하거나 거르는 목록 쿼리다. 게시판 목록처럼 자주 호출되면서 전체를 훑는 쿼리가 가장 큰 비중을 차지했다.


둘째는 페이지네이션의 전체 개수 세기다. 페이지를 나누려고 매번 전체 행 수를 세는데, 이 세는 작업 자체가 전체를 훑는다. 목록을 보여줄 때마다 전체를 한 번 더 훑는 셈이라 읽기가 배로 든다. 셋째는 목록의 각 행마다 딸린 정보를 따로 조회하는 경우다. 열 개 글을 보여주면서 각 글의 댓글 수를 따로 세면, 그 세기가 열 번 반복된다.


이 주범들은 공통점이 있다. 자주 호출되면서 전체를 훑는다는 것이다. 자주 호출되는데 인덱스를 안 타면, 그 비효율이 호출 횟수만큼 곱해진다. 그래서 최적화는 자주 호출되면서 많이 훑는 쿼리를 찾아 인덱스를 태우거나 캐시하는 데서 시작한다. 이 시리즈가 다룰 내용이 바로 그것이다.


읽기를 폭증시키는 주범들이 정해져 있다는 게 오히려 다행이다. 무한히 다양한 게 아니라 몇 가지 패턴으로 좁혀지기 때문이다. 목록 정렬, 개수 세기, 행별 반복 조회. 이 몇 가지만 알고 점검하면 대부분의 폭증을 잡는다. 이 시리즈는 그 패턴들을 하나씩 짚어간다.


조금 더 실무적으로, 이 주범들을 찾을 때 자주 호출되는지를 먼저 본다. 아무리 무거운 쿼리도 드물게 호출되면 총 부담은 작고, 가벼운 쿼리도 초당 수십 번이면 총 부담이 크다. 그래서 무거운 쿼리보다 자주 호출되면서 무거운 쿼리를 우선 잡는다. 빈도와 무게를 곱한 총 부담이 진짜 기준이다.

4. 최적화 전후의 극적인 차이

이 원리를 이해하고 최적화하니 결과가 극적이었다. 한도의 네 배를 넘던 읽기량이, 인덱스를 정비하고 캐시를 쌓고 페이지네이션을 재설계하자 한도 안으로 들어왔다. 같은 트래픽인데 읽기량이 몇 분의 일로 줄었다. 콘텐츠도 기능도 그대로인데 데이터베이스만 효율적으로 바꾼 것이다.


중요한 건 이게 사이트를 줄이거나 기능을 뺀 게 아니라는 점이다. 보이는 건 똑같은데 뒤에서 데이터베이스가 훨씬 적게 일하게 만든 것이다. 사용자는 아무 차이를 못 느끼지만, 응답은 빨라지고 비용은 줄었다. 최적화는 이렇게 겉으로 안 보이는 곳에서 큰 차이를 만든다.


그리고 이 최적화는 성능과 비용을 동시에 잡는다. 적게 훑는 쿼리는 빠르게 응답하니 페이지가 빨라지고, 적게 훑으니 비용도 준다. 앞선 시리즈에서 다룬 페이지 속도가 검색 순위에도 영향을 준다고 했으니, 데이터베이스 최적화는 비용, 속도, 검색까지 두루 이롭다. 한 작업이 여러 이득을 낳는다.


최적화 전후의 차이가 극적이라는 게 이 작업의 보람이다. 코드 몇 줄, 인덱스 몇 개로 비용이 몇 분의 일이 되는 걸 보면, 데이터베이스 최적화만큼 투자 대비 효과가 큰 일도 드물다. 그것도 사용자는 아무 불편 없이 오히려 더 빠른 경험을 하면서 말이다. 겉으로 안 보이는 이 개선이 사이트를 오래 살아남게 한다.

5. 이 시리즈가 다룰 것

이 시리즈는 데이터베이스를 빠르고 저렴하게 만드는 실전 기법을 순서대로 다룬다. 먼저 어느 쿼리가 무거운지 측정하는 법에서 시작한다. 그다음 인덱스의 원리와 설계, 실행 계획을 읽는 법, 복합 인덱스와 부분 인덱스 같은 심화 기법을 다룬다.


이어서 페이지네이션의 함정과 재설계, 반복 조회 문제, 비정규화, 그리고 캐시 계층까지 나아간다. 마지막으로 이 모든 걸 하나의 루틴으로 엮어, 측정하고 최적화하는 습관을 정리한다. 각 편은 내가 실제로 부딪히고 해결한 경험을 담는다.


정리하면 데이터베이스가 느려지고 비싸지는 이유는 방문자 수가 아니라 쿼리가 얼마나 많이 훑느냐에 있고, 이 관점만 잡아도 최적화의 방향이 보인다. 다음 편에서는 그 첫걸음, 어느 쿼리가 얼마나 훑는지를 측정하는 법을 다룬다. 감으로 고치는 게 아니라 데이터로 찾는 것이 최적화의 시작이다.


이 시리즈가 다룰 것을 미리 알아두면 길을 잃지 않는다. 측정에서 시작해 인덱스로 무기를 갖추고, 페이지네이션과 반복 조회 같은 구조적 문제를 풀고, 캐시로 마무리한다. 각 편이 앞 편 위에 쌓이는 구조라, 순서대로 따라오면 데이터베이스를 다루는 감각이 자연스럽게 는다.


이 시리즈를 관통하는 태도를 미리 밝히면, 감이 아니라 데이터로 판단한다는 것이다. 앞선 시리즈들에서도 반복한 원칙인데, 데이터베이스에서는 특히 중요하다. 비용이 걸린 문제라 짐작으로 손대면 돈이 새기 때문이다. 재고, 고치고, 다시 재는 이 방식이 데이터베이스 최적화의 처음이자 끝이다.