카테고리 : MySQL/기술노트

Aurora MySQL 연결 관리: max_connections 산정과 Proxy/RDS Proxy 활용 기준

Aurora MySQL에서 max_connections를 메모리·동시성·장애 전파 관점으로 산정하고, 애플리케이션 pool만으로 충분한 경우와 proxy 또는 RDS Proxy가 필요한 경우를 운영 기준으로 정리한다.

저자: MySQL 기술 노트 작성: 2026.06.26 약 14분 8,068자
다운로드

1. 왜 Aurora MySQL의 연결 관리는 단순히 max_connections 숫자 조정으로 끝나지 않는가

운영 현장에서 연결 문제는 흔히 두 가지 오해에서 시작된다. 첫째, max_connections를 크게 잡으면 애플리케이션 확장성이 좋아진다고 생각하는 경우다. 둘째, connection pool이 있으니 연결 수는 이미 잘 관리되고 있다고 가정하는 경우다. 그러나 Aurora MySQL에서는 두 가정이 모두 부분적으로만 맞다.

Aurora는 스토리지 계층과 DB 인스턴스 계층이 분리되어 있고, reader endpoint, writer failover, 클러스터 재선출, 짧은 네트워크 흔들림 같은 이벤트가 일반 MySQL보다 더 자주 운영 변수로 등장한다. 이 환경에서는 연결 수 그 자체보다 연결이 어떤 속도로 늘어나는지, 연결이 길게 유지되는지, 장애 시 재연결이 얼마나 집중되는지, 그리고 세션 상태가 어느 계층에 남아 있는지가 더 중요하다.

따라서 이 주제의 핵심은 단순히 다음 질문이 아니다.

max_connections를 얼마로 둘 것인가?

실제로 운영자가 답해야 하는 질문은 다음 네 가지다.

  1. 이 인스턴스가 안정적으로 처리할 수 있는 실효 동시 실행 수는 얼마인가.
  2. 애플리케이션 fleet 전체가 그 예산을 초과하지 않도록 pool과 worker를 어떻게 배분할 것인가.
  3. 장애나 failover 직후 연결 폭증을 줄이기 위해 중간 proxy 계층이 필요한가.
  4. Aurora의 writer/reader 전환, DNS 갱신, 세션 pinning 특성을 고려할 때 Amazon RDS Proxy를 둘 실익이 있는가.

이 글에서는 이 네 가지를 하나의 운영 체계로 묶어 설명한다.

2. Aurora MySQL에서 연결 수는 메모리 한도이면서 동시에 동시성 한도다

max_connections는 표면적으로는 “최대 접속 수” 변수지만, 실제로는 다음 세 가지를 동시에 제한한다.

  • 세션 객체와 thread 관리 오버헤드
  • 세션별 메모리 사용 가능성
  • 동시에 DB에 압력을 가할 수 있는 요청 수의 상한

문제는 이 세 가지가 서로 다른 방식으로 병목을 만든다는 점이다.

2.1 메모리 관점

연결이 많아지면 세션 자체의 메타데이터와 thread stack, 네트워크 버퍼, 각종 session buffer가 늘어난다. 여기에 쿼리 실행 중에는 sort_buffer_size, join_buffer_size, read_buffer_size, read_rnd_buffer_size, internal temporary table 메모리 등이 추가된다. 중요한 점은 이들 메모리가 모든 연결에서 항상 동일하게 잡히는 것은 아니지만, 피크 시 여러 세션이 동시에 비슷한 종류의 작업을 시작하면 메모리 사용량이 계단식으로 증가한다는 점이다.

Aurora MySQL에서는 buffer pool과 redo/undo, background thread 영역뿐 아니라 Performance Schema, connection/thread 관리, temporary object까지 모두 인스턴스 메모리 안에서 경쟁한다. 따라서 max_connections를 크게 잡을수록 “평소엔 조용하지만 피크 때 한 번에 메모리가 터지는” 구조가 된다.

2.2 동시성 관점

