Published on

MySQL 8.0 복제 지연 원인 7가지와 튜닝

Authors

서버가 멀쩡해 보이는데도 Seconds_Behind_Source 가 치솟고, 읽기 트래픽을 Replica로 분산하려던 계획이 무너지는 순간이 옵니다. MySQL 8.0의 복제는 예전보다 훨씬 똑똑해졌지만, 지연의 원인은 여전히 애플리케이션 트랜잭션 패턴부터 스토리지 I/O까지 폭이 넓습니다.

이 글은 MySQL 8.0 기준으로 복제 지연을 만드는 대표 원인 7가지를 짚고, 각 원인별로 무엇을 봐야 하고(관측), 어떤 설정/패턴을 바꿔야 하는지(튜닝)를 실전 관점에서 정리합니다.

운영에서 흔히 겪는 패턴은 “원인 1개”가 아니라 2~3개가 겹쳐서 지연이 커지는 경우입니다. 따라서 아래 항목을 체크리스트처럼 순서대로 훑는 것이 효과적입니다.

복제 지연을 먼저 측정하는 방법(필수 관측)

MySQL 8.0에서는 SHOW SLAVE STATUS 대신 SHOW REPLICA STATUS 와 Performance Schema를 적극 활용하는 편이 좋습니다.

1) Replica 상태 요약

SHOW REPLICA STATUS\G

여기서 최소한 아래를 봅니다.

  • Seconds_Behind_Source (지연의 결과)
  • Replica_IO_Running, Replica_SQL_Running (I/O 스레드와 SQL 적용 스레드)
  • Relay_Log_Space (릴레이 로그가 쌓이는지)
  • Last_SQL_Error, Last_IO_Error (에러로 멈추면 지연이 아니라 “정지”)

2) 복제 적용 병목(워커/코디네이터)

SELECT
  THREAD_ID, NAME, TYPE, PROCESSLIST_STATE
FROM performance_schema.threads
WHERE NAME LIKE 'thread/sql/%replica%';

또는 복제 워커 상태를 직접 봅니다.

SELECT *
FROM performance_schema.replication_applier_status_by_worker\G
  • LAST_APPLIED_TRANSACTION 이 특정 워커에서만 멈춰 있으면 단일 트랜잭션/단일 테이블 병목 가능성이 큽니다.

3) 지연이 “읽기”인지 “적용”인지 분리

  • I/O 스레드 문제(소스에서 바이너리 로그를 못 가져옴)라면 Replica_IO_Running 이 흔들리고 릴레이 로그가 잘 안 쌓입니다.
  • SQL 적용 문제(릴레이 로그는 쌓이는데 적용이 느림)라면 Relay_Log_Space 가 증가하고 워커가 바쁩니다.

이제 원인 7가지로 들어갑니다.

원인 1) 대형 트랜잭션(긴 커밋)으로 인한 적용 지연

증상

  • 소스에서 한 번에 많은 행을 변경하는 배치/마이그레이션 후 지연 급증
  • Replica 워커가 병렬이어도 특정 트랜잭션 하나가 길게 잡고 늘어짐

왜 느려지나

복제는 결국 “트랜잭션 단위”로 적용됩니다. 트랜잭션이 크면

  • Replica에서 redo/undo, 페이지 flush 부담이 커지고
  • 커밋 시점에 fsync가 길어지며
  • 병렬 워커가 있어도 그 트랜잭션 자체는 쪼개지지 않습니다.

튜닝

  • 애플리케이션/배치에서 트랜잭션을 더 작게 쪼개기(가장 효과적)
  • INSERT ... SELECT / UPDATE 를 범위로 나누기

예: PK 범위를 잘라 커밋을 자주 하는 패턴

-- 예시: 1만 건 단위로 나눠 업데이트(애플리케이션에서 루프)
UPDATE orders
SET status = 'ARCHIVED'
WHERE id BETWEEN ? AND ?;

추가로, 배치가 느리게 적용되는 이유가 SQL 자체의 비효율이면 실행 계획도 함께 봐야 합니다.

EXPLAIN ANALYZE
UPDATE orders
SET status = 'ARCHIVED'
WHERE id BETWEEN 100000 AND 110000;

원인 2) 병렬 복제 설정 미흡(또는 병렬화가 안 되는 워크로드)

증상

  • CPU 코어는 많은데 Replica 적용 스레드가 1개처럼 동작
  • replication_applier_status_by_worker 에서 대부분 워커가 놀고 있음

핵심 개념

MySQL 8.0의 병렬 복제는 “어떻게 트랜잭션을 그룹핑해서 동시에 적용할지”가 중요합니다.

  • replica_parallel_workers: 워커 수
  • replica_parallel_type: 병렬화 단위(LOGICAL_CLOCK 가 일반적으로 유리)
  • replica_preserve_commit_order: 커밋 순서 보존(일관성 요구 시 켜지만 병렬성에 영향)

