카테고리 : MySQL/기술노트

MySQL 연결 수명주기: handshake, authentication, thread allocation

MySQL 클라이언트 연결이 생성될 때 handshake, 인증, 세션 스레드 할당이 어떤 순서로 진행되는지 운영 관점에서 정리한다.

저자: MySQL 기술 노트 작성: 2026.06.19 약 14분 7,904자
다운로드

1. 연결 수명주기를 이해해야 하는 이유

MySQL 성능 문제를 다룰 때 많은 운영자는 먼저 느린 SQL, 인덱스, InnoDB 락, buffer pool을 떠올린다. 그러나 실제 장애 현장에서는 SQL이 실행되기도 전에 문제가 시작되는 경우가 적지 않다. 애플리케이션이 갑자기 Too many connections, Access denied, Lost connection, Aborted connection, Communications link failure 같은 오류를 내거나, DB CPU가 낮은데도 접속 지연이 커지는 상황이 대표적이다.

이때 필요한 관점은 “쿼리가 왜 느린가”가 아니라 연결 하나가 MySQL 서버 안에서 어떤 순서로 만들어지고, 인증되고, 실행 스레드에 배정되며, 종료되는가이다. 연결 수명주기를 이해하면 다음 문제를 더 정확히 분류할 수 있다.

  • 애플리케이션 connection pool 설정이 max_connections와 맞지 않아 접속 폭주가 나는지 확인할 수 있다.
  • DNS 역조회, TLS, 인증 플러그인, 패스워드 검증, 계정 매칭 중 어느 단계에서 지연이 생기는지 가설을 세울 수 있다.
  • Threads_connected, Threads_running, Threads_created, Connections, Aborted_connects 같은 상태값을 연결 생명주기와 연결해 해석할 수 있다.
  • thread cache가 충분한지, 매 연결마다 OS thread 생성 비용을 반복하고 있는지 판단할 수 있다.
  • Aurora MySQL처럼 엔드포인트, 프록시, failover, IAM 인증, TLS 정책이 개입하는 환경에서 애플리케이션 접속 오류를 DB 내부 문제와 구분할 수 있다.

MySQL 연결은 단순히 TCP 소켓이 열리는 사건이 아니다. 서버 프로세스, 네트워크 스택, protocol handshake, 계정/host 매칭, 인증 플러그인, TLS, connection attributes, session 초기화, thread allocation, 권한 컨텍스트가 모두 관여하는 작은 트랜잭션에 가깝다. 짧고 빈번하게 반복되므로 평소에는 보이지 않지만, 장애 시에는 매우 큰 비용으로 드러난다.

2. 전체 흐름: TCP accept에서 세션 준비까지

MySQL 서버는 클라이언트 접속 요청을 받으면 대략 다음 순서로 연결을 처리한다. 세부 구현은 버전과 설정, TLS 사용 여부, 인증 플러그인, 운영체제에 따라 달라질 수 있지만, 운영 관점에서의 큰 흐름은 안정적으로 이해해 둘 필요가 있다.

sequenceDiagram
    autonumber
    participant C as Client / Connector
    participant N as TCP Socket
    participant S as mysqld Listener
    participant A as Authentication Layer
    participant T as Thread / Session Context
    participant E as SQL Executor

    C->>N: TCP connect
    N->>S: accept()
    S->>C: Initial Handshake Packet
    C->>A: Handshake Response<br/>user, capabilities, auth data, attrs
    A->>A: account host match<br/>auth plugin verification<br/>TLS / password policy checks
    A-->>C: OK Packet or ERR Packet
    A->>T: THD/session context 준비
    T->>T: thread cache 재사용 또는 새 thread 생성
    C->>E: COM_QUERY / COM_STMT_PREPARE 등 명령 전송
    E-->>C: result / OK / error
    C->>S: COM_QUIT 또는 socket close
    S->>T: 세션 정리 및 thread cache 반환

