Skip to content

15b화 · 실습과 기록 ​

여기서 하는 일: 얼어붙은 정산일을 컨테이너 시계를 건드리지 않고 재현하고, 캐싱 4계층을 같은 일에 나란히 재고, 무효화와 FORCE, 경합을 확인하고, 정산일 함수를 고쳐 경계에서 대조하고, T1 리포트 11호를 쓴다.1_사건.md를 읽은 것은 "선배가 하는 걸 지켜본 것"이고, 이 문서를 끝내야 직접 해 본 것이 된다. 개념이 막히면 2_파고들기.md를 펼친다.

시간이 없는 주에는 STEP 0 → 1 → 2 → 3 → 8만 한다(약 40분). 무효화, FORCE, 경합은 다음 주에 돌아와서 해도 된다.


이 화의 실습 지도 ​

스크립트무엇계정소요
실습/91_결과캐시_권한_SYSDBA.sqlDBMS_RESULT_CACHE 실행 권한(한 번)SYS1초
실습/00_준비와_점검.sql15화 종료 상태 확인 · 복사본(C15B) · 함수 판(版)들 · 수첩 · 옆 세션 연락판sqlp5초
실습/01_얼어붙은_정산일.sql → 01b_옆세션.sql★ 오늘의 사건: 의존 객체 · '초' 판 · 목록 · 시간대 · NLS · 세션 상태 · 거절/받아 줌 · 녹이기 · 네 판 · 경계 시험sqlp(세션 둘을 차례로)15초 + 5초
실습/02_캐싱_4계층.sql + 일꾼.sql★ 캐싱 4계층 · 칸 · DETERMINISTIC의 범위 · Bypasssqlp × 210초
실습/03_무효화.sql + 일꾼.sql커밋 전 · 뒤 · ROLLBACK · 같은 값 · 한 명 → 전부 · RELIES_ON · 예외 · 컴파일sqlp × 25초
실습/04_FORCE_세션.sqlRESULT_CACHE_MODE = FORCE(내 세션에서만)sqlp5초
실습/05_경합.sql + 일꾼.sql둘이 같은 답을 동시에 찾으면 · 래치sqlp × 25초
실습/93_결과캐시_무결성_SYSDBA.sql(선택) ENFORCED면 무엇을 거절하나 · 끝에서 원복SYS5초
실습/99_정리.sql → 92_결과캐시_권한회수_SYSDBA.sqlC15B 삭제 · 결과 캐시 비우기 · 15화 종료 상태 확인 → 권한 회수sqlp → SYS수 초

PowerShell에서는 먼저 $OutputEncoding = New-Object System.Text.UTF8Encoding $false를 한 줄 실행한다. 모든 스크립트를 두 번씩 돌려 같은 판정이 나오는 것을 확인했다. 본문 실행 수, 찾기, 만들기, 논리 읽기, 건수는 같고 경과 시간과 래치 misses만 흔들린다. 판마다 달라지는 것이 하나 있는데, 칸이 밀려날 때 어느 결과가 밀려나는지다. 이것은 해당하는 자리에 적었다. 원본 M_CUS, T_ORD_STLM, T_ORD_BIG에는 한 줄도 쓰지 않는다. 고치는 것은 전부 _C15B 복사본이다. ℹ️ 스크립트를 다시 돌리면 실행계획 자리에 ORA-01403: no data found가 가끔 찍힌다(교재 실측: 02 [A]에서 아홉 판 중 한 번). 00이 복사본을 다시 만들어 커서가 무효화된 직후라 DISPLAY_CURSOR가 계획을 집지 못한 것이고, 측정 자체는 됐다. 그 스크립트를 한 번 더 돌리면 된다(00_자료준비/04_일상_운용과_문제해결.md). ⚠️ 스케줄러 잡은 쓰지 않는다(이 DB는 job_queue_processes = 1). '옆 세션'은 셸이 두 번째 sqlplus를 띄워 만든다(일꾼.sql). 일꾼은 지휘 스크립트가 끝에 보내는 EXIT를 받으면 끝나고, 60초 동안 지시가 없으면 스스로 끝난다.


STEP 0 — 직전 리비전을 연다 · 전제를 확인한다 (5분) ★ 매주 처음 하는 일 ​

