# 운영 서버 부하·용량 확인 방법 (260916) > 서비스 중인 초코마에 운영 서버(AWS EC2 + MariaDB)에서 아래 3가지를 확인하기 위한 **방법 모음**입니다. > 각 항목마다 여러 방법을 나열했으니, 권한·설치 가능 여부·측정 기간에 맞춰 골라서 쓰세요. > > | 확인 항목 | 문서 위치 | > |---|---| > | ① EC2 인스턴스에서 MariaDB에 걸리는 성능 부하 | [A. MariaDB 성능 부하 측정](#a-mariadb-성능-부하-측정) | > | ② 접속 서비스(chocomae / chocoadmin)별 pool 할당·부하 | [B. 접속 서비스별 연결 pool·부하 측정](#b-접속-서비스별-연결-pool부하-측정) | > | ③ `best_record` 등 기록 테이블에 데이터가 몇 개까지 들어가는지 | [C. 테이블 최대 저장 가능 행 수](#c-테이블-최대-저장-가능-행-수) | > > ⚠️ 이 문서는 **측정 방법만** 다룹니다. 실제 값은 `result/`에 저장하고, 결론은 [90-결과-기록-양식](#90-결과-기록-양식)에 채웁니다. --- ## 0. 사전 준비 ### 0-1. 용어 정리 - 질문의 **`best_score` 테이블**은 실제 스키마에 없습니다. 점수/기록을 담는 테이블은 아래 4개입니다. 이 문서에서는 이 4개를 "기록 테이블"로 부릅니다. | 테이블 | 내용 | PK | |---|---|---| | `best_record` | 게임 플레이 기록 (가장 큼, 약 121만 행) | `BestRecordID INT UNSIGNED AUTO_INCREMENT` | | `app_highest_record` | 앱별 최고 기록 | `AppHighestRecordID INT UNSIGNED AUTO_INCREMENT` | | `typing_exam_record` | 타자 시험 기록 (약 13만 행) | `TypingExamRecordID INT UNSIGNED AUTO_INCREMENT` | | `typing_exam_highest_record` | 타자 시험 최고 기록 | `TypingExamHighestRecordID INT UNSIGNED AUTO_INCREMENT` | ### 0-2. 접속 경로 3가지 측정 방법마다 필요한 접속 경로가 다릅니다. | 경로 | 접속 방법 | 할 수 있는 것 | |---|---|---| | **(가) EC2 SSH** | `ssh ubuntu@13.124.33.235` (chocomae.jisangs.com) | OS 지표(CPU/메모리/디스크 I/O), 설정 파일, 로그 파일, 로컬 mariadb 접속 | | **(나) 원격 DB 클라이언트** | `mariadb -h chocomae.jinaju.com -P 3306 -u <계정> -p chocomae` | SQL로 가능한 모든 측정 (상태 변수, PROCESSLIST, EXPLAIN) | | **(다) 시놀로지 SSH** | `ssh happyhome@jisangs.synology.me` | chocoadmin 컨테이너 설정·로그, 시놀로지→운영 DB 연결 확인 | > ⚠️ 2026-09-15 작업에 쓰던 임시 계정 `jisangs`는 작업 후 **삭제**했습니다. 이번 측정에도 조회 전용 계정이 필요하니, 아래 중 하나를 먼저 정하세요. > > 1. 관리자(root) 계정으로 직접 측정 (가장 간단, 권한 문제 없음) > 2. 측정 전용 임시 계정을 다시 만들고 끝나면 삭제 > ```sql > -- 관리자 계정으로 실행 > CREATE USER 'perfcheck'@'182.217.174.221' IDENTIFIED BY '<임시 비밀번호>'; > GRANT SELECT, PROCESS, REPLICATION CLIENT ON *.* TO 'perfcheck'@'182.217.174.221'; > GRANT SELECT ON mysql.slow_log TO 'perfcheck'@'182.217.174.221'; > -- 측정 종료 후 > -- DROP USER 'perfcheck'@'182.217.174.221'; > ``` > 3. EC2에 SSH로 들어가 `localhost` 소켓으로 접속 (원격 계정을 새로 만들지 않아도 됨) ### 0-3. 측정에 필요한 권한 | 측정 내용 | 필요 권한 | |---|---| | `SHOW GLOBAL STATUS`, `SHOW VARIABLES`, `information_schema.TABLES` | 기본 (일반 계정 가능) | | `SHOW FULL PROCESSLIST` (다른 계정 세션까지 보기), `SHOW ENGINE INNODB STATUS` | `PROCESS` | | `mysql.slow_log` 조회 | `mysql.slow_log` SELECT | | `SET GLOBAL ...` (slow log 켜기, `userstat` 켜기) | `SUPER` | | OS 지표, 설정 파일, 로그 파일 | EC2 SSH (`ubuntu` 계정, 일부는 `sudo`) | ### 0-4. 결과 저장 위치 ```bash cd doc/plan/db/260916-load-and-capacity-check/result ``` > 개인정보·비밀번호 해시가 섞일 수 있는 파일(`*grants*`, 덤프)은 커밋하지 마세요. 2026-09-15 작업의 관례를 따릅니다. ### 0-5. MariaDB가 도커인지 호스트 설치인지 먼저 확인 문서마다 명령어가 달라지므로 **가장 먼저** 확인합니다. (EC2 SSH) ```bash sudo docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Ports}}' systemctl status mariadb --no-pager | head -5 ls -l /var/lib/mysql | head -5 ``` - 도커면: 이후 모든 `mariadb ...` 명령 앞에 `sudo docker exec -i <컨테이너명> ` 을 붙입니다. - 호스트 설치면: 명령을 그대로 씁니다. (README 기준 설정은 `/etc/mysql`, 로그는 `/var/log/mysql`) --- ## A. MariaDB 성능 부하 측정 > "지금 이 EC2에서 MariaDB가 얼마나 힘들어하는가"를 보는 방법들입니다. > **OS 레벨(A-1)** 과 **DB 레벨(A-2~A-5)** 을 같이 봐야 원인을 가릴 수 있습니다. ### A-0. 방법 비교 | 방법 | 보는 것 | 설치 필요 | 측정 부하 | 지속 관찰 | 추천도 | |---|---|---|---|---|---| | A-1 OS 지표 | CPU·메모리·디스크 I/O·스왑 | 없음(일부 `sysstat`) | 매우 낮음 | 스냅샷~단기 | ★★★ | | A-2 상태 변수 델타 | QPS, 버퍼 풀 적중률, 임시 테이블, 잠금 | 없음 | 매우 낮음 | 단기 | ★★★ | | A-3 PROCESSLIST·INNODB STATUS | 지금 실행 중인 쿼리, 대기·잠금 | 없음 | 매우 낮음 | 스냅샷 | ★★★ | | A-4 slow query log | 느린 쿼리 목록·빈도 | 없음 | 낮음(설정 주의) | 장기 | ★★★ | | A-5 userstat / performance_schema | 쿼리 다이제스트별·계정별 누적 부하 | 없음(설정 변경) | 낮음~중간 | 장기 | ★★☆ | | A-6 모니터링 도구 | 시계열 그래프, 알림 | 있음 | 낮음 | 상시 | ★★☆ | **추천 조합:** 먼저 A-1 + A-2 + A-3으로 현재 상태를 잡고, A-4를 24시간 돌려 실제 서비스 쿼리를 확인합니다. 상시 감시가 필요해지면 A-6. --- ### 방법 A-1. OS 레벨 지표 (EC2 SSH) **A-1-1. 한 번에 훑기** ```bash # 인스턴스 사양·부하 평균 nproc; free -h; uptime # 프로세스별 CPU·메모리 상위 10개 (mysqld가 몇 위인지) ps -eo pid,comm,pcpu,pmem,rss --sort=-pcpu | head -11 ``` **A-1-2. 실시간 관찰** ```bash top -b -n 3 -d 5 | grep -E 'Cpu|Mem|mysqld' > os_top.txt cat os_top.txt ``` **A-1-3. CPU·디스크 I/O 시계열 (sysstat 필요)** ```bash sudo apt-get install -y sysstat # 이미 있으면 생략 # 5초 간격 12회 = 1분치 CPU·메모리·스왑 vmstat 5 12 > os_vmstat.txt # 5초 간격 12회 디스크 I/O (await, %util 확인) iostat -dx 5 12 > os_iostat.txt # mysqld 프로세스만 따로 pidstat -p $(pgrep -o mysqld) 5 12 > os_pidstat_mysqld.txt ``` **판단 기준** | 지표 | 정상 | 주의 | |---|---|---| | `vmstat` `r`(실행 대기) | vCPU 수 이하 | vCPU 수보다 계속 큼 → CPU 포화 | | `vmstat` `si/so`(스왑) | 0 | 0보다 큼 → 메모리 부족, DB 성능 급락 | | `iostat` `%util` | 50% 이하 | 90% 이상 지속 → 디스크 I/O 병목 | | `iostat` `await` | 10ms 이하(EBS gp3) | 수십 ms 이상 → I/O 대기 | | `free -h` `available` | 여유 있음 | 0에 가까움 → 버퍼 풀 축소 검토 | **A-1-4. 도커로 돌고 있다면** ```bash sudo docker stats --no-stream ``` **A-1-5. AWS CloudWatch (SSH 없이 확인)** EC2 콘솔 → 대상 인스턴스 → **모니터링** 탭에서 `CPUUtilization`, `EBSReadOps`/`EBSWriteOps`, `NetworkIn/Out`을 기간별로 봅니다. 기본 지표는 5분 간격이며, 메모리·디스크 사용률은 CloudWatch Agent를 설치해야 나옵니다. **과거 추이**를 보는 데 가장 편합니다. --- ### 방법 A-2. MariaDB 상태 변수 델타 측정 절대 누적값은 서버 기동 이후 합계라 의미가 약합니다. **일정 시간 동안의 증가량(델타)** 을 봐야 합니다. **A-2-1. 가장 간단한 방법 — `mariadb-admin`의 상대값 출력** ```bash # 10초 간격 6회, 이전 값 대비 증가량만 출력 mariadb-admin -h chocomae.jinaju.com -u <계정> -p extended-status -i 10 -c 6 -r \ | grep -vE '\|\s+0\s+\|' > db_status_delta.txt cat db_status_delta.txt ``` **A-2-2. 요약 한 줄 보기** ```bash # 5초 간격으로 Queries/초, Threads, Opens, Slow queries 표시 (Ctrl+C로 종료) mariadb-admin -h chocomae.jinaju.com -u <계정> -p status -i 5 ``` **A-2-3. 직접 SQL로 60초 델타 뽑기** ```bash mariadb -h chocomae.jinaju.com -u <계정> -p chocomae -N -B -e " SHOW GLOBAL STATUS;" > st1.txt sleep 60 mariadb -h chocomae.jinaju.com -u <계정> -p chocomae -N -B -e " SHOW GLOBAL STATUS;" > st2.txt join st1.txt st2.txt | awk '{d=$3-$2; if (d>0) printf "%-40s %12d\n", $1, d}' \ | sort -k2 -nr | head -40 > db_status_delta_60s.txt cat db_status_delta_60s.txt ``` **A-2-4. 꼭 봐야 할 값** | 상태 변수 | 의미 | 해석 | |---|---|---| | `Questions`, `Com_select`, `Com_insert` | 처리한 쿼리 수 | 60초 델타 ÷ 60 = QPS | | `Threads_connected` / `Threads_running` | 연결 수 / 실제 실행 중 | `Threads_running`이 지속적으로 vCPU 수 이상이면 과부하 | | `Innodb_buffer_pool_read_requests` vs `Innodb_buffer_pool_reads` | 논리 읽기 vs 디스크 읽기 | 적중률 = (1 - reads/read_requests) × 100, **99% 미만이면 버퍼 풀 부족 의심** | | `Created_tmp_disk_tables` | 디스크 임시 테이블 | 증가하면 `GROUP BY`/`ORDER BY`가 디스크로 넘어감 | | `Select_scan`, `Select_full_join` | 풀 스캔 쿼리 수 | 증가하면 인덱스 미사용 쿼리 존재 | | `Slow_queries` | 느린 쿼리 수 | A-4와 함께 확인 | | `Table_locks_waited`, `Innodb_row_lock_waits`, `Innodb_row_lock_time_avg` | 잠금 대기 | 0에 가까워야 정상 | | `Aborted_connects`, `Connection_errors_max_connections` | 연결 실패 | 0보다 크면 B 항목에서 원인 추적 | **A-2-5. 버퍼 풀 적중률·크기 한 번에 계산** ```bash mariadb -h chocomae.jinaju.com -u <계정> -p -e " SELECT (SELECT VARIABLE_VALUE FROM information_schema.GLOBAL_VARIABLES WHERE VARIABLE_NAME='innodb_buffer_pool_size')/1024/1024 AS buffer_pool_mb, (SELECT ROUND(SUM(DATA_LENGTH+INDEX_LENGTH)/1024/1024,1) FROM information_schema.TABLES WHERE TABLE_SCHEMA='chocomae') AS chocomae_total_mb, ROUND(100 - ( (SELECT VARIABLE_VALUE FROM information_schema.GLOBAL_STATUS WHERE VARIABLE_NAME='Innodb_buffer_pool_reads') / (SELECT VARIABLE_VALUE FROM information_schema.GLOBAL_STATUS WHERE VARIABLE_NAME='Innodb_buffer_pool_read_requests') ) * 100, 3) AS hit_rate_pct;" > db_buffer_pool.txt cat db_buffer_pool.txt ``` > `chocomae_total_mb`가 `buffer_pool_mb`보다 작으면 데이터 전체가 메모리에 올라가므로 디스크 I/O 부하는 거의 없습니다. 이 값은 C-3(실용적 상한 계산)에서 다시 씁니다. --- ### 방법 A-3. 실행 중인 쿼리·잠금 관찰 **A-3-1. 지금 무엇이 돌고 있는가** ```bash mariadb -h chocomae.jinaju.com -u <계정> -p -e " SELECT ID, USER, HOST, DB, COMMAND, TIME, STATE, LEFT(INFO,120) AS query FROM information_schema.PROCESSLIST WHERE COMMAND <> 'Sleep' ORDER BY TIME DESC;" ``` **A-3-2. 1초 간격으로 반복 관찰 (부하가 걸리는 시간대에)** ```bash for i in $(seq 1 60); do date +%FT%T mariadb -h chocomae.jinaju.com -u <계정> -p<비밀번호> -N -B -e " SELECT COUNT(*) AS running FROM information_schema.PROCESSLIST WHERE COMMAND<>'Sleep';" sleep 1 done > db_running_1min.txt ``` > 비밀번호를 명령줄에 쓰면 셸 히스토리에 남습니다. `~/.my.cnf`(`[client]` 섹션)에 적어두고 `-p`를 빼는 편이 안전합니다. **A-3-3. InnoDB 내부 상태 (잠금·대기·I/O)** ```bash mariadb -h chocomae.jinaju.com -u <계정> -p -e "SHOW ENGINE INNODB STATUS\G" > db_innodb_status.txt grep -A12 'TRANSACTIONS' db_innodb_status.txt grep -A10 'BUFFER POOL AND MEMORY' db_innodb_status.txt grep -A8 'LATEST DETECTED DEADLOCK' db_innodb_status.txt ``` **A-3-4. 대기 중인 잠금만 콕 집어 보기** ```sql -- MariaDB 10.5+ (information_schema.INNODB_TRX + metadata lock 정보) SELECT trx_id, trx_state, trx_started, trx_wait_started, trx_mysql_thread_id, LEFT(trx_query,100) FROM information_schema.INNODB_TRX ORDER BY trx_started; SELECT * FROM information_schema.METADATA_LOCK_INFO; -- 플러그인 설치 시 ``` --- ### 방법 A-4. Slow query log 분석 2026-09-15 작업 때 이미 켰던 경로라 재사용이 쉽습니다. **A-4-1. 현재 설정 확인** ```bash mariadb -h chocomae.jinaju.com -u <계정> -p -e " SHOW GLOBAL VARIABLES WHERE Variable_name IN ('slow_query_log','slow_query_log_file','long_query_time','log_output', 'log_queries_not_using_indexes','log_slow_admin_statements','min_examined_row_limit');" > slow_log_settings.txt cat slow_log_settings.txt ``` **A-4-2. 측정 기간에만 임계값 낮추기 (`SUPER` 필요)** ```sql -- 원래 값을 먼저 기록해 두고 실행 SET GLOBAL long_query_time = 0.5; -- 0.5초 이상 쿼리를 기록 SET GLOBAL slow_query_log = ON; -- 측정 종료 후 원래 값으로 복원 (예: 3초) -- SET GLOBAL long_query_time = 3; ``` > ⚠️ `log_queries_not_using_indexes`는 켜지 마세요. 인덱스를 안 쓰는 모든 쿼리가 기록돼 로그가 폭증합니다. `log_output`에 `TABLE`이 포함돼 있으면 `mysql.slow_log`가 계속 커지고 자동으로 비워지지 않습니다. **A-4-3. 파일 로그 분석 (EC2 SSH)** ```bash # 요약: 실행 시간 합계가 큰 순서로 정렬 sudo mariadb-dumpslow -s t -t 20 /var/log/mysql/mariadb-slow.log > slow_top20.txt cat slow_top20.txt # 더 정밀한 분석이 필요하면 (percona-toolkit 설치 필요) sudo apt-get install -y percona-toolkit sudo pt-query-digest /var/log/mysql/mariadb-slow.log > slow_digest.txt ``` **A-4-4. 테이블 로그 분석 (`log_output`에 `TABLE`이 있을 때)** ```sql -- 일자별 건수 SELECT DATE(start_time) AS d, COUNT(*) AS cnt, ROUND(MAX(query_time_sec),1) AS max_sec FROM (SELECT start_time, TIME_TO_SEC(query_time) AS query_time_sec FROM mysql.slow_log) t GROUP BY d ORDER BY d; -- 계정·호스트별 느린 쿼리 (B 항목에서 서비스 구분에 활용) SELECT user_host, COUNT(*) AS cnt, ROUND(AVG(TIME_TO_SEC(query_time)),2) AS avg_sec FROM mysql.slow_log GROUP BY user_host ORDER BY cnt DESC; ``` > 2026-09-15 측정에서는 slow log에 **애플리케이션 쿼리가 한 건도 없었고**, 매일 04:00 백업 덤프의 `best_record` 전체 SELECT(약 5초)만 기록됐습니다. 이번에도 같은 항목이 계속 잡히니 성능 저하로 오해하지 마세요. --- ### 방법 A-5. 누적 통계 (userstat / performance_schema) **A-5-1. MariaDB `userstat` (가볍고 설치 불필요, `SUPER` 필요)** ```sql SET GLOBAL userstat = 1; -- 켠 시점부터 누적 (재시작하면 꺼짐) -- 잠시 서비스 트래픽을 받은 뒤 SELECT * FROM information_schema.CLIENT_STATISTICS\G -- 접속 호스트별 SELECT * FROM information_schema.USER_STATISTICS\G -- 계정별 SELECT * FROM information_schema.TABLE_STATISTICS WHERE TABLE_SCHEMA='chocomae' ORDER BY ROWS_READ DESC; SELECT * FROM information_schema.INDEX_STATISTICS WHERE TABLE_SCHEMA='chocomae' ORDER BY ROWS_READ DESC; FLUSH CLIENT_STATISTICS; -- 구간 측정을 하려면 초기화 후 다시 수집 ``` - **B 항목(서비스별 부하)에 가장 직접적인 방법**입니다. `CLIENT_STATISTICS`는 접속 호스트별 `CONCURRENT_CONNECTIONS`, `CONNECTED_TIME`, `BUSY_TIME`, `ROWS_READ`, `SELECT_COMMANDS` 등을 보여줍니다. - `INDEX_STATISTICS`에 안 나오는 인덱스는 **한 번도 안 쓰인 인덱스**입니다. 불필요한 인덱스 정리 판단에 쓰세요. - 영구 적용하려면 설정 파일에 `userstat=1`을 넣고 재시작해야 합니다. **A-5-2. performance_schema 다이제스트 (기본 OFF)** ```sql SHOW VARIABLES LIKE 'performance_schema'; -- OFF면 아래는 못 씀 ``` OFF인 경우 `/etc/mysql/my.cnf`(또는 `conf.d/*.cnf`)에 `performance_schema=ON`을 넣고 **재시작**해야 합니다. 서비스 중단이 생기므로 급하지 않으면 A-4/A-5-1로 대체하세요. ```sql -- 총 실행 시간이 큰 쿼리 유형 순 SELECT SCHEMA_NAME, DIGEST_TEXT, COUNT_STAR, ROUND(SUM_TIMER_WAIT/1e12,2) AS total_sec, ROUND(AVG_TIMER_WAIT/1e9,2) AS avg_ms, SUM_ROWS_EXAMINED, SUM_ROWS_SENT FROM performance_schema.events_statements_summary_by_digest WHERE SCHEMA_NAME='chocomae' ORDER BY SUM_TIMER_WAIT DESC LIMIT 20; -- 계정별 부하 (B 항목에도 사용) SELECT * FROM performance_schema.events_statements_summary_by_account_by_event_name WHERE EVENT_NAME='statement/sql/select' ORDER BY SUM_TIMER_WAIT DESC LIMIT 10; ``` --- ### 방법 A-6. 상시 모니터링 도구 | 도구 | 설치 위치 | 특징 | |---|---|---| | `mytop` / `innotop` | EC2 (`apt-get install mytop percona-toolkit`) | 터미널 실시간 대시보드. QPS·슬로우·연결·실행 쿼리를 한 화면에서 봄. 가장 가볍게 시작 가능 | | Netdata | EC2 (에이전트 1개) | 설치 5분, MySQL 플러그인 자동 인식, 웹 UI로 시계열 확인 | | Prometheus + `mysqld_exporter` + Grafana | 수집기는 시놀로지, exporter는 EC2 | 장기 보관·대시보드·알림. 이미 시놀로지 도커가 있으니 구성 부담이 적음 | | Percona PMM | 도커 1개 + 에이전트 | MySQL 전용 대시보드·쿼리 분석기(QAN)가 강력. 리소스는 좀 더 씀 | | AWS CloudWatch (+Agent) | EC2 | 인스턴스 지표·알림. DB 내부 지표는 안 보임 | **mytop 예시** ```bash sudo apt-get install -y mytop mytop -h 127.0.0.1 -u root -p --dbname chocomae ``` > **선택 가이드:** 이번처럼 "한 번 확인"이면 A-1~A-5로 충분합니다. "앞으로 계속 보겠다"면 `mysqld_exporter` + 시놀로지 Grafana 조합이 운영 부담 대비 효과가 가장 좋습니다. --- ## B. 접속 서비스별 연결 pool·부하 측정 ### B-0. 먼저 알아야 할 구조 | 서비스 | 위치 | 접속 방식 | pool 존재 여부 | |---|---|---|---| | **chocomae** (PHP) | 같은 EC2 | `new mysqli($db_host, ...)` — [connect_db.php:17](../../../../src/web/server/setup/connect_db.php#L17), `localhost`(또는 도커 호스트명) | **pool 없음.** PHP 요청마다 새 연결을 맺고 요청이 끝나면 끊습니다. `p:` 접두사(persistent)를 쓰지 않으므로, 동시 연결 수의 상한은 사실상 **Apache의 동시 프로세스 수(MaxRequestWorkers)** 입니다 | | **chocoadmin** (Next.js/Node) | 시놀로지 도커 | 인터넷 경유로 `chocomae.jinaju.com:3306` | Node의 MySQL 드라이버(`mysql2` 등)를 쓰면 **커넥션 풀 존재**. `connectionLimit` 기본값은 보통 10. 컨테이너/프로세스 수만큼 배수가 됨 | | **배치/백업** | EC2 cron, 시놀로지 cron | 단발 연결 | 매일 04:00 풀백업이 대표적 | **따라서 확인할 것은 3가지입니다.** 1. DB가 허용하는 상한 (`max_connections`)과 실제 최대 사용치 (`Max_used_connections`) 2. 출처별 현재·최대 연결 수 (EC2 로컬 vs 시놀로지 공인 IP) 3. 각 애플리케이션 쪽 설정값 (Apache 워커 수, Node pool `connectionLimit`) ### B-0-1. 방법 비교 | 방법 | 보는 것 | 서비스 구분 가능? | 필요 권한 | |---|---|---|---| | B-1 연결 상한·사용치 | 전체 연결 수, 한계 근접 여부 | ✗ | 기본 | | B-2 PROCESSLIST 집계 | 출처 IP·계정별 현재 연결 수 | ○ (IP로) | `PROCESS` | | B-3 userstat | 출처별 누적 연결 시간·쿼리 수·읽은 행 수 | ◎ (가장 정확) | `SUPER` + 조회 | | B-4 계정 분리 | 서비스별 완전 분리 + 상한 제한 | ◎ | `CREATE USER`, `GRANT` | | B-5 애플리케이션 설정 | pool 할당값 자체 | ◎ | 서버 접근 | | B-6 네트워크 소켓 | TCP 연결 수·상태 | ○ (IP로) | EC2 SSH | | B-7 주기 샘플링 | 시간대별 추이 | ○ | `PROCESS` + cron | --- ### 방법 B-1. 연결 상한과 실제 사용치 ```bash mariadb -h chocomae.jinaju.com -u <계정> -p -e " SHOW GLOBAL VARIABLES WHERE Variable_name IN ('max_connections','max_user_connections','thread_handling','thread_pool_size', 'wait_timeout','interactive_timeout','back_log','table_open_cache','open_files_limit'); SHOW GLOBAL STATUS WHERE Variable_name IN ('Threads_connected','Threads_running','Threads_created','Threads_cached', 'Max_used_connections','Max_used_connections_time','Connections', 'Aborted_connects','Aborted_clients','Connection_errors_max_connections');" > conn_limits.txt cat conn_limits.txt ``` **해석** | 값 | 의미 | |---|---| | `Max_used_connections` / `max_connections` | **연결 여유율.** 80%를 넘으면 상한을 올리거나 pool을 줄여야 합니다 | | `Max_used_connections_time` | 최고치를 찍은 시각 → 그 시간대에 무슨 작업이 있었는지 추적 | | `Connection_errors_max_connections` | 0보다 크면 **연결 거부가 실제로 발생**한 것 | | `Aborted_connects` | 인증 실패·타임아웃. 시놀로지↔AWS 인터넷 구간 문제의 신호일 수 있음 | | `Threads_created` ≫ `Connections`의 일부 | 스레드 캐시 부족 (`thread_cache_size` 조정 대상) | | `wait_timeout` | pool의 유휴 연결이 이 시간을 넘기면 서버가 끊습니다. chocoadmin에서 간헐적 `ECONNRESET`/`Connection lost`가 난다면 1순위 원인 | --- ### 방법 B-2. PROCESSLIST로 출처별 집계 ```bash mariadb -h chocomae.jinaju.com -u <계정> -p -e " SELECT USER, SUBSTRING_INDEX(HOST, ':', 1) AS client_ip, DB, COUNT(*) AS conns, SUM(COMMAND <> 'Sleep') AS active, SUM(COMMAND = 'Sleep') AS idle, MAX(TIME) AS max_idle_or_run_sec FROM information_schema.PROCESSLIST GROUP BY USER, client_ip, DB ORDER BY conns DESC;" > conn_by_source.txt cat conn_by_source.txt ``` **읽는 법** - `client_ip`가 `localhost`/`127.0.0.1`/도커 내부 IP(`172.x`) → **chocomae(PHP)** - `client_ip`가 시놀로지 공인 IP → **chocoadmin** - `idle`이 크고 `max_idle_or_run_sec`이 수백 초 → pool이 유휴 연결을 계속 붙잡고 있는 상태 (정상이지만 `max_connections` 여유를 잡아먹음) - `active`가 계속 큼 → 실제 쿼리 부하 > 시놀로지 공인 IP를 모른다면 시놀로지에서 `curl -s ifconfig.me` 로 확인하세요. README 기준 출발지 IP는 `182.217.174.221`입니다. --- ### 방법 B-3. userstat으로 출처별 누적 부하 (가장 정확) ```sql SET GLOBAL userstat = 1; FLUSH CLIENT_STATISTICS; -- 구간 측정 시작점 -- (서비스 트래픽을 받는 동안 대기: 예를 들어 1시간) SELECT CLIENT, TOTAL_CONNECTIONS, CONCURRENT_CONNECTIONS, ROUND(CONNECTED_TIME/60,1) AS connected_min, ROUND(BUSY_TIME,1) AS busy_sec, ROUND(CPU_TIME,1) AS cpu_sec, ROWS_READ, ROWS_SENT, SELECT_COMMANDS, UPDATE_COMMANDS, OTHER_COMMANDS FROM information_schema.CLIENT_STATISTICS ORDER BY BUSY_TIME DESC; ``` - `BUSY_TIME`·`CPU_TIME` = **그 출처가 DB를 실제로 점유한 시간**입니다. "chocomae와 chocoadmin 중 어느 쪽이 DB를 더 쓰는가"에 대한 가장 직접적인 답입니다. - `USER_STATISTICS`는 같은 형식을 **계정 단위**로 보여줍니다. B-4(계정 분리)를 먼저 하면 IP가 아니라 계정만으로 깔끔하게 구분됩니다. --- ### 방법 B-4. 서비스별 DB 계정 분리 + 연결 상한 지정 (구조 개선) 지금은 chocomae와 chocoadmin이 **같은 `chocomae` 계정**을 쓸 가능성이 큽니다(README 기준 계정은 `root`/`chocomae`/`backup` 3개). 계정을 나누면 측정과 제어가 동시에 쉬워집니다. ```sql -- 관리자 계정으로 실행 CREATE USER 'chocoadmin'@'182.217.174.221' IDENTIFIED BY '<새 비밀번호>'; GRANT SELECT, INSERT, UPDATE, DELETE ON chocomae.* TO 'chocoadmin'@'182.217.174.221'; -- 이 계정 하나가 쓸 수 있는 동시 연결 수 상한 (폭주 시 서비스 보호) ALTER USER 'chocoadmin'@'182.217.174.221' WITH MAX_USER_CONNECTIONS 20; FLUSH PRIVILEGES; SHOW GRANTS FOR 'chocoadmin'@'182.217.174.221'; ``` - 계정을 나눈 뒤에는 B-2/B-3의 집계가 `USER` 기준으로 바로 읽힙니다. - `MAX_USER_CONNECTIONS`를 걸어두면 chocoadmin 쪽 pool 설정 실수로 운영 서비스가 연결을 못 맺는 사고를 막습니다. - ⚠️ 계정 변경은 chocoadmin 배포와 함께 진행해야 하니, 측정만이 목적이라면 B-2/B-3(IP 기준)으로 충분합니다. --- ### 방법 B-5. 애플리케이션 쪽 설정 확인 (pool 할당값 자체) **B-5-1. chocomae (PHP / Apache) — EC2 SSH** ```bash # 동시 처리 가능한 요청 수 = 사실상 최대 동시 DB 연결 수 apache2ctl -V | grep -i 'Server MPM' grep -RnE 'MaxRequestWorkers|ServerLimit|StartServers|MaxConnectionsPerChild' /etc/apache2/mods-available/mpm_*.conf /etc/apache2/apache2.conf # php-fpm을 쓴다면 grep -RnE '^(pm|pm\.max_children|pm\.start_servers|pm\.max_spare_servers)' /etc/php/*/fpm/pool.d/*.conf 2>/dev/null # persistent connection 관련 설정 (현재 코드는 사용 안 함) php -i | grep -iE 'mysqli.allow_persistent|mysqli.max_persistent|mysqli.max_links|default_socket_timeout' ``` > **판단:** `MaxRequestWorkers`(prefork 기본 150) ≤ `max_connections`여야 합니다. 그렇지 않으면 트래픽 급증 시 PHP가 "Too many connections"를 만납니다. > > 현재 코드는 요청마다 연결을 새로 맺습니다(`new mysqli`). 연결 비용이 부담되면 `p:localhost` 형태의 persistent 연결을 검토할 수 있지만, 연결이 프로세스에 남아 `max_connections`를 오래 점유하므로 **먼저 B-1의 여유율을 확인한 뒤** 결정하세요. **B-5-2. chocoadmin (Next.js / Node) — 시놀로지 SSH** ```bash cd /volume1/docker/service/jinaju/chocoadmin # pool 설정값 찾기 grep -RniE 'createPool|connectionLimit|pool|queueLimit|connectTimeout|enableKeepAlive' \ --include='*.ts' --include='*.js' --include='*.mjs' . | grep -v node_modules | head -40 # 환경변수 (비밀번호는 출력하지 말 것) grep -vE 'PASS|PWD|SECRET|TOKEN' .env.production # 실행 중인 컨테이너·프로세스 수 (pool이 몇 배로 늘어나는지) sudo docker ps --filter name=chocoadmin sudo docker exec chocoadmin sh -c 'ps -eo pid,comm | head' ``` **확인 포인트** | 항목 | 확인 내용 | |---|---| | `connectionLimit` | 명시하지 않았다면 `mysql2` 기본값 10 | | 컨테이너/워커 수 | pool은 **프로세스마다 하나**입니다. Next.js를 여러 워커로 띄우면 `connectionLimit × 워커 수`가 실제 상한 | | 모듈 스코프 재사용 | pool을 요청마다 만들면 연결이 계속 늘어납니다. 모듈 최상단에서 한 번만 생성하는지 확인 | | `connectTimeout`, `enableKeepAlive` | 시놀로지→AWS 인터넷 구간이라 지연·끊김 대비 필요 | | `wait_timeout`(B-1) 대비 유휴 시간 | 서버가 먼저 끊으면 죽은 연결을 재사용하다 오류가 납니다 | **B-5-3. chocoadmin 쪽 실제 연결 확인 (시놀로지 SSH)** ```bash # 컨테이너에서 운영 DB로 맺은 연결 수 sudo docker exec chocoadmin sh -c "ss -tn state established '( dport = :3306 )' 2>/dev/null | wc -l" \ || sudo netstat -tn | grep ':3306' | wc -l ``` --- ### 방법 B-6. 네트워크 소켓 레벨 확인 (EC2 SSH) DB에 로그인하지 않고도 **누가 3306에 붙어 있는지** 셀 수 있습니다. ```bash # 출처 IP별 3306 연결 수 sudo ss -tn state established '( sport = :3306 )' \ | awk 'NR>1 {split($5,a,":"); print a[1]}' | sort | uniq -c | sort -nr > conn_by_ip_ss.txt cat conn_by_ip_ss.txt # 프로세스까지 같이 보기 sudo ss -tnp state established '( sport = :3306 )' | head -30 # TIME_WAIT 등 상태별 집계 (연결을 자주 맺고 끊는지 확인 — PHP 특성상 많을 수 있음) sudo ss -tan '( sport = :3306 )' | awk 'NR>1 {print $1}' | sort | uniq -c ``` > `TIME_WAIT`가 수천 개면 "요청마다 연결/해제"의 비용이 눈에 보이는 수준이라는 뜻입니다. persistent 연결이나 pool 도입을 검토할 근거가 됩니다. --- ### 방법 B-7. 시간대별 추이 샘플링 (권장: 24시간) 한 번의 스냅샷으로는 피크를 놓칩니다. cron으로 1분마다 찍어 CSV로 모읍니다. ```bash # EC2에 저장: /home/ubuntu/conn_sample.sh cat > /home/ubuntu/conn_sample.sh <<'EOS' #!/bin/bash TS=$(date +%FT%T) mariadb -N -B -e " SELECT '$TS', USER, SUBSTRING_INDEX(HOST,':',1), COUNT(*), SUM(COMMAND<>'Sleep') FROM information_schema.PROCESSLIST GROUP BY USER, 3;" >> /home/ubuntu/conn_sample.tsv mariadb -N -B -e " SELECT '$TS','__global__','-', (SELECT VARIABLE_VALUE FROM information_schema.GLOBAL_STATUS WHERE VARIABLE_NAME='Threads_connected'), (SELECT VARIABLE_VALUE FROM information_schema.GLOBAL_STATUS WHERE VARIABLE_NAME='Threads_running');" \ >> /home/ubuntu/conn_sample.tsv EOS chmod +x /home/ubuntu/conn_sample.sh # 1분마다 실행 ( crontab -l 2>/dev/null; echo "* * * * * /home/ubuntu/conn_sample.sh" ) | crontab - ``` 수집 후 정리: ```bash # 시간대별 최대 연결 수 awk -F'\t' '$2=="__global__" {split($1,t,"T"); split(t[2],h,":"); \ key=t[1]" "h[1]"시"; if ($4>m[key]) m[key]=$4} END {for (k in m) print k, m[k]}' \ /home/ubuntu/conn_sample.tsv | sort > conn_hourly_peak.txt cat conn_hourly_peak.txt # 측정 끝나면 cron 제거 crontab -l | grep -v conn_sample.sh | crontab - ``` --- ## C. 테이블 최대 저장 가능 행 수 > "`best_record`에 데이터가 몇 개까지 들어갈 수 있나"는 상한이 **세 겹**입니다. 셋 중 **가장 먼저 걸리는 것**이 실제 한계입니다. > > | 구분 | 상한 요인 | 대략적인 값 | > |---|---|---| > | C-1 논리적 상한 | PK `INT UNSIGNED AUTO_INCREMENT` 최대값 | **4,294,967,295 (약 42.9억)** | > | C-2 물리적 상한 | InnoDB 테이블스페이스 / 디스크 여유 공간 | 디스크 여유 ÷ 행당 바이트 | > | C-3 실용적 상한 | 버퍼 풀·인덱스 크기 대비 성능이 버티는 지점 | 보통 **위 둘보다 훨씬 먼저** 도달 | ### 방법 C-1. 논리적 상한 — AUTO_INCREMENT 소진까지 기록 테이블 4개 모두 PK가 `INT UNSIGNED AUTO_INCREMENT`이므로 **42.9억**이 상한입니다. 중요한 점은 **행을 삭제해도 AUTO_INCREMENT 값은 되돌아가지 않는다**는 것입니다. 즉 "현재 행 수"가 아니라 "지금까지 발급된 ID 값"이 기준입니다. ```bash mariadb -h chocomae.jinaju.com -u <계정> -p -e " SELECT TABLE_NAME, TABLE_ROWS AS approx_rows, AUTO_INCREMENT AS next_id, 4294967295 - AUTO_INCREMENT AS remaining_ids, ROUND(AUTO_INCREMENT / 4294967295 * 100, 4) AS used_pct FROM information_schema.TABLES WHERE TABLE_SCHEMA='chocomae' AND AUTO_INCREMENT IS NOT NULL ORDER BY used_pct DESC;" > capacity_auto_increment.txt cat capacity_auto_increment.txt ``` **남은 기간 계산** ```bash mariadb -h chocomae.jinaju.com -u <계정> -p chocomae -e " SELECT DATE_FORMAT(RecordDateTime,'%Y-%m') AS ym, COUNT(*) AS cnt FROM best_record WHERE RecordDateTime >= DATE_SUB(CURDATE(), INTERVAL 13 MONTH) GROUP BY ym ORDER BY ym;" > capacity_monthly_load.txt cat capacity_monthly_load.txt ``` > 남은 기간 = `remaining_ids` ÷ (월 적재량 × 12) > > **2026-09-15 기준 개략 계산:** 월 평균 약 57,968행 → 연 약 69.6만 행. 42.9억 ÷ 69.6만 ≈ **6,000년 이상**. > 지금 증가 속도가 100배가 되어도 60년이 넘습니다. → **AUTO_INCREMENT는 실질적인 제약이 아닙니다.** > > 단, 아래 경우는 소진이 빨라지니 `used_pct`를 연 1회 확인하세요. > - `INSERT ... ON DUPLICATE KEY UPDATE` / `REPLACE`를 많이 쓰면 실패한 INSERT도 ID를 소비합니다 > - 대량 삭제 후 재적재를 반복하는 경우 > > **넘어갈 조짐이 보이면:** `ALTER TABLE best_record MODIFY BestRecordID BIGINT UNSIGNED NOT NULL AUTO_INCREMENT;` (테이블 재구성이 필요해 시간과 디스크 여유가 많이 듭니다. 미리 계획하세요) ### 방법 C-2. 물리적 상한 — 디스크 여유 공간 **C-2-1. 현재 테이블 크기와 행당 바이트** ```bash mariadb -h chocomae.jinaju.com -u <계정> -p -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, ROUND((DATA_LENGTH+INDEX_LENGTH)/1024/1024,1) AS total_mb, ROUND(DATA_FREE/1024/1024,1) AS free_mb, ROUND((DATA_LENGTH+INDEX_LENGTH)/NULLIF(TABLE_ROWS,0), 1) AS bytes_per_row FROM information_schema.TABLES WHERE TABLE_SCHEMA='chocomae' ORDER BY (DATA_LENGTH+INDEX_LENGTH) DESC;" > capacity_table_sizes.txt cat capacity_table_sizes.txt ``` > `TABLE_ROWS`는 InnoDB 추정치입니다. 정확한 값이 필요하면 `SELECT COUNT(*) FROM best_record;`를 쓰되, 운영 시간대에는 부하를 고려하세요. **C-2-2. 디스크 여유 (EC2 SSH)** ```bash df -h /var/lib/mysql sudo du -sh /var/lib/mysql sudo du -sh /var/lib/mysql/chocomae/*.ibd 2>/dev/null | sort -h | tail -10 ``` **C-2-3. 계산** ``` 추가 가능 행 수 ≈ (디스크 여유 바이트 × 0.8) ÷ bytes_per_row 남은 기간(년) ≈ 추가 가능 행 수 ÷ (월 적재량 × 12) ``` > 0.8은 안전 계수입니다. 백업·바이너리 로그·임시 파일·`ALTER` 시 임시 테이블 공간을 남겨둬야 합니다. 특히 **인덱스 추가나 `BIGINT` 변경 같은 온라인 DDL은 테이블 크기만큼의 여유 공간을 추가로 요구**합니다. > > **2026-09-15 기준 개략 계산:** `best_record`는 약 121만 행에 data 72.5MB + index 72.1MB ≈ 145MB → **행당 약 125바이트**. 42.9억 행을 다 채우면 약 537GB가 필요합니다. 즉 **디스크가 AUTO_INCREMENT보다 훨씬 먼저 한계**입니다. 현재 `df -h` 결과로 실제 여유를 확인하세요. **C-2-4. InnoDB 자체 상한(참고)** - 테이블스페이스 최대 크기: 페이지 16KB 기준 **64TB** (`SHOW VARIABLES LIKE 'innodb_page_size'`로 확인) - `innodb_file_per_table`이 ON이면 테이블마다 `.ibd` 파일 → 파일시스템 최대 파일 크기도 확인 대상 (ext4는 16TB) - 실무에서 여기에 닿는 일은 없습니다. 디스크 여유(C-2-2)가 먼저 걸립니다. ### 방법 C-3. 실용적 상한 — 성능이 버티는 지점 (가장 중요) 디스크가 남아도 **인덱스가 버퍼 풀을 넘어가는 순간** 조회 성능이 급격히 나빠집니다. "몇 개까지 넣을 수 있나"의 현실적인 답은 대개 여기입니다. ```bash mariadb -h chocomae.jinaju.com -u <계정> -p -e " SELECT ROUND((SELECT VARIABLE_VALUE FROM information_schema.GLOBAL_VARIABLES WHERE VARIABLE_NAME='innodb_buffer_pool_size')/1024/1024, 0) AS buffer_pool_mb, ROUND((SELECT SUM(DATA_LENGTH+INDEX_LENGTH) FROM information_schema.TABLES WHERE TABLE_SCHEMA='chocomae')/1024/1024, 1) AS chocomae_total_mb, ROUND((SELECT SUM(INDEX_LENGTH) FROM information_schema.TABLES WHERE TABLE_SCHEMA='chocomae')/1024/1024, 1) AS chocomae_index_mb;" \ > capacity_buffer_pool.txt cat capacity_buffer_pool.txt ``` **판단 기준** | 상태 | 의미 | 조치 | |---|---|---| | `chocomae_total_mb` < `buffer_pool_mb` | 데이터 전체가 메모리에 상주. 디스크 읽기 거의 없음 | 여유 있음 | | `chocomae_index_mb` < `buffer_pool_mb` < `chocomae_total_mb` | 인덱스는 메모리, 데이터는 일부만 | 정상 범위. 버퍼 풀 적중률(A-2-5) 감시 | | `buffer_pool_mb` < `chocomae_index_mb` | 인덱스조차 다 못 올림 | **성능 급락 구간.** 버퍼 풀 증설 / 아카이빙 / 파티셔닝 필요 | **한계 도달 시점 추정** ``` 현재 인덱스 크기 ÷ 현재 행 수 = 행당 인덱스 바이트 (버퍼 풀 크기 × 0.8 - 현재 인덱스 크기) ÷ 행당 인덱스 바이트 = 여유 행 수 여유 행 수 ÷ (월 적재량 × 12) = 남은 기간(년) ``` > 인덱스 크기는 2026-09-15 작업에서 인덱스 5개 + UNIQUE 2개를 추가한 뒤의 값으로 다시 재야 정확합니다. 인덱스가 늘면 행당 바이트도 늘어납니다. **추가 확인: 실제 메모리 여유 (EC2 SSH)** ```bash free -h # 버퍼 풀을 키울 때는 (버퍼 풀 + 연결당 버퍼 × max_connections + OS) < 물리 메모리 여야 합니다 mariadb -e "SHOW VARIABLES WHERE Variable_name IN ('innodb_buffer_pool_size','sort_buffer_size','join_buffer_size','read_buffer_size', 'read_rnd_buffer_size','tmp_table_size','max_heap_table_size','max_connections');" ``` ### 방법 C-4. 증가 속도 기반 예측 (한 번에 정리) 아래 쿼리 하나로 세 상한을 같이 뽑을 수 있습니다. ```bash mariadb -h chocomae.jinaju.com -u <계정> -p chocomae -e " SELECT t.TABLE_NAME, t.TABLE_ROWS AS approx_rows, t.AUTO_INCREMENT AS next_id, ROUND((t.DATA_LENGTH+t.INDEX_LENGTH)/1024/1024,1) AS total_mb, ROUND((t.DATA_LENGTH+t.INDEX_LENGTH)/NULLIF(t.TABLE_ROWS,0),1) AS bytes_per_row, ROUND((4294967295 - t.AUTO_INCREMENT) / (:monthly_rows * 12), 1) AS years_to_autoinc_limit, ROUND((4294967295 - t.AUTO_INCREMENT) * (t.DATA_LENGTH+t.INDEX_LENGTH)/NULLIF(t.TABLE_ROWS,0) /1024/1024/1024, 1) AS gb_needed_for_autoinc_limit FROM information_schema.TABLES t WHERE t.TABLE_SCHEMA='chocomae' AND t.TABLE_NAME IN ('best_record','app_highest_record', 'typing_exam_record','typing_exam_highest_record');" > capacity_projection.txt cat capacity_projection.txt ``` > `:monthly_rows` 자리에는 C-1에서 구한 **월 평균 적재량**(2026-09 기준 약 57968)을 숫자로 직접 넣으세요. MariaDB CLI에는 바인드 변수가 없습니다. ### 방법 C-5. 상한에 가까워졌을 때의 대응 (기존 문서 연결) | 대응 | 효과 | 문서 | |---|---|---| | 인덱스·쿼리 최적화 | 같은 데이터로 더 오래 버팀 | [02-option1-index-and-query-rewrite.md](../02-option1-index-and-query-rewrite.md) | | 일별 요약 테이블 | 랭킹 조회가 원본 테이블을 안 읽음 | [03-option2-daily-summary-tables.md](../03-option2-daily-summary-tables.md) | | 아카이빙 (오래된 기록 분리) | 활성 테이블 크기 감소 → 버퍼 풀 여유 | [04-option3-archiving.md](../04-option3-archiving.md) | | 파티셔닝 (날짜 기준) | 조회 시 읽는 파티션만 접근 | [05-option4-partitioning.md](../05-option4-partitioning.md) | | DB 분리 (RDS 등) | 웹/DB 리소스 경합 해소 | [09-aws-db-separation.md](../09-aws-db-separation.md) | | 시놀로지 복제 | 읽기 부하(chocoadmin) 분산 | [08-synology-replication-setup.md](../08-synology-replication-setup.md) | > chocoadmin이 DB를 많이 읽는 것으로 B-3에서 확인된다면, **복제본을 두고 chocoadmin만 복제본을 보게 하는 것**이 가장 효과가 큰 조치입니다. --- ## 90. 결과 기록 양식 측정 후 아래 표를 채워 넣으세요. ### A. MariaDB 부하 | 항목 | 측정값 | 측정 방법 | 판단 | |---|---|---|---| | vCPU / 메모리 | | A-1-1 | | | CPU 사용률 (peak) | | A-1-3 / A-1-5 | | | 스왑 사용 | | A-1-3 | 0이어야 정상 | | 디스크 %util / await | | A-1-3 | | | QPS (평균 / peak) | | A-2-1 | | | `Threads_running` (peak) | | A-2-4 | vCPU 이하면 정상 | | 버퍼 풀 적중률 | | A-2-5 | 99% 이상 | | 디스크 임시 테이블 (60초) | | A-2-3 | | | 잠금 대기 | | A-2-4 / A-3-3 | | | 느린 쿼리 (24시간) | | A-4 | 백업 SELECT 제외 | ### B. 서비스별 연결 | 항목 | chocomae (EC2) | chocoadmin (시놀로지) | 측정 방법 | |---|---|---|---| | 접속 방식 | `new mysqli` (pool 없음) | Node pool | B-0 | | 설정상 최대 연결 | Apache MaxRequestWorkers = | connectionLimit × 워커 수 = | B-5 | | 현재 연결 수 | | | B-2 | | 최대 연결 수 (24h) | | | B-7 | | 유휴 연결 수 | | | B-2 | | 누적 BUSY_TIME | | | B-3 | | 읽은 행 수 | | | B-3 | | 전체 `max_connections` | | 여유율: | B-1 | | 연결 거부 발생 | | | B-1 | ### C. 테이블 용량 | 테이블 | 현재 행 수 | next_id | AUTO_INCREMENT 사용률 | total_mb | 행당 바이트 | 디스크 기준 남은 행 | 남은 기간 | |---|---|---|---|---|---|---|---| | best_record | | | | | | | | | app_highest_record | | | | | | | | | typing_exam_record | | | | | | | | | typing_exam_highest_record | | | | | | | | | 항목 | 값 | |---|---| | 월 평균 적재량 | | | `innodb_buffer_pool_size` | | | chocomae 전체 크기 (data+index) | | | 디스크 여유 (`/var/lib/mysql`) | | | **가장 먼저 걸리는 상한** | (AUTO_INCREMENT / 디스크 / 버퍼 풀) | | **예상 도달 시점** | | --- ## 91. 주의사항 1. **측정 자체가 부하입니다.** `SELECT COUNT(*)`나 `SHOW ENGINE INNODB STATUS`를 반복 실행하지 마세요. 정확한 행 수가 꼭 필요할 때만 한 번 실행합니다. 2. **`SET GLOBAL`로 바꾼 값은 반드시 복원합니다.** 변경 전 값을 파일로 먼저 저장하세요 (`long_query_time`, `slow_query_log`, `userstat`). 3. **`log_queries_not_using_indexes`는 켜지 않습니다.** 로그가 폭증합니다. 4. **cron 샘플링(B-7)은 측정이 끝나면 지웁니다.** 지우지 않으면 TSV가 무한히 커집니다. 5. **매일 04:00 풀백업**이 돌고 있습니다. 이 시간대 측정값은 백업 부하가 섞이니 따로 표시하세요. 6. **비밀번호를 명령줄에 쓰지 마세요.** 셸 히스토리와 `ps` 출력에 남습니다. `~/.my.cnf`(권한 600)를 쓰세요. ```ini [client] host=chocomae.jinaju.com user=perfcheck password=<비밀번호> ``` 7. **임시 계정·임시 권한은 측정 후 회수합니다.** (`DROP USER`, `REVOKE`) 8. **README에 평문으로 남아 있는 DB 비밀번호**(`root`/`chocomae`/`backup`이 모두 같은 값)는 이 작업과 별개로 교체가 필요합니다.