Skip to content
튜닝 연습Lock과 동시성 제어
LOCK-004

출고 작업자 8명이 한 명처럼 일한다

중급SELECT이커머스499 / 700

이 문제는 교재 18화 졸업 과제와 같은 이커머스 데이터베이스를 씁니다. 『튜너의 1년』을 순서대로 읽고 있다면 졸업 과제를 먼저 풀고 오세요. 아래 「테이블과 인덱스」 칸에 과제의 답 일부(제약 조건, 인덱스, 통계의 실제 상태)가 나옵니다.

상황 ​

이커머스 물류센터에서는 출고 작업자 8명이 태블릿의 「다음 건」 버튼으로 출고 대기 한 건을 받아 포장한다. 평일 오전 출고 대기는 400건 남짓이고, 포장 한 건에 2~4분이 걸린다. 센터장의 말이다. "8명이 있는데 출고 속도는 한 명일 때랑 비슷해요. 태블릿이 자꾸 모래시계예요."

테이블과 인덱스 ​

  • shipping: 34,908행 · 183블록 · PK shipping_id · status(READY 출고 대기 · PACKED · SHIPPED · DELIVERED …) · 오전 09:00 READY 412행 · status에는 인덱스도 히스토그램도 없다
  • 앱은 트랜잭션 하나로 일한다: 「다음 건」 SELECT → 화면에 첫 행 → 포장 → 「완료」 UPDATE · COMMIT

원본 SQL ​

sql
-- 「다음 건」 — 출고 대기 중 가장 오래된 한 건 (앱은 첫 행만 화면에 띄운다)
SELECT shipping_id, order_id, address_id
  FROM shipping
 WHERE status = 'READY'
 ORDER BY shipping_id
   FOR UPDATE;

-- 포장이 끝나면 「완료」
UPDATE shipping SET status = 'PACKED' WHERE shipping_id = :id;
COMMIT;

실행계획 학습용 예시 ​

xplan
[「다음 건」 한 번 — ALLSTATS LAST (앱은 한 행만 가져갔다)]
------------------------------------------------------------------------------------------------------
| Id  | Operation           | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Used-Mem |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |          |      1 |        |      1 |00:00:00.01 |     597 |          |
|   1 |  FOR UPDATE         |          |      1 |        |      1 |00:00:00.01 |     597 |          |
|   2 |   SORT ORDER BY     |          |      1 |   5818 |    412 |00:00:00.01 |     183 | 36864 (0)|
|*  3 |    TABLE ACCESS FULL| SHIPPING |      1 |   5818 |    412 |00:00:00.01 |     183 |          |
------------------------------------------------------------------------------------------------------
   3 - filter("STATUS"='READY')

[시간표 — 평일 09:00 · 출고 대기 412 건 · 태블릿 8대 중 셋]
시각       작업자 A (SID 201)                        작업자 B (SID 214)                       작업자 C (SID 233)
09:00:00   「다음 건」 → 412 행 잠금 · #88123 포장 시작
09:00:05                                             「다음 건」 → 기다림
09:00:12                                                                                      「다음 건」 → 기다림
09:03:10   「완료」 UPDATE · COMMIT → 412 행 풀림     깨어남 → 남은 411 행 잠금 · #88124        (이제 B 를 기다림)
09:06:02                                             「완료」 COMMIT                          깨어남 → 410 행 잠금 · #88125

[V$SESSION — 09:02:00]
SID   EVENT                            SECS   BLOCKING_SESSION   하고 있는 문장
201   SQL*Net message from client       120                      (포장 중 — 트랜잭션을 연 채)
214   enq: TX - row lock contention     115   201                SELECT … FOR UPDATE
233   enq: TX - row lock contention     108   201                SELECT … FOR UPDATE

[V$LOCK — 09:02:00]
SID   TYPE  대상 / 이름표       LMODE  REQUEST  BLOCK
201   TM    SHIPPING              3      0        0
201   TX    A 의 트랜잭션          6      0        1
214   TM    SHIPPING              3      0        0
214   TX    A 의 트랜잭션          0      6        0
233   TM    SHIPPING              3      0        0
233   TX    A 의 트랜잭션          0      6        0

[09:00 ~ 10:00 출고 완료] 21 건 — 8명이 포장 2 ~ 4분이면 120 ~ 240 건이어야 한다

문제 ​

  1. 실행계획과 시간표, V$LOCK을 근거로 B와 C가 무엇을 기다리는지, 첫 행 하나만 보는 SELECT가 왜 다른 작업자를 막는지 설명하시오.
  2. 8명이 동시에 서로 다른 건을 받도록 고치시오.
  3. SKIP LOCKED, NOWAIT, WAIT n이 각각 어떤 화면에 맞는지 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자