테마
이 문제는 교재 18화 졸업 과제와 같은 이커머스 데이터베이스를 씁니다. 『튜너의 1년』을 순서대로 읽고 있다면 졸업 과제를 먼저 풀고 오세요. 아래 「테이블과 인덱스」 칸에 과제의 답 일부(제약 조건, 인덱스, 통계의 실제 상태)가 나옵니다.
이커머스 플랫폼은 매주 수요일 10:00에 한 상품을 반값에 파는 타임딜을 연다(재고 500개). 지난 수요일 10:00:05부터 「주문하기」를 누른 고객의 화면이 몇 분씩 돌았고, 10:03에는 타임딜과 상관없는 상품 목록과 로그인까지 멈췄다. 10:12에 운영팀이 WAS를 재시작했다. 그날 타임딜 상품은 500개 중 14개만 팔렸다. 주문 처리는 재고를 먼저 빼고, PG 결제창을 띄워 고객이 결제를 마칠 때까지 기다렸다가 COMMIT한다. 결제창은 평균 40초 걸리고, 고객이 창을 닫으면 3분 뒤 타임아웃으로 ROLLBACK한다.
product_stock 상품 재고(이 문제의 파생 테이블): products와 1:1 · 280행 · 4블록 · PK product_id · stock_qtyorders: 34,908행 · 193블록 · PK만 · status (PENDING · PAID · CANCELLED …)order_items: 73,498행 · 290블록 · PK만payments: 34,908행 · 192블록 · PK만 · order_id와 1:1stock_hold 재고 선점(새로 만들어도 되는 테이블): hold_id · product_id · customer_id · qty · status · expire_at-- PKG_ORDER.PLACE: 「주문하기」부터 결제 완료까지 한 트랜잭션
UPDATE product_stock
SET stock_qty = stock_qty - :qty
WHERE product_id = :pid
AND stock_qty >= :qty; -- 0건이면 품절
INSERT INTO orders (order_id, customer_id, order_date, status, total_amount)
VALUES (:oid, :cid, SYSDATE, 'PENDING', :amt);
INSERT INTO order_items (order_item_id, order_id, product_id, quantity, unit_price)
VALUES (:oiid, :oid, :pid, :qty, :price);
-- PG 결제창을 띄우고 고객의 결제를 기다린다 (평균 40초 · 창을 닫으면 3분 뒤 타임아웃 → ROLLBACK)
INSERT INTO payments (payment_id, order_id, payment_method, amount, paid_at)
VALUES (:payid, :oid, :method, :amt, SYSDATE);
UPDATE orders SET status = 'PAID' WHERE order_id = :oid;
COMMIT;
[시간표 — 수요일 10:00 · 타임딜 상품 product_id 117 (재고 500)]
시각 고객 1 (SID 1204) 고객 2 ~ 3,100
10:00:01 UPDATE product_stock (117) → 499 · 행 잠금
결제창 — COMMIT 전
10:00:01 ~ UPDATE product_stock (117) → 기다림 (고객 1 의 TX)
10:00:44 결제 완료 · COMMIT 고객 2 깨어남 → 498 · 결제창 … 나머지는 계속 기다림
10:01:30 고객 4 결제창을 닫음 → 3분 동안 행을 쥔 채 (타임아웃 대기)
10:03:00 커넥션 풀 200개가 전부 이 UPDATE 에서 기다린다 → 다른 화면도 커넥션을 얻지 못한다
10:12 WAS 재시작 → 기다리던 트랜잭션 전부 ROLLBACK · 그때까지 팔린 것 14개
[V$SESSION — 10:02:30]
SID EVENT SECS BLOCKING_SESSION 하고 있는 문장
1311 SQL*Net message from client 52 (결제창 — 트랜잭션을 연 채)
1204 … enq: TX - row lock contention 149 1311 UPDATE PRODUCT_STOCK SET STOCK_QTY = STOCK_QTY - :B1 …
(기다리는 세션 196개 · 모두 BLOCKING_SESSION 1311 · 모두 같은 문장)
[V$LOCK — 같은 시각]
SID TYPE 대상 LMODE REQUEST BLOCK
1311 TM PRODUCT_STOCK 3 0 0
1311 TX SID 1311 의 트랜잭션 6 0 1
그 밖 TX SID 1311 의 트랜잭션 0 6 0 × 196 ← 같은 행 하나를 원한다
[WAS 로그] 10:03 ~ 10:12 커넥션 대기 타임아웃 18,420건 (상품 목록 · 로그인 · 장바구니 포함)
V$SESSION, V$LOCK을 근거로 재고 행 하나가 어떻게 사이트 전체를 멈추게 했는지 설명하시오.