[TIL] Real MySQL COUNT 쿼리

2025.03.20
·
4 min read

COUNT 쿼리 기본 이해

  • Count 쿼리는 행 수를 세는 데 사용한다.

  • COUNT(*) 는 테이블의 모든 행을 센다.

    • NULL 값도 포함되는데

  • COUNT(fd) 의 경우

    • 지정된 컬럼이 NULL이 아닌 경우에만 행을 센다.

    • NULL 값이 있는 행은 제외된다.

    • fd1 컬럼이 NULL 을 허용하는 경우 SELECT COUNT(fd1) 의 경우 SELECT COUNT(*) 보다 적은 수를 반환할 수 있음.

성능 최적화 팁

  • 인덱스를 잘 활용 해야함

    • 커버링 인덱스 사용하는 경우 쿼리가 인덱스만 읽으며

    • 데이터 파일을 추가로 읽지 않아도 속도가 빨라진다.

  • MySQL 8.0 에서는 innodb_parallel_read_threads 설정을 통해

    • 일부 COUNT(*) 쿼리를 병렬 처리 가능

    • 큰 테이블에서 유리하다..

  • 모든 쿼리가 병렬 처리되는 것은 아니다.

    • 설정과 쿼리 구조에 따라 다름

MySQL COUNT 쿼리 분석

쿼리 종류 차이

  • COUNT(*) COUNT(column)

    • COUNT(*)

      • 테이블의 모든 행을 센다. null 값도 포함된다.

      • 스토리지 엔진 ha_records API 로 빠르게 행 수 반환 가능

    • COUNT(column)

      • 지정된 컬럼이 null 이 아닌 경우 카운트

      • fd1 컬럼이 nullable 이고 일부 행이 NULL 인 경우

      • SELECT COUNT(fd1)SELECT COUNT(*) 보다 적은 수 를 반환

      • 내부적으로 ha_index_next API 사용함.

        • 행을 하나 씩 스캔해 NULL 여부를 확인해 성능이 느리다

     create table counter
    (
        fd1 int not null,
        fd2 int null
    );
    
     
     mydb> select count(fd1)
          from counter
    [2025-03-20 21:20:12] 1 s 225 ms (execution: 958 ms, fetching: 267 ms)에서 1부터 1개 행을 불러왔습니다
    mydb> select count(*)
          from counter
    [2025-03-20 21:20:18] 1 s 235 ms (execution: 938 ms, fetching: 297 ms)에서 1부터 1개 행을 불러왔습니다
    mydb> select count(fd2)
          from counter
    [2025-03-20 21:20:27] 7 s 137 ms (execution: 6 s 799 ms, fetching: 338 ms)에서 1부터 1개 행을 불러왔습니다
    
    • 보면 nullable 컬럼의 count 가 무려 7배나 느리다.

      • 컬럼은 100만건이 insert 되어 있음 1로만 구성..

정리

쿼리 타입 실행 시간 (초) 설명

COUNT(*)

1.235

병렬 처리 활성화, 커버링 인덱스 사용

COUNT(fd1, NOT NULL)

1.225

NOT NULL 컬럼, ha_records 사용

COUNT(fd2, nullable)

7.137

nullable 컬럼, ha_index_next 사용, 느림

난 COUNT(*) 이 성능이 더 안좋은 줄 알았다..

  • 근데 실제로는 COUNT(*) 을 사용하는 경우가 성능 최적화에 더 도움이 된다;;

  • 커버링인덱스로 짜여진 COUNT 와 COUNT(*) 대부분 성능이 비슷하고

    • 병렬처리된 경우 COUNT(*) 이 거의 5배 이상으로 성능이 더 좋다

      • 비교 대상 ) NULLABLE 컬럼, NOT NULL 컬럼 COUNT(fd) 경우







- 컬렉션 아티클