[database] 카디널리티(cardinality)란?
- 4 minscardinality
카디널리티에 대해 공부하다가 개념이 너무 어렵게 설명되어있어서,
핵심적인 내용만 (최대한) 쉽게 정리해보고자 한다.
우선 결론부터 말하자면,
중복도가 ‘낮으면’ 카디널리티가 ‘높다’고 표현한다.
중복도가 ‘높으면’ 카디널리티가 ‘낮다’고 표현한다.
카디널리티는 전체 행에 대한 특정 컬럼의 중복 수치를 나타내는 지표이다.
이정도만 설명해도 말이 너무 어려운 것 같다.
하지만 최소한 내가 이해한 바로, 카디널리티는 그렇게 어려운 개념이 아니다.
처음 카디널리티의 개념이 헷갈렸던 이유는,
카디널리티가 ‘상대적인 개념’이라는 것을 이해하지 못했기 때문이었다.
예를들어,
주민등록번호 같은 경우는 중복되는 값이 없으므로 카디널리티가 높다고 할 수 있다.
이에 비해 이름같은 경우는 ‘주민등록번호에 비해’ 중복되는 값이 많으므로,
이름은 ‘주민등록번호에 비해’ 카디널리티가 낮다고 할 수 있다.
‘주민등록번호에 비해’라는 부분을 강조한 이유는, 카디널리티는 상대적인 개념으로 이해해야하기 때문이다.
id | name | location |
---|---|---|
0 | lee | seoul |
1 | park | pusan |
2 | choi | seoul |
3 | park | seoul |
4 | kim | seoul |
5 | bae | incheon |
6 | ahn | seoul |
7 | lee | seoul |
8 | lee | seoul |
9 | kim | seoul |
위와 같은 테이블이 있을때, (users 테이블이라고 하자)
id의 경우 전체 10건의 로우에 대해 중복된 값이 없으므로(주민등록번호처럼 컬럼이 unique한 값들로 이루어져 있으므로) 카디널리티가 ‘높다’.
name의 경우 distinct 값이 6건이므로 (lee, park, choi, kim, bae, ahm) ‘id컬럼보다는 카디널리티가 낮다’.
location의 경우 distinct 값이 3건밖에 없으므로 (seoul, pusan, incheon) 카디널리티가 ‘제일 낮다’.
참고로 distinct 값이 많다는건 중복도가 낮다는 의미로 보면 된다.
id 컬럼의 경우 distinct 값이 10건이다.
users 테이블의 컬럼간 distinct 값의 차이를 보자면 id(10건) > name(6건) > location(3건)
이다
즉, distinct 값이 많으면 중복도가 낮다 = 카디널리티가 높다고 생각해도 된다.
즉, 카디널리티는 객관적 수치보다는 상대적인 개념으로 이해해야한다.
인덱스를 걸 때, 내가 원하는 데이터를 선택하는 과정에서 최대한 많은 데이터가 걸러져야 성능이 좋을것이다.
(선택하는 데이터가 많아질수록 full scan에 가까워지므로)
즉, 여러 컬럼을 동시에 인덱싱할때, 다음과 같이 카디널리티가 높은 컬럼을(중복이 적은 컬럼을) 우선순위로 두는 것이 인덱싱 전략에 유리하다.
users 테이블에 인덱스를 걸어보자.
CREATE INDEX idx_localtion_first ON users(location, name, id)
CREATE INDEX idx_id_first ON users(id, name, location)
앞서 예제로 들었던 테이블에 대하여 두 개의 인덱스를 만들었다.
idx_location_first
라는 인덱스와 idx_id_first
라는 인덱스이다.
두 인덱스 모두 세 개의 컬럼을 전부 인덱스 컬럼으로 사용했다.
전자의 경우 이름 그대로 location 컬럼을 가장 먼저 인덱스 컬럼으로 사용했고,
후자의 경우 id 컬럼을 가장 먼저 인덱스 컬럼으로 사용했다.
다시 말해, 전자의 경우 카디널리티가 낮은 컬럼(중복도가 높은 컬럼)을 우선적으로 인덱싱 했고,
후자의 경우 카디널리티가 높은 컬럼(중복도가 낮은 컬럼)을 우선적으로 인덱싱했다.
단순히 인덱싱 컬럼의 순서를 바꾼것이 실제 동작시 어떠한 차이를 가져올까?
다음 쿼리를 통해 이해해보자.
SELECT *
FROM users
use index (idx_location_first)
WHERE id = '0'
AND name = 'lee'
AND location = 'seoul';
세 번째 줄을 보면 “use index” 키워드를 사용했는데,
이를 사용하지 않으면 쿼리 실행시 옵티마이저가 자동으로 인덱스를 선택한다.
본 쿼리에서는 idx_location_first
와 idx_id_first
인덱스를 테스트하기위해,
use index 키워드를 사용해 명시적으로 인덱스를 지정해주었다.
우선 카디널리티가 상대적으로 낮은 idx_location_first
인덱스로 테스트 해보았다.
idx_location_first
인덱스를 사용했으므로 location 컬럼을 우선적으로 인덱싱 할 것이다.
쿼리에서는 WHERE절에서 id, name, location 순으로 질의를 했지만,
location 컬럼에 우선적으로 인덱싱이 되어있으므로 내부적으로는 location 컬럼을 먼저 탐색하게된다.
즉, 먼저 인덱스에서 location이 ‘seoul’인 값을 거르므로, 해당하는 8개의 데이터가 남는다. (왜 8개가 남는지는 위 테이블 참조)
다음으로 name이 ‘lee’인 데이터를 거르게 되는데, 남은 8개 데이터에 대해서만 확인하고, 일치하는 3개 데이터가 남는다.
마지막으로, id가 ‘0’인 값을 거르기 위해 남은 3개 데이터에 대해 검색을 실시한다.
이번에는 idx_id_first
인덱스를 사용했을 경우 어떻게 동작하는지 생각해보자.
쉽게 생각하면, idx_location_first
에 비해 카디널리티가 더 높은 컬럼에 인덱스를 걸어준 경우이다.
SELECT *
FROM users
use index (idx_id_first)
WHERE id = '0'
AND name = 'lee'
AND location = 'seoul';
idx_id_first
의 경우는 id 컬럼을 우선적으로 인덱싱한다.
즉, 인덱스에서 id = ‘0’인 값을 거르므로 이미 첫 번째 검색해서 단 한건의 데이터만 남는다.
다음으로 남은 한 건의 데이터에 대해서만 name = ‘lee’인지 여부를 확인한다.
마지막으로, 역시 남은 한 건의 데이터에 대해서만 location = ‘seoul’인지 여부를 확인한다.
예시에서는 데이터 갯수가 작아 체감이 안될수도 있지만,
수천, 수만, 나아가 수십 수백만 개의 데이터에 대한 인덱싱에서는 현저한 성능차이를 보일것이다.
따라서 인덱싱 컬럼을 선택시, 카디널리티에 대한 고려는 중요한 부분이라고 할 수 있다.
카디널리티를 너무 어렵게 생각하지 말자.
그리고 그 개념 자체에 너무 얽매이지 말자.
인덱스 컬럼을 선택할때, 당연히 컬럼의 데이터 중복도에 대한 고려는 필수일 것이고,
카디널리티는 그 중복도를 고려할때 참고용으로 사용하는 하나의 지표일 뿐이다.