연결 수가 많다고 해서 처리량이 선형으로 늘지는 않는다. 오히려 일정 시점을 넘으면 다음 현상이 나타난다.

  • Threads_running 증가
  • row lock 대기 증가
  • CPU scheduler 경쟁 증가
  • redo flush와 commit 지연 증가
  • buffer pool miss 또는 working set 흔들림
  • tail latency 악화

즉, max_connections는 단순한 수용량 파라미터가 아니라 DB를 얼마나 깊은 대기열 상태로 밀어 넣을 수 있는가를 결정하는 변수다.

2.3 장애 전파 관점

연결 한도가 높고 pool 제한이 느슨하면, 애플리케이션은 DB가 느려지는 순간 더 많은 요청을 밀어 넣는다. 그 결과 DB만 느려지는 것이 아니라 애플리케이션 worker, API timeout, upstream retry, queue 적체가 함께 증폭된다. 결국 연결 관리는 DB 튜닝이 아니라 시스템 안정화 설계다.

3. Aurora 환경에서 일반 MySQL보다 더 조심해야 하는 이유

Aurora MySQL는 MySQL 호환 엔진이지만 운영 동작은 단순한 단일 인스턴스 MySQL과 다르다. 연결 관리에서는 특히 아래 차이가 중요하다.

3.1 failover와 재연결 폭발

writer 장애나 planned failover가 발생하면 기존 연결 중 일부는 끊기거나 오류를 반환한다. 애플리케이션 fleet이 동시에 재연결을 시도하면 다음 비용이 짧은 시간에 집중된다.

  • DNS 재해석
  • TLS handshake
  • authentication
  • 세션 초기화 SQL 재실행
  • prepared statement 재준비
  • 애플리케이션 pool 내부 dead connection 정리

따라서 평소 max_connections가 넉넉해 보여도 failover 직후에는 정상 부하 + 재연결 부하 + warm-up 부하가 합쳐진다.

3.2 reader endpoint와 connection skew

reader endpoint 뒤에 여러 reader 인스턴스가 있어도, 클라이언트의 DNS 캐시나 connection pool 재사용 패턴 때문에 연결이 특정 reader로 쏠릴 수 있다. 특히 장수명 connection pool은 DNS 변화를 즉시 반영하지 못할 수 있다. 그 결과 클러스터 전체 연결 수는 적절해 보여도 개별 reader의 Threads_connected와 CPU가 비대칭으로 커질 수 있다.

3.3 세션 상태와 pinning 문제

Aurora 앞단에 proxy를 두더라도 모든 세션이 완전히 자유롭게 재사용되는 것은 아니다. 트랜잭션이 열린 상태, temporary table 사용, session variable 변경, prepared statement 사용 패턴, locking read, user variable 같은 요소는 세션 pinning을 유발할 수 있다. 특히 Amazon RDS Proxy는 많은 연결을 효율적으로 재사용해 주지만, 애플리케이션이 세션 상태를 강하게 의존할수록 multiplexing 이득이 줄어든다.

4. max_connections는 메모리 수치가 아니라 동시성 예산에서 출발해야 한다

실무에서 더 안전한 접근 순서는 다음과 같다.

  1. 먼저 DB가 안정적으로 감당 가능한 Threads_running 범위를 찾는다.
  2. 그 범위를 기준으로 전체 서비스의 DB 동시성 예산을 정한다.
  3. 각 애플리케이션 인스턴스의 worker 수, coroutine fan-out, pool size를 그 예산 안에 배분한다.
  4. 마지막으로 max_connections는 운영 여유와 관리 세션을 포함한 가드레일로 둔다.

즉, 순서는 보통 사람들이 생각하는 것과 반대다.

  • 잘못된 순서: max_connections를 크게 잡음 → 애플리케이션이 그만큼 사용함
  • 올바른 순서: 안정적인 동시 실행 범위를 찾음 → 애플리케이션이 그 범위를 넘지 않게 설계함 → max_connections는 최종 보호벽으로 둠

4.1 산정의 실무적 출발점

