# 07단계: 사후 분석 및 최적화 > 운영 서버 적용 후 실제 성능을 분석하고 추가 최적화를 검토합니다. --- ## 📊 실제 성능 측정 결과 ### 운영 DB에서 측정한 개선율 `comparison.txt`(04단계)의 값을 옮겨 적었습니다 (2026-09-15). 6개 쿼리 전체 값은 [result/comparison.txt](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_output`에 `TABLE`이 있어야 합니다. 조회가 끝나면 05단계 사후 작업대로 slow query log 설정을 원래 값으로 복원하세요. ```bash # 일일 성능 리포트 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 추가 최적화 검토 - [ ] 팀 회의에서 결과 공유 - [ ] 성능 모니터링 대시보드 구축 --- **운영 서버 성능 개선 프로젝트 완료!** 🎉