테마
버거원 본사는 매달 1일 03:00에 지난달 주문상품을 가맹 컨설팅팀용 사본 CNS_ORD_ITEM에 다시 채운다(매장, 날짜, 시간대, 메뉴, 수량). 몇 년 전에 만든 프로시저는 루프 안의 INSERT에 /*+ APPEND */를 붙여 두었고, 4초 남짓에 끝났다.
지난주 조민우가 실행계획을 보다가 그 INSERT가 직접 경로로 돌지 않는다는 것을 알고 APPEND_VALUES로 바꿨다. 리허설에서 둘째 행에 ORA-12838이 나자 INSERT마다 커밋을 넣었다. 이번에는 끝까지 돌았지만 5분 7초가 걸렸고, 컨설팅 리포트(사본 전체 스캔)가 약 420블록에서 104,780블록을 읽게 됐다. 조민우: "직접 경로가 더 빠르다고 해서 바꾼 건데요."
ORD: 1,000,000행 · 22,422블록 · ORD_X01 (ORD_DT, ORD_HMS) · 2013년 12월 주문 42,408건(리프 약 140블록, 테이블 951블록)ORD_ITEM: 2,472,717행 · 55,265블록 · ORD_ITEM_X01 (ORD_DT, ORD_HMS) · 2013년 12월 주문상품 104,750행(리프 약 390블록, 테이블 약 2,340블록)CNS_ORD_ITEM(이 문제용): SHOP_NO · ORD_DT · HR_BAND('M' 오전 · 'L' 점심 · 'A' 오후 · 'D' 저녁) · ITEM_ID · QTY · 인덱스, 트리거, FK 없음 · 한 달 치를 일반 INSERT로 넣으면 약 420블록(블록당 약 250행)이다. 이 배치만 쓰고, 낮에는 컨설팅 리포트가 읽기만 한다.-- 처음 판 (몇 년 전) · 4초 남짓
PROCEDURE P_CNS_LOAD(p_ym VARCHAR2) IS -- p_ym = '201312'
v_band VARCHAR2(1);
BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE CNS_ORD_ITEM';
FOR r IN (SELECT o.SHOP_NO, i.ORD_DT, i.ORD_HMS, i.ITEM_ID, i.ORD_ITEM_QTY
FROM ORD o, ORD_ITEM i
WHERE o.ORD_DT BETWEEN p_ym || '01' AND p_ym || '31'
AND i.ORD_DT BETWEEN p_ym || '01' AND p_ym || '31'
AND i.ORD_NO = o.ORD_NO) LOOP
v_band := CASE WHEN r.ORD_HMS < '110000' THEN 'M'
WHEN r.ORD_HMS < '140000' THEN 'L'
WHEN r.ORD_HMS < '170000' THEN 'A'
ELSE 'D' END;
INSERT /*+ APPEND */ INTO CNS_ORD_ITEM (SHOP_NO, ORD_DT, HR_BAND, ITEM_ID, QTY)
VALUES (r.SHOP_NO, r.ORD_DT, v_band, r.ITEM_ID, r.ORD_ITEM_QTY);
END LOOP;
COMMIT;
END;
-- 지난주 바꾼 판 (조민우) · 루프 안의 INSERT 만 다르다
INSERT /*+ APPEND_VALUES */ INTO CNS_ORD_ITEM (SHOP_NO, ORD_DT, HR_BAND, ITEM_ID, QTY)
VALUES (r.SHOP_NO, r.ORD_DT, v_band, r.ITEM_ID, r.ORD_ITEM_QTY);
COMMIT; -- 둘째 행의 ORA-12838 을 피하려고 넣었다
[처음 판 — 12월분 104,750행 · 세션 통계]
경과 시간 3.9 초
INSERT 실행 횟수 104,750
user commits 2 (TRUNCATE 의 암시적 커밋 + 마지막 COMMIT)
redo size 약 27.4 MB
undo change vector size 약 6.9 MB
루프 안 INSERT 의 실행계획 INSERT STATEMENT → LOAD TABLE CONVENTIONAL CNS_ORD_ITEM
적재 뒤 CNS_ORD_ITEM 약 420블록
[바꾼 판 ① — APPEND_VALUES · 커밋 없이 (리허설)]
첫 행 1 row created (실행계획 LOAD AS SELECT CNS_ORD_ITEM)
둘째 행 ORA-12838: cannot read/modify an object after modifying it in parallel
→ 롤백 · 0행
[바꾼 판 ② — APPEND_VALUES + INSERT 마다 COMMIT (리허설) · 세션 통계]
경과 시간 5분 7초
INSERT 실행 횟수 104,750
user commits 104,752 (행마다 104,750 + TRUNCATE + 마지막 COMMIT)
log file sync 대기 약 2분 41초
physical writes direct 104,750
redo size 약 112 MB (적재마다 고수위선과 공간 비트맵을 고친 기록 · 커밋 기록)
CNS_ORD_ITEM (USER_SEGMENTS) 104,780블록 · 행 104,750
컨설팅 리포트 (CNS_ORD_ITEM 전체 스캔) Buffers 약 420 → 104,780
/*+ APPEND */가 직접 경로로 돌지 않은 이유와, 바꾼 판 ①이 둘째 행에서 ORA-12838로 멈춘 이유를 설명하시오.INSERT /*+ APPEND */ … SELECT 한 문장, (나) BULK COLLECT와 FORALL … APPEND_VALUES, (다) FORALL 일반 INSERT를 실행 횟수, 커밋, 블록 수, redo, 잠금으로 비교하고 고른 방법으로 다시 작성하시오. 이미 커진 사본을 되돌리는 방법도 쓰시오.