Skip to content
튜닝 연습파싱과 바인드 변수
PARSE-030

포인트 적립 API는 INSERT 한 줄인데 이벤트 날 cursor mutex 대기가 쌓인다

중급INSERT이커머스575 / 700

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

상황 ​

이커머스 포인트 적립 API(Python, python-oracledb)는 point_transactions에 한 건씩 넣는다. 평소에는 분당 300건쯤이고, 지난 토요일 쿠폰 이벤트 때는 분당 9,000건이었다. 그 두 시간 동안 DB 대기 상위에 cursor: mutex X와 library cache: mutex X가 올랐고 API 응답이 1초를 넘겼다. 개발팀이 말했다. "바인드 변수도 쓰고 SQL은 한 줄뿐이에요. 하드 파싱이 생길 리가 없어요."

테이블과 인덱스 ​

  • point_transactions: 45,000행 · 210블록 · PK(tx_id)만 · customer_id NUMBER · amount NUMBER · tx_type VARCHAR2(10)
  • 이 문제의 변형: order_id NUMBER(NULL 허용, 쿠폰 적립은 NULL)와 memo VARCHAR2(200)(비어 있거나 10 ~ 150자) 컬럼이 더 있다
  • 시퀀스 seq_point_tx CACHE 1000 · DB 문자셋 AL32UTF8
  • 호출 경로 둘: 주문 확정(고객 번호를 정수로, 주문 번호를 넘긴다)과 쿠폰 이벤트(요청 JSON의 고객 번호를 문자열 그대로, 메시지를 넘긴다)

원본 SQL ​

sql
# point_api.py
SQL = """INSERT INTO point_transactions (tx_id, customer_id, amount, tx_type, order_id, memo, created_at)
         VALUES (seq_point_tx.NEXTVAL, :cust, :amt, :tp, :ord, :memo, SYSDATE)"""

def earn(cur, cust, amt, ord_id=None, memo=None):
    cur.execute(SQL, cust=cust, amt=amt, tp="EARN", ord=ord_id, memo=memo)

# 주문 확정 :   earn(cur, 1234, 500, ord_id=88123)
# 쿠폰 이벤트 : earn(cur, body["customer_id"], 1000, memo=body["msg"])   # "1234" · 메시지 길이 제각각

실행계획 학습용 예시 ​

xplan
[V$SQLAREA — 토요일 15:00]  VERSION_COUNT 14 · LOADS 23 · EXECUTIONS 412,880 · SHARABLE_MEM 1.9MB
[V$SQL_SHARED_CURSOR — Y 칸 세기 (자식 14개)]  BIND_MISMATCH 9 · BIND_LENGTH_UPGRADEABLE 4

[V$SQL_BIND_CAPTURE — 자식 셋만]
 CHILD  :CUST           :ORD            :MEMO             호출 경로
     0  NUMBER          NUMBER          VARCHAR2(32)      주문 확정 (memo 없음)
     3  VARCHAR2(32)    VARCHAR2(32)    VARCHAR2(128)     쿠폰 · 짧은 메시지 (ord 없음)
     7  VARCHAR2(32)    VARCHAR2(32)    VARCHAR2(2000)    쿠폰 · 긴 메시지

[AWR — 토요일 14:00 ~ 16:00 · Top Timed Events]
  cursor: mutex X              21%
  DB CPU                       38%
  library cache: mutex X        9%
  parse count (hard)           이 문장 23번 · 다른 문장 합계 40번

[한 번 실행의 계획]
---------------------------------------------------------------------------------------------------------
| Id  | Operation                | Name               | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------------------
|   0 | INSERT STATEMENT         |                    |      1 |        |      0 |00:00:00.01 |       7 |
|   1 |  LOAD TABLE CONVENTIONAL | POINT_TRANSACTIONS |      1 |        |      0 |00:00:00.01 |       7 |
|   2 |   SEQUENCE               | SEQ_POINT_TX       |      1 |        |      1 |00:00:00.01 |       0 |
---------------------------------------------------------------------------------------------------------

문제 ​

  1. 텍스트가 하나인 INSERT에 자식이 14개 생긴 이유를 위 자료로 설명하시오. 하드 파싱이 23번뿐인데도 이벤트 때 mutex 대기가 쌓인 이유도 쓰시오.
  2. 자식이 하나로 모이도록 API 코드를 고치시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자