Files
chocomae/doc/plan/db/260915-improve-production/01-premeasure.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

246 lines
9.0 KiB
Markdown

# 01단계: 운영 DB 현황 측정 (사전준비)
> 운영 서버에 인덱스를 추가하기 전 현재 상태를 정확히 파악합니다.
>
> **진행 상태 (2026-09-15):** 모든 항목 측정 완료. 결과 요약은 [result/premeasure_summary.txt](result/premeasure_summary.txt)에 있습니다.
모든 명령은 `result/` 디렉터리에서 실행합니다.
```bash
cd doc/plan/db/260915-improve-production/result
```
---
## 1-0. 계정 권한 확인
03단계 중복 삭제(DELETE), 02단계 slow query log 켜기(`SET GLOBAL`), 04단계 `mysql.slow_log` 조회에는 `SELECT, ALTER` 외의 권한이 필요합니다. 실제 권한을 먼저 확인하세요.
**아래 명령어를 터미널에 복사-붙여넣기해서 실행하세요:**
```bash
mariadb -h chocomae.jinaju.com -u jisangs -p chocomae -e "SHOW GRANTS;" > premeasure_grants.txt
cat premeasure_grants.txt
```
**확인할 점:**
- [00-overview.md의 단계별 필요 권한](00-overview.md#단계별-필요-권한) 표와 비교합니다.
- 권한이 없는 작업은 관리자 계정으로 실행할지, 작업 시간 동안만 권한을 받을지 정합니다.
**결과 (2026-09-15):** `GRANT SELECT, ALTER ON chocomae.*`만 있습니다. 중복 삭제(03-0-3), `SET GLOBAL`(02-1, 05), `mysql.slow_log` 조회(04-5, 07)는 관리자 계정으로 실행해야 합니다.
**임시 권한 부여 후 (2026-09-15, `premeasure_grants_after.txt`):** SUPER, 최고기록 테이블 2개 INSERT·DELETE, `mysql.slow_log` SELECT 추가. 05단계에서 회수합니다.
---
## 1-1. 테이블·인덱스 크기
**아래 명령어를 터미널에 복사-붙여넣기해서 실행하세요:**
```bash
mariadb -h chocomae.jinaju.com -u jisangs -p chocomae -e "
SELECT TABLE_NAME,
TABLE_ROWS AS approx_rows,
ROUND(DATA_LENGTH / 1024 / 1024, 1) AS data_mb,
ROUND(INDEX_LENGTH / 1024 / 1024, 1) AS index_mb
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'chocomae'
ORDER BY DATA_LENGTH DESC;" > premeasure_table_sizes.txt
cat premeasure_table_sizes.txt
```
**저장할 수치:**
- `best_record` data_mb, index_mb
- `typing_exam_record` data_mb, index_mb
> `TABLE_ROWS`는 InnoDB 추정치라 실제 행 수와 몇 % 차이가 날 수 있습니다.
---
## 1-2. 월별 적재량 (증가 속도)
**아래 명령어를 터미널에 복사-붙여넣기해서 실행하세요:**
```bash
mariadb -h chocomae.jinaju.com -u jisangs -p chocomae -e "
SELECT DATE_FORMAT(RecordDateTime, '%Y-%m') AS ym, COUNT(*) AS cnt
FROM best_record
GROUP BY ym
ORDER BY ym;" > premeasure_monthly_load.txt
cat premeasure_monthly_load.txt
```
**확인할 점:**
- 최근 3개월 증가 추이
- 월 평균 증가량
- 마지막 달은 측정일까지의 값이므로 평균에서 제외
---
## 1-3. 대표 쿼리 기준값 (EXPLAIN)
### 1-3-1. 기록이 많은 선생님·앱 찾기
**아래 명령어를 터미널에 복사-붙여넣기해서 실행하세요:**
```bash
mariadb -h chocomae.jinaju.com -u jisangs -p chocomae -e "
SELECT MaestroID, AppID, COUNT(*) AS cnt
FROM best_record
WHERE RecordDateTime >= NOW() - INTERVAL 30 DAY
GROUP BY MaestroID, AppID
ORDER BY cnt DESC
LIMIT 5;" > premeasure_top_maestro_app.txt
cat premeasure_top_maestro_app.txt
```
**결과 (2026-09-15):**
```
MaestroID AppID cnt
181 21 1089
181 1 591
389 103 562
...
```
→ 다음 단계에서 MaestroID=181, AppID=21을 사용합니다.
---
### 1-3-2. 일간 랭킹 쿼리 EXPLAIN
위에서 나온 MaestroID, AppID를 사용하세요:
**아래 명령어를 터미널에 복사-붙여넣기해서 실행하세요:**
```bash
mariadb -h chocomae.jinaju.com -u jisangs -p chocomae -N -r -e "
EXPLAIN FORMAT=JSON
SELECT BR.PlayerID, U.Name, MAX(BR.BestRecord) AS HighScore
FROM best_record BR, player U
WHERE BR.MaestroID = 181
AND BR.PlayerID = U.PlayerID
AND YEAR(BR.RecordDateTime) = YEAR(NOW())
AND MONTH(BR.RecordDateTime) = MONTH(NOW())
AND DAYOFMONTH(BR.RecordDateTime) = DAYOFMONTH(NOW())
AND BR.AppID = 21
GROUP BY BR.PlayerID
ORDER BY MAX(BR.BestRecord) DESC;" > premeasure_explain_daily_ranking.json
cat premeasure_explain_daily_ranking.json
```
> `-N`(열 이름 생략)과 `-r`(이스케이프 없이 출력)이 없으면 파일 첫 줄에 `EXPLAIN` 헤더가 붙고 줄바꿈이 `\n` 글자로 저장되어 올바른 JSON이 되지 않습니다.
**확인할 점:** MariaDB JSON에서는 `type`이 아니라 `access_type` 필드를 봅니다.
| 필드 | 의미 | 나쁜 값 | 운영 측정값 (2026-09-15) |
|---|---|---|---|
| `access_type` | 접근 방식 | `ALL` (전체 스캔) | `index_merge` |
| `key` 또는 `index_merge` | 사용한 인덱스 | 없음 | `MaestroID``AppID` |
| `rows` | 검사 예상 행 수 | 전체 행 수에 가까움 | 10,100 |
> 운영에는 `MaestroID`, `AppID` 단일 인덱스가 있어 전체 스캔은 아닙니다. 하지만 날짜 조건은 인덱스로 거르지 못해 10,100행을 하나씩 확인합니다. 복합 인덱스 `(MaestroID, AppID, RecordDateTime)`로 이 수를 해당 날짜의 실제 기록 수 수준으로 줄이는 것이 목표입니다.
---
## 1-4. Slow Query Log 현재 설정 저장
> 설정 변경(`SET GLOBAL`)은 SUPER 권한이 필요하고 운영에 영향을 주므로, 여기서는 **현재 값만 저장**합니다. 켜는 작업은 02단계 2-1에서, 원래 값 복원은 05단계에서 이 파일을 보고 합니다.
**아래 명령어를 터미널에 복사-붙여넣기해서 실행하세요:**
```bash
mariadb -h chocomae.jinaju.com -u jisangs -p chocomae -e "
SHOW GLOBAL VARIABLES WHERE Variable_name IN
('slow_query_log', 'long_query_time', 'log_output',
'log_queries_not_using_indexes', 'slow_query_log_file');" > premeasure_slow_log_settings.txt
cat premeasure_slow_log_settings.txt
```
> 이전 버전 문서의 1-4(`SET GLOBAL ...`)를 이미 성공적으로 실행했다면 저장된 값은 원래 값이 아닙니다. MariaDB 기본값은 `slow_query_log=OFF`, `long_query_time=10`, `log_output=FILE`입니다. 서버 설정 파일의 값을 확인해 05단계 복원에 사용하세요.
**결과 (2026-09-15):** `slow_query_log=ON`, `long_query_time=3`, `log_output=FILE`, `log_queries_not_using_indexes=OFF`, 로그 파일 `/var/log/mysql/mariadb-slow.log`
> 이전 버전 1-4의 `SET GLOBAL`은 적용되지 않았습니다 (SUPER 권한 없음. 적용됐다면 `long_query_time=1`, `log_output=TABLE`). 따라서 위 값이 05단계에서 복원할 원래 값입니다.
---
## 1-5. 최고기록 테이블 중복 점검
### 1-5-1. 중복 상위 10개 (완료)
**아래 명령어를 터미널에 복사-붙여넣기해서 실행하세요:**
```bash
mariadb -h chocomae.jinaju.com -u jisangs -p chocomae -e "
-- app_highest_record 중복 확인
SELECT MaestroID, PlayerID, AppID, COUNT(*) AS cnt
FROM app_highest_record
GROUP BY MaestroID, PlayerID, AppID
HAVING cnt > 1
ORDER BY cnt DESC
LIMIT 10;" > premeasure_app_highest_duplicates.txt
mariadb -h chocomae.jinaju.com -u jisangs -p chocomae -e "
-- typing_exam_highest_record 중복 확인
SELECT MaestroID, PlayerID, WritingID, COUNT(*) AS cnt
FROM typing_exam_highest_record
GROUP BY MaestroID, PlayerID, WritingID
HAVING cnt > 1
ORDER BY cnt DESC
LIMIT 10;" > premeasure_exam_highest_duplicates.txt
echo "=== app_highest_record 중복 ===" && cat premeasure_app_highest_duplicates.txt
echo "=== typing_exam_highest_record 중복 ===" && cat premeasure_exam_highest_duplicates.txt
```
> 결과가 없으면 `-e` 모드는 열 이름도 출력하지 않아 **빈 파일**이 됩니다. 에러는 파일이 아니라 터미널에 표시되므로, 에러 없이 빈 파일이면 중복 0건입니다.
**결과 (2026-09-15):** app_highest_record는 상위 10개 조합이 각 17~33행, typing_exam_highest_record는 0건.
### 1-5-2. 전체 중복 개수
`LIMIT 10`이라 위 결과만으로는 삭제할 행 수를 알 수 없습니다.
**아래 명령어를 터미널에 복사-붙여넣기해서 실행하세요:**
```bash
mariadb -h chocomae.jinaju.com -u jisangs -p chocomae -e "
SELECT COUNT(*) AS dup_groups, SUM(cnt - 1) AS rows_to_delete
FROM (SELECT COUNT(*) AS cnt FROM app_highest_record
GROUP BY MaestroID, PlayerID, AppID HAVING cnt > 1) t;" > premeasure_app_highest_dup_count.txt
cat premeasure_app_highest_dup_count.txt
```
**확인할 점:**
- `dup_groups`: 중복이 있는 조합 수
- `rows_to_delete`: 조합마다 1행만 남길 때 지워지는 행 수 → 03단계 3-0에서 실제 삭제 행 수와 비교
**결과 (2026-09-15):** `dup_groups` 4,823 / `rows_to_delete` 8,795 (app_highest_record 약 23만 행의 약 3.8%)
**중복이 있으면:**
- 03-add-indexes.md 3-0에서 UNIQUE 키 추가 전에 **점수 기준으로** 정리합니다.
---
## 1-6. 측정 결과 기록
모든 측정값을 [result/premeasure_summary.txt](result/premeasure_summary.txt)에 정리했습니다.
> 이전 버전의 `cat > premeasure_summary.txt << 'EOF'` 명령은 채워 둔 요약을 빈 양식으로 덮어쓰므로 삭제했습니다.
---
## 다음 단계
✅ 사전 측정 완료 → **02-baseline.md로 이동**
(사전 측정 수치는 모두 `result/`의 txt/json 파일에 저장되어 있습니다)