IT

SQL 최적화, 왜 느릴까? 실무에서 바로 통하는 성능 개선 해결법

생각의 만물상 – 만물쟁이 2026. 3. 2. 06:00

최근 서비스가 고도화되고 데이터가 기하급수적으로 증가하면서, 잘 돌아가던 쿼리가 갑자기 느려지는 경험을 하시는 개발자분들이 많습니다.

특히 트래픽이 몰리는 피크 타임에 응답 속도가 눈에 띄게 지연되면 서비스 품질은 물론 사용자 경험에도 치명적인 영향을 미치죠.

저 또한 과거 프로젝트 운영 중, 평소 0.1초면 충분했던 조회 API가 데이터 증가와 함께 5초 이상 지연되면서 식은땀을 흘리며 긴급 점검을 했던 기억이 납니다.

당시 문제를 해결하며 깨달은 것은 **"서버 스펙 업(Scale-up) 보다 중요한 것은 근본적인 쿼리 구조와 데이터베이스 엔진의 이해"**라는 점이었습니다.

오늘은 "SQL 최적화 왜 느릴까?"라는 고민을 가진 분들을 위해, 실제 성능 저하의 원인부터 실행계획 분석, 인덱스 재설계까지 단계별 해결 전략을 5,000자 이상의 상세한 가이드로 정리해 드리겠습니다.

본 이미지는 AI를 통해 생성되었습니다.

 


1. SQL이 느려지는 3가지 결정적 원인

문제 해결의 첫걸음은 원인 파악입니다. SQL 성능 저하는 대개 다음과 같은 이유로 발생합니다.

① 공포의 전체 테이블 스캔 (Full Table Scan)

가장 흔하면서도 치명적인 원인입니다. 적절한 인덱스가 없거나, 인덱스가 있더라도 조건절(WHERE)에서 함수를 사용하거나 형변환이 일어나는 등 비효율적으로 작성되면 데이터베이스는 인덱스를 타지 못하고 수백만 건의 데이터를 처음부터 끝까지 다 뒤지게 됩니다.

② 비효율적인 리소스 사용 (SELECT *, ORDER BY)

습관적으로 사용하는 SELECT *는 필요하지 않은 대용량 컬럼(TEXT, BLOB 등)까지 네트워크로 전송하게 하여 대역폭 낭비를 초래합니다.

또한, 인덱스 없이 수행되는 ORDER BY는 'Filesort'라는 과정을 거치며 디스크에 임시 파일을 생성해 정렬하므로 엄청난 I/O 부하를 일으킵니다.

③ 낡은 통계 정보와 옵티마이저의 오판

DB 엔진의 뇌 역할을 하는 '옵티마이저'는 테이블 통계 정보를 바탕으로 실행 경로를 결정합니다.

하지만 데이터가 급변했음에도 통계 정보가 갱신되지 않았다면, 옵티마이저는 인덱스 대신 전체 스캔이 빠르다고 잘못 판단할 수 있습니다.


2. 실행계획(EXPLAIN) 분석: 데이터베이스의 속마음 읽기

방향을 모른 채 운전하는 것만큼 위험한 것이 실행계획 확인 없이 쿼리를 수정하는 것입니다.

EXPLAIN 명령어는 쿼리가 어떤 경로로 데이터를 찾아가는지 보여주는 나침반입니다.

실행계획에서 반드시 체크해야 할 3요소

  1. Access Type (접근 방식): * ALL: 전체 테이블 스캔. 무조건 개선 대상입니다.
    • index: 인덱스 전체 스캔. ALL보다는 낫지만 데이터가 많으면 느립니다.
    • range: 인덱스를 범위로 스캔. 비교적 양호한 상태입니다.
    • ref / eq_ref: 특정 값을 인덱스로 정확히 찾는 경우. 매우 이상적입니다.
  2. Rows (예측 행 수): 옵티마이저가 쿼리를 위해 살펴봐야 한다고 예측한 데이터 건수입니다. 이 수치가 실제 데이터양과 너무 큰 차이가 난다면 통계 정보 갱신(ANALYZE TABLE)이 필요합니다.
  3. Extra (추가 정보): * Using filesort: 인덱스로 정렬하지 못해 메모리나 디스크에서 정렬 중이라는 경고입니다.
    • Using temporary: 쿼리 처리를 위해 내부적으로 임시 테이블을 만드는 중입니다. 성능에 매우 좋지 않습니다.

