카테고리 : MySQL/기술노트

ProxySQL 개요: 커뮤니티 기반 라우팅과 read/write split, failover 보조

ProxySQL가 MySQL 앞단에서 hostgroup, query rule, connection multiplexing으로 어떤 운영 문제를 풀고, read/write split과 failover 보조를 어디까지 맡길 수 있는지 정리한다.

저자: MySQL 기술 노트 작성: 2026.06.27 약 13분 7,330자
다운로드

1. 왜 ProxySQL를 별도 계층으로 이해해야 하는가

MySQL 운영에서 프록시는 흔히 두 가지 상반된 기대를 동시에 받는다. 하나는 “애플리케이션이 단순해지도록 read/write split을 대신해 주는 계층”이라는 기대이고, 다른 하나는 “장애가 나도 자동으로 우회해 주는 만능 고가용성 장치”라는 기대다. 첫 번째 기대는 상당 부분 맞지만, 두 번째 기대는 과장될 때가 많다.

ProxySQL는 Community MySQL 환경에서 널리 쓰이는 L4/L7 성격의 데이터베이스 프록시다. MySQL 프로토콜을 이해하고, backend 서버를 hostgroup 단위로 분류하며, query rule과 사용자별 정책을 통해 연결과 쿼리를 다른 목적지로 보낼 수 있다. 또한 backend health check, connection pool, multiplexing, runtime 설정 반영 기능을 제공하므로, 단순 TCP 로드밸런서보다 훨씬 DBA 친화적인 제어 지점을 만든다.

운영 관점에서 ProxySQL를 두는 이유는 대체로 다음 네 가지로 요약된다.

  1. 애플리케이션이 직접 writer/reader topology를 세세하게 알지 않도록 만들기 위해
  2. read/write split, 사용자별 라우팅, maintenance drain 같은 정책을 중앙에서 관리하기 위해
  3. backend MySQL 연결 수를 애플리케이션 연결 수보다 더 보수적으로 제어하기 위해
  4. failover 시 애플리케이션의 재배포 없이 routing target을 바꿀 수 있게 하기 위해

그러나 여기서 중요한 단서가 있다. ProxySQL는 장애를 없애는 장치가 아니라, 장애를 더 제어 가능한 형태로 바꾸는 장치다. writer 선출, 데이터 정합성 보장, replication 재동기화, split-brain 방지, 애플리케이션 트랜잭션 재시도 정책까지 ProxySQL 혼자 해결해 주지는 않는다. 따라서 이 글의 핵심은 “ProxySQL를 붙이면 무엇이 쉬워지고 무엇은 여전히 운영자가 책임져야 하는가”를 구분하는 데 있다.

2. ProxySQL의 기본 구조: listener, hostgroup, query rule, backend pool

ProxySQL의 내부를 이해할 때는 먼저 “MySQL 서버 앞에 또 하나의 세션 관리 계층이 생긴다”라고 생각하면 된다. 애플리케이션은 ProxySQL에 접속하고, ProxySQL는 backend MySQL 서버들과 별도 연결 풀을 유지한다. 이때 frontend 연결과 backend 연결은 항상 1:1로 고정되지 않는다.

flowchart LR
    A[애플리케이션] --> B[ProxySQL listener : 6033]
    B --> C[frontend session]
    C --> D{query rule / user rule / transaction state}
    D --> E[writer hostgroup]
    D --> F[reader hostgroup]
    E --> G[(MySQL primary)]
    F --> H[(MySQL replica 1)]
    F --> I[(MySQL replica 2)]
    J[ProxySQL admin : 6032] --> K[mysql_servers / mysql_users / mysql_query_rules]
    K --> B

핵심 구성 요소는 다음과 같다.

2.1 listener

일반적으로 애플리케이션은 ProxySQL의 data plane 포트(기본 6033)에 접속한다. DBA나 자동화 스크립트는 admin 포트(기본 6032)에 접속해 설정을 조회·수정한다. 이 분리가 중요한 이유는 운영자가 backend 서버 설정을 직접 건드리지 않고도 라우팅 정책을 즉시 바꿀 수 있기 때문이다.

2.2 hostgroup

ProxySQL는 backend 서버를 hostgroup이라는 논리 묶음으로 관리한다. 보통 다음과 같은 형태가 가장 흔하다.

  • hostgroup 10: writer
  • hostgroup 20: readers
  • hostgroup 30: offline/maintenance

운영자는 “이 쿼리를 어느 개별 서버로 보낼 것인가”보다 “이 종류의 트래픽을 어떤 역할 그룹으로 보낼 것인가”를 먼저 정의한다. 이 추상화 덕분에 장애 조치나 증설이 발생해도 애플리케이션 DSN을 바꾸는 대신 hostgroup 멤버십만 갱신하면 되는 경우가 많다.

