SQL 성능 최적화는 데이터베이스의 부하를 줄이고 빠른 응답 속도를 유지하는 핵심 기술입니다. 잘못 작성된 SQL 쿼리는 Full Table Scan(전체 테이블 검색)을 유발하며, 이는 성능 저하의 주요 원인이 됩니다.
이 글에서는 SQL 쿼리 최적화를 위한 7가지 핵심 체크리스트를 자세히 살펴보겠습니다. 각 항목마다 비효율적인 예제와 최적화된 쿼리 예제를 함께 제공하여, 실제 개발 환경에서 바로 적용할 수 있도록 구성했습니다.
1. SELECT * 대신 필요한 컬럼만 조회하기
1.1 문제점: SELECT * 사용 시 불필요한 데이터 조회
SELECT *
를 사용하면 모든 컬럼을 조회하게 되는데, 이는 네트워크 트래픽 증가, 메모리 사용량 증가, 그리고 쿼리 실행 속도 저하로 이어집니다.
📌 비효율적인 쿼리
-- 불필요한 컬럼까지 조회
SELECT * FROM users;
- 불필요한 컬럼까지 조회하여 데이터 전송량 증가
- 사용되지 않는 컬럼까지 불러와 메모리 낭비
- 인덱스 최적화 어려움
📌 최적화된 쿼리
-- 필요한 컬럼들만 조회
SELECT first_name, last_name FROM users;
- 필요한 컬럼만 조회하여 속도 개선
- 네트워크 트래픽 감소로 효율적인 데이터 전송
✅ Tip: 개발 단계에서 SELECT *
를 사용하여 테스트할 수 있지만, 운영 환경에서는 반드시 필요한 컬럼만 조회하도록 쿼리를 최적화해야 합니다.
2. WHERE 절에서 연산 및 함수 사용 피하기
2.1 문제점: WHERE 절에서 컬럼에 연산을 적용하면 인덱스가 무시됨
WHERE 절에서 함수나 연산을 사용하면 인덱스가 적용되지 않아 Full Table Scan
이 발생할 수 있습니다.
📌 비효율적인 쿼리
-- 함수 사용으로 인덱스 활용 불가
SELECT * FROM users WHERE YEAR(created_at) = 2023;
YEAR(created_at) = 2023
조건 때문에created_at
컬럼이 변형됨- 기존 인덱스를 활용하지 못하고 전체 데이터를 탐색함
📌 최적화된 쿼리
-- 범위 조건 사용하여 인덱스 활용
SELECT * FROM users WHERE created_at BETWEEN '2023-01-01' AND '2023-12-31';
- 연산을 제거하고 범위 조건을 사용하여 인덱스 활용 가능
- 검색 속도 향상 및 불필요한 스캔 최소화
✅ Tip: 컬럼 값을 그대로 활용할 수 있도록 쿼리 구조를 변경하는 것이 중요합니다.
3. LIKE 연산자 최적화 (와일드카드 %
위치 조정)
3.1 문제점: %
가 앞쪽에 위치하면 인덱스를 사용할 수 없음
LIKE
연산자에서 %
가 문자열 앞에 위치하면 B-Tree 인덱스를 활용하지 못하고 Full Table Scan
이 발생합니다.
📌 비효율적인 쿼리
-- %가 앞쪽에 있어 인덱스 사용 불가
SELECT * FROM users WHERE name LIKE '%son';
%
가 앞에 위치하여 모든 데이터를 검색해야 함- 인덱스 활용 불가능 → 성능 저하
📌 최적화된 쿼리
-- %가 뒤쪽에 있어 인덱스 활용 가능
SELECT * FROM users WHERE name LIKE 'son%';
- 인덱스를 활용 가능하여 검색 속도 향상
- 효율적인 문자열 검색 수행
✅ Tip: 만약 %가 앞쪽에 있어야 한다면 FULL TEXT INDEX
를 고려해야 합니다.
3.2 참고: FULL TEXT INDEX란?
FULL TEXT INDEX
는 텍스트 기반 검색을 최적화하기 위한 특수한 인덱스입니다. 일반적인 LIKE '%keyword%'
검색은 인덱스를 사용할 수 없어 느리지만, FULL TEXT INDEX를 활용하면 빠르게 텍스트를 검색할 수 있습니다.
LIKE '%keyword%'
는 Full Table Scan 발생 → 느림FULL TEXT INDEX
를 사용하면 검색 속도 대폭 향상
📌 사용 예시
-- FULL TEXT INDEX 생성
CREATE FULLTEXT INDEX idx_content ON articles(content);
-- 빠른 키워드 검색 (LIKE 대신 사용)
SELECT * FROM articles
WHERE MATCH(content) AGAINST ('database optimization');
- 텍스트 검색이 많은 환경(예: 블로그, 뉴스, 댓글 시스템)에서는
FULL TEXT INDEX
활용하는 것이 좋음
4. OR 대신 UNION ALL 활용
4.1 문제점: OR 연산자는 인덱스를 제대로 활용하지 못함
OR
연산자는 각 조건을 따로 평가해야 하기 때문에 인덱스를 비효율적으로 사용합니다.
📌 비효율적인 쿼리
-- OR 사용으로 인덱스 비효율적
SELECT * FROM users WHERE first_name = 'John' OR last_name = 'Doe';
OR
연산으로 인해 인덱스 탐색이 비효율적- 경우에 따라
Full Table Scan
발생 가능
📌 최적화된 쿼리
-- 각 조건별로 인덱스 활용 가능
SELECT * FROM users WHERE first_name = 'John'
UNION ALL
SELECT * FROM users WHERE last_name = 'Doe';
- 각 조건별로 인덱스 활용 가능
- 쿼리 성능 향상 효과 극대화
✅ Tip: UNION
은 중복을 제거하는 추가 연산이 필요하므로, 중복을 허용할 경우 UNION ALL
을 사용하는 것이 더 빠릅니다.
5. JOIN 최적화 (작은 테이블을 먼저 배치)
5.1 문제점: 큰 테이블을 먼저 조회하면 성능 저하
JOIN
시 큰 테이블을 먼저 읽으면 불필요한 데이터가 많아져 성능이 저하됩니다.
📌 비효율적인 쿼리
-- 큰 테이블을 먼저 조회하여 비효율적
SELECT u.first_name, u.last_name, o.order_date
FROM orders o
JOIN users u ON o.user_id = u.id;
orders
테이블이 크다면 필요하지 않은 데이터를 많이 가져옴- 조인 후 불필요한 데이터 필터링 발생
📌 최적화된 쿼리
-- 작은 테이블을 먼저 조회하여 필터링
SELECT u.first_name, u.last_name, o.order_date
FROM users u
JOIN orders o ON u.id = o.user_id;
- 작은 테이블을 먼저 조회하여 필요한 데이터만 필터링
- 조인 후 처리할 데이터 양이 줄어 성능 향상
✅ Tip: EXPLAIN을 사용하여 JOIN
순서를 확인하고 최적화하는 것이 중요합니다.
6. 인덱스를 적절히 활용
6.1 문제점: 적절한 인덱스가 없으면 Full Table Scan 발생
인덱스를 활용하지 않으면 전체 테이블을 스캔하는 비효율적인 쿼리가 됩니다.
📌 비효율적인 쿼리
-- 인덱스가 없어 Full Table Scan 발생
SELECT * FROM orders WHERE status = 'completed';
status
컬럼에 인덱스가 없으면Full Table Scan
발생
📌 최적화된 인덱스 추가
-- 인덱스 생성
CREATE INDEX idx_status ON orders(status);
-- 인덱스를 활용하여 검색 속도 향상
SELECT * FROM orders WHERE status = 'completed';
- 인덱스를 활용하여 검색 속도 향상
- 불필요한 데이터 스캔 방지로 최적화
✅ Tip: 복합 인덱스를 사용할 때는 자주 사용되는 컬럼을 앞쪽에 배치하는 것이 중요합니다.
7. ORDER BY 최적화: 정렬 비용 최소화
7.1 문제점: ORDER BY 연산은 추가적인 정렬 연산을 발생시킴
ORDER BY
를 사용하면 추가적인 연산 비용이 발생할 수 있습니다.
📌 비효율적인 쿼리
-- 정렬을 위한 추가 연산 발생
SELECT * FROM users ORDER BY last_name;
last_name
에 인덱스가 없으면 정렬 연산이 추가됨- 대용량 데이터에서 성능 저하
📌 최적화된 쿼리
-- 인덱스 생성
CREATE INDEX idx_last_name ON users(last_name);
-- 정렬된 인덱스를 활용
SELECT first_name, last_name FROM users ORDER BY last_name;
- 이미 정렬된 인덱스 데이터를 활용하여 성능 최적화
- 불필요한 정렬 연산 제거
8. 결론
SELECT *
대신 필요한 컬럼만 조회- WHERE 절에서 연산 및 함수 사용 피하기
LIKE
연산자에서%
위치 조정하기OR
대신UNION ALL
활용JOIN
최적화 (작은 테이블을 먼저 배치)- 적절한 인덱스 활용
ORDER BY
정렬 비용 최소화
'Database' 카테고리의 다른 글
[Database] 인덱스를 타지 않는 쿼리 10가지 경우 정리 (0) | 2025.01.05 |
---|---|
[Database] 데이터베이스 PK 전략 정리 (0) | 2025.01.05 |