이 흐름에서 운영자가 특히 주목해야 할 지점은 네 가지다.

  1. TCP 연결 수립: DB 서버까지 네트워크 경로가 정상인지, 방화벽·로드밸런서·프록시가 idle timeout을 끊고 있지 않은지 확인해야 한다.
  2. Initial Handshake와 capability 협상: 서버 버전, connection ID, 인증 플러그인, character set, protocol capability가 교환된다.
  3. Authentication: MySQL 계정은 user@host 조합으로 매칭되며, 인증 플러그인과 TLS 조건, 권한 상태가 확인된다.
  4. Thread allocation과 session context: 연결은 서버 내부의 THD 구조와 실행 thread에 연결된다. thread cache가 부족하면 새 OS thread 생성 비용이 반복된다.

연결 수명주기 문제는 보통 단일 지표 하나로 확정하기 어렵다. 예를 들어 Aborted_connects 증가는 인증 실패, 패킷 오류, handshake 실패, 클라이언트 조기 종료 등 여러 원인을 가질 수 있다. 따라서 연결 관련 상태값, error log, 애플리케이션 connection pool 로그, 네트워크 계층 로그를 함께 읽어야 한다.

3. Handshake: 서버와 클라이언트가 서로의 능력을 맞추는 단계

클라이언트가 MySQL 포트에 TCP 연결을 열면, 서버는 먼저 Initial Handshake Packet을 보낸다. 여기에는 protocol version, server version, connection ID, 인증 scramble, 기본 인증 플러그인, capability flags 등이 포함된다. 클라이언트는 이에 대한 응답으로 사용자명, 선택한 capability, character set, 인증 응답 데이터, connection attributes 등을 보낸다.

Handshake 단계에서 중요한 운영 포인트는 다음과 같다.

  • TLS 사용 여부: TLS를 강제하는 계정이나 서버 설정에서는 클라이언트가 TLS capability를 맞추지 못하면 인증 전에 실패할 수 있다.
  • 인증 플러그인 호환성: MySQL 8.0의 기본 인증 플러그인은 caching_sha2_password 계열이다. 오래된 connector는 이를 제대로 지원하지 못해 접속 실패를 만들 수 있다.
  • 클라이언트 capability 차이: prepared statement, multi statements, compression, plugin authentication 등은 capability 협상에 영향을 받는다.
  • connection attributes: 일부 connector는 애플리케이션명, 드라이버 버전, program name 같은 속성을 서버에 보낸다. Performance Schema에서 이를 관측할 수 있으면 장애 추적에 도움이 된다.

현재 서버의 연결 관련 기본 변수와 상태값은 다음처럼 확인할 수 있다.

SELECT VERSION() AS mysql_version;

SHOW VARIABLES
WHERE Variable_name IN (
  'max_connections',
  'thread_cache_size',
  'skip_name_resolve',
  'default_authentication_plugin',
  'require_secure_transport'
);

SHOW GLOBAL STATUS
WHERE Variable_name IN (
  'Connections',
  'Threads_connected',
  'Threads_running',
  'Threads_created',
  'Aborted_connects',
  'Connection_errors_max_connections'
);

실행 결과(MySQL 8.0.46):

mysql> SELECT VERSION() AS mysql_version;

+---------------+
| mysql_version |
+---------------+
| 8.0.46        |
+---------------+
1 row in set (0.00 sec)

mysql> SHOW VARIABLES
    -> WHERE Variable_name IN (
    ->   'max_connections',
    ->   'thread_cache_size',
    ->   'skip_name_resolve',
    ->   'default_authentication_plugin',
    ->   'require_secure_transport'
    -> );

+-------------------------------+-----------------------+
| Variable_name                 | Value                 |
+-------------------------------+-----------------------+
| default_authentication_plugin | caching_sha2_password |
| max_connections               | 30                    |
| require_secure_transport      | OFF                   |
| skip_name_resolve             | ON                    |
| thread_cache_size             | 8                     |
+-------------------------------+-----------------------+
5 rows in set (0.01 sec)

