MySQL 'Too many connections' 에러 완벽 해결: 커넥션 풀(Connection Pool) 튜닝 가이드
1. 트래픽의 파도와 무너져 내리는 DB 병목
이벤트 프로모션이나 마케팅 푸시 알림을 보낸 직후, 유저들이 앱에 폭발적으로 접속할 때 서버 로그에 시뻘겋게 찍히는 ERROR 1040 (HY000): Too many connections 메시지는 백엔드 개발자들의 등골을 서늘하게 만드는 악몽 1순위입니다. 웹 서버(Node.js, Spring Boot 등)는 멀티 스레딩이나 비동기 I/O를 통해 초당 수천 건의 트래픽 요청을 우습게 받아내지만, 네트워크의 뒤편에 자리 잡은 관계형 데이터베이스(MySQL)는 디스크 I/O 병목과 ACID 트랜잭션 락(Lock)을 관리하느라 요청을 병렬로 처리하는 속도에 물리적인 한계가 분명히 존재합니다.
클라이언트 요청이 들어올 때마다 DB에 매번 새로운 연결(TCP Handshake, 인증, 소켓 오픈 등)을 생성하고 끊는 행위는 엄청난 CPU 리소스를 낭비합니다. MySQL 서버의 max_connections 설정값이 기본적으로 151로 세팅되어 있는데, 접속량이 이를 초과하여 큐(Queue)에 무한정 쌓이게 되면 DB는 스스로 보호를 위해 새로운 커넥션을 매몰차게 거절해 버리고, 앱 전체가 통신 불량으로 완전히 다운되어 버립니다.
2. 커넥션 풀(Connection Pool)이라는 거대한 댐
이 문제를 타파하기 위해 등장한 소프트웨어 아키텍처 개념이 바로 커넥션 풀(Connection Pool)입니다. 애플리케이션이 처음 켜질 때 미리 DB와의 파이프라인(커넥션)을 10개 혹은 50개 정도 맺어두고 수영장(Pool)에 보관해 둡니다. 유저의 API 요청이 오면 풀에서 빈 커넥션을 하나 임대해주고, 쿼리 응답이 끝나면 연결을 아예 끊는 대신 다시 풀로 반납하는 방식입니다. 이를 통해 매번 연결을 맺고 끊는 막대한 네트워크 오버헤드를 제로(0)에 가깝게 소멸시킬 수 있습니다.
하지만 여기서 치명적인 실수가 발생합니다. 무턱대고 MySQL의 max_connections를 2000으로 늘리고 서버의 커넥션 풀 사이즈를 1000으로 잡으면 트래픽 처리가 빨라질까요? 절대 아닙니다. 활성 커넥션 수가 물리적 코어 스레드 한계를 넘어가면, CPU가 이 수많은 커넥션들 사이를 왔다 갔다 하며 컨텍스트 스위칭(Context Switching)을 하느라 정작 핵심 쿼리는 실행하지 못하고 시스템 전체가 뻗어버리는 데드락 현상이 발생합니다. 자바 생태계의 히카리CP(HikariCP) 창시자가 남긴 유명한 공식에 따르면, (코어 수 * 2) + 디스크(유효 스핀들) 수 정도의 아주 작은 풀 사이즈(보통 코어가 8개라면 16~20개 수준)가 가장 압도적이고 안정적인 DB 성능을 뿜어냅니다.
3. 무중단 쿼리를 위한 최적화 점검 리스트
- 1) 쿼리 타임아웃 및 누수 세팅: 앱 서버 로직에 버그가 생겨 커넥션을 빌려 간 뒤 영원히 반납하지 않는 커넥션 누수(Leak)가 가장 흔한 원인입니다. MySQL의
wait_timeout설정(기본 8시간)을 과감하게 10분 이내로 확 줄여서, 오랜 시간 쿼리를 날리지 않고 좀비처럼 살아있는 유휴 커넥션을 DB가 선제적으로 강제 종료시켜버리게 만들어야 합니다. - 2) 트랜잭션 바운더리 좁히기: 외부 API 호출(예: PG사 결제 모듈 통신)을 하는 3~5초의 긴 시간 동안 DB 트랜잭션을 열어두고 커넥션을 물고 있으면 절대 안 됩니다. 커넥션은 무조건 DB 쿼리를 쏘기 직전에 빌리고, 쿼리가 끝나는 즉시 로컬 연산으로 넘어가도록 트랜잭션 범위를 초단위 이하로 리팩토링해야 합니다.
- 3) 프록시 서버(ProxySQL) 도입: 앱 서버가 오토 스케일링으로 수십 대로 늘어나면, 각 서버당 풀 사이즈가 10개만 되어도 DB 입장에서는 커넥션이 수백 개로 폭증합니다. 이때 앱 서버 집단과 DB 사이에
ProxySQL이나AWS RDS Proxy를 중간 멀티플렉싱 계층으로 두어 수많은 커넥션을 단 몇십 개의 물리 커넥션으로 압축해 DB에 전달하는 방어망 아키텍처가 대규모 트래픽에서 필수적입니다.
4. 자주 묻는 질문 (FAQ)
Q. 에러가 나면 max_connections만 계속 올리면 되는 거 아닌가요?
초보 인프라 관리자가 범하는 가장 위험한 임시방편입니다. 커넥션 하나당 할당되는 RAM 메모리 버퍼(sort_buffer_size, read_buffer_size 등)가 스레드마다 곱해지기 때문에, 리미트를 무한정 늘리면 DB 서버의 가용 물리 메모리를 순식간에 모두 갉아먹고 OS 커널이 램 확보를 위해 DB 프로세스를 강제로 죽여버리는(OOM Killer) 최악의 하드웨어 다운 재앙을 맞이하게 됩니다.
Q. Node.js 환경에서 Sequelize나 Prisma를 쓸 때 적정 커넥션 풀은 몇 개인가요?
Node.js는 단일 스레드 비동기 루프로 동작하므로 엄청난 수의 풀이 필요하지 않습니다. 보통 인스턴스당 5~10개 정도면 충분하며, Prisma의 경우 동시 접속자가 많을 때 자체적인 커넥션 관리 로직이 뛰어납니다. 오히려 너무 큰 값을 주면 Node 프로세스 메모리 누수와 DB 데드락이 겹치는 부작용이 있습니다.
OMANGAZI 편집팀
최신 IT 기술, 오픈소스 AI 생태계, 그리고 모던 웹 개발 트렌드를 연구하고 분석합니다. 단순한 정보 전달을 넘어 개발자들의 실무에 도움이 되는 깊이 있는 인사이트를 제공합니다.