Files
chocomae/doc/plan/db/260915-improve-production/07-post-analysis.md
T
jisangs 03eea2098e 운영 서버에서 DB index 적용 작업 진행
- 01~07 작업 문서를 운영 측정값 기준으로 정리 (문서의 DB 비밀번호 삭제)
- app_highest_record 중복 8,795행 점수 기준 정리, 인덱스 5개 + UNIQUE 키 2개 추가
- 02·04 공통 측정 스크립트와 측정 결과, comparison.txt 추가
- 서버 처리 시간 18~292배 단축, 결과 행 수 동일
- 운영 데이터 백업과 권한(비밀번호 해시) 파일은 커밋에서 제외

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 23:30:42 +09:00

5.6 KiB

07단계: 사후 분석 및 최적화

운영 서버 적용 후 실제 성능을 분석하고 추가 최적화를 검토합니다.


📊 실제 성능 측정 결과

운영 DB에서 측정한 개선율

comparison.txt(04단계)의 값을 옮겨 적었습니다 (2026-09-15). 6개 쿼리 전체 값은 result/comparison.txt를 참고하세요.

항목 베이스라인 (02) 개선 후 (04) 개선율 상태
EXPLAIN rows (q2 시간별·범위) 10,103 22 459배 ⬇️ [x] 확인
EXPLAIN rows (q4 일간·범위) 10,103 99 102배 ⬇️ [x] 확인
EXPLAIN rows (q3 일간·함수) 10,103 5,851 1.7배 ⬇️ [x] 확인
서버 처리 시간 (q4, ANALYZE) 75.2ms 0.47ms 161배 ⬇️ [x] 확인
서버 처리 시간 (q3 일간·함수, ANALYZE) 75.3ms 2.44ms 31배 ⬇️ [x] 확인
느린 쿼리 수 (0.5초 이상) 측정 안 함 (당시 기준 3초·파일 로그) 앱 쿼리 0건 (작업 후 1시간, 작업 SQL 5건만) - [ ] 24시간 후 재확인

참고: 01단계 사전 측정의 일간 랭킹(함수형)은 index_merge, rows 10,100이었습니다.

MariaDB 10.11의 EXPLAIN FORMAT=JSON에는 cost 필드가 없어 스테이징의 cost 수치와 직접 비교할 수 없습니다. rows와 실행 시간으로 비교하고, 실제 실행 통계가 필요하면 ANALYZE FORMAT=JSON <쿼리>r_rows, r_total_time_ms를 사용하세요 (쿼리를 실제로 실행함).

저장된 파일

result/
├── premeasure_*.txt, premeasure_*.json  ← 01 사전 측정
├── baseline_explain_q*.json             ← 02 EXPLAIN
├── baseline_perf.txt                    ← 02 실행 시간
├── backup_app_highest_dup_rows.sql      ← 03 삭제 행 백업
├── add_indexes_result.txt               ← 03 인덱스 추가 결과
├── after_explain_q*.json                ← 04 EXPLAIN
├── after_perf.txt                       ← 04 실행 시간
├── explain_summary.txt, timing_summary.txt
└── comparison.txt

🔍 성능 분석

쿼리 응답 시간 추이 (24시간 모니터링)

시간대별 성능:
- 평상시: _____ ms
- 피크 시간: _____ ms
- 최고 응답 시간: _____ ms

느린 쿼리 로그:
- 0.5초 이상: _____ 개 (개선 전: _____)
- 1초 이상: _____ 개 (개선 전: _____)
- 5초 이상: _____ 개 (개선 전: _____)

📈 비즈니스 영향

지표 개선 전 개선 후 변화
동시 사용자 _____ _____ _____
평균 응답 시간 _____ ms _____ ms _____ ms
실패율 _____ % _____ % _____ %
CPU 사용률 _____ % _____ % _____ %
메모리 사용률 _____ % _____ % _____ %

성공 여부 판단

성공 기준

  • 범위형 쿼리(q2·q4·q6)의 rows가 베이스라인 대비 대폭 감소 (목표: 조회 기간의 실제 기록 수 수준)
  • 느린 쿼리 대폭 감소
  • 결과 행 수 변화 없음, 최고기록 화면 정상
  • 서비스 가용성 100% 유지
  • 사용자 불만 없음

결론

[ ] 성공: 모든 기준 달성, 운영 정상화
[ ] 부분 성공: 대부분 달성, 모니터링 계속
[ ] 실패: 목표 미달, 추가 최적화 필요

🔧 추가 최적화 제안

단계별 추가 개선

1단계: 쿼리 최적화 (안 1)

  • 함수 제거 쿼리 적용 (03단계 3-4, Stage에서 수정 완료한 코드 배포)
  • 예상 효과: 04단계의 함수형↔범위형 비교값(q1↔q2, q3↔q4, q5↔q6) 참고

2단계: 집계 테이블 도입 (안 2)

  • daily_best_record, daily_typing_exam_record 생성
  • 예상 효과: 히스토리/랭킹 조회 극적 개선

3단계: 아카이빙 (안 3)

  • 13개월 이전 기록 아카이빙
  • 예상 효과: 테이블 크기 감소, 백업 시간 단축
  • 참고: 월 적재량이 전년 대비 약 2.1배로 늘고 있음 (01 사전 측정)

4단계: 애플리케이션 최적화 (안 5)

  • N+1 쿼리 제거
  • SQL Injection 보안 강화
  • 최고기록 저장을 INSERT ... ON DUPLICATE KEY UPDATE로 변경 (UNIQUE 키 활용, 동시 저장 시 더 좋은 기록 유지)
  • 예상 효과: 동시 사용자 처리량 2~3배 향상

📋 모니터링 계획

지속적 모니터링 (24시간 이후)

mysql.slow_log SELECT 권한이 필요하고, log_outputTABLE이 있어야 합니다. 조회가 끝나면 05단계 사후 작업대로 slow query log 설정을 원래 값으로 복원하세요.

# 일일 성능 리포트
mariadb -h chocomae.jinaju.com -u jisangs -p chocomae -e "
SELECT 
  DATE_FORMAT(start_time, '%Y-%m-%d %H:00:00') AS hour,
  COUNT(*) AS slow_query_count,
  AVG(query_time) AS avg_time,
  MAX(query_time) AS max_time
FROM mysql.slow_log
WHERE start_time >= CURDATE()
GROUP BY hour
ORDER BY hour;
"

📝 최종 보고서 항목

  1. 실행 요약

    • 작업 목표 및 결과
    • 성과 지표
  2. 기술 분석

    • EXPLAIN 개선 결과
    • 실행 시간 비교
    • 중복 정리 결과 (삭제 행 수, 백업 파일)
  3. 비즈니스 임팩트

    • 사용자 경험 개선
    • 서버 비용 절감 효과
  4. 추가 제안

    • 다음 단계 최적화 계획
    • 예상 추가 효과

🎯 다음 단계

  • 스테이징에서 안 2 (집계 테이블) 테스트
  • 운영 DB 추가 최적화 검토
  • 팀 회의에서 결과 공유
  • 성능 모니터링 대시보드 구축

운영 서버 성능 개선 프로젝트 완료! 🎉