메뉴 여닫기
개인 메뉴 토글
로그인하지 않음
만약 지금 편집한다면 당신의 IP 주소가 공개될 수 있습니다.

틀:난이도

난이도

틀:요점

menu_book
요점정리 내용 누설을 원하지 않는다면 열람을 삼가주세요.


{{요점
|내용 = <center>요점정리 <b>[[내용 누설]]</b>을 원하지 않는다면 열람을 삼가주세요.</center>
|배경색 = #f7d4063e
|선굵기 = 3px
|테두리색 = #de5a5a
|사이즈 = 100%
|둥굴기 = 4px
|스타일 = dashed
}}


틀:개요


menu_book 보더색
{{요점
|제목=
|보더색=
|배경색=
|아이콘색색=
|아이콘=
|폰트색=
}}

틀:관련글

이전글
다음글

{{관련글
|이전글=이전글을 입력하세요
|다음글=다음글을 입력하세요
|아이콘이름1=exposure_neg_1
|아이콘이름2=exposure_plus_1
}}

틀:서브

   제목을 입력하세요.
   내용을 입력하세요
{{서브
|제목아이콘=exposure_neg_1
|제목=이전글을 입력하세요
|내용아이콘=exposure_plus_1
|내용=내용을 입력하세요
}}

틀:핵심

  • 핵심1

  vpn_key NL 조인의 영문의 뜻 : 중첩된(Nested) + 반복(Loop) + 연결(Join)

  playlist_add_check 프로그래밍 언어에서 For 문장 처럼 반복(Loop) 처리

  1. 두개의 테이블에서, 하나의 집합(테이블)을 기준으로 순차적으로 상대방 테이블의 row 를 결합하여 원하는 결과를 추출하는 테이블 연결 방식
  2. 결합하기 위해 기준이 되는 테이블(선행테이블=드라이빙(driving) 테이블=outer테이블) : 드라이빙(driving) 테이블( OUTER 테이블, 즉 바깥쪽 테이블)
  3. 결합되어지는 테이블(후행=드리븐(driven) 테이블=inner테이블) : 드리븐(driven) 테이블(INNER 테이블, 즉 안쪽 테이블)
  4. NL 조인에서는 드라이빙 테이블의 각 row 에 대하여 loop 방식으로 조인이 되는데 드라이빙 테이블의 집합을 줄여주는 조건이 (where에서 사용된 컬럼에 인덱스가 있는가?) NL 조인의 성능을 결정함.
{{틀:핵심
|제목=''' <big>NL 조인의 영문의 뜻 : 중첩된(Nested) + 반복(Loop) + 연결(Join)</big>'''
|내용= ''' <big>프로그래밍 언어에서 For 문장 처럼 반복(Loop) 처리</big>'''
:::# 두개의 테이블에서, 하나의 집합(테이블)을 기준으로 순차적으로 상대방 테이블의 row 를 결합하여 원하는 결과를 추출하는 테이블 연결 방식
:::# 결합하기 위해 기준이 되는 테이블(선행테이블=드라이빙(driving) 테이블=outer테이블) : 드라이빙(driving) 테이블( OUTER 테이블, 즉 바깥쪽 테이블)
:::# 결합되어지는 테이블(후행=드리븐(driven) 테이블=inner테이블) : 드리븐(driven) 테이블(INNER 테이블, 즉 안쪽 테이블)
:::# NL 조인에서는 드라이빙 테이블의 각 row 에 대하여 loop 방식으로 조인이 되는데 드라이빙 테이블의 집합을 줄여주는 조건이 (where에서 사용된 컬럼에 인덱스가 있는가?) NL 조인의 성능을 결정함.
}}
  • 핵심2

  vpn_key NL조인에서 인덱스의 중요성

  1. 선행(드라이빙,outer) 테이블은 where 절에 사용된 컬럼의 인덱스 여부가 중요
  2. 후행(드리븐,inner) 테이블은 조인조건 컬럼의 인덱스가 중요

  playlist_add_check outer 테이블 조회 후 1건씩 순차적(sequential)으로 inner 테이블에 접근

  • INDEX 구성
  • EMP.IX_EMP_01 ( DEPTNO)
  • DEPT.IX_DEPT_01 ( DEPTNO))
{{틀:핵심
|제목='''NL조인에서 인덱스의 중요성''' 
:::# 선행(드라이빙,outer) 테이블은 where 절에 사용된 컬럼의 인덱스 여부가 중요
:::# 후행(드리븐,inner) 테이블은 조인조건 컬럼의 인덱스가 중요
|내용= '''<big> outer 테이블 조회 후 1건씩 순차적(sequential)으로 inner 테이블에 접근 </big>'''
:::* INDEX 구성 
:::* EMP.IX_EMP_01 ( DEPTNO)
:::* DEPT.IX_DEPT_01 ( DEPTNO))
}}

틀:타이틀

 notifications_active스타일리쉬 타이틀바
{{타이틀
|제목|스타일리쉬 타이틀바
|폰트크기|12
|글자색깔|#489cdf
|아이콘이름|notifications_active
}}

