Skip to content
튜닝 연습인덱스 사용 조건
IDX-025

「없는 쿠폰」 장애를 고친 뒤로 결제 화면이 쿠폰 한 장마다 멈칫한다

중급SELECT한빛유통25 / 700

상황 ​

한빛유통은 2023년부터 새 쿠폰에 12자리 번호를 붙인다. 쿠폰 테이블은 그 전 프로모션 시스템에서 옮겨 온 것이라 번호 컬럼이 CHAR(16)이다. 새 결제 앱을 내놓은 날, 12자리 쿠폰이 전부 「없는 쿠폰」으로 나왔다. 그런데 SQL Developer에서 WHERE CPN_NO = 'K7Q2M9X4P1R8'로 치면 쿠폰이 나온다. 조민우가 급히 조건을 TRIM(CPN_NO) = :v_cpn_no로 바꿔 장애를 닫았다. 그 뒤로 결제 한 건이 쿠폰을 확인할 때마다 0.1초 가까이 걸린다. 결제는 하루 3만 건이다.

테이블과 인덱스 ​

  • T_CPN 발급 쿠폰(이 문제에서 쓰는 파생 테이블, 구 프로모션 시스템에서 이관): 800,000행 · 6,020블록 · CPN_NO CHAR(16) · CUS_ID · ISU_DT · EXP_DT · USE_YN · DC_AMT
  • CPN_NO는 구 번호 16자리 420,000건과 2023년 이후 12자리 380,000건이다. 12자리 번호는 CHAR라서 뒤에 공백 4칸이 채워져 저장된다
  • 인덱스 PK_T_CPN (CPN_NO): BLEVEL 2 · 리프 약 3,330블록 · 통계 최신
  • 결제 앱은 쿠폰 번호를 문자열 바인드(VARCHAR2)로 넘긴다

원본 SQL ​

sql
-- [장애 때] WHERE CPN_NO = :v_cpn_no   → 12자리 쿠폰이 0건
-- [핫픽스]  조민우
SELECT CPN_NO, CUS_ID, EXP_DT, USE_YN, DC_AMT
  FROM T_CPN
 WHERE TRIM(CPN_NO) = :v_cpn_no

실행계획 학습용 예시 ​

xplan
[장애 때 · CPN_NO = :v_cpn_no · 'K7Q2M9X4P1R8']
------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |          |      1 |        |      0 |00:00:00.01 |       3 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T_CPN    |      1 |      1 |      0 |00:00:00.01 |       3 |
|*  2 |   INDEX UNIQUE SCAN         | PK_T_CPN |      1 |      1 |      0 |00:00:00.01 |       3 |
------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("CPN_NO"=:V_CPN_NO)

[핫픽스 · TRIM(CPN_NO) = :v_cpn_no · 같은 번호]
------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |          |      1 |        |      1 |00:00:00.09 |    6024 |
|*  1 |  TABLE ACCESS FULL          | T_CPN    |      1 |   8000 |      1 |00:00:00.09 |    6024 |
------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter(TRIM("CPN_NO")=:V_CPN_NO)

문제 ​

  1. 장애 때 계획은 PK로 정확히 찾아갔는데도 0건이었다. SQL Developer의 리터럴과 앱의 바인드가 왜 다른 결과를 냈는지 CHAR 비교 규칙으로 설명하시오.
  2. 핫픽스가 느린 이유를 설명하고, 컬럼을 감싸지 않으면서 12자리와 16자리 쿠폰을 모두 찾도록 SQL을 고치시오.
  3. 근본 대책과 그 대가를 설명하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자