테마
상담원 화면의 「결제 대기 목록」은 5분마다 새로 고친다. 이 화면을 빠르게 하려고 지난달 주문 테이블을 상태별 LIST 파티션 복사본 T_ORD_LS로 옮겼다. 결제 대기(PENDING) 칸만 읽으면 되기 때문이다.
옮긴 첫날 결제 완료 처리가 모두 ORA-14402로 실패했고, 조민우가 ENABLE ROW MOVEMENT로 급히 막았다. 그 뒤로 에러는 없지만 결제 처리가 1.1ms에서 2.9ms로 늘고 redo가 세 배가 되었다. 금요일 타임세일에는 5분 동안 결제가 약 1만 건 몰린다.
상담원들은 "금액 정정을 눌렀는데 가끔 저장되지 않는다"고 한다. 상담원 화면은 목록을 띄울 때 받아 둔 ROWID로 금액을 고친다(오래된 프레임워크의 기본 동작이다).
T_ORD_LS 주문(파티션 복사본, 문제 설정): T_ORD 100,000행을 PARTITION BY LIST (ORD_ST) 세 칸으로 나눴다. P_PENDING 8,000행(약 72블록) · P_COMPLETE 90,000행(약 806블록) · P_CANCEL 2,000행(약 18블록)ORD_DT는 주문 시각(시분초 있음)이고 2023년 1월 ~ 2024년 12월이다.PK_T_ORD_LS (ORD_SEQ)는 파티션 키가 없어 GLOBAL이다. 다른 인덱스는 없다. 통계는 최신이다.CREATE TABLE T_ORD_LS
PARTITION BY LIST (ORD_ST)
( PARTITION P_PENDING VALUES ('PENDING'),
PARTITION P_COMPLETE VALUES ('COMPLETE'),
PARTITION P_CANCEL VALUES ('CANCEL') )
AS SELECT * FROM T_ORD;
ALTER TABLE T_ORD_LS ADD CONSTRAINT PK_T_ORD_LS PRIMARY KEY (ORD_SEQ);
ALTER TABLE T_ORD_LS ENABLE ROW MOVEMENT; -- 옮긴 첫날 ORA-14402 를 막으려고
-- 결제 완료 처리 (결제마다)
UPDATE T_ORD_LS
SET ORD_ST = 'COMPLETE', PAY_DT = SYSDATE, PAY_TP = :pay_tp, PAY_AMT = :pay_amt
WHERE ORD_SEQ = :ord_seq;
-- 「결제 대기 목록」 (상담원 화면 · 5분마다 · :v_from = 사흘 전 0시)
SELECT ROWID AS rid, ORD_SEQ, CUS_ID, ORD_DT, ORD_AMT
FROM T_ORD_LS
WHERE ORD_ST = 'PENDING'
AND ORD_DT >= :v_from
ORDER BY ORD_DT;
-- 상담원 금액 정정
UPDATE T_ORD_LS SET ORD_AMT = :amt WHERE ROWID = :rid;
[옮긴 첫날 09:00 · 결제 완료 처리 · ROW MOVEMENT 를 켜기 전]
ORA-14402: updating partition key column would cause a partition change
[지금 · 결제 완료 처리 한 건]
--------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Pstart | Pstop |
--------------------------------------------------------------------------------------------------------------------------------
| 0 | UPDATE STATEMENT | | 1 | | 0 | 00:00:00.01 | 14 | | |
| 1 | UPDATE | T_ORD_LS | 1 | | 0 | 00:00:00.01 | 14 | | |
| 2 | TABLE ACCESS BY GLOBAL INDEX ROWID | T_ORD_LS | 1 | 1 | 1 | 00:00:00.01 | 3 | ROWID | ROWID |
|* 3 | INDEX UNIQUE SCAN | PK_T_ORD_LS | 1 | 1 | 1 | 00:00:00.01 | 2 | | |
--------------------------------------------------------------------------------------------------------------------------------
3 - access("ORD_SEQ"=:ORD_SEQ)
[세션 통계 · 결제 1,000건]
옮기기 전 T_ORD 지금 T_ORD_LS (ROW MOVEMENT)
redo size 0.41 MB 1.32 MB
undo change vector size 0.13 MB 0.47 MB
db block changes 4,060 11,930
결제 처리 평균 1.1 ms 2.9 ms
[「결제 대기 목록」]
----------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Pstart | Pstop |
----------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 33 | 00:00:00.01 | 72 | | |
| 1 | SORT ORDER BY | | 1 | 33 | 33 | 00:00:00.01 | 72 | | |
| 2 | PARTITION LIST SINGLE | | 1 | 33 | 33 | 00:00:00.01 | 72 | 1 | 1 |
|* 3 | TABLE ACCESS FULL | T_ORD_LS | 1 | 33 | 33 | 00:00:00.01 | 72 | 1 | 1 |
----------------------------------------------------------------------------------------------------------------
3 - filter("ORD_DT">=:V_FROM)
[상담원 금액 정정 · 그사이 결제된 주문]
UPDATE T_ORD_LS SET ORD_AMT = :amt WHERE ROWID = :rid → 0 행 (에러 없음)