- 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>
10 KiB
운영 서버 DB 성능 개선 (260915-improve-production)
스테이징에서 성공한 인덱스 추가를 운영 서버에 적용합니다.
📋 개요
| 항목 | 내용 |
|---|---|
| 목표 | 복합 인덱스 추가로 랭킹·기록 조회 쿼리의 검사 행 수와 실행 시간 감소 |
| 대상 DB | chocomae.jinaju.com (AWS EC2 Docker, MariaDB 10.11.13) |
| 작업 방식 | 온라인 DDL (ALGORITHM=INPLACE, LOCK=NONE) |
| 예상 시간 | 약 1시간 (백업·측정·검증 포함, 인덱스 생성은 여유 있게 30분) |
| 위험도 | 인덱스 추가: 낮음 (스테이징 검증 완료) / 중복 정리: 중간 (운영 데이터 삭제, 백업 후 진행) |
| 영향 범위 | 인덱스: 쿼리 응답 시간만 개선 (서비스 무중단) / 중복 정리: app_highest_record의 중복 행 삭제 |
📏 운영 사전 측정 결과 (01단계, 2026-09-15)
| 항목 | 값 | 비고 |
|---|---|---|
| best_record | 약 115만 행, data 72.5MB / index 72.1MB | 월별 합계 1,210,482행 |
| typing_exam_record | 약 13만 행, data 10.3MB / index 10.9MB | |
| 월 적재량 | 최근 3개월(6~8월) 평균 57,968행/월 | 전년 같은 기간(27,049행)의 약 2.1배 |
| 일간 랭킹 EXPLAIN | index_merge (MaestroID ∩ AppID), rows 10,100 |
전체 스캔(ALL)이 아님 |
| app_highest_record 중복 | 4,823조합, 삭제 대상 8,795행 (약 3.8%) | 03-0에서 점수 기준으로 정리 |
| typing_exam_highest_record 중복 | 0건 | |
| jisangs 계정 권한 | SELECT, ALTER + 임시 부여(SUPER, 최고기록 테이블 2개 INSERT·DELETE, mysql.slow_log SELECT) |
05단계에서 회수 |
| Slow query log | ON, long_query_time=3, log_output=FILE | 05단계 복원 기준값 |
운영 DB에는
MaestroID,AppID,PlayerID단일 인덱스가 이미 있어 스테이징(type=ALL)과 기준값이 다릅니다. 개선율은 스테이징 수치가 아니라 운영에서 측정한 02(베이스라인)와 04(검증) 결과로 계산합니다.
상세: result/premeasure_summary.txt
🎯 기대 효과
| 항목 | 스테이징 결과 | 운영 예상 |
|---|---|---|
| EXPLAIN cost | 8.63 → 0.004 (2,158배 ⬇️) | MariaDB 10.11 EXPLAIN JSON에는 cost가 없어 rows·실행 시간으로 비교 |
| EXPLAIN rows | 5,804 → 1 (5,800배 ⬇️) | 10,100 → 해당 시간·날짜의 실제 기록 수 수준 (범위 조건 쿼리) |
| 접근 방식 | ALL → range | index_merge → range |
| 실행 시간 | ~2초 (충분) | 02·04에서 측정 |
| 느린 쿼리 제거 | 0.5초 이상 → 없음 | 04-5에서 확인 |
📊 스테이징 결과 요약
✅ 완료한 작업
- 베이스라인 측정: 전체 스캔 (type=ALL)
- 중복 데이터 정리: app_highest_record, typing_exam_highest_record
- 인덱스 5개 + UNIQUE 키 2개 추가 (총 7개)
- 쿼리 조건 수정: 7개 파일, 8+ 함수 (DATE()/HOUR() → 범위 조건)
- 개선 효과 검증: 2,000배 이상 향상 확인 (인덱스 + 쿼리 수정)
- Slow query log: 개선 쿼리 0.5초 이하
📈 성능 개선 상세 (Stage 검증)
- 인덱스만 적용: cost 8.63 → 1.0 (8배), rows 5,804 → 200 (29배)
- 인덱스 + 쿼리 수정: cost 8.63 → 0.004 (2,158배), rows 5,804 → 1 (5,800배)
📁 저장된 파일
260915-improve-stage/result/
├── baseline_query1_explain.json
├── baseline_query2_explain.json
├── baseline_results.txt
├── after_query1_explain.json
├── after_query2_improved_explain.json
├── verify_results.txt
└── add_indexes.sql
🔐 운영 DB 접속 정보
# 운영 DB (AWS EC2 Docker)
Host: chocomae.jinaju.com
Port: 3306
Database: chocomae
User: jisangs (임시 계정 — 2026-09-15 작업 후 삭제)
Password: 문서에 적지 않음 (별도로 전달받은 값 사용)
출발지 IP: 182.217.174.221
# 부여된 권한 (2026-09-15 SHOW GRANTS로 확인)
GRANT SELECT, ALTER ON chocomae.* TO jisangs@'182.217.174.221'
# 작업 기간 임시 권한 (2026-09-15 부여 → 같은 날 회수 완료, result/grants_after_revoke.txt)
GRANT SUPER ON *.* TO jisangs@'182.217.174.221'
GRANT INSERT, DELETE ON chocomae.app_highest_record TO jisangs@'182.217.174.221'
GRANT INSERT, DELETE ON chocomae.typing_exam_highest_record TO jisangs@'182.217.174.221'
GRANT SELECT ON mysql.slow_log TO jisangs@'182.217.174.221'
✅ 임시 계정
jisangs는 작업 후 삭제했습니다 (2026-09-15).⚠️ 같은 비밀번호가 예전 커밋 기록과 메일(Gmail SMTP)·Python 배치(DB root) 소스 코드에도 평문으로 남아 있습니다. 계정 삭제와 별개로 그 비밀번호들은 바꿔야 합니다.
단계별 필요 권한
| 단계 | 작업 | 필요 권한 | jisangs 계정으로 가능? |
|---|---|---|---|
| 01, 02, 04 | 조회, EXPLAIN | SELECT | ✅ |
| 03-0 | 삭제 대상 행 백업 (mariadb-dump) |
SELECT | ✅ |
| 03-0 | 중복 행 삭제 | app_highest_record DELETE |
✅ 임시 부여 |
| 03-2 | 인덱스·UNIQUE 키 추가 | ALTER | ✅ |
| 02-1, 05 | Slow query log 켜기·복원 (SET GLOBAL) |
SUPER | ✅ 임시 부여 |
| 04-5, 07 | mysql.slow_log 조회 |
mysql.slow_log SELECT |
✅ 임시 부여 |
| 06 | 삭제 행 복원 | 최고기록 테이블 INSERT | ✅ 임시 부여 |
| 06 | ANALYZE TABLE best_record, 복제 상태 확인 |
best_record INSERT, REPLICA MONITOR 등 |
❌ 관리자 계정 |
2026-09-15에 관리자 계정으로 아래 권한을 임시 부여했습니다 (확인: result/premeasure_grants_after.txt). ❌ 작업은 문제가 생겼을 때만 필요하며 관리자 계정으로 실행합니다. 임시 권한은 05단계 사후 작업에서 회수합니다.
-- 관리자 계정으로 실행함 (2026-09-15)
GRANT DELETE, INSERT ON chocomae.app_highest_record TO 'jisangs'@'182.217.174.221';
GRANT DELETE, INSERT ON chocomae.typing_exam_highest_record TO 'jisangs'@'182.217.174.221';
GRANT SELECT ON mysql.slow_log TO 'jisangs'@'182.217.174.221';
GRANT SUPER ON *.* TO 'jisangs'@'182.217.174.221';
SUPER는 서버 전체 권한(다른 연결의 쿼리 중단, 모든 전역 설정 변경, read_only 우회)이므로 작업이 끝나면 바로 회수하세요.
⚠️ 주의사항
-
운영 DB 백업 필수
- EC2 EBS 스냅샷 생성 (MariaDB 데이터 볼륨 포함)
- 또는 서버에서
mariadb-dump로 전체 DB 백업
-
온라인 DDL 사용
- ALGORITHM=INPLACE (테이블 잠금 최소화)
- LOCK=NONE (동시 읽기/쓰기 가능)
-
중복 정리는 데이터 삭제
- 03-0에서 삭제 대상 행을 먼저 파일로 백업한 뒤 삭제
- 인덱스와 달리 DROP INDEX로 되돌릴 수 없음 → 06-rollback.md의 "중복 정리 되돌리기" 참고
-
모니터링 필수
- 인덱스 추가 중 CPU/메모리 모니터링
- SHOW PROCESSLIST로 진행 상황 확인
-
Slow query log 부하 주의
log_output에TABLE이 있으면mysql.slow_log에 계속 쌓이고 자동으로 비워지지 않음log_queries_not_using_indexes는 켜지 않음 (인덱스를 안 쓰는 모든 쿼리가 기록되어 로그가 급증)- 작업 후 01단계 1-4에서 저장한 원래 값으로 복원
-
롤백 계획
- DROP INDEX 명령어 준비 (06-rollback.md)
- 문제 발생 시 즉시 실행 가능
📅 작업 일정
01단계 사전 측정은 2026-09-15에 완료했습니다.
권장: 새벽 2시~3시 (트래픽 최소 시간)
| 시간 | 작업 | 예상 소요 |
|---|---|---|
| 02:00 | DB 백업 (EBS 스냅샷 또는 전체 덤프) | 5분 |
| 02:05 | 성능 기준선 측정 (02-baseline.md) | 10분 |
| 02:15 | 중복 정리 + 인덱스 추가 (03-add-indexes.md) | 30분 |
| 02:45 | 인덱스 생성 확인 | 5분 |
| 02:50 | 성능 개선 검증 (04-verify.md) | 10분 |
| 03:00 | 최종 체크리스트, slow log 복원, 임시 권한 회수 (05-checklist.md) | 10분 |
📂 디렉토리 구조
260915-improve-production/
├── 00-overview.md ← 이 파일
├── 01-premeasure.md ← 운영 DB 현황 측정
├── 02-baseline.md ← 성능 기준선 측정
├── 03-add-indexes.md ← 중복 정리 + 인덱스 추가
├── 04-verify.md ← 개선 효과 검증
├── 05-checklist.md ← 최종 체크리스트
├── 06-rollback.md ← 롤백 계획
├── 07-post-analysis.md ← 사후 분석
└── result/ ← 결과 파일 저장
├── premeasure_*.txt, premeasure_*.json ← 01 사전 측정
├── premeasure_summary.txt ← 01 요약
├── params.sql, queries/*.sql ← 02·04 공통 측정 쿼리
├── run_measure.sh, summarize_explain.py ← 02·04 공통 측정 도구
├── baseline_explain_*.json, baseline_perf.txt ← 02
├── backup_app_highest_dup_rows.sql ← 03-0 삭제 행 백업
├── add_indexes.sql, add_indexes_result.txt ← 03
├── after_explain_*.json, after_perf.txt ← 04
└── comparison.txt ← 04 비교
🚀 다음 단계
01-premeasure.md — 모든 측정 완료 (2026-09-15).
필요한 권한은 jisangs 계정에 임시 부여했습니다 (2026-09-15). 02-baseline.md로 이동합니다.
📞 문제 발생 시
| 상황 | 대응 |
|---|---|
인덱스 추가 실패 (Duplicate entry) |
3-0 다시 실행 후 add_indexes.sql 재실행 (이미 만든 인덱스는 건너뜀) |
| 인덱스 추가 실패 (기타) | 에러 확인 후 필요하면 DROP INDEX (06-rollback.md) |
| 서비스 영향 | KILL QUERY + 인덱스 삭제 (06-rollback.md) |
| 최고기록 값 이상 | 삭제 행 백업으로 되돌리기 (06-rollback.md) |
| 성능 악화 | 인덱스 삭제 → 그래도 안 되면 백업에서 복원 |
| 기타 문제 | 07-post-analysis.md 참고 |
01단계 완료, 작업 권한 준비 완료. 02단계부터 진행합니다.