권장 설정 예시

-- 동적 변경 가능(운영 정책에 맞게 적용)
SET GLOBAL replica_parallel_workers = 8;
SET GLOBAL replica_parallel_type = 'LOGICAL_CLOCK';
SET GLOBAL replica_preserve_commit_order = ON;

주의

  • 워크로드가 “한 테이블에만 쓰기 집중”이면 병렬 워커를 늘려도 효과가 제한됩니다.
  • 커밋 순서 보존은 읽기 일관성 측면에서 안전하지만, 상황에 따라 지연을 키울 수 있습니다.

원인 3) 단일 핫 테이블/핫 파티션으로 워커가 경쟁

증상

  • 병렬 워커는 많은데 특정 테이블 DML에서만 지연
  • Replica에서 해당 테이블에 대한 락 대기 또는 인덱스 경합이 증가

왜 느려지나

병렬 복제는 “서로 독립적인 트랜잭션”을 동시에 적용할 때 강합니다. 하지만 모든 트랜잭션이 같은 테이블/인덱스 페이지를 두드리면

  • InnoDB 내부 락/래치 경쟁
  • 버퍼풀 히트율 악화
  • 인덱스 페이지 split 비용

으로 병렬성이 무력화됩니다.

튜닝

  • 쓰기 집중 테이블을 기능적으로 분리(샤딩/테이블 분리)
  • 파티셔닝 도입(핫 키를 분산)
  • 인덱스 설계 최적화(불필요한 secondary index 줄이기)

인덱스가 과도하면 Replica 적용 비용도 그대로 증가합니다.

SHOW INDEX FROM hot_table;

원인 4) Replica I/O 병목(디스크, fsync, 릴레이 로그)

증상

  • Relay_Log_Space 가 빠르게 증가
  • CPU는 한가한데 적용이 느림
  • 디스크 사용률/대기 시간이 높음

체크 포인트

  • InnoDB flush 정책과 redo 로그 크기
  • 릴레이 로그/바이너리 로그 쓰기 fsync

특히 클라우드 환경에서는 스토리지 IOPS/throughput 한계로 인해 지연이 발생하기 쉽습니다.

튜닝 방향

  • 스토리지 성능(가장 확실하지만 비용)
  • innodb_flush_log_at_trx_commit 정책 검토(내구성 요구사항에 따라)
  • sync_binlog 정책 검토(Replica에서도 바이너리 로그를 켜는 구성이라면 영향)

예: 내구성과 성능 트레이드오프(신중히)

-- 강한 내구성: 1, 성능 우선: 2 또는 0(권장 여부는 환경에 따라 다름)
SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit';
SHOW VARIABLES LIKE 'sync_binlog';

운영 장애 디버깅을 빠르게 끝내는 관점은 MySQL만의 문제가 아니라 시스템 레벨 관측도 중요합니다. 재시작 루프나 리소스 제한 같은 이슈는 아래 글의 접근법이 도움이 됩니다.

원인 5) 메타데이터 락(MDL) 또는 DDL로 복제 적용 정체

증상

  • Replica SQL 스레드가 특정 DDL에서 멈춘 듯 보임
  • 운영 중 ALTER TABLE, 인덱스 추가/변경 이후 지연 급증

왜 느려지나

DDL은 테이블 메타데이터 락을 요구하고, Replica에서도 동일하게 재현됩니다. Replica에서 읽기 쿼리가 길게 돌고 있으면 DDL이 MDL을 못 잡아 대기하고, 그 뒤의 트랜잭션 적용도 밀립니다.

점검 쿼리

SELECT
  OBJECT_TYPE, OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_STATUS, OWNER_THREAD_ID
FROM performance_schema.metadata_locks
WHERE LOCK_STATUS = 'PENDING';

또한 어떤 세션이 오래 잡고 있는지 봅니다.

SHOW PROCESSLIST;

튜닝

  • 대형 DDL은 온라인 DDL 옵션/도구 사용(가능한 경우)
  • Replica에서 장시간 읽기 트랜잭션을 피하기(특히 REPEATABLE READ + 긴 트랜잭션)
  • DDL 윈도우를 분리하고, 사전 리허설로 소요 시간 측정

원인 6) Row 기반 복제에서 “적용 SQL” 자체가 비효율(인덱스 부재 등)

증상

  • Replica에서 특정 테이블 변경 적용이 유독 느림
  • 소스에서는 빠른데 Replica에서만 느리게 보임(특히 Replica 하드웨어가 약한 경우)

왜 느려지나