다음 항목을 함께 본다.

  • 피크 시 Threads_running 분포
  • p95/p99 latency가 급격히 나빠지는 동시성 구간
  • CPU 사용률과 iowait
  • lock wait과 transaction 체류 시간
  • 메모리 headroom
  • failover 직후 reconnect burst 규모

예를 들어 writer 인스턴스가 Threads_running 24~32 구간에서 가장 안정적이고, 40을 넘기면 commit latency와 lock wait이 급증한다면, 애플리케이션 fleet 전체가 동시에 writer에 밀어 넣는 실질 동시 실행은 그 범위를 크게 넘지 않도록 설계하는 편이 낫다. 이때 max_connections를 500으로 올리는 행위는 안정성 향상이 아니라 과부하를 더 늦게 드러내는 조치가 될 수 있다.

4.2 연결 수 상한과 실제 동시 실행 수를 분리해야 한다

pool 환경에서는 다음 둘이 다르다.

  • Threads_connected: 열려 있는 세션 수
  • Threads_running: 실제로 실행 중인 스레드 수

운영자는 Threads_connected가 아니라 실행 중인 스레드와 느린 세션 체류 시간을 먼저 봐야 한다. 다만 Aurora에서는 failover, 배포, 재시작 직후에 Threads_connected의 급증 자체가 리스크 신호가 될 수 있으므로, 두 지표를 분리해서 해석해야 한다.

5. 현재 연결 여유와 재연결 압력을 읽는 기본 SQL

먼저 인스턴스의 연결 상한과 현재 연결 압력을 분리해서 확인한다.

SELECT VERSION() AS mysql_version,
       @@global.max_connections AS max_connections,
       @@global.max_user_connections AS max_user_connections,
       @@global.thread_cache_size AS thread_cache_size,
       @@global.wait_timeout AS wait_timeout,
       @@global.interactive_timeout AS interactive_timeout;

SHOW GLOBAL STATUS LIKE 'Threads_connected';
SHOW GLOBAL STATUS LIKE 'Threads_running';
SHOW GLOBAL STATUS LIKE 'Max_used_connections';
SHOW GLOBAL STATUS LIKE 'Connection_errors_max_connections';
SHOW GLOBAL STATUS LIKE 'Connections';
SHOW GLOBAL STATUS LIKE 'Aborted_connects';

실행 결과(MySQL 8.0.46):

mysql> SELECT VERSION() AS mysql_version,
    ->        @@global.max_connections AS max_connections,
    ->        @@global.max_user_connections AS max_user_connections,
    ->        @@global.thread_cache_size AS thread_cache_size,
    ->        @@global.wait_timeout AS wait_timeout,
    ->        @@global.interactive_timeout AS interactive_timeout;

+---------------+-----------------+----------------------+-------------------+--------------+---------------------+
| mysql_version | max_connections | max_user_connections | thread_cache_size | wait_timeout | interactive_timeout |
+---------------+-----------------+----------------------+-------------------+--------------+---------------------+
| 8.0.46        |              30 |                    0 |                 8 |        28800 |               28800 |
+---------------+-----------------+----------------------+-------------------+--------------+---------------------+
1 row in set (0.00 sec)

mysql> SHOW GLOBAL STATUS LIKE 'Threads_connected';

+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| Threads_connected | 1     |
+-------------------+-------+
1 row in set (0.00 sec)

mysql> SHOW GLOBAL STATUS LIKE 'Threads_running';

+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| Threads_running | 2     |
+-----------------+-------+
1 row in set (0.00 sec)

mysql> SHOW GLOBAL STATUS LIKE 'Max_used_connections';

+----------------------+-------+
| Variable_name        | Value |
+----------------------+-------+
| Max_used_connections | 1     |
+----------------------+-------+
1 row in set (0.00 sec)

mysql> SHOW GLOBAL STATUS LIKE 'Connection_errors_max_connections';

+-----------------------------------+-------+
| Variable_name                     | Value |
+-----------------------------------+-------+
| Connection_errors_max_connections | 0     |
+-----------------------------------+-------+
1 row in set (0.00 sec)

mysql> SHOW GLOBAL STATUS LIKE 'Connections';

+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Connections   | 11    |
+---------------+-------+
1 row in set (0.00 sec)

