Database/Real MySQL Season1 ,2

16) Count(*) vs Count(column)

Tony Lim 2024. 10. 22. 18:17

다른 column들은 14108을 반환했는데 fd1 같은 경우에는 default 가 null이어서 그중에 null이 아닌녀석을 반환해서 숫자가 줄어든 것이다.

returns a count of the number of non-NULL values of expr in the rows retrieved by a SELECT statement

인자로 null을 주면 select count(NULL) = 0 이 된다.

 

where 조건 가진 Count

  • Covering Index
  • Non Covering Index

 

where 조건 없는 Count

  • ha_records() 스토리지 API 사용
  • ha_index_next() 스토리지 API 사용

 

fd1, fd2 둘다 row 갯수는 같다. Not NULL에 상관없이 두 컬럼 모두 다 row에 NULL이없이 값이 들어 있다.

Handler_read_rnd_next is a MySQL status variable that counts the number of times a row was read in a random order during table scans.

read_first가 1이고 rnd_next 값이 큰것은 primary index를 full scan했다는것을 의미한다.

왼쪽은 innodb 의 ha_records api를 딱 한번 호출해서 기록이 남지 않은 것이다.

 

innodb_parallel_read_threads 설정

  • MySQL 8.0 버전은 조건없은 Count()쿼리에 대해서 병렬 처리 지원
  • 병렬 처리를 비활성화하면 결과는?
    • 실행 계획은 동일하지만 쿼리 성능은 달라진다.

  • 모든 쿼리를 병렬로 처리할 수 있는 것은 아니다.기본 병렬로 처리할 수있는 스레드가 4이다.
  • 위 예시는 1로 변경한 경우이다.
  • fd2는 그대로인데 fd1은 병렬 처리를 비활성화하니까 훨씬 느려졌다.
  • 이는 Innodb 스토리지 엔진이 내부적으로 레코더를 읽어서 컬럼을 추출해내는 작업을 했는지 여부에따라 성능이 달라진다.
  • 병렬 처리가 활성화된 경우 fd1, *의 경우에는 컬럼을 추출해내는 작업을 하지 않았다. 이는 fd1이 not null이기 때문이다.
  • 병렬처리가 비활성화된 경우 fd1, fd2 는 컬럼에서 추출해내는 작업을 해서 느려졌다. 엥간하면 *을 사용하는것이 좋겠다.

 

조건없는 Count

  • Count(*) 와 Count (not_null_column)
    • ha_records() 스토리지 엔진 API 사용
    • 레코드 건수에 관계없이 , 1회만 호출된다.
    • Count(*) 쿼리는 레코드로부터 컬럼 추출 수행하지 않는다.
    • innodb_parallel_read_threads >= 2 의 경우 , Count(not_null_column) 쿼리는 컬럼 추출 수행하지 않는다.
  • Count(nullable_column)
    • ha_index_next() 스토리지 엔진 API 사용
    • 레코드 건수 만큼 호출된다.
    • 주어진 컬럼에 대해서 Eval 작업이 필요하다 (레코드의 컬럼 추출필요)

 

 

레코드수를 count하는데 index를 full scan하고 있다는것을 알 수 있다. mysql은 index가 null이 column도 포함하고 있다. 그래서 null 이들어간 column이든 아니든 정확한 record 건수를 가지고 올 수 있다.

Extra에 Using index는 커버링 인덱스로 처리되었다는것을 의미한다. 

커버링인덱스 = 인덱스에 있는 column들만 읽어도 충분함 , 추가적으로 데이터를 읽지않아도 처리가능

primary key index를 읽지 않고 idx1 index를 읽었다는것이다. primary key index는 record의 데이터를 가지고 있기 때문에 크기가 매우 큰 반면에 idx1 인덱스는 컬럼 한개만 가진 인덱스이기 때문에 매우 크기가 작다. mysql에서는 최소의 disk 읽기만 해도 쿼리를 완료할 수 있는 작은 index를 선택한 것이다.

 

실행계획에서는 idx1 index를 사용한다고 하였지만 실제 쿼리가 실행되면서 buffer pool의 페이지를 조사해보니 primary key index가 사용된것을 알 수 있다. 

이는 버그이다. where 없는 것은 맨날 primary key index를 사용하도록 작성이 되어있던것이다. 조만간 고쳐진다 한다.

 

ix1이 comment인것들 중에 fd1 이 null인지 아닌지도 확인을 해야한다.

그래서 Extra에 index라 안뜨는것이다.(covering index)  , 인덱스뿐만아니라 데이터도 실제로 읽어서 확인을 해야하기 때문이다.

실행계획을 해석 해보면

ix1 인덱스를 통해서 comment 인지 확인해서 16개를 찾고 이 16개의 row의 primary key를 통해서 record를 한건식 찾은다음 fd1이 null인지 아닌지 확인하는 것이다.

count(*) 을 통해서 covering index를 활용하는것이 좋아보인다.

 

왼쪽은 covering index가 아니라 위위 예시처럼 null 을확인을 하고 있다.

 

 

 

'Database > Real MySQL Season1 ,2' 카테고리의 다른 글

19) JSON 타입 활용  (0) 2024.10.30
17) NoWait & Skip Locked , 18) Union vs Union All  (0) 2024.10.24
15) 풀스캔 쿼리 패턴 및 튜닝  (0) 2024.10.22
14) UUID 사용 주의사항  (0) 2024.10.10
13) 콜레이션  (0) 2024.10.08