bash
docker start ora-hanorder
cat 리비전/SDT_rev00.sql                               # 오늘 고칠 물건 — 정산일 함수 (6/14 금요일 반영판)
MSYS_NO_PATHCONV=1 docker exec -i ora-hanorder sqlplus -s / as sysdba < 실습/91_결과캐시_권한_SYSDBA.sql
docker exec -i ora-hanorder sqlplus -s sqlp/sqlp@localhost/FREEPDB1 < 실습/00_준비와_점검.sql
powershell
Get-Content 실습\91_결과캐시_권한_SYSDBA.sql -Encoding UTF8 | docker exec -i ora-hanorder sqlplus -s / as sysdba
Get-Content 실습\00_준비와_점검.sql -Encoding UTF8 | docker exec -i ora-hanorder sqlplus -s sqlp/sqlp@localhost/FREEPDB1
확인기대☐
91 주기 전 · 뒤DBA 하나 → DBA · SQLP · ENABLED☐
[전제] big_rows · big_idx · hist · stlm_rows · last_no · pkg · drv · c151000000 · 3 · FREQUENCY · 100000 · 0 · VALID · VALID · 0☐
데이터 지문255,398,623,700 · 25,519,833,100☐
[설정]result_cache_mode MANUAL · max_size 8126464 · max_result 5 · integrity TRUSTED · execution_threshold 2 · auto_blocklist ON · cpu_count 2 · job_queue_processes 1 · STATUS ENABLED☐
만든 것2000 · 100000 · 무효 0☐
USER_PROCEDURESFN_STLM_DT_RC_C15B RESULT_CACHE YES · DETERMINISTIC YES(← 금요일 판) · FN_STLM_DT_DET_C15B NO · YES(← 2019 원본) · FN_STLM_DT_C15B NO · NO(← rev01)☐
메모리 보고1984 blocks · 49 blocks☐

91 없이 00을 돌리면 [권한]에서 ORA-20001 … run 91 … first로 멈춘다. sqlp는 V$RESULT_CACHE_*는 읽지만 DBMS_RESULT_CACHE는 부르지 못한다. 15화의 DBMS_HPROF와 같은 경우다.

SDT_rev00.sql에서 두 단어에 밑줄을 긋는다. DETERMINISTIC(누가 · 언제 · 왜)와 RESULT_CACHE(누가 · 언제 · 왜)다. 그리고 본문 한 줄에서 이 함수의 진짜 입력을 찾는다.


STEP 1 — 예측을 봉인한다 (5분) ​

Q1. 정산일 함수에 RESULT_CACHE 를 붙였다. 정산이 계속 등록되면 그 캐시는 지워질까?            예 / 아니오
Q2. 목록 SQL (정산 10만 행) 이 정산일 함수를 행마다 부른다. 2019 원본(DETERMINISTIC)은 몇 번 부를까?  ______
Q3. 예상 보정액 (정산 10만 행 · 고객 2,000명) 을 RESULT_CACHE 함수로 두 번 돌린다. 두 번째 판의 함수 실행은?  ______
Q4. 고객 한 명의 등급을 고쳐 커밋하면, 보정률 결과 1,500칸 중 몇 칸이 지워질까?              ______
Q5. RESULT_CACHE_MODE = FORCE 로 '시계를 숨긴 함수' 를 부르는 문장을 힌트 없이 돌리면?          시각이 간다 / 멈춘다
Q6. 결과 캐시에 있는 답 하나를 100만 번 찾는다. 혼자일 때와 둘이 동시일 때, 각자의 시간은?      같다 / 조금 는다 / 두 배 가까이
▶ 적고 나서 펼치기
  • Q1: 아니오. 정산일 답의 의존 객체는 함수 자신 하나다. 진짜 입력(시계)은 객체가 아니다.
  • Q2: 대부분 10만이라고 쓴다. 실측은 1이다(인자 없는 DETERMINISTIC 함수는 문장에 한 번만 불린다). 그래서 금요일의 RESULT_CACHE가 번 것은 없다.
  • Q3: 대부분 0이라고 쓴다. 실측은 12,551이다. 결과 캐시 1,984칸에서 답 하나가 칸 하나를 차지하므로 고객 2,000명이 안 들어간다. 1,500명이면 0이다.
  • Q4: 1,500칸 전부. 테이블 단위로 기억한다.
  • Q5: 멈춘다. 힌트 한 줄 없이 금요일 사고가 난다.
  • Q6: 두 배 가까이(20 ~ 21 → 34 ~ 42cs). 캐시 없는 함수는 둘이어도 그대로다.

STEP 2 — 얼어붙은 정산일 (15분) ★ 오늘의 사건 ​

bash
docker exec -i ora-hanorder sqlplus -s sqlp/sqlp@localhost/FREEPDB1 < 실습/01_얼어붙은_정산일.sql
docker exec -i ora-hanorder sqlplus -s sqlp/sqlp@localhost/FREEPDB1 < 실습/01b_옆세션.sql      # 바로 이어서 — 다른 세션