틀:SQL

문서 제목 없음


menu_book

값의 의미

  1. 범위

- **최소값**: 테이블의 블록 수 (완벽하게 정렬된 경우) - **최대값**: 테이블의 행 수 (완전히 무작위로 분산된 경우)

해석 방법

-- Clustering Factor 확인
SELECT 
    index_name,
    table_name,
    clustering_factor,
    num_rows,
    leaf_blocks,
    -- 테이블 블록 수
    (SELECT blocks FROM dba_tables t 
     WHERE t.owner = i.table_owner 
       AND t.table_name = i.table_name) AS table_blocks
FROM dba_indexes i
WHERE table_owner = 'HR'
  AND table_name = 'EMPLOYEES';
    • 판단 기준:**

| Clustering Factor | 상태 | 의미 | |------------------|------|------| | ≈ 테이블 블록 수 | **좋음** ✅ | 데이터가 인덱스 순서대로 잘 정렬됨 | | 테이블 블록 수 ~ 행 수 사이 | 보통 | 부분적으로 정렬됨 | | ≈ 테이블 행 수 | **나쁨** ❌ | 데이터가 무작위로 흩어져 있음 |

---

Clustering Factor의 계산 원리

  1. 인덱스를 순차적으로 스캔하면서:
    1. - 이전 행과 다른 블록에 있으면: CF++
    2. - 이전 행과 같은 블록에 있으면: CF 증가 없음

예제로 이해하기

  • 시나리오 1: 좋은 Clustering Factor
인덱스 순서: 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 (블록 수)

시나리오 2: 나쁜 Clustering Factor

인덱스 순서: 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 (행 수)



    1. 성능에 미치는 영향
      1. 좋은 Clustering Factor (낮은 값)

```sql -- 예: CF = 1,000 (블록 수와 유사) SELECT * FROM employees WHERE department_id BETWEEN 10 AND 50;

-- 결과: 약 1,000개 블록만 읽음 -- 각 블록에서 여러 행을 읽을 수 있음 ```

    • 장점:**

- 인덱스 Range Scan이 효율적 - 물리적 I/O 최소화 - Buffer Cache 효율 증가

      1. 나쁜 Clustering Factor (높은 값)

```sql -- 예: CF = 100,000 (행 수와 유사) SELECT * FROM employees WHERE department_id BETWEEN 10 AND 50;

-- 결과: 약 50,000개 블록 읽음 -- 각 행마다 다른 블록을 읽어야 함 ```

    • 단점:**

- 많은 물리적 I/O 발생 - 동일 블록을 반복 읽기 - Full Table Scan이 더 효율적일 수 있음

---

    1. 실무 조회 쿼리
      1. 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; ```

      1. 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; ```

      1. 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; ```

---

    1. Clustering Factor 개선 방법
      1. 방법 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'; ```

      1. 방법 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'); ```

      1. 방법 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; ```

      1. 방법 4: IOT (Index-Organized Table) 사용

```sql -- 기존 테이블을 IOT로 변환 CREATE TABLE employees_iot ORGANIZATION INDEX INCLUDING employee_id OVERFLOW AS SELECT * FROM employees; ```

---

    1. 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 고려

---

    1. 실전 모니터링 스크립트

```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. 주의사항

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가 크면 영향 감소

---

    1. 요약

| 상황 | Clustering Factor | 권장 사항 | |-----|------------------|---------| | OLTP, 자주 Range Scan | 테이블 블록 수와 유사 | 테이블 재구성 고려 | | Batch, Full Scan 위주 | 높아도 무관 | 현상 유지 | | 자주 변경되는 테이블 | 주기적으로 나빠짐 | 파티셔닝 검토 | | 대용량 테이블 | 매우 높음 | IOT 또는 파티셔닝 |

Clustering Factor는 인덱스 성능을 판단하는 핵심 지표이므로, DBA는 정기적으로 모니터링하고 필요시 개선 작업을 수행해야 합니다!

예제

{{{예제}}}

이전글/다음글

  • [[]]
  • [[]]

관련 문서

  • [[]]
  • [[]]

FAQ

질문
답변

[[분류:]]

{{SQL
|제목=문서 제목 없음
|내용=
|본문=본문 내용을 여기에 입력하세요
|언어=sql
|예제=
|이전글=
|다음글=
|관련1=
|관련2=
|질문1=
|답변1=
|분류=
}}

틀:오라클

문서 제목 없음


menu_book 요점 내용없음

기본 설명

본문 내용을 여기에 입력하세요.

예제

{{{예제|SELECT * FROM DUAL;}}}

관련 문서

  • [[]]
  • [[]]

FAQ

질문
답변

[[분류:]]

{{오라클
|제목=문서 제목 없음
|내용=요점 내용없음
|본문=본문 내용을 여기에 입력하세요.
|언어=
|예제=
|이전글=
|다음글=
|관련1=
|관련2=
|질문1=
|답변1=
|분류=
}}