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

9.0 KiB

01단계: 운영 DB 현황 측정 (사전준비)

운영 서버에 인덱스를 추가하기 전 현재 상태를 정확히 파악합니다.

진행 상태 (2026-09-15): 모든 항목 측정 완료. 결과 요약은 result/premeasure_summary.txt에 있습니다.

모든 명령은 result/ 디렉터리에서 실행합니다.

cd doc/plan/db/260915-improve-production/result

1-0. 계정 권한 확인

03단계 중복 삭제(DELETE), 02단계 slow query log 켜기(SET GLOBAL), 04단계 mysql.slow_log 조회에는 SELECT, ALTER 외의 권한이 필요합니다. 실제 권한을 먼저 확인하세요.

아래 명령어를 터미널에 복사-붙여넣기해서 실행하세요:

mariadb -h chocomae.jinaju.com -u jisangs -p chocomae -e "SHOW GRANTS;" > premeasure_grants.txt

cat premeasure_grants.txt

확인할 점:

결과 (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. 테이블·인덱스 크기

아래 명령어를 터미널에 복사-붙여넣기해서 실행하세요:

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. 월별 적재량 (증가 속도)

아래 명령어를 터미널에 복사-붙여넣기해서 실행하세요:

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. 기록이 많은 선생님·앱 찾기

아래 명령어를 터미널에 복사-붙여넣기해서 실행하세요:

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를 사용하세요:

아래 명령어를 터미널에 복사-붙여넣기해서 실행하세요:

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 사용한 인덱스 없음 MaestroIDAppID
rows 검사 예상 행 수 전체 행 수에 가까움 10,100

운영에는 MaestroID, AppID 단일 인덱스가 있어 전체 스캔은 아닙니다. 하지만 날짜 조건은 인덱스로 거르지 못해 10,100행을 하나씩 확인합니다. 복합 인덱스 (MaestroID, AppID, RecordDateTime)로 이 수를 해당 날짜의 실제 기록 수 수준으로 줄이는 것이 목표입니다.


1-4. Slow Query Log 현재 설정 저장

설정 변경(SET GLOBAL)은 SUPER 권한이 필요하고 운영에 영향을 주므로, 여기서는 현재 값만 저장합니다. 켜는 작업은 02단계 2-1에서, 원래 값 복원은 05단계에서 이 파일을 보고 합니다.

아래 명령어를 터미널에 복사-붙여넣기해서 실행하세요:

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개 (완료)

아래 명령어를 터미널에 복사-붙여넣기해서 실행하세요:

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이라 위 결과만으로는 삭제할 행 수를 알 수 없습니다.

아래 명령어를 터미널에 복사-붙여넣기해서 실행하세요:

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에 정리했습니다.

이전 버전의 cat > premeasure_summary.txt << 'EOF' 명령은 채워 둔 요약을 빈 양식으로 덮어쓰므로 삭제했습니다.


다음 단계

사전 측정 완료 → 02-baseline.md로 이동

(사전 측정 수치는 모두 result/의 txt/json 파일에 저장되어 있습니다)