MySQL 8.0에서 일반적으로 binlog_formatROW 를 많이 씁니다. 이때 Replica는 이벤트를 적용하면서 내부적으로 PK/UK 기반으로 행을 찾고 변경합니다.

  • PK가 없거나
  • 변경 대상 검색이 비효율적이거나
  • 보조 인덱스가 과도하게 많으면

Replica에서의 적용 비용이 커집니다.

튜닝

  • 모든 테이블에 명확한 PK 부여(강력 권장)
  • 필요한 인덱스만 유지
  • 대량 변경 작업 전후로 인덱스 전략 재검토

PK 유무 확인 예시

SELECT
  t.TABLE_SCHEMA, t.TABLE_NAME
FROM information_schema.TABLES t
LEFT JOIN information_schema.TABLE_CONSTRAINTS c
  ON t.TABLE_SCHEMA = c.TABLE_SCHEMA
 AND t.TABLE_NAME = c.TABLE_NAME
 AND c.CONSTRAINT_TYPE = 'PRIMARY KEY'
WHERE t.TABLE_SCHEMA NOT IN ('mysql','information_schema','performance_schema','sys')
  AND c.CONSTRAINT_NAME IS NULL;

원인 7) 네트워크 지연/패킷 손실 또는 소스 바이너리 로그 전송 병목

증상

  • Replica_IO_Running 이 불안정하거나 재연결 반복
  • Last_IO_Error 에 타임아웃/연결 문제
  • 소스는 바쁜데 Replica는 릴레이 로그가 잘 안 쌓임

튜닝

  • 네트워크 품질(패킷 손실, RTT, 대역폭) 점검
  • 소스에서 바이너리 로그 디스크 I/O 병목 점검
  • 복제 전용 네트워크/서브넷 분리(가능하면)

복제 자체는 네트워크 스트리밍이므로, 애플리케이션 레벨의 p99 병목을 잡듯이 “끝단 간 지연”을 측정하는 습관이 중요합니다. 성능 병목을 구조적으로 쪼개는 접근은 아래 글도 참고할 만합니다.

실전 튜닝 체크리스트(우선순위)

운영에서 가장 빨리 효과를 보는 순서로 정리하면 다음과 같습니다.

1) “적용이 느린가, 전송이 느린가”부터 분리

  • 릴레이 로그가 쌓이면 적용 병목
  • 릴레이 로그가 안 쌓이면 전송/소스 binlog 읽기 병목

2) 대형 트랜잭션 제거

  • 배치/마이그레이션 트랜잭션을 작은 단위로
  • EXPLAIN ANALYZE 로 변경 쿼리 비용 확인

3) 병렬 복제 활성화 및 워커 수 조정

  • replica_parallel_workers 를 코어 수/워크로드에 맞게
  • replica_parallel_type 는 보통 LOGICAL_CLOCK

4) 핫 테이블 구조 개선

  • 테이블 분리/파티셔닝/인덱스 정리

5) I/O 병목 제거

  • 스토리지 스펙 확인
  • flush 관련 변수는 내구성 정책과 함께 결정

6) DDL/MDL 리스크 관리

  • DDL 윈도우 운영
  • Replica에서 장시간 읽기 트랜잭션 금지

7) 네트워크 및 소스 binlog 전송 경로 점검

  • RTT/손실률
  • 소스 디스크에서 binlog read 병목

운영 팁: 지연을 “없애기”보다 “통제”하기

복제 지연은 완전히 0으로 만들기보다, 업무 요구사항에 맞게 상한을 관리하는 것이 현실적입니다.

  • 읽기 라우팅에서 “최대 허용 지연”을 기준으로 Replica를 제외하는 로직
  • 특정 리포트/배치 트래픽은 별도 Replica로 격리
  • 지연이 커질 때 자동으로 쓰기 트래픽을 줄이거나 배치를 중단하는 가드레일

예를 들어 애플리케이션에서 Seconds_Behind_Source 를 조회해 라우팅하는 방식은 흔합니다. (단, 값이 NULL 인 경우도 있으니 예외 처리는 필수입니다.)

SHOW REPLICA STATUS;

마무리

MySQL 8.0 복제 지연은 대체로

  • 트랜잭션이 너무 크거나,
  • 병렬화가 안 되는 쓰기 패턴이거나,
  • I/O 및 락(특히 MDL)로 적용이 막히거나,
  • 네트워크/전송 경로가 흔들리는

이 네 축에서 발생합니다.

가장 추천하는 접근은 SHOW REPLICA STATUS 와 Performance Schema로 전송 vs 적용을 먼저 분리하고, 그 다음에 대형 트랜잭션/핫 테이블/DDL/스토리지 순으로 좁혀가는 것입니다. 이 순서대로만 점검해도 “원인을 못 찾는 상태”에서 벗어나는 경우가 많습니다.