mysql> SHOW GLOBAL STATUS LIKE 'Aborted_connects';

+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| Aborted_connects | 0     |
+------------------+-------+
1 row in set (0.00 sec)

이 결과를 읽을 때는 다음 순서를 권장한다.

  1. Max_used_connectionsmax_connections에 얼마나 근접했는지 확인한다.
  2. Threads_connected보다 Threads_running의 피크와 체류 시간을 더 중요하게 본다.
  3. Connection_errors_max_connections가 0이 아니면 이미 hard limit 충돌이 있었다는 뜻이다.
  4. Connections 증가 속도가 배포·failover 직후 비정상적으로 빨라지는지 본다.
  5. Aborted_connects가 튀는 시점을 애플리케이션 로그, 네트워크 이벤트, failover 기록과 대조한다.

이 SQL은 단순하지만, 연결 한도 문제인지, 느린 쿼리/락 경합 문제인지, 재연결 폭발 문제인지 1차 분류하는 데 매우 유용하다.

6. foreground 세션 분포를 보면 pool이 어떻게 실제로 쓰이는지 보인다

총 연결 수만 보면 “연결이 많다/적다” 수준에서 멈춘다. 실제 운영에서는 어떤 사용자와 호스트가 세션을 많이 들고 있는지, 대부분 sleep인지, foreground 쿼리가 얼마나 살아 있는지를 봐야 한다.

SELECT COALESCE(PROCESSLIST_USER, 'system') AS processlist_user,
       COALESCE(PROCESSLIST_HOST, 'internal') AS processlist_host,
       PROCESSLIST_COMMAND,
       COUNT(*) AS session_count
FROM performance_schema.threads
WHERE TYPE = 'FOREGROUND'
GROUP BY COALESCE(PROCESSLIST_USER, 'system'),
         COALESCE(PROCESSLIST_HOST, 'internal'),
         PROCESSLIST_COMMAND
ORDER BY session_count DESC,
         processlist_user,
         processlist_host,
         PROCESSLIST_COMMAND
LIMIT 10;

실행 결과(MySQL 8.0.46):

mysql> SELECT COALESCE(PROCESSLIST_USER, 'system') AS processlist_user,
    ->        COALESCE(PROCESSLIST_HOST, 'internal') AS processlist_host,
    ->        PROCESSLIST_COMMAND,
    ->        COUNT(*) AS session_count
    -> FROM performance_schema.threads
    -> WHERE TYPE = 'FOREGROUND'
    -> GROUP BY COALESCE(PROCESSLIST_USER, 'system'),
    ->          COALESCE(PROCESSLIST_HOST, 'internal'),
    ->          PROCESSLIST_COMMAND
    -> ORDER BY session_count DESC,
    ->          processlist_user,
    ->          processlist_host,
    ->          PROCESSLIST_COMMAND
    -> LIMIT 10;

+------------------+------------------+---------------------+---------------+
| processlist_user | processlist_host | PROCESSLIST_COMMAND | session_count |
+------------------+------------------+---------------------+---------------+
| event_scheduler  | localhost        | Daemon              |             1 |
| root             | localhost        | Query               |             1 |
| system           | internal         | Daemon              |             1 |
+------------------+------------------+---------------------+---------------+
3 rows in set (0.00 sec)

이 쿼리는 다음 판단에 도움이 된다.

  • 특정 애플리케이션 인스턴스가 연결을 비정상적으로 많이 점유하는가
  • Sleep 세션이 대부분인데도 pool 크기가 과도하게 큰가
  • 관리 세션이나 배치 계정이 연결 예산을 잠식하는가
  • foreground 세션이 failover 이후 특정 host로 쏠리는가

Aurora reader endpoint를 쓰는 환경에서는 동일한 애플리케이션 계정이라도 host 분포와 command 분포를 함께 봐야 연결 쏠림을 더 빨리 포착할 수 있다.

7. 메모리 관련 변수는 max_connections와 곱셈하기 전에 의미부터 구분해야 한다