2.3 query rule과 사용자별 정책

ProxySQL는 단순 round-robin만 수행하는 프록시가 아니다. 사용자, schema, digest, 정규식, 트랜잭션 상태 등을 기준으로 라우팅 규칙을 걸 수 있다. 예를 들어 SELECT는 reader hostgroup으로, INSERT/UPDATE/DELETE는 writer hostgroup으로 보내거나, 특정 관리 계정은 무조건 writer로 보내는 정책을 둘 수 있다.

다만 여기서 오해하면 안 되는 점이 있다. SQL 텍스트 기반 라우팅은 강력하지만 완전하지 않다. SELECT ... FOR UPDATE, 사용자 변수, temporary table, stored function, read-after-write 요구, 세션 상태 의존 로직은 단순 정규식 기반 분기만으로 안전하게 처리되지 않을 수 있다. 결국 라우팅 정책은 “문자열 패턴 매칭”이 아니라 애플리케이션의 일관성 요구를 반영한 세션 모델 설계가 되어야 한다.

2.4 backend connection pool과 multiplexing

ProxySQL의 큰 장점 중 하나는 frontend 연결 수와 backend 연결 수를 분리해 생각할 수 있다는 점이다. 애플리케이션 연결은 많아도, 실제 backend MySQL로는 더 적은 수의 연결을 재사용하도록 설계할 수 있다. 이때 multiplexing이 잘 작동하면 backend 세션 수가 줄고, handshake/authentication 비용과 MySQL thread 부담이 완화된다.

하지만 multiplexing은 항상 가능한 것이 아니다. 다음 조건은 backend 세션을 특정 frontend 세션에 오래 묶어 두는 대표 원인이다.

  • 열린 트랜잭션
  • temporary table 사용
  • 세션 변수 변경
  • prepared statement 유지
  • locking read 또는 explicit lock
  • user-defined variable 사용

즉, ProxySQL를 도입해도 애플리케이션이 세션 상태를 강하게 의존하면 backend 절감 효과는 생각보다 작을 수 있다.

3. read/write split은 편의 기능이 아니라 일관성 모델의 선택이다

많은 팀이 ProxySQL를 처음 검토할 때 가장 먼저 보는 기능은 read/write split이다. 구조는 단순해 보인다. 쓰기는 writer로, 읽기는 reader로 보내면 된다. 그러나 실제 운영에서 중요한 것은 단순 분산이 아니라 어떤 읽기를 reader로 보내도 되는가다.

3.1 왜 단순 SELECT -> reader 규칙이 위험할 수 있는가

다음과 같은 요청 흐름을 생각해 보자.

  1. 애플리케이션이 writer에서 주문 상태를 PAID로 변경한다.
  2. 직후 같은 사용자 요청에서 주문 상태를 다시 읽는다.
  3. ProxySQL 규칙이 후속 SELECT를 reader로 보낸다.
  4. replica apply 지연이 있으면 사용자는 아직 PENDING 상태를 본다.

이 문제는 ProxySQL의 버그가 아니라 read/write split이 본질적으로 갖는 일관성 트레이드오프다. 따라서 운영자는 먼저 다음 질문에 답해야 한다.

  • 이 읽기는 최신 쓰기 직후 결과를 반드시 봐야 하는가
  • replica lag가 수백 ms~수초 생겨도 비즈니스적으로 괜찮은가
  • 같은 HTTP 요청 또는 같은 업무 트랜잭션 안에서는 writer stickiness가 필요한가
  • 캐시와 결합할 때 stale read를 어디까지 허용할 것인가

3.2 안전한 기본 원칙

실무적으로는 다음 원칙이 보수적이다.

  • 쓰기 직후 확인 읽기(read-after-write)는 writer로 보낸다.
  • 트랜잭션 내부 쿼리는 전부 writer로 고정한다.
  • 운영/관리 쿼리, DDL, 배치 제어 쿼리는 writer 또는 전용 관리 경로로 보낸다.
  • replica lag가 업무적으로 민감한 도메인은 split 자체를 최소화한다.

ProxySQL는 이 원칙을 구현하는 도구일 뿐, 어떤 쿼리가 어느 일관성 등급을 필요로 하는지는 애플리케이션 설계가 결정한다.

4. failover 보조는 가능하지만, failover 자체를 대체하지는 못한다

ProxySQL가 자주 오해받는 지점이 바로 여기다. ProxySQL는 backend health check와 hostgroup 재구성으로 장애 대응을 보조할 수 있지만, 클러스터 관리 소프트웨어처럼 primary election이나 replication 재구성을 스스로 완결하지는 않는다.

