반응형
Ant_U
DBA 개미
Ant_U
전체 방문자
오늘
어제
  • 분류 전체보기 (271)
    • AWS (3)
    • C# (1)
    • SQL (245)
      • MYSQL (195)
      • MSSQL (50)
    • 자격증 (20)
      • SQLD (12)
      • SQLP (8)

인기 글

최근 글

250x250
hELLO · Designed By 정상우.
Ant_U

DBA 개미

[MySQL] 습관적으로 쓴 SELECT * 가 당신의 DB를 느리게 만드는 이유 (feat. 실행 계획 분석)
SQL/MYSQL

[MySQL] 습관적으로 쓴 SELECT * 가 당신의 DB를 느리게 만드는 이유 (feat. 실행 계획 분석)

2026. 1. 27. 12:26
728x90
반응형

 

 

"그냥 다 가져와서 애플리케이션에서 필요한 것만 쓰면 편하지 않나요?"

개발 초기 단계에서 흔히 하는 생각입니다. 기능 구현이 급할 때 SELECT *는 마법의 키워드처럼 느껴지죠. 컬럼이 추가되거나 이름이 바뀌어도 쿼리를 고칠 필요가 없으니까요. 하지만 서비스가 커지고 데이터가 쌓이기 시작하면, 이 편리함은 데이터베이스의 가장 큰 병목으로 돌아옵니다.

오늘은 왜 SELECT *가 DB 성능의 '조용한 살인자'가 되는지, 그리고 MySQL 실행 계획(EXPLAIN)을 통해 이를 어떻게 눈으로 확인할 수 있는지 알아보겠습니다.


1. 커버링 인덱스(Covering Index)를 무용지물로 만든다

SELECT *가 성능에 악영향을 미치는 가장 결정적인 이유는 인덱스 활용도를 떨어뜨리기 때문입니다.

MySQL의 최적화 기법 중 커버링 인덱스라는 개념이 있습니다. 쿼리가 필요한 모든 데이터를 인덱스(Index)에서만 찾을 수 있다면, 실제 데이터 파일(Table Data)을 열어보지 않고 인덱스 트리기만 타고 처리를 끝내는 방식입니다. 이는 책의 '목차'만 보고 답을 찾는 것과 같아서 속도가 비약적으로 빠릅니다.

하지만 SELECT *를 쓰면 인덱스에 포함되지 않은 컬럼까지 모두 가져와야 하므로, MySQL은 어쩔 수 없이 실제 데이터 행을 찾아가는 랜덤 I/O(Random I/O) 작업을 수행하게 됩니다.

  • SELECT col1 FROM table (인덱스 있음): 인덱스만 읽고 끝. (빠름, 메모리 효율적)
  • SELECT * FROM table: 인덱스를 탔더라도, 나머지 컬럼을 가지러 디스크의 데이터 블록을 다시 읽어야 함. (느림, 디스크 I/O 발생)

2. 네트워크와 메모리 리소스의 낭비

불필요한 컬럼, 특히 TEXT나 LONGBLOB 같은 대용량 데이터 타입이 포함되어 있다면 문제는 더 심각해집니다.

  • 네트워크 부하: DB 서버에서 애플리케이션 서버로 전송되는 데이터 양(Packet Size)이 불필요하게 커집니다.
  • 메모리 낭비: MySQL 내부의 임시 테이블이나 애플리케이션의 메모리 버퍼가 더 많은 공간을 차지하게 되어, 동시 접속자가 많을 경우 OOM(Out of Memory)의 원인이 될 수 있습니다.

주니어 개발자를 위한 비유:

편의점에서 "물 한 병"만 사면 되는데, SELECT *는 편의점 진열대의 모든 물건을 장바구니에 담아서 계산대로 가져온 뒤, 계산 직전에 "아, 물 빼고 나머지는 다 반품할게요"라고 하는 것과 같습니다. 계산대(CPU)와 점원(I/O) 모두 불필요한 고생을 하게 되죠.


3. 실행 계획(EXPLAIN)으로 범인 색출하기

실제로 내 쿼리가 인덱스를 잘 타고 있는지 확인하려면 EXPLAIN 명령어를 사용해야 합니다.

MySQL 5.7 vs 8.0 확인 방법

쿼리 앞에 EXPLAIN을 붙여서 실행해 보세요.

