통계 정보 = 오브젝트 통계, 시스템 통계
오브젝트 통계 = 테이블 통계, 인덱스 통계, 컬럼 통계
테이블 통계
테이블 통계 수집
begin
dbms_stats.gather_table_stats('scott', 'emp');
end;
/
테이블 통계 조회
select num_rows, blocks, avg_row_len, sample_size, last_analyzed
from all_tables
where owner = 'SCOTT' // 테이블 소유자
and table_name = 'EMP';
통계항목 | 설명 |
---|---|
NUM_ROWS | 테이블에 저장된 총 레코드 개수 |
BLOCKS | 테이블 블록수 = 사용된 익스텐트에 속한 총 블록 수 |
AVG_ROW_LEN | 레코드당 평균 길이(Bytes) |
SAMPLE_SIZE | 샘플링한 레코드 수 |
LAST_ANALYZED | 통계정보 수집일시 |
인덱스 통계
인덱스 통계 수집
-- 인덱스 통계만 수집
begin
dbms_stats.gather_table_stats( ownname => 'scott', indname => 'emp_x01');
end;
/
-- 테이블 통계를 수집하면서 인덱스 통계도 같이 수집
begin
dbms_stats.gather_table_stats( 'scott', 'emp', cascade=> true);
end;
/
인덱스 통계 조회
select blevel, leaf_blocks, num_rows, distinct_keys,
avg_leaf_blocks_per_key, avg_data_blocks_per_key,
clustering_factor, sample_size, last_analyzed
from all_tables
where owner = 'SCOTT' // 분석할 테이블 소유자
and table_name = 'EMP'
and index_name = 'EMP_X01';
통계항목 | 설명 | 용도 |
---|---|---|
BLEVEL | 인덱스 루트에서 리프 블록에 도달하기 직전까지 읽게되는 블록 수 | 인덱스 수직적 탐색 비용 계산 |
LEAF_BLOCKS | 인덱스 리프 블록 총 개수 | 인덱스 수평적 탐색 비용 계산 |
NUM_ROWS | 인덱스에 저장된 레코드 개수 | 인덱스 수평적 탐색 비용 계산 |
DISTINCT_KEYS | 인덱스 키값의 조합으로 만들어지는 값의 종류 개수 => 인덱스에 저장된 데이터 기준으로 실제 입력된 값의 종류 개수를 구해 놓은 수치 | 인덱스 수평적 탐색 비용 계산 |
AVG_LEAF_BLOCKS_PER_KEY | 인덱스 키값을 모두 '=' 조건으로 조회할 때 읽게 될 리프 블록 개수 | 인덱스 수평적 탐색 비용 계산 |
AVG_DATA_BLOCKS_PER_KEY | 인덱스 키값을 모두 '=' 조건으로 조회할 때 읽게 될 테이블 블록 개수 | 인덱스 수평적 탐색 비용 계산 |
CLUSTERING_FACTOR | 인덱스 키값 기준으로 테이블 데이터가 모여 있는 정도 | 테이블 액세스 비용 계산 |
컬럼 통계
컬럼 통계 수집
select num_distinct, density, avg_col_len, low_value,
high_value, num_nulls, last_analyzed, sample_size
from all_tab_columns
where owner = 'SCOTT'
and table_name = 'EMP';
and column_name = 'DEPTNO';
통계항목 | 설명 |
---|---|
NUM_DISTINCT | 컬럼 값의 종류 개수(NDV) |
DENSITY | '=' 조건으로 검색할 때의 선택도를 미리 구해놓은 값 |
AVG_COL_LEN | 컬럼 평균 길이(Bytes) |
LOW_VALUE | 최소 값 |
HIGH_VALUE | 최대 값 |
NUM_NULLS | 값이 NULL인 레코드 수 |
컬럼 히스토그램
'=' 조건에 대한 선택도는 1/NUM_DISTINCT 공식으로 구하거나 미리 구해 놓은 DENSITY값을 이용하면 된다. 단, 데이터 분포가 균일하지 않은 컬럼에는 적합하지 않다.
=> 히스토그램은 컬럼 값별로 데이터 비중 또는 빈도를 미리 계산해 놓은 통계정보다. 실제 데이터를 기반으로 계산해서 데이터 분포가 거의 정확하다.
통계항목 | 설명 |
---|---|
도수분포 | 값별로 빈도수 저장 |
높이균형 | 각 버킷의 높이가 동일하도록 데이터 분포 관리 |
상위도수분포 | 많은 레코드를 가진 상위 n개 값에 대한 빈도수 저장 |
하이브리드 | 도수분포와 높이균형 히스토그램의 특성 결합 |
히스토그램 데이터 수집
begin
dbms_stats.gather_table_stats('scott', 'emp',
cascade=> false, method_opt=> 'for columns ename size 10', deptno size 4);
end;
/
begin
dbms_stats.gather_table_stats('scott', 'emp',
cascade=> false, method_opt=> 'for all columns size 75');
=> 모든 컬럼에 대해서 Histogram bucket 의 수를 75로 한다.
end;
/
begin
dbms_stats.gather_table_stats('scott', 'emp',
cascade=> false, method_opt=> 'for all columns size auto');
=> 즉, Histogram 의 생성여부를 Oracle 이 알아서 판단하게 된다.
end;
/
히스토그램 통계 데이터 조회
select endpoint_value // endpoint_value : 버킷이 담당하는 가장 큰 값
, endpoint_number // endpoint_number : 버킷 번호
from all_histograms
where owner = 'SCOTT'
and table_name = 'EMP'
and column_name = 'DEPTNO'
order by endpoint_value;
시스템 통계
옵티마이저의 통계정보 활용방법
COST의 의미
비용기반(Cost-Based) 옵티마이저는 사용자 쿼리를 위해 후본군이 될만한 실행계획들을 도출하고, 데이터 딕셔너리에 미리 수집해 둔 통계정보를 이용해 각 실행계획의 예상비용을 산정하고, 그중 가장 낮은 비용의 실행계획 하나를 선택하는 옵티마이저
규칙기반(Rule-Based) 옵티마이저는 데이터 특성을 타나태는 통계정보를 전혀 활용하지 않고 단순한 규칙에만 의존하기 때문에 대량 데이터를 처리하는 부적합하다.
인덱스 구조, 연산자, 조건절 형태가 순위 결정
순위 | 액세스 경로 |
---|---|
1 | Single Row By Rowid |
2 | Single Row By Cluster Join |
3 | Single Row By Hash Cluster Key with Unique or Primary Key |
4 | Single Row By Unique or Primary Key |
5 | Clustered Join |
6 | Hash Cluster Key |
7 | Indexed Cluster Key |
8 | Composite Index |
9 | Single-Column Indexes |
10 | Bounded Range Search on Indexed Columns |
11 | UnBounded Range Search on Indexed Columns |
12 | Sort Merge Join |
13 | Max or Min of Indexed Column |
14 | ORDER BY on Indexed Column |
15 | Full Table Scan |
RBO의 좋지 않은 예시
SELECT * FROM 고객 ORDER BY 고객명;
SELECT * FROM 사원 WHERE 연령 >= 60 AND 연봉 BETWEEN 3000 AND 6000;
결론적으로 대용량 데이터, 빅데이터 조회시에 CBO 선택은 필수이고 오라클 조차 CBO만 지원한다고 선언했다.
alter session set optimize_mode = first_rows_1;
alter session set optimize_mode = first_rows_10;
alter session set optimize_mode = first_rows_100;
alter session set optimize_mode = first_rows_1000;
SELECT /*+ first_rows(30)*/ col1, col2, col3 from t where ~~
SELECT NAME, VALUE, ISDEFAULT, DEFAULT_VALUE FROM V$SYS_OPTIMIZER_ENV
SELECT *
FROM (
SELECT ROWNUM NO, 등록일자, 번호, 제목, 회원명, 게시판유형명, 질문유형명, 아이콘, 댓글개수
FROM (
SELECT A.등록일자, A.번호, A.제목, B.회원명, C.게시판유형명, D.질문유형명
, GET_ICON(D.질문유형코드) 아이콘, ( SELECT ~ FROM ~) 댓글개수
FROM 게시판 A, 회원 B, 게시판 유형 C, 질문유형 D
WHERE A.게시판유형 = :TYPE
AND B.회원번호 = A.작성자번호
AND C.게시판유형 = A.게시판유형
AND D.질문유형 = A.질문유형
ORDER BY A.등록일자 DESC, A.질문유형, A.번호
)
WHERE ROWNUM <= (:page * 10)
)
WHERE NO >= (:page-1) * 10 + 1
SELECT /*+ ORDERED USE_NL(B), USE_NL(C), USE_NL(D) */
A.등록일자, A.번호, A.제목, B.회원명, C.게시판유형명, D.질문유형명
, GET_ICON(D.질문유형코드) 아이콘, ( SELECT ~ FROM ~) 댓글개수
FROM (
SELECT
A.*, ROWNUM NO
FROM (
SELECT 등록일자, 번호, 제목, 작성자번호, 게시판유형, 질문유형
, GET_ICON(D.질문유형코드) 아이콘, ( SELECT ~ FROM ~) 댓글개수
FROM 게시판
WHERE A.게시판유형 = :TYPE
AND 작성자번호 IS NOT NULL
AND 게시판유형 IS NOT NULL
AND 질문유형 IS NOT NULL
ORDER BY 등록일자 DESC, 질문유형, 번호
) A
WHERE ROWNUM <= (:page * 10)
) A, 회원 B, 게시판유형 C, 질문유형 D
WHERE NO >= (:page-1) * 10 + 1
AND B.회원번호 = A.작성자번호
AND C.게시판유형 = A.게시판유형
AND D.질문유형 = A.질문유형
ORDER BY A.등록일자 DESC, A.질문유형, A.번호
=> 게시판 테이블에서 필요한 데이터만 조회 후 최종 결과 집합 10건에 대해서만 NL 조인 수행, 최종 결과집합 10건에 대해서만 함수를 호출하고 스칼라 서브 쿼리 수행
=> 결론
데이터 베이스 튜닝
DBA가 되고 싶다면
SQL 튜닝을 잘하고 싶다면 SQL 중심으로 공부해야한다.