테마
지난주 정산팀이 엑셀 내려받기가 느리다고 하자, 개발팀은 커넥션 풀 설정에 defaultRowPrefetch = 5000을 넣었다. 드라이버 기본값(10)을 모든 문장에 대해 바꾼 것이다. 내려받기는 1.9초 → 0.3초가 됐다.
다음 날부터 고객센터 「고객 주문 이력」 화면이 0.01초에서 0.3초 안팎으로 느려졌고, 화면 서버(WAS)의 힙 사용량이 늘어 GC가 잦아졌다. 상담원은 주문 이력의 첫 화면(20건)만 보고 닫는 일이 대부분이다.
박팀장이 문장마다 fetch size를 어떻게 둘지 정하고, 무엇을 잃는지도 적어 달라고 했다.
T_ORD_BIG: 1,000,000행 · 5,826블록 · 고객 2,000명이라 고객 한 명의 주문은 약 500건 · 같은 고객의 주문은 테이블 곳곳에 흩어져 있다(클러스터링 팩터가 나쁘다)IX_ORD_BIG_CUS_DT (CUS_ID, ORD_DT)(이 문제에만 있다) · IX_ORD_BIG_DT (ORD_DT) · 통계 최신T_ORD: 100,000행 · 896블록 · PK_T_ORD (ORD_SEQ)// DataSourceConfig.java (지난주 반영) : 커넥션 풀의 모든 연결에 건다
props.setProperty("defaultRowPrefetch", "5000");
-- [S1] 정산팀 엑셀 내려받기 : 한 달 치 전부 (6월 27,390건)
SELECT ORD_SEQ, CUS_ID, ORD_DT, ORD_AMT
FROM T_ORD_BIG
WHERE ORD_DT >= :d_from AND ORD_DT < :d_to
ORDER BY ORD_DT, ORD_SEQ;
-- [S2] 고객센터 고객 주문 이력 : 화면은 앞 20건을 보여 주고, 상담원이 닫으면 ResultSet을 닫는다
SELECT ORD_SEQ, ORD_DT, ORD_ST, ORD_AMT
FROM T_ORD_BIG
WHERE CUS_ID = :cus
ORDER BY ORD_DT DESC;
-- [S3] 주문 한 건 상세 : PK 단건
SELECT ORD_SEQ, CUS_ID, ORD_DT, ORD_ST, PAY_TP, ORD_AMT, PAY_AMT
FROM T_ORD
WHERE ORD_SEQ = :seq;
[S2 · 같은 고객 (CUS_000731) · DISPLAY_CURSOR 'ALLSTATS LAST']
(지난주까지 · fetch size 10 · 화면이 20건을 읽고 닫음)
----------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads |
----------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 20 |00:00:00.01 | 24 | 19 |
| 1 | TABLE ACCESS BY INDEX ROWID | T_ORD_BIG | 1 | 500 | 20 |00:00:00.01 | 24 | 19 |
|* 2 | INDEX RANGE SCAN DESCENDING | IX_ORD_BIG_CUS_DT | 1 | 500 | 20 |00:00:00.01 | 4 | 1 |
----------------------------------------------------------------------------------------------------------------------
(지금 · defaultRowPrefetch 5000 · 화면은 똑같이 20건만 쓰고 닫음)
----------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads |
----------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 500 |00:00:00.29 | 506 | 471 |
| 1 | TABLE ACCESS BY INDEX ROWID | T_ORD_BIG | 1 | 500 | 500 |00:00:00.29 | 506 | 471 |
|* 2 | INDEX RANGE SCAN DESCENDING | IX_ORD_BIG_CUS_DT | 1 | 500 | 500 |00:00:00.01 | 6 | 2 |
----------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("CUS_ID"=:CUS)
[S1 · 엑셀 내려받기 · FULL + SORT ORDER BY · Used-Mem 1.9M]
fetch size 페치 SQL*Net roundtrips Buffers 경과
10 2,740 2,741 5,826 1.9 초
5000 6 7 5,826 0.3 초
(1000으로 재 보면) 28 29 5,826 0.3 초
[S3 · 주문 한 건] Buffers 3 · 페치 1 · 설정과 상관없이 같다
[WAS · 지난주 대비] 힙 사용량 1.1GB → 2.3GB · GC 시간당 40번 → 310번