EXPLAIN SELECT * FROM users WHERE status = 'active';

여기서 가장 눈여겨봐야 할 항목은 Extra 컬럼입니다.

Extra 컬럼 값 의미 상태
Using index 실제 데이터 접근 없이 커버링 인덱스만으로 처리됨 ✅ 최고 (Best)
Using where 인덱스를 통해 검색했지만, 데이터를 가져오기 위해 테이블 접근 발생 ⚠️ 주의 (Normal)
Using filesort 인덱스를 타지 못해 별도의 정렬 작업 발생 (SELECT *가 원인일 수 있음) ❌ 나쁨 (Bad)

만약 필요한 컬럼만 명시한 쿼리(SELECT id, name ...)에서는 Using index가 나오는데, SELECT *로 바꿨을 때 이 문구가 사라진다면, 불필요한 디스크 읽기가 발생하고 있다는 확실한 증거입니다.

  Tip: MySQL 8.0 사용자라면?

MySQL 8.0부터는 EXPLAIN ANALYZE를 사용할 수 있습니다. 예측값이 아닌 실제 실행 시간과 스캔한 행의 수를 보여주므로 훨씬 정확한 분석이 가능합니다.

EXPLAIN ANALYZE SELECT * FROM users WHERE status = 'active';

마무리 및 실전 팁

SELECT *는 개발 편의성을 주지만, 운영 환경에서는 성능 저하의 시한폭탄이 될 수 있습니다. 특히 트래픽이 몰리는 서비스라면 사소한 I/O 차이가 장애로 이어질 수 있음을 명심해야 합니다.

 

 실전 적용 3가지 원칙

  1. 필요한 컬럼만 명시하라: 귀찮더라도 SELECT id, email, created_at 처럼 필요한 컬럼을 나열하는 습관을 들이세요.
  2. 커버링 인덱스를 노려라: 자주 조회되는 컬럼 조합은 인덱스로 묶어두면 SELECT 성능을 극대화할 수 있습니다.
  3. 애플리케이션 코드 리뷰: ORM(JPA, Hibernate 등)을 쓸 때 의도치 않게 전체 컬럼을 조회하고 있지 않은지 로그를 통해 점검하세요.

더 자세한 실행 계획 분석 방법이 궁금하다면 MySQL 공식 문서를 참고해 보시길 바랍니다.

[공식 문서] MySQL EXPLAIN Output Format

https://dev.mysql.com/doc/refman/8.0/en/explain-output.html

728x90
반응형

'SQL > MYSQL' 카테고리의 다른 글

[MySQL] 논리 연산자 완벽 가이드: AND, OR, NOT 제대로 사용하기  (0) 2026.02.03
[MySQL] WHERE 절 완전 정복: 기본 문법부터 8.0 최적화 팁까지  (0) 2026.01.30
[MySQL] DISTINCT: 중복된 데이터를 세련되게 처리하는 기술 (기초부터 성능 최적화까지)  (0) 2026.01.29
[MySQL] 백틱(`)의 역할과 올바른 사용법: 언제 써야 하고, 언제 피해야 할까?  (0) 2026.01.28
[MySQL] 에러 핸들링의 숨은 조력자: SHOW ERRORS 문 완벽 가이드  (0) 2026.01.26
[MySQL] 스토리지 엔진 확인의 첫걸음: SHOW ENGINES 문법 완벽 가이드  (0) 2026.01.22
[MySQL] SHOW ENGINE 문법 정복: SHOW ENGINE INNODB STATUS로 잠금·데드락·버퍼 이슈 읽어내기  (1) 2026.01.20
[MySQL] SHOW DATABASES 사용법과 실무 활용 가이드  (0) 2025.07.06
    'SQL/MYSQL' 카테고리의 다른 글
    • [MySQL] DISTINCT: 중복된 데이터를 세련되게 처리하는 기술 (기초부터 성능 최적화까지)
    • [MySQL] 백틱(`)의 역할과 올바른 사용법: 언제 써야 하고, 언제 피해야 할까?
    • [MySQL] 에러 핸들링의 숨은 조력자: SHOW ERRORS 문 완벽 가이드
    • [MySQL] 스토리지 엔진 확인의 첫걸음: SHOW ENGINES 문법 완벽 가이드
    Ant_U
    Ant_U

    티스토리툴바