데이터베이스 인덱스 설계와 실행 계획으로 느린 쿼리 찾는 법
“인덱스를 추가했는데도 왜 조회가 빨라지지 않을까요?”
“실행 계획의 rows와 cost를 봐도 무엇이 문제인지 모르겠습니다.”
“복합 인덱스의 컬럼 순서는 어떤 기준으로 정해야 할까요?”
느린 쿼리는 인덱스를 먼저 늘리기보다, 실행 계획에서 실제로 읽는 범위와 쿼리 패턴을 확인한 뒤 그 패턴에 맞는 인덱스 후보를 비교해야 합니다. 핵심 기준은 인덱스의 유무가 아니라 WHERE·JOIN·ORDER BY와 반환 행 수가 만드는 읽기 범위입니다.
알레오 개발자로 개발 업무를 하는 김레오는 성능 문제를 SQL 문장만 보고 추측하기보다 조건, 정렬, 조인, 반환 행 수를 나누어 기록하고 확인하는 방식을 우선합니다. 데이터베이스 제품과 버전에 따라 계획 표기와 최적화 방식은 달라질 수 있으므로, 이 글은 특정 명령어를 외우기보다 검증 순서를 세우는 데 초점을 둡니다.
느린 쿼리에서 인덱스보다 먼저 볼 것은 무엇인가요?
먼저 볼 것은 “어떤 행을 얼마나 읽는가”입니다. 인덱스 추가는 읽기 범위가 과도하다는 점을 확인한 뒤 검토할 선택지입니다. 응답 시간이 길다는 사실만으로 인덱스 누락을 결론 내리면, 반환 행 수가 많거나 정렬·조인 단계가 큰 중간 결과를 만드는 문제를 놓칠 수 있습니다.
문제 쿼리를 다음 질문으로 분해해 보세요.
WHERE조건이 결과 행을 충분히 줄이는가JOIN전에 각 테이블에서 읽는 범위는 어디까지인가ORDER BY,GROUP BY,DISTINCT가 큰 중간 결과를 만들고 있지는 않은가- 애플리케이션이 필요 이상으로 많은 컬럼이나 행을 요청하지는 않는가
- 한 요청 안에서 동일하거나 유사한 조회가 반복되지는 않는가
예를 들어 목록 조회가 느릴 때 status 조건에 인덱스를 추가하는 방안을 떠올릴 수 있습니다. 그러나 해당 상태가 테이블의 많은 행을 포함한다면, 그 조건만으로는 읽기 범위를 충분히 줄이지 못할 수 있습니다. 이때는 날짜 범위, 테넌트 식별자, 페이지네이션 기준처럼 결과 집합을 더 좁히는 조건이 있는지 함께 검토합니다.
복합 인덱스 컬럼 순서는 어떻게 정하나요?
복합 인덱스 순서는 실제 쿼리에서 동등 조건·범위 조건·정렬·조인이 결합되는 순서를 기준으로 정합니다. “선택도가 높은 컬럼을 항상 앞에 둔다”는 식의 한 가지 규칙만으로 결정하지 않는 편이 좋습니다.
후보를 만들 때는 다음 표를 출발점으로 사용할 수 있습니다.
| 쿼리 요소 | 인덱스 설계에서 확인할 점 |
|---|---|
| 동등 비교 | 특정 값으로 결과를 좁히는 조건이 무엇인지 확인합니다. |
| 범위 비교 | 날짜 범위, 크기 비교처럼 넓게 읽을 수 있는 조건의 위치를 검토합니다. |
| 정렬 | 조건을 만족한 뒤 별도 정렬 단계가 필요한지 봅니다. |
| 조인 | 조인 키가 각 테이블에서 어떤 방식으로 탐색되는지 확인합니다. |
| 결과 집합 | 조건 조합이 실제로 반환 범위를 줄이는지 확인합니다. |
가령 tenant_id로 범위를 한정하고 created_at 기준 최신순 목록을 가져오는 쿼리라면, 두 조건과 정렬이 SQL에 어떻게 쓰이는지 한 묶음으로 봐야 합니다. 범위 조건, 함수 적용, 형 변환, 부정 조건이 섞인 쿼리도 별도로 표시해 두면 기대와 다른 접근 경로가 나왔을 때 원인을 좁히기 쉽습니다.
인덱스 후보를 적을 때는 조회 경로만 보지 말고 해당 테이블의 INSERT, UPDATE, DELETE 경로도 함께 적어 두세요. 이 글의 목표는 인덱스를 많이 만드는 일이 아니라, 문제 쿼리의 조건과 정렬에 필요한 후보를 실행 계획으로 비교하는 일입니다.
실행 계획에서는 어떤 값을 어떤 순서로 읽어야 하나요?
실행 계획에서는 인덱스 사용 표시 하나보다 접근 방식, 각 단계의 행 수, 정렬·조인으로 만들어지는 중간 결과를 먼저 읽습니다. 변경 전후에는 같은 호출 조건으로 계획과 결과를 비교해야 판단이 흔들리지 않습니다.
1. 접근 방식과 대상 테이블
각 테이블을 전체로 읽는지, 인덱스를 통해 후보를 찾는지 확인합니다. 전체 스캔 자체를 오류로 단정할 필요는 없습니다. 테이블이 작거나 결과 대부분이 필요한 요청이라면 전체 스캔이 선택될 수 있습니다. 다만 큰 테이블에서 작은 결과만 필요한 쿼리가 반복된다면, 조건과 접근 경로를 우선 점검할 대상이 됩니다.
2. 예상 행 수와 실제 행 수
대상 DB가 추정치와 실행 통계를 함께 보여 주는 환경이라면 둘을 구분해 기록합니다. 차이가 큰 경우에는 통계 상태, 조건 값의 분포, 조건식 변형 여부를 확인할 질문이 생깁니다. 실제 실행을 수반하는 분석은 대상 환경과 실행 시간, 제한 조건을 먼저 검토한 뒤 수행해야 합니다.
3. 정렬·해시·중간 결과의 크기
정렬, 임시 결과, 해시 조인처럼 중간 데이터를 만드는 단계가 있는지 봅니다. 이때는 연산 이름만 보기보다 그 직전 단계가 이미 얼마나 많은 행을 만들었는지 확인하는 편이 유용합니다. 인덱스 후보를 바꿀지, 필터를 보강할지, 조회 화면의 반환 범위를 조정할지 판단하는 근거가 됩니다.
4. 변경 뒤에는 같은 조건으로 다시 측정
인덱스 후보를 적용했다면 같은 파라미터, 비슷한 데이터 규모, 같은 정렬·페이지 조건으로 다시 비교합니다. 개발 환경과 운영 환경의 데이터 분포가 다르면 결과를 그대로 일반화하지 않습니다. 실행 시간뿐 아니라 읽은 행 수, 정렬 발생 여부, 반환 행 수, 쓰기 경로에서 확인할 사항도 함께 남기면 다음 변경을 설명하거나 되돌리기 쉽습니다.
최근 실행 계획과 관측 기록을 함께 보는 논의
최근 관련 자료에서는 실행 계획을 인덱스 사용 여부와 읽기 방식을 확인하는 근거로 두고, 관측 지표를 유지보수 항목과 연결하는 접근이 소개되고 있습니다. 다만 한 자료만으로 국내 업계 전체의 확산이나 표준적 관행을 단정할 수는 없습니다.
GilliLab 블로그의 2025년 최초 발행 글은 실행 계획을 인덱스 사용 여부와 접근 방식을 확인하는 근거로 설명하고, 인덱스 사용률·고비용 쿼리·단편화·통계 갱신을 유지보수 항목과 연결해 다룹니다. 출처: 데이터베이스 인덱스 설계와 실행 계획 최적화
주니어 개발자가 바로 적용할 수 있는 것은 특정 도구를 먼저 도입하는 일이 아니라, 느린 요청마다 쿼리 원문, 호출 조건, 반환 행 수, 실행 계획, 변경 뒤 결과를 한 기록으로 남기는 일입니다. 기록이 있어야 환경과 조건이 달라졌을 때도 왜 특정 인덱스 후보를 검토했는지 설명할 수 있습니다.
결론
인덱스 설계의 출발점은 컬럼 목록이 아니라 실행 계획이 보여 주는 읽기 범위와 쿼리 패턴입니다. 느린 쿼리를 발견하면 결과 집합을 먼저 확인하고, 접근 방식과 행 수를 읽은 뒤, 복합 인덱스·정렬·조인 조건을 함께 비교하세요.
한 번에 여러 인덱스를 추가하지 말고 문제 쿼리 하나의 호출 조건을 고정합니다. 계획을 기록한 뒤 후보를 하나씩 비교하고, 조회 경로의 변화와 쓰기 경로에서 확인할 사항을 함께 남깁니다. 그러면 “인덱스가 있으니 빨라야 한다”는 기대 대신 현재 쿼리와 데이터 조건에 맞춰 선택할 수 있습니다.
자주 묻는 질문
인덱스가 있는데도 풀 스캔이 발생할 수 있나요?
그럴 수 있습니다. 결과 대부분을 읽는 요청이나 데이터 조건에서는 전체 스캔 경로가 계획에 나타날 수 있습니다. 실제 이유는 해당 쿼리의 실행 계획과 반환 범위를 함께 확인해야 합니다.
단일 컬럼 인덱스 여러 개면 복합 인덱스가 필요 없나요?
항상 그렇지는 않습니다. 함께 쓰이는 필터와 정렬 조건이 있다면 복합 인덱스 후보를 별도로 검토할 수 있습니다. 단, 적용 여부는 실제 쿼리와 실행 계획을 비교해 결정하세요.
실행 계획의 cost가 낮으면 무조건 더 좋은 쿼리인가요?
무조건은 아닙니다. cost는 계획을 읽을 때 보는 항목 중 하나이며, 같은 호출 조건에서 행 수, 정렬 단계, 반환 범위와 함께 해석해야 합니다.
모든 조회 조건에 인덱스를 만들어도 되나요?
권장하기 어렵습니다. 사용 빈도와 읽기 범위 개선이 분명한 쿼리 패턴을 중심으로 후보를 검토하고, 쓰기 경로도 함께 점검하세요.
제 도움이 필요하시다면
알레오 개발자 김레오는 개발 업무 범위에서 백엔드 쿼리의 점검 순서를 정리하는 데 도움을 드릴 수 있습니다. 실행 계획에서 무엇을 먼저 읽어야 할지 막막하거나, 특정 조회 쿼리의 조건·정렬·조인에 맞춰 인덱스 후보를 문서화해야 하는 상황이라면 이 글의 기준으로 논의를 시작할 수 있습니다.
도움을 요청하기 전에는 문제 쿼리의 목적, 호출 조건, 대략적인 데이터 규모, 현재 실행 계획, 기대하는 반환 범위를 준비해 보세요. 민감한 운영 데이터와 비밀 정보는 제거한 상태로 공유하는 것이 좋습니다.




