Skip to content
튜닝 연습파싱과 바인드 변수
PARSE-015

점심 피크마다 CPU가 100%인데, 하드 파싱은 거의 없고 라이브러리 캐시 대기가 1위다

실전DDL버거560 / 700

상황 ​

버거 본사 DB는 점심 11:30 ~ 13:00에 CPU가 100%에 붙는다. 하드 파싱 문제는 지난달에 정리해서 이제 거의 없는데도 그렇다. AWR 리포트의 대기 1위는 library cache: mutex X다. 회의에 대안 다섯이 올라왔다.

  • ① 인프라팀: CPU를 16코어에서 32코어로 늘린다.
  • ② DBA: 공유 풀을 3GB에서 6GB로 늘린다.
  • ③ POS 협력사: CURSOR_SHARING = FORCE를 켠다.
  • ④ 선배: SESSION_CACHED_CURSORS를 50에서 200으로 올린다.
  • ⑤ POS 개발사: POS 프로그램에 문장 캐시(ODP.NET Statement Cache Size 0 → 150)를 켜고 재배포한다(2주 걸린다).

테이블과 인덱스 ​

  • POS 단말 400대(매장 200곳 × 2대)가 저마다 DB 세션 하나로 붙는다(2계층, ODP.NET). 세션은 개점할 때 접속해서 마감까지 붙어 있다.
  • 주문 한 건에 SQL을 약 25번 실행한다(ITEM 메뉴 조회, SHOP 매장 조회, ORD 1건, ORD_ITEM 2~3건 등). 점심 흐름에서 세션 하나가 되풀이해 쓰는 문장은 약 130개다.
  • 모든 문장이 바인드 변수로 쓰여 있다. POS 프로그램은 실행할 때마다 명령 객체를 만들고 실행한 뒤 버린다(문장 캐시 0).
  • cpu_count = 16 · 공유 풀 3GB · session_cached_cursors = 50 · open_cursors = 300 · CURSOR_SHARING = EXACT

원본 SQL ​

sql
// PosOrder.cs : 명령을 실행할 때마다 새로 만들고 버린다 (POS 문장 130개가 모두 이 모양)
using (var cmd = new OracleCommand("SELECT ITEM_NM, ITEM_TYPE_CD, SALE_YN FROM ITEM WHERE ITEM_ID = :item_id", conn))
{
    cmd.Parameters.Add("item_id", OracleDbType.Int32).Value = itemId;
    using (var rd = cmd.ExecuteReader()) { /* 메뉴 한 건 */ }
}

// 연결 문자열 : Statement Cache Size를 지정하지 않았다 (0)
// "User Id=pos;Data Source=BURGER;Pooling=false"

-- 지금 파라미터
-- session_cached_cursors = 50 · open_cursors = 300 · shared_pool_size = 3G · cpu_count = 16

실행계획 학습용 예시 ​

xplan
[AWR · 12:00 ~ 13:00 · Load Profile (초당)]
  DB Time                  38.2
  DB CPU                   14.1
  Executes (SQL)        6,240
  Parses (SQL)          6,180
  Hard parses (SQL)         0.3
  Logical read         41,050

[Instance Efficiency]
  Soft Parse %             99.99
  Execute to Parse %        0.96
  % Non-Parse CPU          63.40
  Library Hit %            99.98

[Top Foreground Events]
  이벤트                        대기(초)   % DB time
  library cache: mutex X         51,570        37.5
  DB CPU                         50,760        36.9
  cursor: pin S                  19,530        14.2
  cursor: mutex S                 4,260         3.1
  log file sync                   3,300         2.4

[Instance Activity · 같은 한 시간]
  execute count                    22,464,000
  parse count (total)              22,248,000
  parse count (hard)                    1,080
  session cursor cache hits         2,470,000    (파싱의 11%)
  session cursor cache count        세션마다 50 (설정값에 붙어 있다)

[SQL ordered by Parse Calls · 상위 셋]
  Parse Calls   Executions   SQL
    1,612,400    1,612,400   SELECT ITEM_NM, ITEM_TYPE_CD, SALE_YN FROM ITEM WHERE ITEM_ID = :item_id
    1,108,900    1,108,900   INSERT INTO ORD_ITEM (ORD_NO, ITEM_ID, ORD_ITEM_QTY, ORD_DT, ORD_HMS) VALUES (:1, :2, :3, :4, :5)
      402,300      402,300   SELECT SHOP_NM FROM SHOP WHERE SHOP_NO = :shop_no

문제 ​

  1. 하드 파싱이 거의 없는데 라이브러리 캐시 대기가 1위인 이유를 Instance Efficiency와 Instance Activity의 숫자로 설명하시오.
  2. 대안 다섯을 각각 효과와 부작용으로 평가하시오.
  3. 당장 할 일과 다음에 할 일을 정하고, 필요한 설정과 반영 방법을 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자