|
|
| (다른 사용자 한 명의 중간 판 2개는 보이지 않습니다) |
| 1번째 줄: |
1번째 줄: |
| = {{{제목|문서 제목 없음}}} = | | <onlyinclude> |
| | | <div |
| __TOC__
| | class="notice {{{속성|}}}" {{#if:{{{id|}}}|id="{{{id}}}"}} |
| ----
| | style=" |
| {{요점|내용= {{{내용}}}}} | | margin: 8px 0; |
| ----
| | padding: 8px; |
| == 기본 설명 ==
| | border-style: solid; |
| {{{본문|
| | border-width: 2px 0; |
| ===값의 의미 ===
| | border-color: {{{테두리색|#FFFFFF}}}; |
| # 범위
| | background-color: {{{배경색|#FFFFFF}}}; |
| - **최소값**: 테이블의 블록 수 (완벽하게 정렬된 경우) | | clear: both; |
| - **최대값**: 테이블의 행 수 (완전히 무작위로 분산된 경우) | | width: 100%; |
| | | "> |
| === 해석 방법 ===
| | <span style=" |
| <source lang=sql>
| | display: inline-block; |
| -- Clustering Factor 확인
| | transform: translate(10%, 10%); |
| SELECT
| | color:green; |
| index_name,
| | "> |
| table_name,
| | <i class="material-icons icon-image-preview">{{{아이콘이름|post_add}}}</i></span> |
| clustering_factor,
| | <span style=" |
| num_rows,
| | font-weight: bold; |
| leaf_blocks,
| | font-size: 1.2em; |
| -- 테이블 블록 수
| | ">{{{타입|sql}}} </span> |
| (SELECT blocks FROM dba_tables t
| | <div style="margin-top: 6px; padding: 10px 12px; background-color: {{{코드배경색|#1E1E1E}}}; color: {{{코드글자색|#D4D4D4}}}; font-family: Consolas, 'Courier New', monospace; font-size: 0.95em; line-height: 1.5; white-space: pre-wrap; word-break: break-all; overflow-x: auto; border-radius: 4px;">{{{SQL}}}</div> |
| WHERE t.owner = i.table_owner
| | </div> |
| AND t.table_name = i.table_name) AS table_blocks
| |
| FROM dba_indexes i
| |
| WHERE table_owner = 'HR'
| |
| AND table_name = 'EMPLOYEES';
| |
| </source>
| |
| | |
| **판단 기준:**
| |
| | Clustering Factor | 상태 {{!}} 의미 {{!}}
| |
| |------------------|------|------| | |
| | ≈ 테이블 블록 수 | **좋음** ✅ {{!}} 데이터가 인덱스 순서대로 잘 정렬됨 {{!}}
| |
| | 테이블 블록 수 ~ 행 수 사이 | 보통 {{!}} 부분적으로 정렬됨 {{!}}
| |
| | ≈ 테이블 행 수 | **나쁨** ❌ {{!}} 데이터가 무작위로 흩어져 있음 {{!}} | |
| | |
| ---
| |
| | |
| === Clustering Factor의 계산 원리 ===
| |
| # 인덱스를 순차적으로 스캔하면서:
| |
| ## - 이전 행과 다른 블록에 있으면: CF++
| |
| ## - 이전 행과 같은 블록에 있으면: CF 증가 없음
| |
| | |
| ==== 예제로 이해하기 ====
| |
| * 시나리오 1: 좋은 Clustering Factor
| |
| <source lang=sql>
| |
| 인덱스 순서: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
| |
| 블록 배치: [1,2,3] [4,5,6] [7,8,9] [10]
| |
| Block1 Block2 Block3 Block4
| |
| | |
| Clustering Factor = 4 (블록 수)
| |
| </source> | |
| | |
| 시나리오 2: 나쁜 Clustering Factor
| |
| | |
| <source lang=sql>
| |
| 인덱스 순서: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
| |
| 블록 배치: [1] [5] [2] [7] [3] [9] [4] [10] [6] [8]
| |
| B1 B2 B3 B4 B5 B6 B7 B8 B9 B10
| |
| | |
| Clustering Factor = 10 (행 수)
| |
| </source>
| |
| | |
| | |
| ---- | |
| | |
| ## 성능에 미치는 영향
| |
| | |
| ### 좋은 Clustering Factor (낮은 값)
| |
| | |
| ```sql
| |
| -- 예: CF = 1,000 (블록 수와 유사)
| |
| SELECT * FROM employees
| |
| WHERE department_id BETWEEN 10 AND 50;
| |
| | |
| -- 결과: 약 1,000개 블록만 읽음
| |
| -- 각 블록에서 여러 행을 읽을 수 있음
| |
| ```
| |
| | |
| **장점:**
| |
| - 인덱스 Range Scan이 효율적
| |
| - 물리적 I/O 최소화
| |
| - Buffer Cache 효율 증가
| |
| | |
| ### 나쁜 Clustering Factor (높은 값)
| |
| | |
| ```sql
| |
| -- 예: CF = 100,000 (행 수와 유사)
| |
| SELECT * FROM employees
| |
| WHERE department_id BETWEEN 10 AND 50;
| |
| | |
| -- 결과: 약 50,000개 블록 읽음
| |
| -- 각 행마다 다른 블록을 읽어야 함
| |
| ```
| |
| | |
| **단점:**
| |
| - 많은 물리적 I/O 발생
| |
| - 동일 블록을 반복 읽기
| |
| - Full Table Scan이 더 효율적일 수 있음
| |
| | |
| ---
| |
| | |
| ## 실무 조회 쿼리
| |
| | |
| ### 1. Clustering Factor 상태 확인
| |
| | |
| ```sql
| |
| SELECT
| |
| i.owner,
| |
| i.table_name,
| |
| i.index_name,
| |
| i.clustering_factor,
| |
| t.num_rows,
| |
| t.blocks AS table_blocks,
| |
| -- CF 품질 지표
| |
| ROUND(i.clustering_factor / NULLIF(t.blocks, 0), 2) AS cf_ratio,
| |
| CASE
| |
| WHEN i.clustering_factor <= t.blocks * 1.2 THEN '좋음 ✅'
| |
| WHEN i.clustering_factor <= t.blocks * 5 THEN '보통 ⚠️'
| |
| WHEN i.clustering_factor <= t.num_rows * 0.5 THEN '나쁨 ❌'
| |
| ELSE '매우 나쁨 🚫'
| |
| END AS cf_status,
| |
| -- 효율성 점수 (낮을수록 좋음)
| |
| ROUND((i.clustering_factor / NULLIF(t.num_rows, 0)) * 100, 2) AS efficiency_pct
| |
| FROM dba_indexes i
| |
| JOIN dba_tables t ON i.table_owner = t.owner
| |
| AND i.table_name = t.table_name
| |
| WHERE i.owner = 'HR'
| |
| AND t.num_rows > 0
| |
| ORDER BY efficiency_pct DESC;
| |
| ```
| |
| | |
| ### 2. 문제가 있는 인덱스 찾기
| |
| | |
| ```sql
| |
| SELECT
| |
| i.owner,
| |
| i.table_name,
| |
| i.index_name,
| |
| i.clustering_factor,
| |
| t.blocks AS table_blocks,
| |
| t.num_rows,
| |
| ROUND(i.clustering_factor / t.num_rows * 100, 2) AS cf_pct,
| |
| ROUND((t.num_rows * t.avg_row_len) / 1024 / 1024, 2) AS table_size_mb
| |
| FROM dba_indexes i
| |
| JOIN dba_tables t ON i.table_owner = t.owner
| |
| AND i.table_name = t.table_name
| |
| WHERE i.owner NOT IN ('SYS', 'SYSTEM')
| |
| AND t.num_rows > 10000 -- 작은 테이블 제외
| |
| AND i.clustering_factor > t.blocks * 10 -- CF가 블록 수의 10배 이상
| |
| ORDER BY (i.clustering_factor / t.num_rows) DESC;
| |
| ```
| |
| | |
| ### 3. 인덱스 효율성 분석
| |
| | |
| ```sql
| |
| WITH index_stats AS (
| |
| SELECT
| |
| i.owner,
| |
| i.table_name,
| |
| i.index_name,
| |
| i.clustering_factor,
| |
| t.blocks,
| |
| t.num_rows,
| |
| -- 인덱스로 100행을 읽을 때 예상 블록 접근 수
| |
| CASE
| |
| WHEN t.num_rows > 0 THEN
| |
| ROUND((i.clustering_factor / t.num_rows) * 100, 0)
| |
| ELSE 0
| |
| END AS blocks_per_100_rows
| |
| FROM dba_indexes i
| |
| JOIN dba_tables t ON i.table_owner = t.owner
| |
| AND i.table_name = t.table_name
| |
| WHERE i.owner = 'HR'
| |
| )
| |
| SELECT
| |
| owner,
| |
| table_name,
| |
| index_name,
| |
| clustering_factor,
| |
| blocks AS table_blocks,
| |
| num_rows,
| |
| blocks_per_100_rows,
| |
| CASE
| |
| WHEN blocks_per_100_rows <= 10 THEN 'Range Scan 효율적'
| |
| WHEN blocks_per_100_rows <= 50 THEN 'Range Scan 사용 가능'
| |
| ELSE 'Full Scan 고려'
| |
| END AS recommendation
| |
| FROM index_stats
| |
| ORDER BY blocks_per_100_rows DESC;
| |
| ```
| |
| | |
| ---
| |
| | |
| ## Clustering Factor 개선 방법
| |
| | |
| ### 방법 1: 테이블 재구성 (MOVE)
| |
| | |
| ```sql
| |
| -- 인덱스 컬럼 순서대로 테이블 재정렬
| |
| ALTER TABLE hr.employees MOVE;
| |
| | |
| -- 인덱스 재구성
| |
| ALTER INDEX hr.emp_dept_idx REBUILD;
| |
| | |
| -- 통계 재수집 | |
| EXEC DBMS_STATS.GATHER_TABLE_STATS('HR', 'EMPLOYEES');
| |
| | |
| -- CF 확인
| |
| SELECT index_name, clustering_factor
| |
| FROM user_indexes
| |
| WHERE index_name = 'EMP_DEPT_IDX';
| |
| ```
| |
| | |
| ### 방법 2: 테이블을 인덱스 순서로 재생성 | |
| | |
| ```sql
| |
| -- 임시 테이블 생성 (인덱스 순서대로)
| |
| CREATE TABLE employees_new AS
| |
| SELECT * FROM employees
| |
| ORDER BY department_id, employee_id;
| |
| | |
| -- 원본 테이블 교체 | |
| DROP TABLE employees;
| |
| RENAME employees_new TO employees;
| |
| | |
| -- 인덱스 재생성
| |
| CREATE INDEX emp_dept_idx ON employees(department_id);
| |
| | |
| -- 통계 수집
| |
| EXEC DBMS_STATS.GATHER_TABLE_STATS('HR', 'EMPLOYEES');
| |
| ```
| |
| | |
| ### 방법 3: Partitioning 사용
| |
| | |
| ```sql
| |
| -- 파티션 테이블로 변환
| |
| ALTER TABLE employees MODIFY
| |
| PARTITION BY RANGE (department_id)
| |
| (
| |
| PARTITION p1 VALUES LESS THAN (20),
| |
| PARTITION p2 VALUES LESS THAN (40),
| |
| PARTITION p3 VALUES LESS THAN (60),
| |
| PARTITION p4 VALUES LESS THAN (MAXVALUE)
| |
| );
| |
| | |
| -- 로컬 인덱스 생성
| |
| CREATE INDEX emp_dept_idx ON employees(department_id) LOCAL;
| |
| ```
| |
| | |
| ### 방법 4: IOT (Index-Organized Table) 사용
| |
| | |
| ```sql
| |
| -- 기존 테이블을 IOT로 변환
| |
| CREATE TABLE employees_iot
| |
| ORGANIZATION INDEX
| |
| INCLUDING employee_id
| |
| OVERFLOW
| |
| AS SELECT * FROM employees;
| |
| ```
| |
| | |
| --- | |
| | |
| ## Optimizer가 Clustering Factor를 사용하는 방법
| |
| | |
| ```sql
| |
| -- 실행 계획에서 Clustering Factor의 영향 확인
| |
| EXPLAIN PLAN FOR
| |
| SELECT * FROM employees
| |
| WHERE department_id BETWEEN 10 AND 50;
| |
| | |
| SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY(null, null, 'BASIC +COST'));
| |
| | |
| -- Cost 계산에 Clustering Factor가 사용됨
| |
| -- Cost = (Clustering Factor / Table Blocks) * Index Selectivity
| |
| ```
| |
| | |
| **Optimizer의 판단:**
| |
| - **CF가 낮으면**: Index Range Scan 선호 | |
| - **CF가 높으면**: Full Table Scan 고려 | |
| | |
| ---
| |
| | |
| ## 실전 모니터링 스크립트
| |
| | |
| ```sql
| |
| -- 일일 CF 모니터링
| |
| CREATE OR REPLACE VIEW v_clustering_factor_monitor AS
| |
| SELECT
| |
| i.owner,
| |
| i.table_name,
| |
| i.index_name,
| |
| i.clustering_factor,
| |
| t.blocks,
| |
| t.num_rows,
| |
| ROUND(i.clustering_factor / NULLIF(t.blocks, 0), 2) AS cf_block_ratio,
| |
| ROUND(i.clustering_factor / NULLIF(t.num_rows, 0) * 100, 2) AS cf_row_pct,
| |
| i.last_analyzed,
| |
| CASE
| |
| WHEN i.clustering_factor <= t.blocks * 2 THEN 'GOOD'
| |
| WHEN i.clustering_factor <= t.blocks * 10 THEN 'FAIR'
| |
| ELSE 'POOR'
| |
| END AS status
| |
| FROM dba_indexes i
| |
| JOIN dba_tables t ON i.table_owner = t.owner
| |
| AND i.table_name = t.table_name
| |
| WHERE i.owner NOT IN ('SYS', 'SYSTEM', 'XDB', 'OUTLN')
| |
| AND t.num_rows > 1000;
| |
| | |
| -- 사용 | |
| SELECT * FROM v_clustering_factor_monitor
| |
| WHERE status = 'POOR'
| |
| ORDER BY cf_row_pct DESC;
| |
| ```
| |
| | |
| --- | |
| | |
| ## 주의사항
| |
| | |
| 1. **통계 정보 최신화**
| |
| ```sql
| |
| -- CF는 통계 수집 시 계산됨
| |
| EXEC DBMS_STATS.GATHER_INDEX_STATS('HR', 'EMP_DEPT_IDX');
| |
| ```
| |
| | |
| 2. **DML 작업 후 CF 변화**
| |
| - INSERT/UPDATE/DELETE 후 CF가 나빠질 수 있음
| |
| - 주기적인 통계 수집 필요
| |
| | |
| 3. **파티션 테이블**
| |
| - 파티션별로 CF가 다를 수 있음
| |
| - USER_IND_PARTITIONS에서 확인
| |
| | |
| 4. **Multiblock Read**
| |
| - CF가 나빠도 db_file_multiblock_read_count가 크면 영향 감소
| |
| | |
| ---
| |
| | |
| ## 요약
| |
| | |
| | 상황 | Clustering Factor {{!}} 권장 사항 {{!}}
| |
| |-----|------------------|---------|
| |
| | OLTP, 자주 Range Scan | 테이블 블록 수와 유사 {{!}} 테이블 재구성 고려 {{!}}
| |
| | Batch, Full Scan 위주 | 높아도 무관 {{!}} 현상 유지 {{!}}
| |
| | 자주 변경되는 테이블 | 주기적으로 나빠짐 {{!}} 파티셔닝 검토 {{!}}
| |
| | 대용량 테이블 | 매우 높음 {{!}} IOT 또는 파티셔닝 {{!}}
| |
| | |
| Clustering Factor는 인덱스 성능을 판단하는 핵심 지표이므로, DBA는 정기적으로 모니터링하고 필요시 개선 작업을 수행해야 합니다!
| |
| }}}
| |
| | |
| == 예제 ==
| |
| <source lang="{{{언어}}}">
| |
| {{{예제}}} | |
| </source> | |
| | |
| == 이전글/다음글 ==
| |
| * [[{{{이전글|}}}]]
| |
| * [[{{{다음글|}}}]]
| |
| | |
| == 관련 문서 ==
| |
| * [[{{{관련1|}}}]]
| |
| * [[{{{관련2|}}}]]
| |
| | |
| == FAQ ==
| |
| ; {{{질문1|질문}}}
| |
| : {{{답변1|답변}}}
| |
| | |
| ; {{{질문2|질문}}}
| |
| : {{{답변2|답변}}}
| |
| | |
| [[분류:{{{분류|oracle}}}]]
| |