'날짜가 바뀐 상황'은 이렇게 만든다. 컨테이너 시계는 바꾸지 않는다. ① 같은 모양 · 단위만 초: 정산일 함수에서 TRUNC만 뺀 RETURN SYSDATE. 진짜 SYSDATE로 진짜 2초를 기다린다. ② 세션 시간대: -11:00과 +13:00(24시간 차이). SYSDATE는 세션 시간대를 따르지 않으므로 이 판만 CURRENT_DATE로 썼다. FIXED_DATE는 쓰지 않는다. 되돌린 뒤에도 PDB 설정에 한 줄이 남았기 때문이다(2_파고들기 §5.2).

[A] 무효화가 고장 났나 ​

확인기대☐
정산일 결과의 의존 객체SQLP.FN_STLM_DT_RC_C15B 하나☐
보정률 결과의 의존 객체함수 · SQLP.M_CUS_C15B☐
[A1] 정산 한 줄 등록 · 커밋 뒤둘 다 Published☐
[A2] 고객 마스터 같은 값 UPDATE · 커밋 뒤보정률 Invalid · 정산일 Published☐

[B] ~ [E] 숨은 입력 넷 ​

판캐시 없는 판RESULT_CACHE 판☐
[B] '초' 판 2초 간격 세 번시각이 간다첫 값에 멈춘다 · 본문 1 · 0 · 0☐
[C] 목록 · 2초 뒤 한 줄 등록100,001100,000 · MINUS 1 / 0 · 빠진 것 900002☐
[C] 그 한 줄을 지우면100,000100,000. 경계 데이터가 없으면 대조가 통과한다☐
[D] -11:00('어제') → +13:00('오늘')D-1 → DD-1 → D-1☐
[D] sysdate · current_date두 세션 sysdate 같은 시각 · current_date만 24시간 이동☐
[E1] NLS MM-DD → DD/MM06-14 → 14/0606-14 → 06-14☐
[E2] 직원 kim → lee(CLIENT_IDENTIFIER)owner=kim → owner=leeowner=kim → owner=kim☐

[F] 오라클이 거절하는 것 · 받아 주는 것 ​

문장 (/*+ RESULT_CACHE */ · 2초 간격 두 번)계획에 RESULT CACHE시각☐
F1 SYSDATE 직접 · F4 SYSTIMESTAMP 직접 · F5 CURRENT_DATE 직접없다(거절)간다☐
F2 SYSDATE를 숨긴 그냥 함수있다멈춘다☐
F3 같은 함수 + DETERMINISTIC있다멈춘다☐
캐시에 남은 문장 결과F2 · F3만☐

[G] 녹이기 — 그리고 다시 언다 ​

☐ 다시 컴파일 → Invalid 1 → 다음 호출이 그 순간으로 다시 언다 (2초 뒤에도 같은 값)
☐ DBMS_RESULT_CACHE.INVALIDATE → 다시 그 순간으로

[H] 선배는 왜 붙였나 — 목록 SQL 네 판 (정산 10만 행 · 두 번) ​

판bodyfind경과논리 읽기☐
H plain(그냥 함수)100,000012 ~ 13cs549(첫 판 584 ~ 669)☐
H det(2019)100 ~ 1cs549☐
H rc(rev00 · 금요일 판)010 ~ 1cs549☐
H rev01 call(스칼라 서브쿼리)100cs549☐
☐ [H5] 금요일 판의 계획 — TABLE ACCESS FULL · Starts 1 · A-Rows 100K · Buffers 549 · filter("STLM_DT"<="FN_STLM_DT_RC_C15B"())
☐ 계획 해시 — plain · det · rc 셋 다 3721302985 · rev01 call 1560056510   (교재 실측값 · 해시는 판마다 같았다)
☐ RESULT_CACHE 가 이 목록에서 번 것 = 없다 (2019 의 DETERMINISTIC 이 이미 1 번)

[I] 고친 판(rev01)의 시험 — 경계 ​

시각원장rev01rev00☐
06-14 FRI 17:59:5906-14 FRI06-14 FRID (now)☐
06-14 FRI 18:00:0006-15 SAT06-15 SATD (now)☐
06-14 FRI 23:59:59 · 06-15 SAT 00:00:0006-15 SAT · 06-15 SAT같다D (now)☐
06-16 SUN 18:30:00 · 06-17 MON 09:00:0006-17 MON · 06-17 MON같다D (now)☐
06-17 MON 18:00:0006-18 TUE06-18 TUED (now)☐
한 주 7분 간격 1,440점MINUS 0 / 0☐

01b — 다른 세션 ​