3. 인덱스 재설계 전략: 양보다 질이 중요하다

많은 분이 "인덱스는 많을수록 좋다"고 오해하지만, 인덱스는 생성될 때마다 쓰기(INSERT, UPDATE) 성능을 갉아먹습니다.

① 선택도(Selectivity) 높은 컬럼을 잡아라

선택도가 높다는 것은 해당 컬럼의 값이 유니크할 확률이 높다는 뜻입니다. (예: 주민등록번호 > 이름 > 성별). 성별처럼 데이터 종류가 적은 컬럼에 인덱스를 거는 것은 효과가 거의 없습니다.

② 복합 인덱스의 '순서' 법칙

두 개 이상의 컬럼을 묶은 복합 인덱스에서는 컬럼의 순서가 성능을 결정합니다. 반드시 WHERE 절에서 = 조건으로 자주 쓰이는 컬럼을 앞쪽에 배치해야 합니다. 범위 조건(<, >)이 들어가는 컬럼은 뒤로 빼는 것이 기본 공식입니다.

③ 커버링 인덱스 (Covering Index) 활용

SELECT 하는 모든 컬럼이 인덱스에 포함되어 있다면, DB는 실제 테이블 데이터를 보러 갈 필요 없이 인덱스 페이지만 보고 응답을 보냅니다. 이를 통해 디스크 I/O를 획기적으로 줄일 수 있습니다.


4. 쿼리 구조의 근본적인 체질 개선

단순히 인덱스를 추가하는 것보다 쿼리 자체의 논리 구조를 바꾸는 것이 더 큰 효과를 볼 때가 많습니다.

① 서브쿼리보다는 조인(JOIN)

최신 DB들은 서브쿼리 최적화가 잘 되어 있지만, 여전히 복잡한 상관 서브쿼리는 성능 저하의 주범입니다.

가능하면 조인 구조로 변경하여 한 번에 데이터를 가져오도록 유도하세요.

② 페이징의 함정, OFFSET 해결법

게시판 하단으로 갈수록 쿼리가 느려지는 경험 해보셨나요? OFFSET 1000000 LIMIT 10은 앞선 100만 건을 다 읽고 버리는 비효율을 발생시킵니다.

대신 마지막으로 읽은 ID값을 조건으로 거는 Keyset Pagination(No-offset) 방식을 도입하면 데이터 양과 상관없이 일정한 속도를 유지합니다.


5. 인프라 및 캐시 전략: 최후의 보루

쿼리 튜닝으로도 한계에 부딪혔다면, 이제 시야를 넓혀 인프라 단을 살펴봐야 합니다.

  • Redis 캐싱: 자주 바뀌지 않으면서 호출이 잦은 데이터는 DB까지 가지 않고 메모리 단에서 처리하는 것이 가장 빠릅니다. 실제 후기에 따르면 Redis 도입만으로 응답 속도를 3배 이상 끌어올린 사례가 많습니다.
  • Read Replica (읽기 전용 복제본): 쓰기 전용 Master DB와 읽기 전용 Slave DB를 분리하세요. 복잡한 통계 쿼리나 단순 조회가 Master의 트랜잭션을 방해하지 않도록 부하를 분산하는 핵심 전략입니다.

마무리하며

"SQL 최적화 왜 느릴까"라는 고민의 정답은 결국 데이터를 얼마나 '적게' 읽느냐에 달려 있습니다.

저 또한 처음에는 무작정 고사양 서버로 교체하는 것이 답인 줄 알았습니다.

하지만 근본적인 원인을 분석하고 실행계획을 뜯어보며 구조를 개선했을 때, 하드웨어 증설 비용을 아끼면서도 더 안정적인 성능을 확보할 수 있다는 것을 체감했습니다.

오늘 정리해 드린 5가지 전략 중 하나라도 여러분의 프로젝트에 적용해 보세요.

작은 쿼리 하나를 수정하는 것만으로도 서비스 전체의 응답 속도가 놀랍게 개선되는 경험을 하실 수 있을 것입니다.

성능 개선은 결국 개발자의 꼼꼼한 관찰과 분석에서 시작됩니다.

여러분의 쾌적한 데이터베이스 환경을 응원합니다!

 

※ 본 콘텐츠는 AI 도구의 도움을 받아 일부 제작되었으며, 최종 수정은 작성자가 진행했습니다.

LIST