mysql> SHOW GLOBAL STATUS
    -> WHERE Variable_name IN (
    ->   'Connections',
    ->   'Threads_connected',
    ->   'Threads_running',
    ->   'Threads_created',
    ->   'Aborted_connects',
    ->   'Connection_errors_max_connections'
    -> );

+-----------------------------------+-------+
| Variable_name                     | Value |
+-----------------------------------+-------+
| Aborted_connects                  | 0     |
| Connection_errors_max_connections | 0     |
| Connections                       | 11    |
| Threads_connected                 | 1     |
| Threads_created                   | 1     |
| Threads_running                   | 2     |
+-----------------------------------+-------+
6 rows in set (0.00 sec)

이 쿼리는 연결 수명주기 전체를 한 번에 보여 주지는 않는다. 다만 “접속 총량”, “현재 연결 수”, “실행 중인 thread 수”, “새 thread 생성 누적”, “handshake/인증 전후 실패”를 같은 화면에서 보게 해 준다. 특히 Connections 증가 속도에 비해 Threads_created가 계속 빠르게 증가하면 thread cache가 충분하지 않거나 짧은 연결을 과도하게 만들고 있을 가능성이 있다.

4. Authentication: user@host 매칭과 인증 플러그인

MySQL 계정은 단순히 사용자명만으로 식별되지 않는다. 서버는 접속한 클라이언트의 host 정보와 사용자명을 조합해 mysql.user의 계정 행을 찾는다. 이때 'app'@'10.%', 'app'@'%', 'app'@'localhost'처럼 여러 후보가 있을 수 있으며, MySQL은 더 구체적인 host 매칭을 우선한다. 따라서 같은 사용자명이라도 접속 위치에 따라 다른 계정 행, 다른 인증 플러그인, 다른 권한이 적용될 수 있다.

인증 단계의 대표적인 실패 원인은 다음과 같다.

  • 비밀번호 오류 또는 connector가 잘못된 계정으로 접속한다.
  • user@host 매칭이 의도와 다르다.
  • 서버는 caching_sha2_password를 기대하지만 connector가 해당 플러그인을 지원하지 못한다.
  • TLS 필수 계정인데 클라이언트가 TLS 없이 접속한다.
  • DNS 역조회가 느리거나 실패해 host 매칭 지연이 발생한다.
  • 계정 잠금, password expired, 권한 변경 직후의 연결 재시도 등 계정 상태 문제가 있다.

다음 쿼리는 테스트 서버에서 계정별 인증 플러그인을 확인하는 예시다. 운영 서버에서 실행할 때는 계정명이 민감 정보일 수 있으므로 결과 공유 범위에 주의해야 한다.

SELECT user, host, plugin, account_locked, password_expired
FROM mysql.user
ORDER BY user, host
LIMIT 10;

실행 결과(MySQL 8.0.46):

mysql> SELECT user, host, plugin, account_locked, password_expired
    -> FROM mysql.user
    -> ORDER BY user, host
    -> LIMIT 10;

+------------------+-----------+-----------------------+----------------+------------------+
| user             | host      | plugin                | account_locked | password_expired |
+------------------+-----------+-----------------------+----------------+------------------+
| mysql.infoschema | localhost | caching_sha2_password | Y              | N                |
| mysql.session    | localhost | caching_sha2_password | Y              | N                |
| mysql.sys        | localhost | caching_sha2_password | Y              | N                |
| root             | %         | caching_sha2_password | N              | N                |
| root             | localhost | caching_sha2_password | N              | N                |
+------------------+-----------+-----------------------+----------------+------------------+
5 rows in set (0.00 sec)