확인기대☐
[A] 새 세션(PL/SQL) · SQL에서result_cache가 지금보다 3 ~ 4초 앞 · body 0(01 [G]가 마지막으로 얼린 값)☐
[A] 캐시에 있는 '초' 판 결과앞의 것들 Invalid · 마지막 하나 Published☐
[B] +13:00으로 붙은 직원plain D · result_cache D-1☐
[C] NLS DD.MM · 직원 parkplain 14.06 · owner=park / result_cache 06-14 · owner=kim☐

STEP 3 — 캐싱 4계층 (15분) ★ 오늘의 표 ​

bash
H="docker exec -i ora-hanorder sqlplus -s sqlp/sqlp@localhost/FREEPDB1"
$H < 실습/일꾼.sql > /dev/null & $H < 실습/02_캐싱_4계층.sql; wait
powershell
$j = Start-Job -ArgumentList (Resolve-Path 실습\일꾼.sql) -ScriptBlock { param($p)
  Get-Content $p -Encoding UTF8 | docker exec -i ora-hanorder sqlplus -s sqlp/sqlp@localhost/FREEPDB1 }
Get-Content 실습\02_캐싱_4계층.sql -Encoding UTF8 | docker exec -i ora-hanorder sqlplus -s sqlp/sqlp@localhost/FREEPDB1
$j | Wait-Job | Remove-Job

맨 앞의 worker_ready가 1이어야 한다(0이면 일꾼이 뜨지 않은 것이다. 이때는 옆 세션 줄만 빠지고 나머지는 돈다).

판기대☐
[A] Q2 버퍼 캐시만 × 10한 번 5,868 · 열 번 lio 약 58,700 · 112 ~ 117cs☐
[B] Q2 RESULT_CACHE 계획: 첫 번RESULT CACHE 연산자 · Buffers 5,867☐
[B] 두 번째아래 줄 Starts 0 · Buffers 칸이 사라진다☐
[B] W1 #2 ~ #10lio 0 · find 9☐
[B] 옆 세션lio 0 · 같은 답 A 10000 / 2564322100☐
[C] plain / 스칼라 서브쿼리 / DETERMINISTIC100,000 / 57,664 / 57,664(두 번 다)☐
[C] RESULT_CACHE 2000 #1 · #214,467 · 12,551(evict 12,497 · 12,551)☐
[C] 합계네 판 모두 73,753,436☐
[D] 메모리 보고1984 blocks · Used 1974 · PLSQL 1970 blocks (1970 count)☐
[D] 1500 #1 · #2 · plain1,500 · 0 · 75,000☐
[D] 옆 세션 1500body 0 · 답 55,294,096☐
[E] Q2 after grade남을 수도, 밀려날 수도 있다(판마다 다르다. 교재 여덟 번: 커서 모양만 밀림 4 · 둘 다 밀림 1 · 둘 다 남음 3)☐
[F] DETERMINISTICPL/SQL 직접 100,000 · 커서 루프 99,938 · 집계 한 번 57,664 · arraysize 15/500/5000 → 100,000 / 97,991 / 86,143☐
[G] 큰 결과Bypass · row_count 0 · 두 번 다 lio 1,548☐
☐ 스칼라 서브쿼리와 DETERMINISTIC 이 같은 수(57,664)인 까닭을 07화 §9 의 '칸' 으로 설명할 수 있다
☐ RESULT_CACHE 두 번째 판이 0 이 아닌 까닭을 메모리 보고의 숫자 둘(1,984 · 1,970)로 설명할 수 있다
☐ Q2 의 0 을 운영에 옮기지 못하는 까닭 — 의존 테이블(주문)의 커밋 빈도 (STEP 5 [E] 에서 확인)

STEP 4 — 무효화 (10분) ​

bash
$H < 실습/일꾼.sql > /dev/null & $H < 실습/03_무효화.sql; wait
판기대☐
[A] fill · hit5,867 · 0☐
[B] 커밋 전: 옆 세션lio 0 · 옛 답 A 10000 / 2564322100☐
[B] 커밋 전: 내 세션lio 5,867 · find 0 · create 0 · 새 답 A 10500 / 2688614600 · 결과는 Published☐
[C] 커밋 뒤Q2 result Invalid · SQLP.M_CUS_C15B 줄 둘(INVALIDATIONS 1 · 0) · 옆 세션 5,868 · 새 답 · 그다음 내 세션 0☐
[D] ROLLBACK 뒤lio 0 · find 1☐
[E] 같은 값 UPDATE 커밋 뒤lio 5,867 · create 1(지워졌다)☐
[F] 정산 복사본 커밋 뒤 · 고객 한 명 커밋 뒤Result Published 1500 · Result Invalid 1500 → body 1,500 · 보정률 .001☐
[G] RELIES_ONSP2-0806 경고 · PLW-05019 … deprecated beginning with version 11.2☐
[H] 없는 고객 × 5body 5 · NO_DATA_FOUND 5☐
[I] 재컴파일Result Invalid 1500☐
끝복사본 = 원본(MINUS 0 · 0)☐

