테마
작년 가을 영수증 화면 때문에 공유 풀 경보가 울리자, DBA가 유통 운영 DB 인스턴스에 CURSOR_SHARING = FORCE를 켰다. 영수증 화면은 그 뒤 바인드 변수로 고쳤다. 이번 달 함수 기반 인덱스 사고까지 겪고 나서 박팀장이 안건을 올렸다. "FORCE를 EXACT로 되돌립시다. 다만 끄면 어디가 터지는지 먼저 보고해 주세요."
선배가 V$SQLAREA에서 FORCE가 손댄 문장(텍스트에 SYS_B_가 들어간 문장)을 실행 수로 줄 세웠다. 매출 리포트 담당자는 "월말 리포트만 유난히 느리다"는 말을 덧붙였다.
CF10 판매: 500,000행 · 4,583블록 · CF10_PK (SLE_NO) · IX_CF10_SLE_DT (SLE_DT)(리프 1,393블록) · 하루 약 700건, 한 달 약 20,800건CF11 판매상품: 1,096,708행 · 8,204블록 · CF11_PK (SLE_NO, ITM_NO) · 판매 한 건에 상품 약 2.2개CF01 상품: 109행 · 5블록 · CF12 발주: 734행 · 10블록CURSOR_SHARING = FORCE(인스턴스) · 바인드 피킹 켬 · 매출 리포트는 일 마감(하루 한 번)과 월말(한 달에 한 번)이 같은 프로그램에서 날짜 범위만 바꿔 나간다.-- ① 판매상품 상세 팝업 (2011년 Java · 오전에만 약 2만 번) : 영수증 번호와 상품 번호를 이어 붙인다
SELECT d.ITM_NO, i.ITM_NM, d.SLE_QTY, d.SLE_UPRC
FROM CF11 d, CF01 i
WHERE d.SLE_NO = '20231105-0412'
AND d.ITM_NO = 1052
AND i.ITM_NO = d.ITM_NO;
-- ② 매출 리포트 (일 마감과 월말이 같은 문장 모양) : 날짜 범위를 리터럴로 쓴다
SELECT h.SHP_NO, SUM(d.SLE_QTY * d.SLE_UPRC) AS SLE_AMT
FROM CF10 h, CF11 d
WHERE h.SLE_DT BETWEEN '20230601' AND '20230630'
AND d.SLE_NO = h.SLE_NO
GROUP BY h.SHP_NO;
-- ③ 영수증 조회 (작년에 바인드 변수로 고쳤다)
SELECT h.SLE_NO, h.SLE_DT, h.SLE_HMS, d.ITM_NO, d.SLE_QTY
FROM CF10 h, CF11 d
WHERE h.SLE_NO = :1
AND d.SLE_NO = h.SLE_NO;
-- ④ 발주 진행 탭 (상태값 셋 · 탭마다 상수)
SELECT ORD_NO, ORD_DT, PTNR_NO FROM CF12 WHERE ORD_PRGS_CD = '12' ORDER BY ORD_DT DESC;
-- 안건 : 오늘 밤
ALTER SYSTEM SET CURSOR_SHARING = EXACT SCOPE = BOTH;
[V$SQLAREA · 오전 09 ~ 12시 · FORCE가 손댄 문장 (SQL_TEXT에 SYS_B_) · 실행 수 순]
# EXECUTIONS VERSION_COUNT SQL_TEXT (앞부분)
① 21,480 1 SELECT d.ITM_NO, i.ITM_NM, d.SLE_QTY, d.SLE_UPRC FROM CF11 d, CF01 i WHERE d.SLE_NO = :"SYS_B_0" AND d.ITM_NO = :"SYS_B_1" …
④ 412 1 SELECT ORD_NO, ORD_DT, PTNR_NO FROM CF12 WHERE ORD_PRGS_CD = :"SYS_B_0" ORDER BY ORD_DT DESC
② 1 1 SELECT h.SHP_NO, SUM(d.SLE_QTY * d.SLE_UPRC) AS SLE_AMT FROM CF10 h, CF11 d WHERE h.SLE_DT BETWEEN :"SYS_B_0" AND :"SYS_B_1" …
(③은 SYS_B_가 없다 · 같은 시간 실행 18,730)
[② 매출 리포트 · 지난달 커서 하나로 일 마감 30번과 월말 1번을 처리 · 아래는 월말 실행]
Plan hash value: 3318806174 (6월 1일 일 마감 때 하드 파싱 · Peeked Binds '20230601', '20230601')
---------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
---------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 6 |00:00:00.61 | 83614 |
| 1 | HASH GROUP BY | | 1 | 6 | 6 |00:00:00.61 | 83614 |
| 2 | NESTED LOOPS | | 1 | 1540 | 45760 |00:00:00.55 | 83614 |
| 3 | NESTED LOOPS | | 1 | 1540 | 45760 |00:00:00.31 | 62781 |
| 4 | TABLE ACCESS BY INDEX ROWID BATCHED| CF10 | 1 | 706 | 20833 |00:00:00.04 | 251 |
|* 5 | INDEX RANGE SCAN | IX_CF10_SLE_DT | 1 | 706 | 20833 |00:00:00.01 | 60 |
|* 6 | INDEX RANGE SCAN | CF11_PK | 20833 | 2 | 45760 |00:00:00.19 | 62530 |
| 7 | TABLE ACCESS BY INDEX ROWID | CF11 | 45760 | 1 | 45760 |00:00:00.17 | 20833 |
---------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
5 - access("H"."SLE_DT">=:SYS_B_0 AND "H"."SLE_DT"<=:SYS_B_1)
6 - access("D"."SLE_NO"="H"."SLE_NO")
(같은 커서로 일 마감 하루치를 돌리면 Buffers 2,836)
[참고 · 월말 문장을 내 세션에서 EXACT로 따로 파싱하면 (ALTER SESSION)]
HASH JOIN · CF10 인덱스 범위 251 + CF11 FULL 8,204 → Buffers 8,455 · 0.31초
[① 팝업 · 내 세션만 EXACT로 바꿔 2만 번 재현 (ALTER SESSION SET CURSOR_SHARING = EXACT)]
parse count (hard) 20,014 · parse time cpu 1,640 cs · 경과 19.2초 (FORCE에서는 1.9초)