운영 해석에서 중요한 점은 Access denied가 모두 같은 문제가 아니라는 것이다. 비밀번호가 틀린 경우, host 매칭이 다른 경우, 플러그인 호환성이 맞지 않는 경우, TLS 요구 조건이 맞지 않는 경우 모두 클라이언트에는 비슷한 접속 실패로 보일 수 있다. 애플리케이션 로그의 에러 코드와 MySQL error log, 계정 정의, connector 버전을 함께 확인해야 한다.

skip_name_resolve도 인증 단계에서 자주 등장한다. 이 옵션이 비활성화되어 있으면 MySQL은 접속한 IP를 host name으로 역조회할 수 있다. DNS가 느리거나 불안정하면 접속 자체가 느려질 수 있다. 대규모 운영 환경에서는 보통 skip_name_resolve=ON과 IP/CIDR 기반 계정 정책을 선호하지만, 이 경우 host 이름 기반 계정은 사용할 수 없으므로 계정 설계를 함께 정리해야 한다.

5. Thread allocation: 연결은 어디에서 실행되는가

인증이 끝난 연결은 MySQL 내부에서 session context와 실행 thread에 연결된다. MySQL의 전통적인 Community Server 구조에서는 foreground connection마다 thread가 배정되는 모델을 기본으로 이해하면 된다. 연결이 종료되면 thread는 즉시 사라질 수도 있고, thread_cache_size 설정과 현재 cache 상태에 따라 thread cache로 돌아가 다음 연결에서 재사용될 수도 있다.

이 모델은 단순하지만, 연결 폭주에 민감하다. connection pool 없이 요청마다 새 연결을 만들면 MySQL은 매번 handshake, authentication, thread allocation, session initialization을 반복한다. 쿼리 자체가 가벼워도 연결 생성 비용이 전체 응답 시간을 지배할 수 있다.

Performance Schema가 활성화되어 있으면 foreground thread와 processlist 정보를 다음처럼 확인할 수 있다.

SELECT THREAD_ID,
       PROCESSLIST_ID,
       PROCESSLIST_USER,
       PROCESSLIST_HOST,
       PROCESSLIST_COMMAND,
       PROCESSLIST_STATE
FROM performance_schema.threads
WHERE TYPE = 'FOREGROUND'
  AND PROCESSLIST_ID IS NOT NULL
ORDER BY THREAD_ID
LIMIT 10;

실행 결과(MySQL 8.0.46):

mysql> SELECT THREAD_ID,
    ->        PROCESSLIST_ID,
    ->        PROCESSLIST_USER,
    ->        PROCESSLIST_HOST,
    ->        PROCESSLIST_COMMAND,
    ->        PROCESSLIST_STATE
    -> FROM performance_schema.threads
    -> WHERE TYPE = 'FOREGROUND'
    ->   AND PROCESSLIST_ID IS NOT NULL
    -> ORDER BY THREAD_ID
    -> LIMIT 10;

+-----------+----------------+------------------+------------------+---------------------+------------------------+
| THREAD_ID | PROCESSLIST_ID | PROCESSLIST_USER | PROCESSLIST_HOST | PROCESSLIST_COMMAND | PROCESSLIST_STATE      |
+-----------+----------------+------------------+------------------+---------------------+------------------------+
|        41 |              5 | event_scheduler  | localhost        | Daemon              | Waiting on empty queue |
|        45 |              7 | NULL             | NULL             | Daemon              | Suspending             |
|        51 |             13 | root             | localhost        | Query               | executing              |
+-----------+----------------+------------------+------------------+---------------------+------------------------+
3 rows in set (0.00 sec)

이 결과는 현재 접속한 세션이 서버 내부에서 어떤 thread로 표현되는지 보여 준다. 운영 서버에서는 PROCESSLIST_USER, PROCESSLIST_HOST, PROCESSLIST_COMMAND, PROCESSLIST_STATE를 기준으로 애플리케이션별 접속 상태를 분리해서 볼 수 있다. 단, Performance Schema 설정과 MySQL 버전에 따라 관측 가능한 컬럼과 값이 달라질 수 있으므로, 자동화 스크립트에서는 대상 버전에서 컬럼 존재 여부를 먼저 확인하는 것이 안전하다.