[G]의 SP2-0806: Function created with compilation warnings는 기대한 출력이다. 실패가 아니다.


STEP 5 — RESULT_CACHE_MODE = FORCE (5분) ​

bash
docker exec -i ora-hanorder sqlplus -s sqlp/sqlp@localhost/FREEPDB1 < 실습/04_FORCE_세션.sql
판MANUALFORCE☐
[A] 힌트 없는 Q2—RESULT CACHE 생김 · 두 번째 Starts 0☐
[B] NO_RESULT_CACHE—빠짐 · Buffers 5,868☐
[C] PK 조회 2,000개create 0create 2,001 · evict 약 1,011 ~ 1,018 · fa_left 0 또는 1(판마다)☐
[D] 그냥 함수 10만 행 #139 ~ 41cs61 ~ 69cs · create 75,587 · evict 74,597☐
[D] #239 ~ 41cs0(바깥 문장째)☐
[D] 바깥 문장의 의존 객체FN_ADJ_RATE_C15B · M_CUS_C15B · T_ORD_STLM_C15B☐
[D] 고객 한 명 고친 뒤 FORCEbody 100,000 · force 73741702 / manual 73741702 (same)☐
[E] 고치고 읽기 × 1000create 0create 1,000 · find 0 · invalidations +999☐
[F] '초' 판을 부르는 문장 (힌트 없음)시각이 멈춘다 · SYSDATE 직접 문장은 간다☐
[G] 원복session MANUAL · system MANUAL☐

STEP 6 — 경합 (5분) ​

bash
$H < 실습/일꾼.sql > /dev/null & $H < 실습/05_경합.sql; wait
판기대☐
A solo plain · solo rc7 ~ 8cs · 20 ~ 21cs☐
[C] solo rc 래치Result Cache: Set · Blocklist Latch gets 1,000,001 · misses 0☐
B pair plain(me · other)8 ~ 9cs(혼자와 같다)☐
B pair rc(me · other)34 ~ 42cs(각자 두 배 가까이)☐
[C] pair rc 래치misses 수천 ~ 수만(Set 15,445 ~ 58,104 · Blocklist 7,681 ~ 58,381) · sleeps 0☐

⚠️ Free는 cpu_count가 2다. 세션을 셋 넘게 띄우면 CPU 차례 기다림이 섞여 래치만 따로 읽을 수 없다. 수백 세션의 경합은 대규모 EE 환경을 다루는 별권에서 본다.


STEP 7 — (선택) ENFORCED는 무엇을 믿나 (3분) ​

bash
MSYS_NO_PATHCONV=1 docker exec -i ora-hanorder sqlplus -s / as sysdba < 실습/93_결과캐시_무결성_SYSDBA.sql
문장 (2초 간격 두 번)기대☐
E1 SYSDATE 직접시각이 간다(거절)☐
E2 SYSDATE를 숨긴 그냥 함수시각이 간다(TRUSTED와 달리 거절)☐
E3 같은 함수 + DETERMINISTIC시각이 멈춘다(받아 준다)☐
캐시에 남은 것E3만☐
끝TRUSTED · spfile_rows 0☐

이 파라미터는 세션 단위로 바꿀 수 없다. 그래서 PDB를 SCOPE = MEMORY로 몇 초 동안 바꾸고 같은 스크립트 끝에서 되돌린다. 연습 DB에서만 하는 일이다.


STEP 8 — T1 단건 SQL 튜닝 리포트 11호 ★ 오늘의 산출물 ​

양식: T1_단건SQL_튜닝리포트.md · 붙이는 것: 정합성_회귀_체크리스트.md(08화)