연결 수를 메모리 관점에서 논의할 때 흔히 다음과 같은 단순 계산이 나온다.

세션당 버퍼 합 × max_connections = 필요한 메모리

이 계산은 방향성은 맞지만 그대로 믿기에는 위험하다. 이유는 버퍼가 항상 동시에, 같은 크기로, 모든 세션에 잡히지 않기 때문이다. 반대로 “항상 그런 건 아니니 무시해도 된다”는 결론도 위험하다. 운영에서는 어떤 버퍼가 상시 비용인지, 어떤 버퍼가 실행 중 급증 비용인지를 구분해야 한다.

SELECT @@global.read_buffer_size AS read_buffer_size,
       @@global.read_rnd_buffer_size AS read_rnd_buffer_size,
       @@global.sort_buffer_size AS sort_buffer_size,
       @@global.join_buffer_size AS join_buffer_size,
       @@global.thread_stack AS thread_stack,
       @@global.tmp_table_size AS tmp_table_size,
       @@global.max_heap_table_size AS max_heap_table_size,
       @@global.innodb_buffer_pool_size AS innodb_buffer_pool_size;

실행 결과(MySQL 8.0.46):

mysql> SELECT @@global.read_buffer_size AS read_buffer_size,
    ->        @@global.read_rnd_buffer_size AS read_rnd_buffer_size,
    ->        @@global.sort_buffer_size AS sort_buffer_size,
    ->        @@global.join_buffer_size AS join_buffer_size,
    ->        @@global.thread_stack AS thread_stack,
    ->        @@global.tmp_table_size AS tmp_table_size,
    ->        @@global.max_heap_table_size AS max_heap_table_size,
    ->        @@global.innodb_buffer_pool_size AS innodb_buffer_pool_size;

+------------------+----------------------+------------------+------------------+--------------+----------------+---------------------+-------------------------+
| read_buffer_size | read_rnd_buffer_size | sort_buffer_size | join_buffer_size | thread_stack | tmp_table_size | max_heap_table_size | innodb_buffer_pool_size |
+------------------+----------------------+------------------+------------------+--------------+----------------+---------------------+-------------------------+
|           131072 |               262144 |           262144 |           262144 |      1048576 |       16777216 |            16777216 |                67108864 |
+------------------+----------------------+------------------+------------------+--------------+----------------+---------------------+-------------------------+
1 row in set (0.00 sec)

이 결과를 해석할 때의 실무 원칙은 다음과 같다.

  • innodb_buffer_pool_size는 큰 고정 예산에 가깝다.
  • thread_stack과 연결 메타데이터는 연결 수 증가와 함께 비교적 예측 가능하게 늘어난다.
  • sort_buffer_size, join_buffer_size, read_buffer_size, read_rnd_buffer_size는 실행 패턴에 따라 사용량이 튄다.
  • tmp_table_sizemax_heap_table_size는 세션당 즉시 예약되는 메모리가 아니라 internal temporary table 동작의 상한 성격으로 봐야 한다.

따라서 max_connections를 산정할 때는 “이론상 최악의 곱셈”과 “평소 관측치” 사이에서 안전한 headroom을 두는 편이 낫다. 특히 Aurora에서 writer failover 직후 캐시가 덜 warm한 상태에서는 평소보다 쿼리 시간이 길어지고, 그만큼 세션 체류 시간이 길어져 메모리 압력도 늦게 해소될 수 있다.

8. proxy가 필요한 경우와 애플리케이션 pool만으로 충분한 경우를 구분해야 한다

모든 Aurora MySQL 환경에 proxy가 필요한 것은 아니다. 반대로 connection pool이 있다는 이유로 proxy가 불필요한 것도 아니다. 핵심은 문제가 연결 재사용 효율의 부족인지, 재연결 폭발과 장애 흡수의 부족인지, 또는 세션 상태 때문에 pool이 과도하게 커지는지를 구분하는 데 있다.

8.1 애플리케이션 connection pool만으로 충분한 경우

