운영 DB 메모리가 계속 늘어난다는 리포트를 받았을 때 - 버퍼풀 vs 진짜 누수 구분하기
Kafka 메시지를 소비해서 DB에 적재하는 컨슈머 애플리케이션을 운영 중인데, “DB 메모리가 계속 증가한다”는 리포트를 받았다. 결론부터 말하면 아직 원인을 100% 확정하지 못했고, 지금은 재기동 후 추이를 지켜보는 중이다. 완결된 해결기가 아니라 진행 중인 진단 과정을 그대로 남긴다.
리포트 자체가 불안정했다
가장 먼저 한 일은 “얼마나, 어떤 주기로 늘어나는지”를 명확히 하는 것이었다. 처음 전달받은 내용은
“하루에 3%씩 증가한다”
였는데, 확인 과정에서
“하루가 아니라 주 단위로 2~3%씩 증가한다”
로 정정됐다. 숫자 하나 확정하는 데도 몇 번을 되물어야 했다. 모니터링 주체가 다르면(운영팀 vs 개발자) 같은 현상도 다르게 요약되어 전달될 수 있다는 걸 새삼 느꼈다. 원인 분석에 들어가기 전에 “그래서 정확히 뭐가, 얼마나, 언제”를 먼저 고정해야 그다음 숫자들이 의미를 가진다.
의심 1순위: 최근에 추가한 중복 방지 로직
시점상 가장 최근에 건드린 코드가 있었다 — 같은 요청이 중복으로 들어오는 걸 막는 로직을 애플리케이션에 새로 추가했었다. “내가 최근에 뭘 바꿨나”가 항상 1순위 용의자가 되는 건 자연스럽다. 다만 이번 리포트는 애플리케이션(JVM) 메모리가 아니라 DB 자체의 메모리였다. 둘은 서로 다른 프로세스, 다른 메모리 공간이라 애플리케이션 코드 수정만으로 DB 메모리 증가를 설명하려면 “그 코드가 DB에 어떤 부하/락/연결을 만드는가”를 봐야지, 코드 자체의 메모리 누수를 보는 건 방향이 다르다는 걸 짚고 넘어갔다.
DB 메모리를 늘릴 수 있는 후보들을 체크리스트로 훑기
DB 프로세스 메모리가 늘어날 수 있는 경로를 하나씩 소거법으로 확인했다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-- 1. InnoDB 버퍼풀 설정값과 실사용량 비교 (가장 먼저 봐야 할 지표)
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
SHOW STATUS LIKE 'Innodb_buffer_pool_bytes_data';
-- 2. 열려 있는(오래 안 끝난) 트랜잭션
SELECT * FROM information_schema.INNODB_TRX;
-- 3. History List Length - 언두 로그가 쌓이고 있는지
SHOW ENGINE INNODB STATUS\G
-- 4. 커넥션/스레드 재사용률
SHOW STATUS LIKE 'Threads_connected';
SHOW STATUS LIKE 'Threads_created';
SHOW STATUS LIKE 'Connections';
SHOW STATUS LIKE 'Aborted_clients';
-- 5. MyISAM 키 캐시 / 쿼리 캐시 / performance_schema
SHOW VARIABLES LIKE 'key_buffer_size';
SHOW VARIABLES LIKE 'query_cache_size';
SHOW VARIABLES LIKE 'performance_schema';
하나씩 지워나간 결과:
- 열린 트랜잭션 없음, 락 대기 없음, 최근 데드락 없음 — 잠긴 채로 안 끝난 트랜잭션이 메모리를 붙들고 있는 상황은 아니었다.
- History List Length가 매우 작음 — 언두 로그가 쌓여서 부풀어 오르는 전형적인 패턴도 아니었다.
- 커넥션 수 / 스레드 생성-재사용 비율 / Aborted_clients 비율 모두 정상 범위 — 커넥션 누수로 스레드가 계속 새로 생기며 메모리를 먹는 패턴도 아니었다.
- key_buffer_size(MyISAM), query_cache_size, performance_schema 모두 사실상 꺼져 있거나 무의미한 값 — 이쪽도 용의선상에서 제외.
남은 건 InnoDB 버퍼풀이었다. 확인해보니 innodb_buffer_pool_size가 물리 메모리의 상당 비중을 차지하도록 크게 설정돼 있었고, 실제 사용량이 그 설정값의 거의 100%에 가깝게 채워져 있었다. 버퍼풀은 설정된 한도까지 차오르는 게 정상 동작이라, 지금까지 관측된 “DB 메모리 사용량”의 상당 부분은 이 설정값 자체로 설명이 됐다.
그런데 이게 전부는 아닐 수도 있다
문제는 운영팀의 관찰이 “지금 이 순간에도 계속 늘고 있다”는 것이었다. 버퍼풀이 이미 설정 상한 근처에서 꽉 찬 상태라면, 거기서 더 늘어나는 부분은 단순 warm-up으로는 설명이 안 된다. 즉 “버퍼풀 설정값이 원래 큰 것”과 “실제로 뭔가가 계속 새고 있는 것”이 동시에 존재할 가능성을 배제할 수 없는 상태로 남았다.
여기서 얻은 교훈: 하나의 그럴듯한 설명을 찾았다고 바로 “해결”로 선언하면 안 된다. 그 설명이 관측된 현상 전체(특히 “여전히 진행 중”이라는 시간축 정보)를 다 덮는지 별도로 검증해야 한다.
지금 상태와 다음 단계
- DB 자체 지표(트랜잭션/락/History List/커넥션)에서는 이상 신호를 찾지 못했다.
- 버퍼풀 설정값(전체 메모리 대비 큰 비중)이 지금까지의 사용량 대부분을 설명한다고 잠정 판단했다.
- 다만 “여전히 증가 중”이라는 보고를 완전히 설명하지는 못해서, 원인 분석은 아직 미완료로 남겨뒀다.
- DB를 재기동하고, 재기동 전후의 증가율을 비교해서 “설정값 때문이었는지 / 실제로 새는 게 있었는지”를 가리기로 했다. 재기동 전에는 컨슈머 애플리케이션을 정상 종료 절차로 먼저 내려서 처리 중이던 메시지가 유실되지 않게 했다.
결론이 나면 후속 글로 이어서 정리할 예정이다.