┌────────────────────────────────────────────────────────────────────────────────┐
│ T1 단건 SQL 튜닝 리포트 — 11호   정산일 함수 (정산 등록 화면)            T1-011  │
├────────────────────────────────────────────────────────────────────────────────┤
│ 측정 환경 : 연습 DB 복사본 · 26ai Free · cpu 2 · 결과 캐시 7,936 KB (4 KB × 1,984)  │
│             정산 복사본 10만 (정산일 전부 과거) · 고객 복사본 2,000 · 단독 + 옆 세션 1 │
└────────────────────────────────────────────────────────────────────────────────┘
#항목내용
1대상① 정산일 함수 FN_STLM_DT: SDT_rev00 → rev01(diff: 리비전/SDT_rev01.sql 머리말) + 부르는 쪽(목록 SQL) 한 줄 ② 보정률 함수 FN_ADJ_RATE의 RESULT_CACHE(6/14): 6/17 09:40 되돌림 · 재적용 보류
2측정 환경위 머리말 · result_cache_mode MANUAL · integrity TRUSTED · max_result 5(49블록) · 통계는 00이 복사본에 수집
3부하 조건단일 세션(+ 옆 세션 1: 일꾼.sql) · 워크로드 프로파일 없음
4현상운영: 6/14 17:30 ~ 6/17 09:40 등록분 정산일이 6/14로 고정. 연습 DB 재현: '초' 판이 첫 값에 멈춤 · 목록 100,001 vs 100,000(MINUS 1/0) · 시간대 -11:00 → +13:00에서 D-1 → D-1. 4수치: TABLE ACCESS FULL · Starts 1 · E-Rows 5000 / A-Rows 100K · Buffers 549 · filter("STLM_DT"<="FN_STLM_DT_RC_C15B"()). 4수치로는 보이지 않는다. 계획, Buffers, 계획 해시(3721302985)가 그냥 함수 판과 같다
5원인정산일 답의 의존 객체 = 함수 자신 하나(V$RESULT_CACHE_DEPENDENCY). 진짜 입력(시계)은 의존 객체가 아니라서 아무도 지우지 않는다. 판단 근거: 6/14 윤세라. 작성자의 DETERMINISTIC 표시(2019-04-08 hd.kang)를 '언제나 같은 답'으로 읽음 · 대조는 같은 날 오후 안에서만(시각 경계 없음). [I권 Result 캐시 p.472 · Deterministic 함수 사용 시 주의사항 p.512] [lnpls PL/SQL Function Result Cache · tgdba Tuning the Result Cache]
6개선안채택: rev01. 시각을 인자로(p_at DATE DEFAULT SYSDATE) · RESULT_CACHE · DETERMINISTIC 제거 · 목록은 (SELECT FN_STLM_DT FROM dual). 채택하지 않은 대안: ⓐ 매일 자정 INVALIDATE: 18시 마감은 자정이 아니다 · 잊는 날 같은 사고 · 이 DB job_queue_processes 1 ⓑ 시각을 인자로 받고 RESULT_CACHE 유지: 답은 맞지만 인자가 초마다 달라 칸만 먹는다 ⓒ 2019 원본에서 멈춤: 답은 맞다 · 거짓 DETERMINISTIC이 남고 시험할 수 없다
7효과목록 Buffers 549 → 549(변화 없음) · 정산일 본문 0 → 1 · 결과 캐시 찾기 1 → 0 · 경과 0 → 0cs. 정합성 불일치 → 통과(8번). 이 리포트의 효과는 속도가 아니라 답이다
8정합성 회귀 검증아래 표
9환경 의존성결과 캐시 크기(연습 DB 7,936KB = 1,984칸 · 함수의 답 하나 = 칸 하나) · result_cache_integrity(ENFORCED여도 DETERMINISTIC이면 받아 준다: 93) · 단일 인스턴스(RAC는 별권)
10리스크① 목록 SQL을 고치지 않은 호출부는 행마다 부른다(12cs · 답은 맞다) ② 운영에 RESULT_CACHE가 붙은 다른 함수가 있을 수 있다 ③ 보정률 함수 재적용 시 칸 부족(2,000 > 1,984, 연습 DB 기준) ④ 정정 대상(6/14 17:30 ~ 6/17 09:40 등록분): 정산팀
11롤백 절차rev01 → 2019 원본(DETERMINISTIC만 · 인자 없음)으로 CREATE OR REPLACE. rev00(RESULT_CACHE 판)으로는 되돌리지 않는다. 다시 만들면 그 함수의 결과 캐시도 지워진다(01 [G])
12재측정 계획① 운영 result_cache_max_size · 고객 수 확인 → 보정률 재적용 판단 ② 운영 USER_PROCEDURES.RESULT_CACHE = 'YES' 전수 목록: 각 함수가 시계 · 세션 설정 · 세션 상태 · 패키지 변수를 읽는가 ③ 정산 등록 화면을 배포할 때마다 01 [I]의 경계 대조 ④ V$RESULT_CACHE_STATISTICS의 Delete Count Valid(밀려남)를 주 1회 확인

8. 정합성 회귀(08화 체크리스트)

