디스크 사용량이 갑자기 늘고 ibdata1이 줄지 않을 때, 범인은 대개 오래 살아있는 트랜잭션입니다. InnoDB는 MVCC를 위해 변경 전 데이터를 undo 로그에 보관하는데, 아직 끝나지 않은 트랜잭션이 그 행을 참조하는 한 undo는 절대 정리되지 않습니다. 트랜잭션 하나가 몇 시간 열려 있으면 그동안 쌓인 모든 undo가 누적됩니다.
왜 늘어나는가 — undo와 purge
InnoDB는 커밋된 트랜잭션의 undo를 백그라운드 purge 스레드가 회수합니다. 그런데 purge는 "현재 활성 트랜잭션 중 가장 오래된 것"보다 과거의 undo만 지울 수 있습니다. 즉 활성 트랜잭션이 하나라도 길게 살아 있으면 그 시점 이후의 undo는 전부 보존 대상이 됩니다.
| 상태 | undo 정리 | 디스크 영향 |
|---|---|---|
| 짧게 커밋/롤백 | purge가 즉시 회수 | 안정 |
| 트랜잭션 장시간 미커밋 | purge 차단(history 누적) | undo·ibdata1 증가 |
autocommit=0 후 SELECT만 | 트랜잭션 계속 열림 | 조용히 누적 |
마지막 행이 가장 위험합니다. autocommit을 끈 세션이 조회만 하고 커밋하지 않으면, 쓰기를 한 적이 없어도 읽기 일관성 스냅샷이 유지되어 purge가 막힙니다.
진단 — 무엇을 볼지
먼저 누적된 undo 양(history list length)과 가장 오래된 트랜잭션을 확인합니다.
SHOW ENGINE INNODB STATUS\G -- TRANSACTIONS 섹션의 History list length 확인
History list length가 수십만 이상이면 purge가 밀린 상태입니다. 이어서 오래 열린 트랜잭션을 찾습니다.
SELECT trx_id, trx_started,
TIMESTAMPDIFF(SECOND, trx_started, NOW()) AS age_sec,
trx_mysql_thread_id, trx_query
FROM information_schema.innodb_trx
ORDER BY trx_started ASC;
age_sec가 비정상적으로 큰 행(trx_query가 NULL이면 더 의심)이 원인 트랜잭션입니다.
해결 절차
- 원인 세션 식별 — 위 쿼리의
trx_mysql_thread_id를processlist와 대조합니다. - 방치된 세션 종료 — 애플리케이션이 커밋을 안 한 채 떠난 경우
KILL <thread_id>로 끊습니다. 종료 시 롤백되며 purge가 풀립니다. - purge 진행 확인 — 다시
SHOW ENGINE INNODB STATUS로 history list length가 줄어드는지 봅니다. - 재발 방지 — 커넥션 풀에 idle 트랜잭션 타임아웃을 두고, 트랜잭션 안에서 외부 API 호출 같은 느린 작업을 빼며, 조회 전용 로직은 트랜잭션을 빨리 닫게 만듭니다.
참고로 ibdata1이 한번 커진 시스템 테이블스페이스는 자동으로 줄지 않습니다. undo를 별도 테이블스페이스로 분리(innodb_undo_tablespaces)해두면 truncate로 공간 회수가 가능합니다.
요점 정리
- undo 증가의 본질은 디스크 부족이 아니라 purge가 막힌 것이고, 막는 주범은 오래 열린 트랜잭션입니다.
innodb_trx의trx_started로 가장 오래된 트랜잭션을 먼저 찾습니다.- 쓰기가 없어도
autocommit=0조회 세션이 purge를 막을 수 있습니다.
트랜잭션 격리 수준과 MVCC가 undo에 어떻게 연결되는지 직접 실습으로 확인하려면 데이터베이스 트랙에서 회원가입 없이 무료로 시작할 수 있습니다.