테마
상담팀이 「고객 주문 내역」 화면이 굼뜨다고 하자, 오과장은 토요일 밤 T_ORD를 고객 순서로 다시 실었다. 04화에서 본 대로 테이블을 인덱스 순서로 다시 쌓으면 그 인덱스의 클러스터링 팩터가 좋아진다. 월요일에 상담 화면은 실제로 빨라졌다(고객 한 명 Buffers 52 → 4).
그런데 정산팀 「월 주문 합계」(월을 고르면 그 달의 건수와 금액, 하루 수백 번)가 블록을 여든 배 넘게 읽는다. 오과장은 이렇게 말했다. "계획은 지난주와 똑같아요. 인덱스도 그대로 타고, 통계도 토요일 밤에 야간 잡이 다 모았어요."
조민우가 인덱스 통계를 다시 모아 보자고 하자 오과장이 덧붙였다. "그러면 날짜 인덱스 CF가 10만 가까이 나와서 FULL로 갈 거예요. TABLE_CACHED_BLOCKS를 최대로 올리면 CF가 작게 나온다니까 그걸 같이 하죠."
T_ORD: 100,000행 · 896블록(블록당 약 112행) · ORD_DT DATE(시분초 있음, 2023-01-01 ~ 2024-12-31, 하루 약 137건) · 2024년 6월 4,110건 · 고객 2,000명 × 50건CUS_ID, ORD_DT 순서로 쌓여 있어서 한 고객의 주문 50건이 블록 한두 개에 모여 있다.PK_T_ORD (ORD_SEQ) · IX_T_ORD_DT (ORD_DT) BLEVEL 1 · 리프 265 · IX_T_ORD_CUS (CUS_ID) BLEVEL 1 · 리프 298 · 세 인덱스 모두 2024년 3월에 만들었다PKG_STATS.NIGHTLY(매일 23:40): 2021년에 T_ORD_BIG 인덱스 셋을 모으는 시간을 줄이려고 cascade => FALSE로 바꿨다. 주석에는 "인덱스 통계는 만들 때와 리빌드할 때 모인다"라고 적혀 있다.T_ORD의 TABLE_CACHED_BLOCKS 선호도는 기본값(1)이다.-- 토요일 22:00 · 오과장 : 상담 화면을 위해 T_ORD 를 고객 순서로 다시 싣는다 (인덱스 셋은 그대로 둔다)
CREATE TABLE T_ORD_SORT AS SELECT * FROM T_ORD ORDER BY CUS_ID, ORD_DT;
TRUNCATE TABLE T_ORD;
INSERT INTO T_ORD SELECT * FROM T_ORD_SORT;
COMMIT;
DROP TABLE T_ORD_SORT PURGE;
-- 통계는 야간 잡에 맡긴다
-- 야간 통계 잡 PKG_STATS.NIGHTLY (매일 23:40 · 2021-04 판)
BEGIN
DBMS_STATS.GATHER_SCHEMA_STATS(ownname => 'SQLP',
options => 'GATHER STALE',
cascade => FALSE); -- 인덱스 통계는 만들 때와 리빌드할 때 모인다
END;
/
-- 정산팀 「월 주문 합계」
SELECT COUNT(*) AS cnt, SUM(ORD_AMT) AS amt, SUM(PAY_AMT) AS pay
FROM T_ORD
WHERE ORD_DT >= :v_from
AND ORD_DT < :v_to
[USER_TAB_STATISTICS · USER_INDEXES · 월요일 09:30]
OBJECT_NAME 종류 NUM_ROWS BLOCKS / LEAF_BLOCKS CLUSTERING_FACTOR LAST_ANALYZED
T_ORD TABLE 100,000 896 - 토 23:41 (야간 잡)
PK_T_ORD INDEX 100,000 187 889 2024-03-11 (인덱스를 만든 날)
IX_T_ORD_DT INDEX 100,000 265 892 2024-03-11
IX_T_ORD_CUS INDEX 100,000 298 97,915 2024-03-11
[월 주문 합계 · 2024년 6월 · 월요일 10:05]
Plan hash value: 1830562947
-------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.03 | 4118 |
| 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.03 | 4118 |
|* 2 | FILTER | | 1 | | 4110 |00:00:00.03 | 4118 |
| 3 | TABLE ACCESS BY INDEX ROWID| T_ORD | 1 | 4105 | 4110 |00:00:00.03 | 4118 |
|* 4 | INDEX RANGE SCAN | IX_T_ORD_DT | 1 | 4105 | 4110 |00:00:00.01 | 12 |
-------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(:V_FROM<:V_TO)
4 - access("ORD_DT">=:V_FROM AND "ORD_DT"<:V_TO)
[같은 SQL · 지난주 금요일] Plan hash value 1830562947 (같은 계획) · Buffers 49 · 0.01초
USER_INDEXES의 어느 숫자가 지금 테이블과 맞지 않는지, 그 숫자가 왜 고쳐지지 않았는지도 쓰시오.TABLE_CACHED_BLOCKS를 올리자는 오과장의 안을 평가하고, 그 선호도가 맞는 테이블은 어떤 테이블인지 쓰시오.