#항목대상 / 방법결과판정
A기준원장 = 규칙을 그대로 옮긴 식(HH24 >= 18이면 다음 날). 직전 판 rev00과 같은 날 안에서 대조하면 전부 같으므로 기준으로 쓰지 않는다—정함
B건수 · MINUS · 중복rev01 vs 원장: 한 주 7분 간격 1,440점 · rev00 목록 vs 캐시 없는 판(01 [C])rev01: MINUS 0 / 0 · rev00: 100,000 vs 100,001 · MINUS 0 / 1rev01 통과 · rev00 불일치
C잘라 내기 전 전체해당 없음. 목록의 페이징, FETCH FIRST와 무관한 함수—해당 없음
D조인마다 1 쪽해당 없음. 조인이 없다—해당 없음
E경계 데이터★ 시각(금 17:59:59 · 18:00:00 · 자정 · 일 18:30 · 월 09:00 · 월 18:00) · ★ 세션(다른 세션 · 시간대 -11:00/+13:00 · NLS · 직원) · 복사본에 '방금 등록한 정산' 한 줄rev01 일곱 경계 = 원장 · rev00은 다른 세션 · 시간대 · NLS · 직원 전부 처음 세션의 것rev01 통과
F계획 해시와 무관하게rev00 · 그냥 함수 · 2019 원본의 목록 계획 해시 3721302985(셋이 같다)계획이 같아도 답은 달랐다통과(해시를 근거로 쓰지 않음)
G자동 장치를 근거로 쓰지 않았나"오라클이 자동으로 무효화한다"를 근거로 쓰지 않았다. 무효화는 의존 객체만 본다—통과
H영향 분석FN_STLM_DT를 부르는 곳 전부(USER_DEPENDENCIES · 소스 검색) · RESULT_CACHE가 붙은 함수 전부개발팀 · 운영 확인(12번 ②)조치 필요

STEP 9 — 「SQL 코딩 가이드」에 한 줄 (5분) ​

SQL_코딩가이드.md의 조문 11(15b화 · 근거 1회)을 읽고, 「개정 이력」의 15b화 줄을 확인한다.

11  결과 캐시는 답이 '테이블과 인자' 에만 기대는 곳에만 붙인다.
    시계 · 세션 시간대 · NLS · 세션 상태 · 패키지 변수에 기대는 함수에는 붙이지 않는다 — 숨은 입력은 인자로 꺼낸다.
    DETERMINISTIC 은 아무도 검사하지 않는 약속이다. 거짓이면 쓰지 않는다.
☐ 우리 스키마에서 결과 캐시를 쓰는 함수 · 약속을 한 함수를 찾는 쿼리를 써 봤다
    SELECT object_name, procedure_name, result_cache, deterministic FROM user_procedures
     WHERE result_cache = 'YES' OR deterministic = 'YES';
☐ 그 함수들의 본문에서 SYSDATE · SYSTIMESTAMP · CURRENT_DATE · SYS_CONTEXT · TO_CHAR(날짜) 형식 없음 · 패키지 변수를 찾았다

STEP 10 — 서술 답안 (400~600자) ​

박팀장에게 보내는 회신. "빠르게 하려다 날짜가 틀렸다는 거군요."

▶ 선배의 첨삭 포인트 (쓰고 나서)

① 무엇이 틀렸는지를 먼저 쓰고, 왜 빠르게 하려 했는지는 나중에 쓴다. 정산일이 6/14로 고정된 기간, 정정 대상, 되돌린 시각이 첫 문단에 온다.

② "캐시 버그"라고 쓰지 않는다. 결과 캐시는 설계대로 동작했다. 테이블이 바뀌면 지운다. 날짜는 테이블이 아니다. 고장이 아니라 붙이면 안 되는 자리였다. 그렇게 써야 "그럼 캐시를 다 끄자"는 결론으로 가지 않는다.

③ 얻은 것이 없었다는 사실을 숨기지 않는다. 목록에서는 이미 2019년 판이 한 번만 불리고 있었다. "빠르게 하려다"가 아니라 "재지 않고 붙였다"가 정확하다.

④ 다음 사고를 막는 내용으로 끝낸다. 결과 캐시를 쓰는 함수 전수 점검, 코딩 가이드 조문, 배포 때 경계 시각 대조를 적는다.


STEP 11 — 파인만 자가설명 ​

1단 백지 설명 → 2단 막힘 태그(최소 1개) → 3단 원천 복귀 → 4단 재설명.

태그전형적인 문장
#인과"정산 테이블이 계속 바뀌었는데 왜 캐시가 안 지워졌는지 설명하다 막혔다" → §4.1
#용어"DETERMINISTIC과 RESULT_CACHE의 차이를 한 문장으로 말 못 하겠다" → §1 · §6
#수치"두 번째 판이 왜 12,551인지 숫자로 못 나눠 보겠다" → §3.2