다음 조건이 대체로 만족되면 애플리케이션 pool만으로도 충분할 수 있다.

  • 애플리케이션 인스턴스 수가 많지 않다.
  • 각 인스턴스 pool size가 작고 예측 가능하다.
  • 세션 상태를 오래 유지하지 않는다.
  • failover 시 재연결 폭발 규모가 제한적이다.
  • 짧은 checkout timeout, 명확한 backpressure, fast-fail 정책이 있다.
  • writer와 reader 용도가 코드 수준에서 잘 분리되어 있다.

즉, fleet 전체 연결 수와 reconnect burst를 이미 애플리케이션 계층에서 잘 통제하고 있다면 굳이 중간 계층을 추가하지 않아도 된다.

8.2 중간 proxy 또는 RDS Proxy 도입을 고려해야 하는 경우

아래 조건이 반복되면 proxy 계층 검토 가치가 높다.

  • 서버리스/오토스케일 환경으로 애플리케이션 인스턴스 수가 급격히 늘어난다.
  • Lambda, Fargate, ephemeral pod처럼 연결 churn이 매우 크다.
  • failover 직후 대량 재연결로 writer가 불안정해진다.
  • 애플리케이션이 많아 개별 팀별 pool 조정만으로 총량 통제가 어렵다.
  • reader/writer endpoint 전환, DNS 반영, 연결 재사용을 중앙에서 더 일관되게 다루고 싶다.
  • 연결은 많은데 동시에 실제 일하는 쿼리는 상대적으로 적어 multiplexing 이득이 있다.

9. Amazon RDS Proxy를 선택할 때의 실익과 한계

Amazon RDS Proxy의 장점은 분명하다.

  • 애플리케이션 연결 churn을 DB 백엔드 연결보다 더 많이 흡수할 수 있다.
  • failover 시 애플리케이션이 직접 모든 재연결 충격을 맞지 않도록 완충층을 둔다.
  • credential rotation과 인증 관리를 단순화할 수 있다.
  • 짧은 수명 연결이 매우 많은 워크로드에서 백엔드 연결 수를 줄일 수 있다.

그러나 다음 한계도 반드시 이해해야 한다.

9.1 모든 연결이 자유롭게 multiplexing 되지는 않는다

다음 패턴은 pinning 가능성을 높인다.

  • 긴 트랜잭션
  • temporary table 사용
  • session variable 변경 의존
  • user variable 사용
  • 세션 상태를 전제로 한 prepared statement 사용 패턴
  • 트랜잭션 경계가 불명확한 ORM 사용

즉, RDS Proxy를 넣는다고 자동으로 백엔드 연결 수가 극적으로 줄어드는 것은 아니다. 애플리케이션이 세션 상태에 많이 의존할수록 이득은 줄어든다.

9.2 느린 쿼리 자체는 proxy가 해결하지 못한다

병목의 본질이 lock wait, 잘못된 인덱스, 과도한 sort, hot row contention이라면 proxy는 문제를 숨기거나 완충할 뿐 근본 원인을 제거하지 못한다. proxy 앞단에서 timeout이 줄어드는 것처럼 보여도 writer 내부 Threads_running과 쿼리 latency가 나빠지면 결국 다른 형태의 장애로 돌아온다.

9.3 운영 책임이 줄어드는 대신 관측 포인트가 하나 더 생긴다

proxy 계층을 넣으면 애플리케이션 pool 지표, proxy 지표, Aurora 지표를 함께 봐야 한다. 즉, 단순화되는 부분이 있는 반면 관측 계층은 하나 늘어난다. 장애 분석 시 “애플리케이션 checkout 대기”, “proxy backend connection 부족”, “Aurora writer 내부 과부하”를 구분해야 한다.

10. 운영 판단 흐름: pool, proxy, RDS Proxy를 어떤 순서로 검토할 것인가

다음 흐름으로 판단하면 불필요한 복잡성을 줄일 수 있다.