4.1 ProxySQL가 실제로 잘하는 일

  • 장애 난 backend를 라우팅 대상에서 제외
  • 새 writer 주소나 역할 변경을 runtime에 빠르게 반영
  • maintenance 전에 특정 노드를 drain 상태로 두고 연결 유입 차단
  • reader 집합 변화를 애플리케이션 DSN 변경 없이 흡수

4.2 ProxySQL가 대신하지 못하는 일

  • 어떤 서버가 진짜 primary가 되어야 하는지 결정하는 일
  • replication topology를 재구성하는 일
  • split-brain을 판정하고 강제로 차단하는 일
  • 애플리케이션 트랜잭션 실패를 의미적으로 재시도하는 일
  • semi-sync, Group Replication, Orchestrator, MHA, Aurora failover 같은 상위 HA 메커니즘 자체

따라서 ProxySQL는 보통 Orchestrator, MHA, Group Replication, InnoDB Cluster, Aurora endpoint 같은 상위 failover 체계와 결합해서 본다. 상위 계층이 writer를 바꾸면, ProxySQL는 그 변화를 빠르게 라우팅 정책에 반영해 애플리케이션 영향 범위를 줄이는 역할을 맡는다.

5. MySQL backend가 writer인지 reader인지 먼저 명확히 관측해야 한다

ProxySQL 운영은 프록시 자체 설정만 본다고 끝나지 않는다. 실제 backend 서버가 현재 writer 역할인지, read_only 상태가 올바른지, 어떤 식별자를 갖고 있는지를 함께 확인해야 한다. 다음 SQL은 MySQL 8.0 환경에서 backend 역할 신호를 빠르게 점검할 때 유용하다.

SELECT VERSION() AS mysql_version,
       @@hostname AS hostname,
       @@server_uuid AS server_uuid,
       @@global.read_only AS read_only_mode,
       @@global.super_read_only AS super_read_only_mode;

실행 결과(MySQL 8.0.46):

mysql> SELECT VERSION() AS mysql_version,
    ->        @@hostname AS hostname,
    ->        @@server_uuid AS server_uuid,
    ->        @@global.read_only AS read_only_mode,
    ->        @@global.super_read_only AS super_read_only_mode;

+---------------+--------------+--------------------------------------+----------------+----------------------+
| mysql_version | hostname     | server_uuid                          | read_only_mode | super_read_only_mode |
+---------------+--------------+--------------------------------------+----------------+----------------------+
| 8.0.46        | 71d1ae355544 | cffdfd4b-71bb-11f1-aa1f-0242ac11000f |              0 |                    0 |
+---------------+--------------+--------------------------------------+----------------+----------------------+
1 row in set (0.00 sec)

이 결과는 ProxySQL hostgroup 매핑을 검증할 때 기본 자료가 된다. 특히 수동 failover 직후에는 “ProxySQL 설정상 writer”와 “실제 read_only=OFF인 서버”가 일치하는지 먼저 확인해야 한다.

6. backend 연결 압력은 ProxySQL 도입 후에도 계속 봐야 한다

ProxySQL를 쓰면 애플리케이션 연결 수와 backend 연결 수가 분리되므로, 운영자는 오히려 backend MySQL의 연결 지표를 더 꼼꼼히 봐야 한다. 프록시가 앞단에 있다고 해서 backend가 안전해지는 것은 아니기 때문이다.

SELECT VARIABLE_NAME, VARIABLE_VALUE
FROM performance_schema.global_status
WHERE VARIABLE_NAME IN (
  'Threads_connected',
  'Threads_running',
  'Connections',
  'Aborted_connects',
  'Max_used_connections'
)
ORDER BY VARIABLE_NAME;

실행 결과(MySQL 8.0.46):

mysql> SELECT VARIABLE_NAME, VARIABLE_VALUE
    -> FROM performance_schema.global_status
    -> WHERE VARIABLE_NAME IN (
    ->   'Threads_connected',
    ->   'Threads_running',
    ->   'Connections',
    ->   'Aborted_connects',
    ->   'Max_used_connections'
    -> )
    -> ORDER BY VARIABLE_NAME;

+----------------------+----------------+
| VARIABLE_NAME        | VARIABLE_VALUE |
+----------------------+----------------+
| Aborted_connects     | 0              |
| Connections          | 12             |
| Max_used_connections | 1              |
| Threads_connected    | 1              |
| Threads_running      | 2              |
+----------------------+----------------+
5 rows in set (0.00 sec)