4단 되묻기 3문 (오늘 버전) ​

  1. 조건 반전: "정산일 규칙이 '18시 마감'이 아니라 '정산팀이 테이블에 적어 두는 마감일'이라면, RESULT_CACHE는 맞는 자리가 되나?"
  2. 반례 요구: "DETERMINISTIC을 붙여도 되는 함수 하나와, 붙이면 안 되는데 붙여도 한 문장 안에서는 틀리지 않는 함수 하나는?"
  3. 측정 지목: "운영의 어떤 함수가 결과 캐시에서 '얼어 있는지'를 무엇으로 찾을까?"

태그는 _막힘로그.md로 옮긴다.


정리 — 한빛유통을 15화 종료 상태로 ​

bash
docker exec -i ora-hanorder sqlplus -s sqlp/sqlp@localhost/FREEPDB1 < 실습/99_정리.sql
MSYS_NO_PATHCONV=1 docker exec -i ora-hanorder sqlplus -s / as sysdba < 실습/92_결과캐시_권한회수_SYSDBA.sql
확인기대☐
99 남은 세션0행(일꾼이 남아 있으면 먼저 EXIT를 보낸다)☐
99 c15b_left · c15b_results · big_rows · big_idx · hist · stlm_rows · last_no · pkg · drv · rc_mode0 · 0 · 1000000 · 3 · FREQUENCY · 100000 · 0 · VALID · VALID · MANUAL☐
99 지문255,398,623,700 · 25,519,833,100 · A 20 · B 180 · C 1800☐
92 권한 · 결과 캐시 · 파라미터DBA 하나 · rc_objects 0 · MANUAL · TRUSTED · 8126464 · ENABLED☐

완료 체크리스트 ​

  • [ ] STEP 0: SDT_rev00의 두 단어와 각각의 「그때의 근거」를 읽었고, 본문에서 진짜 입력을 찾았다
  • [ ] STEP 1: 보기 전에 예측 6개를 적었다
  • [ ] STEP 2: 정산일 결과의 의존 객체가 함수 하나인 것을 봤다. '초' 판이 멈추고, 목록에서 한 줄이 빠지고, 어제의 답을 오늘도 돌려주는 것을 봤다. 01b의 다른 세션이 같은 얼음을 받는 것도 봤다
  • [ ] STEP 2: 목록 네 판에서 금요일의 한 단어가 번 것이 없음을 봤고, rev01의 경계 대조에서 MINUS 0 / 0을 확인했다
  • [ ] STEP 3: 네 겹을 나란히 쟀고, 두 번째 판이 0이 아닌 까닭을 칸으로 설명했다
  • [ ] STEP 4: 커밋 전 남의 세션이 옛 답을 받는 것이 맞는 까닭을 적었다(선택)
  • [ ] STEP 5: FORCE에서 힌트 없이 시계가 멈추는 것을 봤다(선택)
  • [ ] STEP 6: 둘이 동시에 찾으면 각자 두 배 가까이 걸리는 것을 봤다(선택)
  • [ ] STEP 8: T1 11호를 쓰면서 4번(4수치로 안 보임), 5번(판단 근거), 8번(정합성 E 시각 · 세션), 11번(롤백) 칸을 채웠다
  • [ ] STEP 11: 막힘 태그 최소 1개를 _막힘로그.md에 옮겼다
  • [ ] 정리: 99 → 92로 15화 종료 상태를 확인했다

완결성 게이트: STEP 2의 [C](100,001 vs 100,000), STEP 8의 정합성 표 E 줄, 11번 롤백 칸이 비어 있으면 이 화는 미완성이다.


다음 화로 넘기는 상태 ​

무엇값
ora-hanorder 원본변화 없음. 15화 종료 상태(T_ORD_BIG 인덱스 3 · ORD_ST 히스토그램 · T_ORD_STLM 10만 · T_STLM_NUM 0 · PKG_ORD_STLM 원본 · PKG_LOAD_DRIVER VALID · C15B 0)
결과 캐시비어 있음 · MANUAL · TRUSTED · sqlp의 DBMS_RESULT_CACHE 권한 회수
정산일 함수SDT_rev01. 리비전 파일(연습 DB의 원본 스키마에는 정산일 함수를 만들지 않았다)
Q2결과 캐시 없이 rev01 그대로(보류 사유: 의존 테이블의 커밋 빈도)
새 산출물T1 11호 · 「SQL 코딩 가이드」 조문 11(근거 1회)
열어 둔 것보정률 함수 결과 캐시 재적용(운영 크기 확인) · RESULT_CACHE 함수 전수 점검 → 18화

다음 → 16화 「처음으로 시스템 전체를 본다」: 15화의 부하 발생기가 처음으로 연습 DB를 바쁘게 돌린다. 오늘 Q2에 결과 캐시를 붙이지 않은 결정이 거기서 어떻게 보이는지 확인한다.