MySQL 'Too many connections' 에러가 뜨는 이유

 

갑자기 웹사이트가 먹통이 되고 에러 로그를 확인해보니 "ERROR 1040 (HY000): Too many connections" 메시지가 반복된다면, 동시접속자가 MySQL 서버의 최대 연결 수를 초과했다는 뜻이다. 특히 트래픽이 몰릴 때나 애플리케이션 버그로 연결이 제대로 종료되지 않을 때 자주 발생한다. 다만 대부분의 개발자들은 이 에러의 정확한 원인을 파악하지 못한 채 무조건 max_connections 값을 올리거나, 최악의 경우 서버를 재부팅해버린다. 이번 글에서는 이 문제가 정확히 뭔지, 왜 생기는지, 어떻게 근본적으로 해결하는지를 실무 기반으로 완벽하게 정리해보겠다.

현재 MySQL 최대 연결 수 확인하기

 

먼저 내 MySQL 서버가 지금 몇 개의 연결을 허용하도록 설정되어 있는지 확인해야 한다. MySQL 클라이언트에 접속해서 다음 명령어를 실행하자.

SHOW VARIABLES LIKE 'max_connections';

✓ 정상 결과: Variable_name이 'max_connections'이고, Value가 151(기본값) 또는 그 이상으로 표시된다.

동시에 현재 연결 수도 확인해보자.

SHOW STATUS LIKE 'Threads_connected';

이 값이 max_connections에 가까워지거나 같으면, 곧바로 'Too many connections' 에러가 터질 수 있는 위험한 상태다.

왜 연결이 쌓이는가? - 주요 원인 3가지

 

1) 연결을 제대로 종료하지 않는 PHP 코드
가장 흔한 원인이다. DB 연결을 열고 나서 mysqli_close()나 PDO 연결을 null로 설정하지 않으면, 스크립트가 종료될 때까지 연결이 메모리에 남아있다.

✗ 잘못된 코드:

<?php
$conn = mysqli_connect('localhost', 'user', 'password', 'database');
$result = mysqli_query($conn, "SELECT * FROM users");
// 연결을 닫지 않고 스크립트 종료
?>

✓ 올바른 코드:

<?php
$conn = mysqli_connect('localhost', 'user', 'password', 'database');
if (!$conn) {
    die('Connection failed: ' . mysqli_connect_error());
}
$result = mysqli_query($conn, "SELECT * FROM users");
while ($row = mysqli_fetch_assoc($result)) {
    echo $row['name'];
}
mysqli_close($conn);  // 반드시 닫기
?>

2) 오래된 연결이 타임아웃되지 않음
PHP 스크립트가 오래 걸리는 작업을 수행할 때, DB 연결이 idle 상태로 유지되고 있으면 MySQL이 계속 메모리를 점유한다.

3) 커넥션 풀 설정 누락
고트래픽 사이트에서 개별 PHP 요청마다 새로운 DB 연결을 만들면, 동시에 수백 개의 연결이 쌓일 수 있다.

MySQL max_connections 확인 및 조정

 

긴급으로 연결 수를 늘려야 한다면, 다음과 같이 임시로 설정할 수 있다.

✓ 현재 세션에서만 적용:

SET GLOBAL max_connections = 500;

다만 이 방법은 MySQL 재시작 후 사라진다. 영구적으로 적용하려면 my.cnf 파일을 수정해야 한다.

✓ 영구 설정 (CentOS/Linux):

sudo vi /etc/my.cnf

다음 라인을 [mysqld] 섹션에 추가하거나 수정하자.

[mysqld]
max_connections = 500

그 후 MySQL을 재시작한다.

sudo systemctl restart mysqld

⚠️ 주의: max_connections를 무작정 크게 설정하면 서버 메모리 부족으로 MySQL이 응답 불가 상태(crash)에 빠질 수 있다. 서버 사양에 맞춰 적절한 값(일반적으로 100~1000)을 설정하자.

오래된 연결 강제 종료

 

이미 쌓여있는 좀비 연결(idle 상태)을 정리하려면, MySQL의 SHOW PROCESSLIST로 확인 후 kill할 수 있다.

SHOW PROCESSLIST;

오래 유휴 상태인 연결을 찾았다면:

KILL [connection_id];

예를 들어, ID가 45인 연결을 종료하려면:

KILL 45;

한 번에 모든 유휴 연결을 정리하는 스크립트:

SELECT GROUP_CONCAT('KILL ',ID,';') FROM INFORMATION_SCHEMA.PROCESSLIST WHERE TIME > 300 AND COMMAND != 'Sleep';

이를 실행하면 300초 이상 유지된 연결을 일괄 종료한다.

PDO를 사용할 때의 연결 관리

 

PHP 최신 표준인 PDO를 쓴다면, 다음과 같이 연결을 명시적으로 닫자.

<?php
try {
    $pdo = new PDO('mysql:host=localhost;dbname=mydb', 'user', 'password');
    $stmt = $pdo->prepare('SELECT * FROM users WHERE id = ?');
    $stmt->execute([1]);
    $result = $stmt->fetchAll();
} catch (PDOException $e) {
    echo 'Connection failed: ' . $e->getMessage();
} finally {
    $pdo = null;  // 연결 해제
}
?>

또는 다음처럼 서버에 'wait_timeout' 값을 설정해서, 자동으로 유휴 연결을 끊도록 할 수 있다.

SET GLOBAL wait_timeout = 600;  -- 10분 후 자동 종료
흔한 실수 vs 올바른 접근

 

잘못된 접근 올바른 접근
무조건 max_connections 값을 1000 이상으로 올리기 서버 메모리를 고려해 적절한 값(보통 200~500) 설정 후, 연결 누수 원인 제거
"에러가 나면 그냥 MySQL 서버 재부팅" SHOW PROCESSLIST로 원인 파악 후, 근본 문제 해결
여러 PHP 스크립트에서 매번 새로운 DB 연결 생성 연결 객체를 재사용하거나, 싱글톤 패턴으로 하나의 연결 유지
연결 닫지 않고 스크립트 종료 기대 명시적으로 mysqli_close() 또는 $pdo = null 실행
실전 진단 체크리스트

 

에러가 발생했을 때 확인해야 할 순서:

1. SHOW STATUS LIKE 'Threads_connected'로 현재 연결 수 확인
2. SHOW PROCESSLIST로 idle 상태 연결 찾기
3. 애플리케이션 로그에서 DB 연결 에러 패턴 분석
4. PHP 코드에서 mysqli_close() 호출 누락 여부 검토
5. 필요시 wait_timeout/interactive_timeout 조정
6. max_connections 값을 점진적으로 상향(한 번에 500씩 증가시키지 말 것)
7. 여전히 에러 발생하면 서버 메모리 추가 or 데이터베이스 분산 검토

마무리

 

MySQL 'Too many connections' 에러는 단순한 설정값 증가만으로는 근본적으로 해결되지 않는다. 문제는 대부분 애플리케이션 단계에서 연결을 제대로 관리하지 못하는 데 있다. 작은 연결 누수가 모여서 시스템 장애로 번지는 것처럼, 평소 코드 리뷰와 DB 연결 습관이 중요하다. 이 글의 SHOW PROCESSLIST 진단 방법과 연결 종료 패턴을 참고해서 지금 당신의 프로젝트를 한 번 점검해보면, 예상치 못한 트래픽 급증에도 안정적인 서버를 유지할 수 있을 것이다.