MySQL 메모리 사용량이 800MB에서 264MB로 상승한 문제 해결
InnoDB 버퍼풀과 누적 JSON 운영 로그를 점검하고 메모리 사용량을 낮춘 기록
현상
Railway의 MySQL 메모리가 약 800MB까지 올라갔다. 애플리케이션의 DB 커넥션 풀을 줄였는데도 메모리가 계속 증가해 커넥션 누수 가능성을 먼저 의심했다.

재시작 직후 메모리는 약 264MB까지 내려갔다. 이는 메모리 누수로 프로세스가 계속 비대해진 것이라기보다, MySQL이 사용한 InnoDB 버퍼풀과 테이블 페이지를 메모리에 유지하고 있었음을 보여준다.
원인 확인
실제 런타임 값을 확인한 결과 연결 수는 원인이 아니었다.
| 항목 | 확인값 |
|---|---|
Threads_connected |
8~9 |
Max_used_connections |
15 |
max_connections |
151 |
innodb_buffer_pool_size |
512MB |
decision_event 크기 |
약 209MB |
training.case 등 학습 JSON |
약 131MB |
특히 decision_event에 큰 JSON 형태의 training.case가 계속 쌓이고 있었다. 기존 보존 작업은 거래 감사 이벤트를 지키기 위해 training phase를 핵심 데이터로 분류하고 있어, 3일 보존 작업의 대상에서 빠져 있었다.
또한 MySQL의 버퍼풀은 조회한 페이지를 캐시하므로, 사용량이 증가해도 곧바로 메모리 누수라고 판단할 수 없다. InnoDB는 확보한 메모리를 OS에 즉시 돌려주지 않기 때문에 재시작 시 그래프가 급격히 내려갈 수 있다.
해결
불필요한 학습 로그 정리
training.case, training.prediction, training.answer 3,593건을 배치로 삭제했다. 주문·체결·청산 감사 기록과 차트 드로잉은 삭제하지 않았다.
앞으로 training 로그도 일반 운영 로그와 동일하게 3일 보존 후 1,000건 단위로 삭제하도록 retention 정책을 바꿨다.
애플리케이션 커넥션 풀 제한
API와 scheduler 각각의 풀을 다음과 같이 제한했다.
pool_size=5max_overflow=2- 서비스별 최대 연결 7개
pool_timeout=20초pool_use_lifo=Truepool_pre_ping=True
MySQL 메모리·연결 상한 조정
현재 트래픽과 최대 연결 사용량에 맞춰 런타임 및 영구 설정을 적용했다.
SET PERSIST innodb_buffer_pool_size = 268435456; -- 256MB
SET PERSIST max_connections = 50;
SET PERSIST wait_timeout = 300;
SET PERSIST interactive_timeout = 300;
performance_schema는 이미 OFF였고, per-connection 버퍼와 임시 테이블 기본값도 과도하지 않아 추가로 낮추지 않았다.
마지막으로 MySQL을 재시작해 버퍼풀을 초기화하고, 재시작 후에도 위 설정이 유지되는지 확인했다.
결과와 교훈
- 메모리: 약 800MB → 약 264MB
- 커넥션 수: 최대 사용 15개로 풀 상한보다 충분히 낮음
- MySQL 설정: 재시작 후에도 256MB buffer pool,
max_connections=50유지 - 차트 히스토리: 별도
chart_drawing에 보존 - 거래 감사 기록: 보존
이번 문제는 커넥션 풀만 줄인다고 해결되지 않았다. 메모리 점검은 다음 순서로 해야 한다.
- 실제 연결 수와 최대 사용 연결 수 확인
- InnoDB buffer pool 크기와 사용량 확인
- 큰 테이블과 JSON 로그의 증가 여부 확인
- 보존 정책이 의도한 phase에 실제 적용되는지 확인
- 정리 후 재시작 또는 테이블 재구성 뒤 메모리 변화를 확인
MySQL 메모리 그래프가 올라간다는 사실만으로 OOM이나 연결 누수라고 단정하지 말고, 버퍼풀 캐시와 실제 데이터 증가를 구분해야 한다.