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

Enq:hw-contention

DBSTUDY

enq: HW - Contention

  • LOB Segment 이슈와 밀접하게 연결되는 대기 이벤트입니다.
  • 특히 동시성 높은 환경에서 LOB 컬럼에 INSERT가 몰릴 때 자주 발생하는 이슈

HW(High Water Mark) Enqueue가 무엇인가?

  • 개념: 세그먼트(Table, Index, LOB Segment 등)가 "새로운 블록 영역으로 확장" 될 때 걸리는 직렬화 락(Lock)
  • HWM(High Water Mark)란?
:이 세그먼트가 지금까지 한 번이라도 사용한 적 있는 블록의 최대 경계선
[세그먼트 내부 이미지]
┌────────┬────────┬────────┬────────┬─────────────┐
│ 사용중 │ 사용중 │ 사용중  │ 미사용  │   미사용    │
│ Block1 │ Block2 │ Block3 │ Block4 │   Block5    │
└────────┴────────┴────────┴────────┴─────────────┘
                            ▲
                           HWM (여기까지가 "사용해본 적 있는" 경계)

→ HWM을 넘어 Block4, Block5로 최초 진입(확장)하려면 
  "누군가 한 명만" HWM을 이동시키는 작업을 해야 함
→ 그 "한 명만 하도록 강제"하는 직렬화 장치가 바로 enq: HW 이다


  • 핵심: HW 이동 자체는 원자적(Atomic)이어야 하므로, 여러 세션이 동시에 HWM을 넘기려고 시도하면 한 세션만 실제로 이동시키고 나머지는 대기합니다.

이 대기가 `enq: HW - contention`입니다.


어떤 상황에서 자주 발생하는가?

  1. 발생 빈발 시나리오 TOP 3
    1순위: LOB Segment 동시 INSERT (가장 빈번)
    :   → 여러 세션이 동시에 CLOB/BLOB 컬럼에 대량 데이터를 INSERT
    :   → 특히 NOCACHE + 병렬 세션 조합에서 극심
    
    2순위: 병렬 Direct-Path INSERT (APPEND Hint)
     :  → 여러 병렬 슬레이브가 동시에 같은 테이블 뒤쪽에 데이터 적재
    
    3순위: 시퀀스 없는 대량 동시 INSERT (일반 Heap 테이블)
    :   → ASSM(Automatic Segment Space Management)이라도 
         극단적으로 짧은 시간에 너무 많은 세션이 몰리면 발생 가능
    • SecureFile LOB에 다수 세션이 동시에 대용량 데이터를 넣는 배치/API 환경이라면, LOB Segment의 HWM 확장 경쟁이 바로 이 대기 이벤트의 주요 원인