Thread cache 상태는 다음 상태값으로 감을 잡을 수 있다.

SHOW GLOBAL STATUS
WHERE Variable_name IN (
  'Connections',
  'Threads_cached',
  'Threads_connected',
  'Threads_created',
  'Threads_running'
);

실행 결과(MySQL 8.0.46):

mysql> SHOW GLOBAL STATUS
    -> WHERE Variable_name IN (
    ->   'Connections',
    ->   'Threads_cached',
    ->   'Threads_connected',
    ->   'Threads_created',
    ->   'Threads_running'
    -> );

+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| Connections       | 14    |
| Threads_cached    | 0     |
| Threads_connected | 1     |
| Threads_created   | 1     |
| Threads_running   | 2     |
+-------------------+-------+
5 rows in set (0.00 sec)

해석 기준은 단순하다.

  • Connections는 서버 시작 이후 누적 접속 시도 수다.
  • Threads_connected는 현재 열린 연결 수다.
  • Threads_running은 현재 실제로 실행 중인 thread 수에 가깝다.
  • Threads_created는 새 thread 생성 누적 수다.
  • Threads_cached는 재사용 대기 중인 thread 수다.

서버 시작 후 Connections가 매우 많지만 Threads_created가 낮게 유지된다면 thread cache가 잘 작동하고 있는 것이다. 반대로 접속이 많을 때 Threads_created가 계속 증가하면 thread cache가 부족하거나 연결 재사용 패턴이 좋지 않을 수 있다. 이때 무조건 thread_cache_size만 키우기보다는 애플리케이션 connection pool의 min/max, idle timeout, validation query, failover 재접속 정책을 함께 점검해야 한다.

6. Connection pool과 max_connections의 관계

max_connections는 단순히 “크면 안전한 값”이 아니다. 연결 하나는 메모리, thread, Performance Schema instrumentation, session variables, 임시 테이블, prepared statement, transaction state를 가질 수 있다. 연결 수를 과도하게 열어 두면 실제 쿼리가 실행되지 않아도 서버 리소스를 점유한다.

운영에서는 다음 관계를 먼저 계산해야 한다.

총 잠재 연결 수 = 애플리케이션 인스턴스 수 × 인스턴스별 최대 pool size × DB 사용자/용도별 pool 수

예를 들어 애플리케이션 서버 20대가 있고 각 서버가 reader/writer pool을 각각 30개씩 유지하면 잠재 연결 수는 1,200개다. 이 값이 DB의 max_connections보다 작더라도 안전하다고 단정할 수 없다. failover, 배치 작업, 운영자 접속, 모니터링, migration 도구, read replica 재접속, connection leak을 위한 여유가 필요하다.

연결 수가 많아질 때 흔히 나타나는 잘못된 대응은 다음과 같다.

  • 애플리케이션 pool을 줄이지 않고 DB의 max_connections만 계속 키운다.
  • Threads_connected가 높지만 Threads_running이 낮은 상황을 DB 처리량 부족으로 오해한다.
  • idle connection이 많은데도 DB 서버 스펙을 증설한다.
  • failover 후 모든 인스턴스가 동시에 재접속하는 현상을 고려하지 않는다.

좋은 설계는 DB와 애플리케이션 양쪽에서 동시에 만들어야 한다. DB는 감당 가능한 동시성 한계를 정하고, 애플리케이션은 pool 크기와 timeout을 그 한계 안에 맞춘다. 요청이 폭주할 때는 무한히 연결을 늘리는 것이 아니라 queueing, backpressure, circuit breaker, retry jitter로 DB를 보호해야 한다.

7. Aurora MySQL에서 달라지는 점

