[Database] SQL 성능 최적화: 빠른 쿼리를 위한 7가지 체크리스트

2025. 3. 5. 17:37·Database

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. 결론

  1. SELECT * 대신 필요한 컬럼만 조회
  2. WHERE 절에서 연산 및 함수 사용 피하기
  3. LIKE 연산자에서 % 위치 조정하기
  4. OR 대신 UNION ALL 활용
  5. JOIN 최적화 (작은 테이블을 먼저 배치)
  6. 적절한 인덱스 활용
  7. ORDER BY 정렬 비용 최소화

'Database' 카테고리의 다른 글

[Database] 트랜잭션의 격리 수준 (Isolation Level)에 대해 완벽하게 이해하기  (0) 2025.03.21
[Database] 인덱스를 타지 않는 쿼리 10가지 경우 정리  (1) 2025.01.05
[Database] 데이터베이스 PK 전략 정리  (0) 2025.01.05
'Database' 카테고리의 다른 글
  • [Database] 트랜잭션의 격리 수준 (Isolation Level)에 대해 완벽하게 이해하기
  • [Database] 인덱스를 타지 않는 쿼리 10가지 경우 정리
  • [Database] 데이터베이스 PK 전략 정리
개발자 동긔
개발자 동긔
배우고 느낀점들을 기록합니다. 열정 넘치는 백엔드 개발자로 남고싶습니다.
  • 개발자 동긔
    Donker Dev
    개발자 동긔
  • 전체
    오늘
    어제
    • Category (39)
      • BackEnd (23)
        • JAVA (15)
        • Spring & JPA (7)
      • Database (4)
      • Computer Science (2)
        • Network (0)
        • Security (0)
        • Web (1)
      • DevOps (6)
        • Docker (1)
        • Jenkins (0)
        • Monitoring (2)
        • CICD (1)
      • 트러블 슈팅 (3)
      • 성능 개선 (1)
      • Project (0)
  • 인기 글

  • 태그

    interface
    docker
    spring boot
    Spring
    Jenkins
    mysql master/slave replication
    CICD
    restful api 설계
    인터페이스
    java
    Database
    @RequestBody
    SSH
    와일드카드
    spring cloud msa
    nginx
    JPA
    docker compose
    master/slave db 이중화 처리
    restful api
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
개발자 동긔
[Database] SQL 성능 최적화: 빠른 쿼리를 위한 7가지 체크리스트
상단으로

티스토리툴바