이 지표를 해석할 때는 다음 순서를 권장한다.

  1. Threads_connected가 프록시 도입 전보다 실제로 안정화되었는가
  2. Threads_running의 피크가 여전히 backend 병목을 드러내는가
  3. 장애 직후 Connections가 급격히 튀며 reconnect storm 신호를 보이는가
  4. Aborted_connects가 인증 실패나 포화 상태를 암시하는가

즉, 프록시의 목표는 연결을 많이 받아 주는 것이 아니라, backend에 전달되는 압력을 더 예측 가능하게 만드는 것이다.

7. 장기 세션과 열린 트랜잭션은 multiplexing 이득을 깎아먹는다

ProxySQL 도입 후 기대한 만큼 backend 세션 수가 줄지 않는다면, 먼저 애플리케이션의 세션 사용 패턴을 점검해야 한다. 특히 열린 트랜잭션이 길거나, 세션이 오래 붙잡혀 있으면 multiplexing 이점이 크게 감소한다.

SELECT t.PROCESSLIST_ID,
       t.PROCESSLIST_USER,
       t.PROCESSLIST_HOST,
       t.PROCESSLIST_DB,
       t.PROCESSLIST_COMMAND,
       t.PROCESSLIST_TIME,
       t.PROCESSLIST_STATE
FROM performance_schema.threads AS t
WHERE t.TYPE = 'FOREGROUND'
ORDER BY t.PROCESSLIST_TIME DESC
LIMIT 10;

실행 결과(MySQL 8.0.46):

mysql> SELECT t.PROCESSLIST_ID,
    ->        t.PROCESSLIST_USER,
    ->        t.PROCESSLIST_HOST,
    ->        t.PROCESSLIST_DB,
    ->        t.PROCESSLIST_COMMAND,
    ->        t.PROCESSLIST_TIME,
    ->        t.PROCESSLIST_STATE
    -> FROM performance_schema.threads AS t
    -> WHERE t.TYPE = 'FOREGROUND'
    -> ORDER BY t.PROCESSLIST_TIME DESC
    -> LIMIT 10;

+----------------+------------------+------------------+-----------------+---------------------+------------------+------------------------+
| PROCESSLIST_ID | PROCESSLIST_USER | PROCESSLIST_HOST | PROCESSLIST_DB  | PROCESSLIST_COMMAND | PROCESSLIST_TIME | PROCESSLIST_STATE      |
+----------------+------------------+------------------+-----------------+---------------------+------------------+------------------------+
|              5 | event_scheduler  | localhost        | NULL            | Daemon              |                6 | Waiting on empty queue |
|              7 | NULL             | NULL             | NULL            | Daemon              |                6 | Suspending             |
|             13 | root             | localhost        | mysql_tech_note | Query               |                0 | executing              |
+----------------+------------------+------------------+-----------------+---------------------+------------------+------------------------+
3 rows in set (0.01 sec)

이 쿼리는 MySQL 쪽에서 오래 붙어 있는 foreground 세션을 빠르게 훑는 용도로 유용하다. 실제 운영에서는 여기에 information_schema.INNODB_TRX, performance_schema.events_statements_current 등을 결합해 장기 트랜잭션과 현재 실행 SQL을 함께 보는 편이 좋다.

8. ProxySQL 관리 평면에서 자주 다루는 항목

ProxySQL는 MySQL과 비슷한 SQL 인터페이스를 admin 포트에 제공하므로 자동화가 쉽다. 다만 이 인터페이스는 MySQL 서버가 아니라 ProxySQL admin plane이라는 점을 분명히 구분해야 한다. 대표적으로 자주 다루는 객체는 다음과 같다.

  • mysql_servers: backend 서버와 hostgroup 구성
  • mysql_users: 사용자별 인증 및 기본 hostgroup 정책
  • mysql_query_rules: digest/정규식 기반 라우팅 규칙
  • runtime_* / disk 반영 절차: 메모리 설정과 영구 설정의 구분

운영 실수도 이 구분에서 자주 발생한다. 예를 들어 admin plane에서 설정을 바꾸고 runtime에만 반영한 뒤 disk에 저장하지 않으면 재시작 후 정책이 사라질 수 있다. 반대로 disk만 갱신하고 runtime에 적용하지 않으면 즉시 기대한 라우팅 변화가 일어나지 않을 수 있다. 결국 ProxySQL는 “설정 저장소”가 아니라 runtime state를 가진 네트워크 장비에 가깝게 운영해야 한다.

9. Aurora MySQL 환경에서 ProxySQL를 둘 때의 판단 기준