Aurora MySQL도 MySQL protocol을 사용하므로 handshake와 인증의 기본 구조는 MySQL Community Server와 유사하다. 그러나 운영상 중요한 차이가 있다.

  • 엔드포인트 계층: writer endpoint, reader endpoint, custom endpoint가 DNS 기반으로 동작한다. failover 직후 DNS cache, connector의 host cache, connection pool의 stale connection 처리가 중요하다.
  • IAM DB Authentication: IAM 인증을 쓰면 password 대신 짧은 수명의 token을 사용한다. token 생성 지연, 만료, clock skew, TLS 요구 조건이 연결 실패 원인이 될 수 있다.
  • TLS 정책: 조직 보안 정책상 TLS를 강제하는 경우 connector별 TLS 설정 차이가 handshake/authentication 실패로 나타날 수 있다.
  • 프록시 사용: RDS Proxy나 애플리케이션 프록시를 쓰면 클라이언트 연결 수와 DB backend 연결 수가 분리된다. 이때 MySQL 내부의 Threads_connected만 보고 애플리케이션 전체 접속 수를 판단하면 안 된다.
  • failover 재접속 폭주: 장애 조치 후 많은 애플리케이션 인스턴스가 동시에 새 writer로 접속하면 handshake/authentication/thread allocation 비용이 순간적으로 집중된다.

Aurora 환경에서는 DB 내부 지표뿐 아니라 CloudWatch, Performance Insights, RDS event, proxy 지표, 애플리케이션 connection pool 로그를 함께 봐야 한다. 특히 failover 테스트에서는 “쿼리가 다시 성공하는 시간”뿐 아니라 “기존 연결이 얼마나 빨리 폐기되고 새 연결이 안정적으로 재생성되는지”를 측정해야 한다.

8. 장애 진단 패턴

연결 관련 장애는 증상별로 접근 순서를 나누는 것이 좋다.

8.1 Too many connections

Too many connections는 서버가 max_connections 한계에 도달했음을 의미한다. 이때 먼저 확인할 것은 실제 실행 중인 쿼리 수가 아니라 현재 열린 연결의 성격이다.

SELECT PROCESSLIST_USER,
       PROCESSLIST_COMMAND,
       COUNT(*) AS connection_count
FROM performance_schema.threads
WHERE TYPE = 'FOREGROUND'
  AND PROCESSLIST_ID IS NOT NULL
GROUP BY PROCESSLIST_USER, PROCESSLIST_COMMAND
ORDER BY connection_count DESC;

실행 결과(MySQL 8.0.46):

mysql> SELECT PROCESSLIST_USER,
    ->        PROCESSLIST_COMMAND,
    ->        COUNT(*) AS connection_count
    -> FROM performance_schema.threads
    -> WHERE TYPE = 'FOREGROUND'
    ->   AND PROCESSLIST_ID IS NOT NULL
    -> GROUP BY PROCESSLIST_USER, PROCESSLIST_COMMAND
    -> ORDER BY connection_count DESC;

+------------------+---------------------+------------------+
| PROCESSLIST_USER | PROCESSLIST_COMMAND | connection_count |
+------------------+---------------------+------------------+
| event_scheduler  | Daemon              |                1 |
| NULL             | Daemon              |                1 |
| root             | Query               |                1 |
+------------------+---------------------+------------------+
3 rows in set (0.00 sec)

Sleep 상태가 대부분이라면 DB 실행 성능보다 connection pool 크기, idle timeout, connection leak을 먼저 의심해야 한다. QueryExecute 상태가 많다면 실제 동시 실행 쿼리 병목도 함께 봐야 한다.

8.2 Aborted_connects 증가

Aborted_connects는 클라이언트가 성공적으로 연결하지 못한 누적 수다. 비밀번호 오류, 인증 패킷 문제, handshake timeout, 잘못된 host, 네트워크 중단 등이 모두 포함될 수 있다. 이 값만으로 원인을 확정할 수 없으므로 error log와 애플리케이션 로그의 시간대를 맞춰야 한다.

