비교할 문제
COUNT 성능에서 확인할 질문은 COUNT(*), COUNT(column)과 EXISTS는 언제 같은 질문이고 언제 다른가입니다. 전체 row 수, NULL이 아닌 값 수와 존재 여부를 서로 다른 요구로 구분합니다. 결과값, 순서, 오류 처리와 부수 효과가 다르면 같은 성능 비교가 아닙니다. 먼저 두 후보가 같은 일을 하는지 작은 입력으로 확인합니다.
원인 분석과 재현
COUNT(*)는 조건에 맞는 정확한 row 수를 계산하고 COUNT(column)은 NULL을 제외하므로 의미가 다르며 존재 확인과도 같은 작업이 아닙니다.
row 1천·1천만, 조건 selectivity 0.01%·50%, covering index 유무와 warm·cold buffer를 나눕니다. runtime version, 운영체제, CPU와 memory 제한도 결과와 함께 기록합니다. 이 예제의 plan과 문법은 MariaDB 기준이며 다른 DB 제품의 결과로 일반화하지 않습니다.
비교 질문은 COUNT(*), COUNT(column)과 EXISTS는 언제 같은 질문이고 언제 다른가입니다.
| 후보 | 유리한 조건 | 함께 치르는 비용 |
|---|---|---|
| COUNT(*) | 조건에 맞는 전체 row 수 | 많은 row를 확인해야 정확한 값을 반환 |
| COUNT(column) | NULL이 아닌 값의 수 | NULL 의미가 COUNT(*)와 다름 |
| EXISTS | 한 건이라도 있는지만 확인 | 전체 개수를 반환하지 않음 |
-- 전체 건수가 필요한 경우
ANALYZE FORMAT=JSON SELECT COUNT(*) FROM orders WHERE customer_id = 42;
-- 존재 여부만 필요한 경우. 반환 의미가 다르므로 같은 기능이라고 부르지 않습니다.
ANALYZE FORMAT=JSON SELECT EXISTS(SELECT 1 FROM orders WHERE customer_id = 42);
해결 방안과 판정
warm-up과 측정 구간을 나누고 여러 번 반복합니다. 서비스 지연은 median과 p95를, 처리량 비교는 단위 시간당 완료 수와 오류율을 함께 기록합니다. CPU time, allocation, RSS·heap, GC, I/O 중 이 작업에 직접 관련된 지표만 선택합니다. 존재 여부에 COUNT를 쓰는 것은 불필요한 전체 계산이 될 수 있지만 optimizer와 index에 따라 실제 plan은 달라집니다. 문법 이름보다 요구와 ANALYZE 결과로 판단합니다.
차이가 반복 실행의 흔들림보다 작다면 더 단순하고 읽기 쉬운 구현을 선택합니다. 차이가 충분히 크더라도 실제 요청 전체의 지연과 자원 사용이 개선되는지 확인한 뒤 적용합니다.
공식 문서와 적용 범위
문서의 기본값과 동작은 version에 따라 달라질 수 있습니다. 현재 DB 제품과 version, schema·index·통계 상태, 데이터 분포와 동시 부하에서 다시 확인합니다.