flowchart TD
    A[연결 문제가 관측됨] --> B{주된 증상이 무엇인가}
    B -->|Threads_running 급증, 쿼리 지연, lock wait| C[먼저 SQL/인덱스/트랜잭션 병목 분석]
    B -->|연결 churn 급증, failover 직후 재연결 폭발| D[proxy 또는 RDS Proxy 검토]
    B -->|Sleep 세션 과다, fleet 총 pool 과대| E[애플리케이션 pool 크기와 worker 예산 재설계]
    C --> F{병목 해소 후에도 연결 폭증이 남는가}
    F -->|예| D
    F -->|아니오| G[애플리케이션 pool 유지]
    D --> H{세션 pinning 요소가 강한가}
    H -->|예| I[RDS Proxy 이득 제한적, 코드/세션 상태 정리 우선]
    H -->|아니오| J[RDS Proxy 또는 적절한 proxy 도입 검토]
    E --> K{서버리스/오토스케일로 churn이 계속 큰가}
    K -->|예| J
    K -->|아니오| G

이 다이어그램이 말하는 핵심은 단순하다. 느린 쿼리 문제를 연결 계층으로 해결하려 하지 말고, 연결 churn 문제를 쿼리 튜닝만으로 해결하려고도 하지 말아야 한다.

11. Aurora 운영에서 특히 주의할 실패 패턴

11.1 max_connections만 올리고 pool을 그대로 두는 패턴

이 경우 장애는 늦게 드러나지만 더 크게 터진다. 애플리케이션은 더 많은 요청을 DB에 밀어 넣고, DB는 더 긴 대기열과 더 큰 메모리 압력을 떠안는다. 장애 시 회복 시간도 길어진다.

11.2 reader endpoint를 쓰지만 실제로는 long-lived pool 때문에 부하 재분산이 잘 안 되는 패턴

클러스터 레벨에서는 reader를 여러 대 운영해도, 애플리케이션이 기존 연결을 오래 붙잡고 있으면 특정 reader에 부하가 고정될 수 있다. 이 경우 pool recycle 정책과 DNS TTL, 커넥션 수명 정책을 함께 봐야 한다.

11.3 failover 후 한꺼번에 warm-up 쿼리가 몰리는 패턴

애플리케이션 기동 시점 또는 failover 후에 prepared statement 재준비, metadata 조회, 초기화 SQL, health check가 동시에 몰리면 정상 쿼리보다 이 warm-up 부하가 먼저 문제를 만들 수 있다. 이 경우 staggered reconnect, exponential backoff, jitter가 필요하다.

11.4 transaction 경계가 느슨해 proxy 이득이 줄어드는 패턴

코드 상에서 auto-commit 여부가 불명확하거나, read-only 조회도 transaction 안에서 길게 머무르면 connection reuse 이득이 줄고 pinning이 늘어난다. RDS Proxy를 도입하기 전에 애플리케이션의 transaction discipline부터 점검해야 하는 이유다.

12. 실무 체크리스트

12.1 max_connections 산정 체크리스트

  • 피크 시 Threads_running
  • max_connections

12.2 애플리케이션 pool 점검 체크리스트

12.3 proxy/RDS Proxy 검토 체크리스트

13. 결론

Aurora MySQL 연결 관리는 결국 숫자 하나를 정하는 작업이 아니다. max_connections는 메모리 한도, 동시성 한도, 장애 완충 한도를 동시에 의미하며, 애플리케이션 pool과 failover 동작, reconnect burst, 세션 상태 의존성이 모두 여기에 얽혀 있다.

실무적으로는 다음 원칙을 기억하는 편이 안전하다.

  1. max_connections는 먼저 키우는 값이 아니라 마지막에 정하는 가드레일이다.
  2. 안정적인 Threads_running 범위를 먼저 찾고, fleet 전체 pool 예산을 그 안에 맞춘다.
  3. 연결 churn과 failover 충격이 크면 proxy 또는 Amazon RDS Proxy를 검토한다.
  4. 다만 세션 pinning과 느린 쿼리 문제는 proxy만으로 해결되지 않는다.

다음 기술노트에서는 Aurora나 MySQL 연결 문제를 더 깊게 해석하기 위해, 실제 세션 상태와 waiting point를 어떻게 관측할지 performance_schema와 운영 지표 관점에서 이어서 다룰 수 있다.