SHOW GLOBAL STATUS
WHERE Variable_name IN (
  'Aborted_connects',
  'Connection_errors_accept',
  'Connection_errors_internal',
  'Connection_errors_max_connections',
  'Connection_errors_peer_address',
  'Connection_errors_select',
  'Connection_errors_tcpwrap'
);

실행 결과(MySQL 8.0.46):

mysql> SHOW GLOBAL STATUS
    -> WHERE Variable_name IN (
    ->   'Aborted_connects',
    ->   'Connection_errors_accept',
    ->   'Connection_errors_internal',
    ->   'Connection_errors_max_connections',
    ->   'Connection_errors_peer_address',
    ->   'Connection_errors_select',
    ->   'Connection_errors_tcpwrap'
    -> );

+-----------------------------------+-------+
| Variable_name                     | Value |
+-----------------------------------+-------+
| Aborted_connects                  | 0     |
| Connection_errors_accept          | 0     |
| Connection_errors_internal        | 0     |
| Connection_errors_max_connections | 0     |
| Connection_errors_peer_address    | 0     |
| Connection_errors_select          | 0     |
| Connection_errors_tcpwrap         | 0     |
+-----------------------------------+-------+
7 rows in set (0.00 sec)

값이 증가하는 순간에 애플리케이션 배포, password rotation, DNS 변경, 보안 그룹 변경, TLS 인증서 갱신, connector 업그레이드가 있었는지 함께 확인해야 한다.

8.3 접속은 되지만 첫 쿼리까지 느림

TCP connect는 빠르고 인증도 성공하지만 첫 쿼리까지 느리다면 다음을 의심한다.

  • connection pool validation query가 불필요하게 무겁다.
  • 세션 초기화 SQL이 많다. 예: SET NAMES, SET time_zone, SET sql_mode, SET autocommit 반복.
  • connector가 server prepared statement나 metadata 조회를 과도하게 수행한다.
  • thread cache miss로 새 thread 생성이 빈번하다.
  • TLS handshake 비용이 높고 연결 재사용이 잘 되지 않는다.

이 경우 DB 내부 slow query log에는 핵심 원인이 잘 남지 않을 수 있다. 애플리케이션 connector의 connection acquisition time, pool wait time, validation time을 별도로 계측해야 한다.

9. 운영 체크리스트

MySQL 연결 수명주기를 안정적으로 운영하려면 다음 항목을 정기적으로 확인해야 한다.

  • 애플리케이션 전체 잠재 connection pool 합계가 DB max_connections
  • skip_name_resolve
  • Connections, Threads_created, Threads_cached
  • Threads_connectedThreads_running
  • Aborted_connectsConnection_errors_%

10. 결론

MySQL 연결 수명주기는 짧은 과정이지만, 장애 시에는 서버 전체 안정성을 좌우한다. TCP 연결이 열리고, protocol handshake가 진행되고, user@host 계정이 선택되고, 인증 플러그인이 검증되고, session context와 thread가 준비된 뒤에야 비로소 SQL 실행이 시작된다. 따라서 접속 장애를 SQL 성능 문제처럼 다루면 원인을 놓치기 쉽다.

운영자는 연결을 하나의 독립된 처리 경로로 보고 max_connections, connection pool, 인증 플러그인, DNS, TLS, thread cache, failover 재접속 정책을 함께 설계해야 한다. 특히 Aurora MySQL이나 프록시 계층이 있는 환경에서는 DB 내부 지표와 외부 연결 계층 지표를 분리해서 읽는 습관이 필요하다. 다음 단계의 성능 분석은 쿼리 실행기와 InnoDB 내부로 들어가지만, 그 전에 연결이 안정적으로 만들어지고 재사용되는지 확인하는 것이 MySQL 운영의 출발점이다.