실습으로 재현해보기

  1. 테스트 테이블
    CREATE TABLE hw_contention_test (
        id       NUMBER,
        data_lob CLOB
    )
    LOB (data_lob) STORE AS SECUREFILE hw_lob_seg (
        DISABLE STORAGE IN ROW
        NOCACHE
        CHUNK 8192
    );
  2. 다수 세션에서 동시 INSERT 발생시키기 (개념적 재현)
    여러 세션에서 동시에 아래를 실행 (SQL*Plus 여러 창 or 병렬 Job):
    -- Session 1~10 동시 실행
    DECLARE
        l_clob CLOB;
    BEGIN
        FOR i IN 1..1000 LOOP
            l_clob := gen_clob(50000, 'X');  -- 50KB LOB
            INSERT INTO hw_contention_test VALUES (i, l_clob);
            COMMIT;
        END LOOP;
    END;
    /
  3. 실시간 대기 이벤트 모니터링
    SELECT sid, event, state, seconds_in_wait, p1, p2, p3
    FROM v$session
    WHERE event = 'enq: HW - contention';
    SID   EVENT                    STATE    SECONDS_IN_WAIT   P1          P2      P3
    ----  -----------------------  -------  ----------------  ----------  ------  ------
     145  enq: HW - contention     WAITING                 2   1213908993   65543       0
     152  enq: HW - contention     WAITING                 2   1213908993   65543       0
     168  enq: HW - contention     WAITING                 1   1213908993   65543       0
    • P1, P2 해석:
      - `P1 = 1213908993` → `Type='HW'(0x48 0x57), Mode` 조합 인코딩 값
      - `P2 = 65543` → 대상 세그먼트의 **Tablespace# + Relative File#** 조합 (같은 세그먼트를 다투고 있다는 증거)
    • 동일한 P2 값을 가진 세션들이 정확히 같은 세그먼트(여기서는 hw_lob_seg)에서 경쟁 중임을 의미합니다.
  4. 원인이 되는 세그먼트를 정확히 찾아내는 방법
    -- P2 값을 이용해 실제 경쟁 중인 세그먼트 역추적
    SELECT s.sid, s.event, 
           o.owner, o.object_name, o.object_type
    FROM v$session s,
         v$lock l,
         dba_extents e,
         dba_objects o
    WHERE s.event = 'enq: HW - contention'
    AND l.sid = s.sid
    AND l.type = 'HW'
    AND e.relative_fno = MOD(l.id2, 65536)
    AND e.file_id = e.relative_fno   -- 환경에 맞게 조정 필요
    AND o.object_id = e.segment_id
    AND o.object_name = e.segment_name;
    * 더 실무적으로 간단한 방법: P1/P2로 직접 조회하는 대신, 실시간으로 어느 세그먼트가 확장 중인지 ASH로 확인
    SELECT sample_time, session_id, 
           current_obj#, 
           (SELECT object_name FROM dba_objects WHERE object_id = ash.current_obj#) AS obj_name,
           event
    FROM v$active_session_history ash
    WHERE event = 'enq: HW - contention'
    ORDER BY sample_time DESC
    FETCH FIRST 20 ROWS ONLY;
    SAMPLE_TIME              SESSION_ID   CURRENT_OBJ#   OBJ_NAME         EVENT
    -----------------------  -----------  -------------  ---------------  ---------------------
    2024-01-15 14:23:01.123          145         123457   HW_LOB_SEG       enq: HW - contention
    2024-01-15 14:23:01.456          152         123457   HW_LOB_SEG       enq: HW - contention
    2024-01-15 14:23:01.789          168         123457   HW_LOB_SEG       enq: HW - contention
    • 확인 완료 : 세 세션 모두 `HW_LOB_SEG`에서 경쟁 중임이 명확히 드러납니다.

왜 이런 구조적 한계가 존재하는가? (ASSM이라도 못 피하는 이유)

오해: "ASSM(자동 세그먼트 공간 관리)이면 
       Freelist Contention이 해결됐으니 HW Contention도 없겠지?"

실제: ASSM은 "이미 HWM 아래에 있는 블록들 사이의 공간 찾기 경쟁"만 해결한다.
      "HWM 자체를 넘어서 새 영역으로 처음 진입하는 순간"의 
      직렬화는 ASSM 여부와 무관하게 항상 존재한다.

즉, Freelist Contention(구시대) ≠ HW Contention(구조적으로 항상 존재)
  1. LOB Segment는 특히 취약한 이유*:
    일반 Table Segment
    → 여러 세션이 각기 다른 블록에 흩어져서 쓸 수 있음 (ASSM Bitmap 분산 배정)
    → HWM 확장 빈도 자체가 상대적으로 낮음
    
    LOB Segment (특히 NOCACHE 대용량 INSERT)
    → 각 LOB 값이 통째로 CHUNK 단위(8K, 16K...)를 즉시 소비
    → 대용량 LOB을 빠르게 연속 INSERT하면 HWM이 "매우 자주" 이동해야 함
    → HWM 이동 빈도가 높을수록 Contention 발생 확률 급증

해결 방안 (실전 우선순위 순)

  1. SECUREFILE의 경우 - Concurrency 관련 파라미터 확인
    -- 현재 LOB의 상세 스토리지 옵션 확인
    SELECT table_name, column_name, securefile, 
           cache, logging, retention
    FROM user_lobs
    WHERE table_name = 'HW_CONTENTION_TEST';
    TABLE_NAME            COLUMN_NAME  SECUREFILE  CACHE      LOGGING  RETENTION
    --------------------  -----------  ----------  ---------  -------  ----------
    HW_CONTENTION_TEST    DATA_LOB     YES         NO         YES      AUTO
    • 개선안: SecureFile은 내부적으로 Space Management를 위한 여러 최적화가 있지만, 극단적 동시성에서는 여전히 근본적 해결이 안 됨. 아래 항목들과 병행 필요.
  2. 파티셔닝으로 세그먼트 자체를 분산 (가장 효과적)
    -- Hash Partition으로 LOB Segment 자체를 물리적으로 분산
    CREATE TABLE hw_contention_test_part (
        id       NUMBER,
        data_lob CLOB
    )
    LOB (data_lob) STORE AS SECUREFILE (
        DISABLE STORAGE IN ROW
        NOCACHE
    )
    PARTITION BY HASH (id) PARTITIONS 8;
    -- 파티션별로 별도의 LOB Segment가 생성되는지 확인
    SELECT table_name, partition_name, segment_name
    FROM user_lob_partitions
    WHERE table_name = 'HW_CONTENTION_TEST_PART';
    TABLE_NAME                PARTITION_NAME   SEGMENT_NAME
    -------------------------  ---------------  ------------------------
    HW_CONTENTION_TEST_PART    SYS_P101         SYS_LOB_P101_00001$$
    HW_CONTENTION_TEST_PART    SYS_P102         SYS_LOB_P102_00002$$
    HW_CONTENTION_TEST_PART    SYS_P103         SYS_LOB_P103_00003$$
    ...
  • 효과 : 세션들이 `id` 값에 따라 8개의 서로 다른 파티션(=서로 다른 LOB Segment)으로 흩어지므로, 동일 세그먼트를 다투는 확률이 1/8로 감소합니다.
  1. 동시 INSERT 세션 수 자체를 조절 (애플리케이션 레벨)
    -- Resource Manager로 동시 병렬도 제한 (배치 작업의 경우)
    BEGIN
      DBMS_RESOURCE_MANAGER.CREATE_PLAN_DIRECTIVE(
        plan => 'BATCH_PLAN',
        group_or_subplan => 'BATCH_GROUP',
        comment => 'LOB Insert 동시성 제한',
        parallel_degree_limit_p1 => 4  -- 동시 병렬도 제한
      );
    END;
    /
  2. CACHE 옵션 재검토 (단, 대용량이면 신중히)
    -- NOCACHE → CACHE READS로 변경 시 HWM 확장 패턴이 달라질 수 있음
    -- (단, Buffer Cache 압박이 커지므로 LOB이 매우 크면 비추천)
    ALTER TABLE hw_contention_test MOVE LOB(data_lob) STORE AS (
        CACHE READS
    );
  3. 배치 커밋 간격 조정 (Commit 빈도와 HWM 확장 빈도는 별개지만 병행 효과 있음)
    -- 매 row마다 COMMIT → 500 row마다 COMMIT으로 변경
    -- (트랜잭션 오버헤드 감소로 전체 처리시간 단축, HW Contention 절대량은 불변이나 
    --  세션 간 타이밍이 분산되어 충돌 확률 감소하는 부수 효과)
    DECLARE
        l_clob CLOB;
    BEGIN
        FOR i IN 1..1000 LOOP
            l_clob := gen_clob(50000, 'X');
            INSERT INTO hw_contention_test VALUES (i, l_clob);
            IF MOD(i, 500) = 0 THEN
                COMMIT;
            END IF;
        END LOOP;
        COMMIT;
    END;
    /
  4. 실전 진단 체크리스트
    -- AWR에서 HW Contention이 Top Wait Event로 잡히는지 확인
    SELECT event, total_waits, time_waited_micro/1000000 AS time_waited_sec
    FROM dba_hist_system_event
    WHERE event = 'enq: HW - contention'
    AND snap_id BETWEEN (SELECT MIN(snap_id) FROM dba_hist_snapshot WHERE begin_interval_time > SYSDATE-1)
                    AND (SELECT MAX(snap_id) FROM dba_hist_snapshot)
    ORDER BY time_waited_sec DESC;
    EVENT                    TOTAL_WAITS   TIME_WAITED_SEC
    -----------------------  ------------  ----------------
    enq: HW - contention             8421            342.56
  • 이 정도 수치(하루 5분 이상 누적 대기)라면 명백히 튜닝 대상 입니다.

최종 정리

enq: HW - contention 핵심 요약

원인: 세그먼트의 High Water Mark를 넘어 새 영역으로 확장할 때 
      발생하는 필연적 직렬화 경쟁

가장 흔한 실전 상황: 다수 세션의 동시 LOB INSERT (특히 NOCACHE 대용량)

ASSM으로도 해결 안 되는 구조적 이슈 (Freelist Contention과는 다른 문제)

가장 효과적인 해결책 우선순위:
  1. 파티셔닝으로 세그먼트 자체를 분산 (근본 해결)
  2. 동시 세션 수/병렬도 제한 (부하 조절)
  3. CACHE 옵션, 배치 커밋 조정 (보조적 완화)