6. 히스토그램
2011.04.24 22:33
6.히스토그램
(1) 히스토그램 유형
- 높이균형(Height-Balanced) 히스토그램
- 도수분포(Frequency) 히스토그램
- 히스토그램 생성조건 : 컬럼 통계 수집 시 버킷 개수를 2 이상으로 지정 (ex. for columns SIZE 10, col1, col2, col3)
- 히스토그램 정보 : dba_histograms, dba_tab_histograms 뷰
- 참고
- 10g 이후, dba_tab_columns 뷰의 histogram 컬럼 값을 통해 히스토그램 유형 파악 가능
- FREQUENCY : 값 별로 빈도수를 저장하는 도수분포 히스토그램 (값의 수 = 버킷 개수)
- HEIGHT-BALANCED : 각 버킷의 높이가 동일한 높이균형 히스토그램 (값의 수 > 버킷 개수)
- NONE : 히스토그램을 생성하지 않은 경우
- 예제 구성 정보
- 테이블명 : Member
- 컬럼 : memb_id(pk), age (2개)
- 2000명 회원 정보 데이터
10대 이하 : 50 40대 : 1200 (40세 회원 1000명 포함)
20대 : 270 50대 : 100
30대 : 330 60대 이상 : 50
create sequence seq ;
create table member( memb_id number, age number(2) );
exec dbms_random.seed(0);
insert into member(memb_id, age)
select seq.nextval, dbms_random.value( 1,19) from dual connect by level <= 50;
insert into member(memb_id, age)
select seq.nextval, dbms_random.value(20,29) from dual connect by level <= 270;
insert into member(memb_id, age)
select seq.nextval, dbms_random.value(30,39) from dual connect by level <= 330;
insert into member(memb_id, age)
select seq.nextval, 40 from dual connect by level <= 1000; ☞ popular value
insert into member(memb_id, age)
select seq.nextval, dbms_random.value(41,49) from dual connect by level <= 200;
insert into member(memb_id, age)
select seq.nextval, dbms_random.value(50,59) from dual connect by level <= 100;
insert into member(memb_id, age)
select seq.nextval, dbms_random.value(60,99) from dual connect by level <= 50 ;
(2) 도수분포 히스토그램 (≒ 'value-based 히스토그램') : 값별로 빈도수를 저장하는 히스토그램
- 특징
- 컬럼 값마다 하나의 버킷 할당 (값의 수 = 버킷 개수)
- 사용자가 요청한 버킷 개수가 컬럼이 가진 값의 수보다 많거나 같을 때 사용
- 최대 254개의 버킷만 허용 ☞ 값의 수가 254개를 넘는 컬럼에 사용 할 수 없음
SQL> SELECT COUNT( * ), COUNT( DISTINCT age ) FROM member ; COUNT(*) COUNT(DISTINCTAGE) ---------- ------------------ 2000 89
☞ age 컬럼 값의 수 (89개) <= 254 (최대 버킷 허용 수) ∴ 도수분포 히스토그램 생성 가능
SQL> begin 2 dbms_stats.gather_table_stats(user, 'member', method_opt => 'for all columns size 100'); 3 end; 4 / SQL> SELECT num_distinct, num_buckets, histogram 2 FROM user_tab_col_statistics 3 WHERE table_name = 'MEMBER' 4 AND column_name = 'AGE' ; NUM_DISTINCT NUM_BUCKETS HISTOGRAM ------------ ----------- --------------- 89 89 FREQUENCY?
☞ 도수분포 히스토그램에서 컬럼 값의 수와 버킷 개수가 항상 일치 (∵ 컬럼 값마다 단일 버킷 할당)
버킷 수를 크게 요청하여도 실제 값의 수만큼만 버킷 할당 ∴ 정확한 히스토그램을 위해 항상 254개 요청하는 것이 좋음
- dba/all/user_histogram 뷰
- endpoint_value : 버킷에 할당된 컬럼 값
- endpoint_number : endpoint_value 정렬 시, 최소 값부터 현재 값까지 누적 수량
SQL> SELECT t.age "연령", t.cnt "인원수(명)", 2 nvl2(h.prev, h.running_total - h.prev, h.running_total) frequency, h.running_total 3 FROM (SELECT age, count(*) cnt FROM member GROUP BY age) t, 4 (SELECT endpoint_value age, endpoint_number running_total, 5 lag(endpoint_number) over (ORDER BY endpoint_value) prev 6 FROM user_histograms 7 WHERE table_name = 'MEMBER' 8 AND column_name = 'AGE') h 9 WHERE h.age = t.age; 연령 인원수(명) FREQUENCY RUNNING_TOTAL ---------- ---------- ---------- ------------- 1 2 2 2 2 3 3 5 3 8 8 13: : : :39 12 12 650 40 1000 1000 1650 41 19 19 1669: : : :98 3 3 1999 99 1 1 2000
- 값별로 빈도수를 미리 계산해 두는 방식 ☞ 쉽고 정확하게 구할 수 있음
- 단, 시스템 자원의 한계로(1000만 고객 도수분포 관리 시 등) 컬럼 값의 빈도수를 모두 동일한 방식으로 저장 할 수 없음
☞ 값의 수가 많을 때 높이 균형 히스토그램 사용
(3) 높이균형 히스토그램 (≒ 'equi-depth 히스토그램')
- 특징
- 컬럼 값의 수보다 버킷 수가 적을 때 생성 (값의 수 > 버킷 개수)
- 하나의 버킷이 여러 개의 값 담당
- 빈도 수가 많은 값 (popular value) 포함 시 두 개 이상의 버킷 할당
SQL> begin 2 dbms_stats.gather_table_stats(user, 'member', method_opt => 'for all columns size 20'); 3 end; 4 /SQL> SELECT num_distinct, num_buckets, histogram 2 FROM user_tab_col_statistics 3 WHERE table_name = 'MEMBER' 4 AND column_name = 'AGE' ;NUM_DISTINCT NUM_BUCKETS HISTOGRAM ------------ ----------- --------------- 89 20 HEIGHT BALANCED
☞ user_tab_col_statistics 뷰에는 사용자가 요청한 20개의 버킷이 생성된 것으로 조회되지만
실제 히스토그램은 다음과 같이 11개의 버킷만 할당 ∵ POPULAR VALUE 압축 표현
SQL> SELECT endpoint_number, endpoint_value 2 FROM user_histograms 3 WHERE table_name = 'MEMBER' 4 AND column_name = 'AGE' 5 ORDER BY 1 ; ENDPOINT_NUMBER ENDPOINT_VALUE --------------- -------------- 0 1 <= 첫번째 레코드 : 버킷이 아니라 '최소 값' 표현 1 21 2 25 3 28 4 32 5 35 6 38 16 40 <= popular value 17 43 18 46 19 55 20 99
- dba/all/user_histogram 뷰
- endpoint_number : 버킷 번호 ☞ 1씩 증가하다가 39 ~ 40 연령대에서 10 칸 (6 → 16) 건너 뜀 (압축 표현)
- endpoint_value : 버킷의 Max 값
SQL> SELECT '~' || age "연령대", endpoint_number, diff, 2 ROUND( 100 * diff /sum(diff) over() ) "RATIO(%)" , 3 ROUND( t.num_rows * diff /sum(diff) over() ) "인원수(명)" 4 FROM (SELECT table_name, endpoint_value age, endpoint_number, 5 endpoint_number - lag(endpoint_number) over (ORDER BY endpoint_value) diff , 6 lag(endpoint_number) over (ORDER BY endpoint_value) prev 7 FROM user_histograms 8 WHERE table_name = 'MEMBER' 9 AND column_name = 'AGE') h, user_tables t 10 WHERE h.endpoint_number > 0 11 AND t.table_name = h.table_name 12 ORDER BY 1 ; 연령대 ENDPOINT_NUMBER DIFF RATIO(%) 인원수(명) --------------- --------------- ---------- ---------- ---------- ~21 1 1 5 100 ~25 2 1 5 100 ~28 3 1 5 100 ~32 4 1 5 100 ~35 5 1 5 100 ~38 6 1 5 100 ~40 16 10 50 1000 ~43 17 1 5 100 ~46 18 1 5 100 ~55 19 1 5 100 ~99 20 1 5 100
연령별 인원수(카디널리티)를 실제로 위처럼 구할 시, 39세, 40세는 각각 500명으로 계산 (실제값과 차이 있음)
☞ 오라클은 popular value(40세)에 대한 카디널리티를 구할 때만 버킷에 의한 계산식 사용
non- popular value는 미리 구해 놓은 density 값 이용
popular value에 대한 선택도 / 카디널리티 계산
- 조건절 값이 두 개 이상 버킷을 가진 popular value 이면 아래 공식을 따름
선택도 = (조건절 값의 버킷 개수) / (총 버킷 개수)
☞ popular value 40 의 경우 총 20개의 버킷 중 10개를 사용하므로 선택도 = 1 / 2
카디널리티 = 총 로우 수 * 선택도
= 총 로우 수 * 조건절 값의 버킷 개수 / 총 버킷 개수
= 2,000 * 10 / 20 = 1,000
non-popular value에 대한 선택도 / 카디널리티 계산
- non-popular value 일 때 미리 구해 놓은 density 값 이용
카디널리티 = 총 로우 수 * 선택도 = 총 로우 수 * density
SQL> SELECT 1 /num_distinct, density, ROUND(density * 2000) cardinality 2 FROM user_tab_col_statistics 3 WHERE table_name = 'MEMBER' 4 AND column_name = 'AGE' ; 1/NUM_DISTINCT DENSITY CARDINALITY -------------- ---------- ----------- .011235955 .2567285 513
※ 참고 density
컬럼 통계 ( *_tab_col_statistics )에서 조회 가능
해당 컬럼을 '=' 조건으로 검색 시, 선택도를 미리 구해 놓은 값
히스토리 유무에 상관없이 100% 균일 분포 시 density 값은 항상 1 / distinct 값과 동일
카디널리티 = 총 로우 수 * 선택도 = num_rows * density
(4) 바인드 변수 사용시 카디널리티 계산
- 변수 바인딩 시점 → 실행시점 (최적화 다음 발생)
∴ SQL 최적화 시점에 조건절 컬럼의 데이터 분포를 화용하지 못하는 문제 발생
☞ 변수 사용 시, 옵티마이저는 평균 분포를 가정한 실행계획 생성 (컬럼 분포가 불균일 할 경우 역효과 발생 가능)
'=' 조건일 때
- 바인드 변수 '=' 검색 시 선택도
- 히스토그램이 없을 때 : 1 / num_distinct 사용
- 도수분포 히스토그램일 때 : 1 / num_distinct 사용
- 높이균형 히스토그램일 때 : density 사용
범위검색 조건일 때
- 범위 검색 조건 사용 시, 옵티마이저는 고정된 규칙으로 선택도 추정 (but, 십중팔구 틀릴 가능성이 크다!! -_-;;)
[ 1~4 번의 선택도는 5%, 5~8 번의 선택도는 0.25로 계산 ]
================================================
| 번호 | 조건절 | 번호 | 조건절
================================================
| 1 | 번호 > :no | 5 | 번호 between :no1 and :no2
| 2 | 번호 < :no | 6 | 번호 > :no1 and 번호 <= :no2
| 3 | 번호 >= :no | 7 | 번호 >= :no1 and 번호 < :no2
| 4 | 번호 <= :no | 8 | 번호 > :no1 and 번호 < :no2
================================================
- 바인드 변수 사용 시, 정확한 컬럼 히스토그램에 근거하지 않고, 정해진 카디널리티 계산식에 근거한 비용 계산으로 최적이 아닌 실행계획 수립 가능
- 상수 조건식을 사용 할 시, 거의 정확한 카디널리티 계산
- DW, OLAP, 배치프로그램(Loop 내 수행 쿼리 제외)에서 수행되는 쿼리는 바인드 변수보다 상수 사용이 유리
(특히, 날짜 컬럼처럼 부등호, between 같은 범위 조건이 자주 검색되는 컬럼일 경우)
(5) 결합 선택도
- 히스토그램을 많이 만들어도 두 개 이상 컬럼에 대한 결합 선택도를 구할 시 정확성 ↓
동적 샘플링(Dynamic Sampling)
- 9i 부터 동적 샘플링을 통해 이 문제를 해결하기 위해 시도
- 소량의 데이터 샘플링을 통해 where 조건절에 사용된 두 개 이상 컬럼의 결합 분포를 구하는 기능 (동적 샘플링 레벨 4 이상 설정 시)
- 교재 p.430~431 예제
- 동적 샘플링(레벨 4 적용) 전/후 실행으로 옵티마이저의 예상 카디널리티의 정확도 차이 비교
다중 컬럼 통계(Multi-column Statistics)
- 11g 부터 '확장형 통계(extended statistics)'라 불리는 기능을 통해 다중 컬럼 히스토그램 생성 가능
- 다중 컬럼 히스토그램 컬럼 그룹 생성 방법
var ret varchar2(30);exec :ret := dbms_stats.create_extended_stats(user, '사원', '(급여, 상여)');print ret;begindbms_stats.gather_table_stats(user, '사원', method_opt > 'for all columns size skewonly for columns (급여, 상여) size 254');end;
댓글 0
번호 | 제목 | 글쓴이 | 날짜 | 조회 수 |
---|---|---|---|---|
20 | 5. 인덱스를 이용한 소트 연산 대체 | 멋진넘 | 2011.06.13 | 3397 |
19 | 6. IOT, 클러스터 테이블 활용 | 휘휘 | 2011.02.26 | 2800 |
» | 6. 히스토그램 | 실천하자 | 2011.04.24 | 11008 |
17 | 6. 조인 제거 | 멋진넘 | 2011.05.30 | 4640 |
16 |
6. Sort Area를 적게 사용하도록 SQL 작성
![]() | 실천하자 | 2011.06.13 | 8928 |
15 | 7. 인덱스 스캔 효율 [1] | 휘휘 | 2011.03.08 | 17043 |
14 | 7. 비용 | 휘휘 | 2011.05.02 | 6218 |
13 | 7. OR-Expansion | 멋진넘 | 2011.05.31 | 9617 |
12 | 7. Sort Area 크기 조정 | 실천하자 | 2011.06.13 | 15240 |
11 | 8. 인덱스 설계 | 멋진넘 | 2011.03.06 | 8389 |
10 | 7. 조인을 내포한 DML 튜닝 | 실천하자 | 2011.04.03 | 7228 |
9 | 8. 통계정보 Ⅱ [1] | 멋진넘 | 2011.04.29 | 32211 |
8 | 8. 공통 표현식 제거 | darkbeom | 2011.06.06 | 5296 |
7 |
9. 비트맵 인덱스
![]() | 실천하자 | 2011.03.05 | 12417 |
6 |
8. 고급 조인 테크닉-1
[1] ![]() | darkbeom | 2011.04.03 | 13367 |
5 | 9. Outer 조인을 Inner 조인으로 변환 | darkbeom | 2011.06.06 | 7927 |
4 |
8. 고급 조인 테크닉-2
![]() | suspace | 2011.04.05 | 7074 |
3 | 10. 실체화 뷰 쿼리로 재작성 | suspace | 2011.06.07 | 5572 |
2 | 11. 집합 연산을 조인으로 변환 | suspace | 2011.06.07 | 6039 |
1 | 12. 기타 쿼리 변환 [3] | 실천하자 | 2011.06.02 | 6702 |