[MySQL] SELECT *와 SELECT COUNT(*)

코린이·2025년 5월 25일

MySQL

목록 보기
14/23

✅ SELECT COUNT(*)

SELECT * SELECT COUNT(*) 쿼리 비교 시 뭐가 더 성능이 좋을까??
-> 일반적으로 두 쿼리 중 SELECT * 쿼리가 더 좋은 성능을 보여준다.

SELECT * 쿼리는 리미트 조건이 일반적으로 있지만, SELECT COUNT(*) 쿼리의 경우 리미트 조건이 없기 때문이다. 만약 리미트 조건이 없는 경우라면 다를 수 있다.

또한 COUNT(*)의 경우 테이블 내 모든 레코드의 건수를 확인해야 하므로 상황에 따라 쿼리 실행 시간이 상이하다.

SELECT * 
FROM TMP_TB;

SELECT COUNT(*) 
FROM TMP_TB;

▶︎ SELECT COUNT(*) 성능 개선

SELECT COUNT(*) 쿼리의 가장 이상적인 성능 개선은 Covering Index를 사용하는 방식이다.

이러한 Covering Index를 통하여 인덱스 컬럼만으로 쿼리가 처리되기 때문에 높은 성능을 자랑할 수 있다.

하지만 모든 쿼리를 Covering Index로 튜닝하는 것은 매우 부적절한 방법이다.

  • 모든 컬럼까지 인덱스에 추가할 경우 불필요한 컬럼까지 인덱스에 추가되기 때문에 얻는 장점보다 단점이 더 많다.

▶︎ SELECT COUNT(*) vs SELECT COUNT(DISTINCT id)

ORM을 통해 레코드의 건수를 확인하려 할 때 MySQL 서버 내부에서 SELECT COUNT(DISTINCT id) 쿼리로 동작되는 경우가 있다.
(위 쿼리에서 "id" Primary key를 의미)

SELECT COUNT(DISTINCT id) 쿼리는 SELECT COUNT(*)보다 성능이 좋지 않다.

  • MySQL 내부에서 중복 제거(DISTINCT)를 위해 중복 제거용 임시 테이블을 생성한다.

    임시 테이블에는 조건에 맞는 레코드를 삽입(INSERT 또는 UPDATE) 작업을 하는데, 이 과정에서 임시 테이블의 레코드를 검색(SELECT)하여 중복되는 값이 있으면 넘어간다.

    해당 과정이 끝난 이후 중복 제거용 임시 테이블의 건수를 확인하여 반환한다.

    이처럼 DISTINCT을 통한 레코드 건수는 다수의 SELECT-INSERT/UPDATE가 발행되기 때문에 2~3배 이상 성능이 좋지 않다.

▶︎ SELECT COUNT(*) 성능 튜닝

SELECT COUNT(*) 쿼리문의 튜닝은 Covering Index를 사용하는 방법이 사실 유일한 방법으로 보인다...

하지만 Covering Index로 튜닝하는것은 현실적으로 어려운 경우가 있으므로 다른 방법을 고안해야 한다.

  • 방법 1: SELECT COUNT(*) 쿼리 자체를 삭제하는 방법이다. 해당 쿼리문을 사용하지 않고 다른 방법을 통해 비즈니스 로직을 해결하는 방법이 있을 수도 있다.
  • 방법 2: SELECT COUNT(*) 쿼리를 삭제하는 것이 불가능한 경우, 표시할 페이지 번호만큼만 레코드 건수를 확인하는 방법과 임의로 페이지 번호를 표기하는 방법, 통계정보를 활용하는 방법 등을 고려해 봐야 한다.

이처럼 SELECT COUNT(*) 쿼리 성능 튜닝은 다양한 방법을 통해 진행할 수 있다. 여기서 쿼리 튜닝의 대상을 둘로 나누어 고민해 볼 필요가 있다.

  • SELECT COUNT(*) 쿼리 삭제 대상
    • WHERE 조건문 없는 쿼리문.
    • WHERE 조건문이 있지만, 레코드 건수가 많은 COUNT(*)
  • 인덱스를 활용해야 하는 대상
    • 정확한 레코드 건수가 필요한 경우
    • 레코드 건수가 적은 경우
    • WHERE 조건이 인덱스로 처리(Covering Index)할 수 있는 경우

0개의 댓글