Aurora MySQL는 이미 cluster endpoint, reader endpoint, failover, RDS Proxy 같은 관리형 선택지를 제공한다. 따라서 Aurora에서 ProxySQL를 쓸지 여부는 Community MySQL보다 더 신중히 판단해야 한다.

9.1 ProxySQL가 여전히 유리할 수 있는 경우

  • 단순 endpoint 분리보다 세밀한 query rule이 필요할 때
  • 애플리케이션별 또는 사용자별로 다른 read policy를 적용해야 할 때
  • 관리형 RDS Proxy보다 더 직접적인 라우팅 제어와 관측이 필요할 때
  • 여러 MySQL 계열 backend를 공통 프록시 계층으로 묶고 싶을 때

9.2 Aurora 기본 기능만으로 충분한 경우

  • writer/reader endpoint 수준의 단순 분리면 충분할 때
  • 운영 복잡도를 늘리지 않는 것이 최우선일 때
  • 세션 pinning이 많아 multiplexing 이득이 제한적일 때
  • 장애 조치 체계를 AWS 관리형으로 최대한 단순화하고 싶을 때

특히 Aurora에서는 storage layer와 failover 메커니즘이 일반 MySQL과 다르므로, ProxySQL를 붙였다고 해서 writer 전환이 더 빨라지는 것은 아니다. 오히려 프록시 hop 하나가 늘어나고, endpoint 해석·health check·운영 자동화까지 함께 관리해야 하므로 얻는 제어권과 늘어나는 운영면적을 비교해야 한다.

10. 흔한 오해와 운영 함정

10.1 "ProxySQL가 있으니 애플리케이션은 일관성을 몰라도 된다"

아니다. 프록시는 읽기를 나눠 줄 수 있지만, 어떤 읽기가 stale해도 되는지 결정하지는 못한다. read-after-write 보장이 필요하면 애플리케이션 또는 프록시 정책에서 writer stickiness를 명시해야 한다.

10.2 "SELECT는 모두 reader로 보내면 된다"

아니다. SELECT ... FOR UPDATE, 트랜잭션 내부 SELECT, replica lag에 민감한 조회, 관리 쿼리는 reader 분리가 오히려 위험할 수 있다.

10.3 "ProxySQL를 붙이면 failover가 자동으로 끝난다"

아니다. writer 선출과 replication 재구성은 별도 HA 체계의 책임이다. ProxySQL는 그 결과를 반영해 라우팅을 전환하는 보조 계층이다.

10.4 "프록시가 있으니 backend max_connections를 크게 올려도 된다"

아니다. 프록시는 압력을 완화할 수 있지만, backend의 CPU·락·메모리·I/O 한계를 없애지 않는다. 오히려 frontend 연결이 많아지면 장애 시 reconnect burst가 더 커질 수 있다.

10.5 "multiplexing은 항상 backend 연결을 크게 줄여 준다"

아니다. 세션 상태 의존, 장기 트랜잭션, prepared statement 유지, temporary table 사용이 많으면 기대한 만큼 줄지 않을 수 있다. 도입 전후에 반드시 실제 backend 지표를 비교해야 한다.

11. 도입 전 의사결정 체크리스트

  • 프록시 도입 후에도 backend의 Threads_running, Connections

12. 결론

ProxySQL는 Community MySQL과 Aurora MySQL 운영에서 모두 유용할 수 있는 프록시 계층이지만, 그 가치는 “프록시를 넣었다”는 사실 자체보다 어떤 라우팅 정책과 일관성 규칙을 중앙에서 제어할 수 있게 되었는가에 있다. read/write split, maintenance drain, 연결수 제어, failover 보조는 실제로 강력한 기능이지만, 그것만으로 데이터 정합성과 고가용성이 자동 완성되지는 않는다.

따라서 ProxySQL를 도입할 때는 기능 목록보다 먼저 다음 순서로 판단하는 편이 안전하다. 첫째, 애플리케이션이 요구하는 일관성 모델을 정한다. 둘째, writer/reader와 failover 책임 경계를 분명히 한다. 셋째, 프록시 도입 전후의 backend 연결 지표와 세션 패턴을 실제 수치로 비교한다. 이 세 가지가 갖춰질 때 ProxySQL는 단순한 중간 홉이 아니라, MySQL 운영을 더 예측 가능하게 만드는 제어 계층이 된다.

다음 단계에서는 ProxySQL 자체의 세부 문법보다, read/write split 정책이 transaction 경계와 replica lag 해석에 어떤 영향을 주는지 더 깊게 다루는 편이 유익하다. 이후 기술노트에서는 replica lag 관측, writer stickiness, 세션 pinning, connection multiplexing의 실제 운영 지표를 이어서 살펴볼 수 있다.