- Authors
- Name
- MySQL 8.4 LTS가 의미하는 것
- InnoDB 클러스터 아키텍처
- InnoDB 클러스터 구축 실전
- 클러스터 운영과 모니터링
- 장애 시나리오와 복구 절차
- 쿼리 최적화 전략
- InnoDB 버퍼 풀 튜닝
- 운영 체크리스트
- 성능 벤치마크 참고값
- 마무리
- References

MySQL 8.4 LTS가 의미하는 것
MySQL 8.4는 Oracle이 공식 지정한 Long-Term Support(LTS) 릴리스다. 2024년 4월 GA 이후 2025~2026년에 걸쳐 8.4.4부터 8.4.8까지 꾸준히 패치가 이어지고 있으며, MySQL 8.0은 2026년 EOL을 앞두고 있다. 운영 환경에서 8.4로의 마이그레이션은 더 이상 선택이 아니라 필수다.
8.4에서 달라진 핵심 사항을 먼저 정리한다.
주요 변경점 요약
- mysql_native_password 기본 비활성화: 8.4.0부터 기본적으로 로드되지 않는다.
caching_sha2_password가 기본 인증 플러그인이다. 레거시 앱이 있다면--mysql-native-password=ON옵션으로 명시적으로 활성화해야 한다. - MASTER/SLAVE 용어 완전 제거:
CHANGE MASTER TO,START SLAVE,SHOW SLAVE STATUS등이 문법 오류를 발생시킨다.CHANGE REPLICATION SOURCE TO,START REPLICA,SHOW REPLICA STATUS로 전환해야 한다. - mysqlpump 제거:
mysqldump또는 MySQL Shell의 dump 유틸리티를 사용한다. - InnoDB Adaptive Hash Index(AHI) 기본 비활성화: 워크로드에 따라 성능 변화가 있을 수 있다. 필요하면
innodb_adaptive_hash_index=ON으로 직접 활성화한다. - InnoDB Change Buffer 기본 비활성화: SSD 기반 스토리지에서는 비활성화가 오히려 유리하다.
- 자동 히스토그램 업데이트: 옵티마이저 통계의 정확도를 높여 쿼리 실행 계획을 개선한다. 서버 재시작 후에도 히스토그램 메타데이터가 유지된다.
- restrict_fk_on_non_standard_key=ON 기본값: 비고유 또는 부분 키를 FK로 사용하는 것이 기본적으로 차단된다.
InnoDB 클러스터 아키텍처
InnoDB 클러스터는 세 가지 핵심 컴포넌트로 구성된다.
- MySQL Group Replication: Paxos 기반 합의 프로토콜을 사용하는 동기 복제 레이어. 데이터 일관성과 자동 페일오버를 보장한다.
- MySQL Shell (AdminAPI): 클러스터의 생성, 노드 추가/제거, 상태 모니터링을 위한 관리 인터페이스.
- MySQL Router: 애플리케이션 트래픽을 적절한 노드로 라우팅하는 미들웨어. Primary로 쓰기를, Secondary로 읽기를 분산한다.
┌─────────────────────────────────────────────────┐
│ Application │
└──────────────────────┬──────────────────────────┘
│
┌────────▼────────┐
│ MySQL Router │
│ (R/W split) │
└───┬────────┬────┘
Write │ │ Read
┌────────▼──┐ ┌──▼────────┐
│ Primary │ │ Secondary │
│ (node-1) │ │ (node-2) │
└────────────┘ └───────────┘
│ │
┌───▼───────────▼───┐
│ Secondary │
│ (node-3) │
└───────────────────┘
← Group Replication (Paxos) →
최소 3노드 구성이 필수이며, 최대 9노드까지 확장 가능하다. 홀수 노드(3, 5, 7)를 사용해야 쿼럼(quorum) 계산에서 split-brain을 방지할 수 있다.
InnoDB Cluster vs NDB Cluster 비교
운영 환경에서 어떤 클러스터링 솔루션을 선택할지 결정할 때 참고할 수 있는 비교표다.
| 항목 | InnoDB Cluster | NDB Cluster |
|---|---|---|
| 스토리지 엔진 | InnoDB (디스크 기반) | NDB (인메모리 + 디스크) |
| 복제 방식 | Group Replication (Paxos) | 동기 2-phase commit |
| 샤딩 | 미지원 (수동 파티셔닝 필요) | 자동 샤딩 (Node Group) |
| 최대 노드 수 | 9 | 255 (데이터 노드 144) |
| 쿼리 실행 | 단일 스레드 | 데이터 노드로 push-down 가능 |
| 장애 허용 | N노드 중 과반수 생존 필요 | Node Group당 1 replica로 운영 가능 |
| 적합 워크로드 | 범용 OLTP, 읽기 스케일링 | 초저지연, 쓰기 집중, 통신/금융 |
| 운영 복잡도 | 낮음 (MySQL Shell로 관리) | 높음 (Management Node 별도 필요) |
| 외래 키 지원 | 완전 지원 | 제한적 |
| 트랜잭션 격리 | 모든 레벨 지원 | READ COMMITTED만 지원 |
대부분의 웹 서비스, SaaS 백엔드, 일반적인 OLTP 워크로드에서는 InnoDB Cluster가 운영 복잡도와 기능 면에서 합리적인 선택이다. NDB Cluster는 수십만 TPS 이상의 초저지연이 필요한 통신, 금융 실시간 시스템에 적합하다.
InnoDB 클러스터 구축 실전
사전 준비
모든 노드에서 동일한 MySQL 8.4 버전을 설치하고, 아래 조건을 확인한다.
- 모든 테이블이 InnoDB 엔진을 사용하는지 확인 (MyISAM 등은 사전에 변환)
- 모든 테이블에 PRIMARY KEY가 존재하는지 확인 (Group Replication 필수 조건)
- GTID 활성화 (
gtid_mode=ON,enforce_gtid_consistency=ON) - 각 노드에 고유한
server_id설정 /etc/hosts파일에 모든 노드의 호스트명 등록 (DNS 해석 일관성 보장)
# 각 노드에서 MySQL 8.4 설치 (Ubuntu/Debian 예시)
sudo apt-get update
sudo apt-get install mysql-server-8.4 mysql-shell mysql-router
# InnoDB 엔진이 아닌 테이블 확인
mysql -u root -p -e "
SELECT TABLE_SCHEMA, TABLE_NAME, ENGINE
FROM information_schema.TABLES
WHERE ENGINE != 'InnoDB'
AND TABLE_SCHEMA NOT IN ('mysql','information_schema','performance_schema','sys');
"
# PRIMARY KEY 없는 테이블 확인
mysql -u root -p -e "
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 c.TABLE_NAME IS NULL
AND t.TABLE_SCHEMA NOT IN ('mysql','information_schema','performance_schema','sys')
AND t.TABLE_TYPE = 'BASE TABLE';
"
my.cnf 설정 (각 노드 공통)
[mysqld]
# 기본 설정
server_id=1 # 노드마다 고유값 (1, 2, 3)
bind-address=0.0.0.0
port=3306
datadir=/var/lib/mysql
# GTID 활성화
gtid_mode=ON
enforce_gtid_consistency=ON
# Binary Log
log_bin=mysql-bin
binlog_format=ROW
binlog_transaction_dependency_tracking=WRITESET
log_replica_updates=ON
# Group Replication 기본 설정
plugin_load_add=group_replication.so
group_replication_group_name="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
group_replication_start_on_boot=OFF
group_replication_local_address="node1:33061"
group_replication_group_seeds="node1:33061,node2:33061,node3:33061"
group_replication_bootstrap_group=OFF
# InnoDB 튜닝
innodb_buffer_pool_size=4G # 물리 메모리의 70~80%
innodb_buffer_pool_instances=4 # buffer_pool_size / 1G
innodb_log_file_size=1G
innodb_flush_log_at_trx_commit=1 # 데이터 안정성 최우선
innodb_flush_method=O_DIRECT
# 비활성화된 기본값 중 워크로드에 따라 활성화 검토
# innodb_adaptive_hash_index=ON # OLTP 핫스팟 쿼리가 많으면 활성화
# innodb_change_buffering=all # HDD 사용 시 활성화
# 복제 관련
replica_parallel_type=LOGICAL_CLOCK
replica_parallel_workers=4 # 4~8 범위 권장
replica_preserve_commit_order=ON
# 스토리지 엔진 제한 (InnoDB 외 사용 방지)
disabled_storage_engines="MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY"
# 트랜잭션 격리 수준 (Multi-Primary 사용 시)
# transaction_isolation=READ-COMMITTED
MySQL Shell로 클러스터 생성
// MySQL Shell (mysqlsh) 접속
// mysqlsh root@node1:3306
// 1. 각 인스턴스 설정 확인 및 자동 구성
dba.configureInstance('root@node1:3306', {
clusterAdmin: 'clusteradmin',
clusterAdminPassword: 'SecureP@ss!2026',
})
dba.configureInstance('root@node2:3306', {
clusterAdmin: 'clusteradmin',
clusterAdminPassword: 'SecureP@ss!2026',
})
dba.configureInstance('root@node3:3306', {
clusterAdmin: 'clusteradmin',
clusterAdminPassword: 'SecureP@ss!2026',
})
// 2. 첫 번째 노드에서 클러스터 생성 (Bootstrap)
shell.connect('clusteradmin@node1:3306')
var cluster = dba.createCluster('prodCluster', {
multiPrimary: false, // Single-Primary 모드 권장
memberWeight: 50,
expelTimeout: 5,
autoRejoinTries: 3,
consistency: 'BEFORE_ON_PRIMARY_FAILOVER',
})
// 3. 나머지 노드 추가 (Clone 방식 권장)
cluster.addInstance('clusteradmin@node2:3306', {
recoveryMethod: 'clone',
})
cluster.addInstance('clusteradmin@node3:3306', {
recoveryMethod: 'clone',
})
// 4. 클러스터 상태 확인
cluster.status()
recoveryMethod: 'clone'을 지정하면 기존 클러스터 멤버의 물리적 스냅샷을 사용하여 새 노드의 데이터를 완전히 동기화한다. 데이터 양이 많거나 바이너리 로그 보존 기간이 짧은 경우 Clone 방식이 안전하다.
MySQL Router 설정
# Router 부트스트랩 (클러스터 메타데이터 자동 설정)
mysqlrouter --bootstrap clusteradmin@node1:3306 \
--directory /opt/mysqlrouter \
--user=mysqlrouter \
--conf-use-sockets \
--conf-bind-address=0.0.0.0
# Router 시작
/opt/mysqlrouter/start.sh
# 또는 systemd로 관리
sudo systemctl enable mysqlrouter
sudo systemctl start mysqlrouter
Router가 부트스트랩되면 기본적으로 다음 포트를 사용한다.
- 6446: R/W 포트 (Primary로 라우팅)
- 6447: R/O 포트 (Secondary로 라우팅, round-robin)
- 6448: R/W X Protocol 포트
- 6449: R/O X Protocol 포트
애플리케이션의 DB 연결 문자열을 Router가 동작하는 호스트의 6446(쓰기) 또는 6447(읽기) 포트로 변경하면 된다.
클러스터 운영과 모니터링
일상 운영 명령어
// MySQL Shell에서 클러스터 객체 가져오기
shell.connect('clusteradmin@node1:3306')
var cluster = dba.getCluster()
// 클러스터 전체 상태 확인
cluster.status({ extended: 1 })
// 특정 노드 상태 상세 확인
cluster.status({ extended: 2 })
// 노드 일시 제거 (유지보수)
cluster.removeInstance('clusteradmin@node2:3306', { force: false })
// 노드 재추가
cluster.addInstance('clusteradmin@node2:3306', { recoveryMethod: 'clone' })
// 클러스터 옵션 조회
cluster.options()
// Primary 수동 전환 (graceful switchover)
cluster.setPrimaryInstance('clusteradmin@node2:3306')
// Router 연결 상태 확인
cluster.listRouters()
핵심 모니터링 쿼리
운영 중 반드시 주기적으로 확인해야 할 메트릭이다.
-- Group Replication 멤버 상태 확인
SELECT MEMBER_ID, MEMBER_HOST, MEMBER_PORT, MEMBER_STATE, MEMBER_ROLE
FROM performance_schema.replication_group_members;
-- 트랜잭션 적용 지연 확인 (각 Secondary에서 실행)
SELECT
CHANNEL_NAME,
COUNT_TRANSACTIONS_IN_QUEUE AS trx_in_queue,
COUNT_TRANSACTIONS_CHECKED AS trx_checked,
COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE AS remote_queue,
LAST_CONFLICT_FREE_TRANSACTION
FROM performance_schema.replication_group_member_stats
WHERE CHANNEL_NAME = 'group_replication_applier';
-- Applier 워커 상태 확인
SELECT
WORKER_ID, LAST_SEEN_TRANSACTION,
APPLYING_TRANSACTION, LAST_APPLIED_TRANSACTION
FROM performance_schema.replication_applier_status_by_worker
WHERE CHANNEL_NAME = 'group_replication_applier';
-- InnoDB 클러스터 메타데이터 확인
SELECT * FROM mysql_innodb_cluster_metadata.clusters;
SELECT * FROM mysql_innodb_cluster_metadata.instances;
경고: 운영 시 반드시 주의할 사항
- DDL 실행 주의: 대규모 테이블의
ALTER TABLE은 Group Replication의 인증(certification) 단계에서 전체 클러스터를 블로킹할 수 있다.pt-online-schema-change또는gh-ost를 사용한다. - 대량 트랜잭션 제한:
group_replication_transaction_size_limit(기본 150MB)을 초과하는 트랜잭션은 롤백된다. 대량 INSERT/UPDATE는 배치 단위로 분할한다. - 네트워크 지연: 노드 간 네트워크 왕복 지연이 쓰기 성능에 직접 영향을 미친다. 같은 데이터센터 내 배치를 권장하며, 크로스 리전 배치 시 ClusterSet을 검토한다.
- 바이너리 로그 관리:
binlog_expire_logs_seconds를 적절히 설정하여 디스크 공간을 관리한다. 기본값은 2592000초(30일)이다. - 백업은 Secondary에서: Primary의 부하를 줄이기 위해 물리 백업(xtrabackup, mysqlbackup)은 Secondary 노드에서 수행한다.
장애 시나리오와 복구 절차
시나리오 1: Secondary 노드 1대 장애
가장 흔한 상황이다. 3노드 클러스터에서 Secondary 1대가 다운되면 클러스터는 2/3 쿼럼으로 정상 운영을 계속한다.
// 장애 노드 복구 후 자동 재합류 확인
cluster.status()
// MEMBER_STATE가 RECOVERING -> ONLINE으로 변경되는지 확인
// 자동 재합류가 실패한 경우
cluster.rejoinInstance('clusteradmin@node3:3306')
// 데이터 격차가 크면 Clone으로 재합류
cluster.removeInstance('clusteradmin@node3:3306', { force: true })
cluster.addInstance('clusteradmin@node3:3306', { recoveryMethod: 'clone' })
시나리오 2: Primary 노드 장애
Primary가 다운되면 Group Replication이 자동으로 새 Primary를 선출한다. member_weight가 높은 노드가 우선 선출된다.
// 새 Primary 확인
cluster.status()
// 기존 Primary 복구 후 Secondary로 재합류
cluster.rejoinInstance('clusteradmin@node1:3306')
MySQL Router는 Primary 변경을 자동 감지하고 쓰기 트래픽을 새 Primary로 라우팅한다. 애플리케이션 레벨에서 재연결 로직(connection retry)이 필요하다.
시나리오 3: 쿼럼 상실 (과반수 노드 다운)
3노드 중 2대가 동시에 다운되면 남은 1대가 쿼럼을 잃어 읽기만 가능하고 쓰기가 불가능해진다.
// 살아 있는 노드에서 강제 쿼럼 복구
// 주의: 데이터 손실 가능성 존재
cluster.forceQuorumUsingPartitionOf('clusteradmin@node1:3306')
// 이후 다운된 노드 복구 후 재합류
cluster.rejoinInstance('clusteradmin@node2:3306')
cluster.rejoinInstance('clusteradmin@node3:3306')
forceQuorumUsingPartitionOf는 마지막 수단이다. 이 명령은 지정한 노드의 데이터를 기준으로 클러스터를 재구성하므로, 다른 노드에서 커밋되었으나 아직 전파되지 않은 트랜잭션이 유실될 수 있다.
시나리오 4: 전체 클러스터 재시작
계획된 전체 정지(인프라 점검 등) 후 재시작 시 순서가 중요하다.
// 1. 가장 최신 GTID를 가진 노드를 먼저 시작
// 각 노드에서 확인:
// SELECT @@gtid_executed;
// 2. 해당 노드에서 클러스터 재부팅
shell.connect('clusteradmin@node1:3306')
var cluster = dba.rebootClusterFromCompleteOutage('prodCluster')
// 3. 나머지 노드는 자동 재합류 또는 수동 합류
cluster.rejoinInstance('clusteradmin@node2:3306')
cluster.rejoinInstance('clusteradmin@node3:3306')
쿼리 최적화 전략
InnoDB 클러스터 환경에서의 쿼리 최적화는 단일 인스턴스와 동일한 원칙을 따르되, Group Replication의 특성을 추가로 고려해야 한다.
슬로우 쿼리 분석 파이프라인
-- 1. 슬로우 쿼리 로그 활성화
SET GLOBAL slow_query_log = 1;
SET GLOBAL long_query_time = 1; -- 1초 이상 쿼리 기록
SET GLOBAL log_queries_not_using_indexes = 1;
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';
-- 2. performance_schema로 상위 슬로우 쿼리 식별
SELECT
SCHEMA_NAME,
DIGEST_TEXT,
COUNT_STAR AS exec_count,
ROUND(SUM_TIMER_WAIT / 1000000000000, 3) AS total_sec,
ROUND(AVG_TIMER_WAIT / 1000000000000, 3) AS avg_sec,
SUM_ROWS_EXAMINED AS rows_examined,
SUM_ROWS_SENT AS rows_sent,
ROUND(SUM_ROWS_EXAMINED / NULLIF(SUM_ROWS_SENT, 0), 1) AS exam_to_sent_ratio,
FIRST_SEEN,
LAST_SEEN
FROM performance_schema.events_statements_summary_by_digest
WHERE SCHEMA_NAME NOT IN ('mysql', 'performance_schema', 'information_schema', 'sys')
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 20;
exam_to_sent_ratio(조회 행 대비 반환 행 비율)가 100 이상이면 인덱스 추가 또는 쿼리 재작성이 필요하다는 강력한 신호다.
# 3. pt-query-digest로 슬로우 로그 분석 (Percona Toolkit)
pt-query-digest /var/log/mysql/slow.log \
--limit=20 \
--order-by=Query_time:sum \
--output report > /tmp/slow_report.txt
EXPLAIN ANALYZE 활용
MySQL 8.4에서 EXPLAIN ANALYZE는 실제 실행 통계를 포함한 실행 계획을 제공한다.
EXPLAIN ANALYZE
SELECT o.order_id, o.order_date, c.customer_name, SUM(oi.quantity * oi.unit_price) AS total
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
JOIN order_items oi ON o.order_id = oi.order_id
WHERE o.order_date BETWEEN '2026-01-01' AND '2026-03-01'
AND c.region = 'APAC'
GROUP BY o.order_id, o.order_date, c.customer_name
ORDER BY total DESC
LIMIT 50;
출력에서 주목해야 할 항목은 다음과 같다.
- actual time: 실제 소요 시간 (첫 행 반환 ~ 마지막 행 반환)
- rows: 실제 처리된 행 수 (예상치와 큰 차이가 있으면 통계가 부정확하다는 의미)
- loops: 해당 단계가 반복 실행된 횟수
- Table scan / Index scan: 풀 테이블 스캔 여부
인덱스 전략
Covering Index (커버링 인덱스)
쿼리에 필요한 모든 컬럼을 인덱스에 포함시켜 테이블 데이터 페이지 접근을 제거하는 전략이다.
-- 자주 실행되는 쿼리 패턴
SELECT customer_id, order_date, status
FROM orders
WHERE customer_id = 12345
AND status = 'SHIPPED'
ORDER BY order_date DESC;
-- 커버링 인덱스 생성
CREATE INDEX idx_orders_covering
ON orders (customer_id, status, order_date DESC, order_id);
-- EXPLAIN에서 Extra: Using index 확인
EXPLAIN SELECT customer_id, order_date, status
FROM orders
WHERE customer_id = 12345
AND status = 'SHIPPED'
ORDER BY order_date DESC;
Invisible Index로 안전한 인덱스 관리
MySQL 8.4에서 인덱스를 삭제하기 전에 invisible로 전환하여 영향을 테스트할 수 있다.
-- 인덱스를 invisible로 변경 (옵티마이저가 무시)
ALTER TABLE orders ALTER INDEX idx_old_index INVISIBLE;
-- 일정 기간 모니터링 후 문제가 없으면 삭제
DROP INDEX idx_old_index ON orders;
-- 문제가 발생하면 즉시 복원
ALTER TABLE orders ALTER INDEX idx_old_index VISIBLE;
히스토그램 활용
MySQL 8.4의 자동 히스토그램 업데이트를 활용하되, 필요한 경우 수동으로도 생성할 수 있다.
-- 컬럼에 히스토그램 생성
ANALYZE TABLE orders UPDATE HISTOGRAM ON status, region WITH 100 BUCKETS;
-- 히스토그램 정보 확인
SELECT
SCHEMA_NAME, TABLE_NAME, COLUMN_NAME,
JSON_EXTRACT(HISTOGRAM, '$.\"number-of-buckets-specified\"') AS buckets,
JSON_EXTRACT(HISTOGRAM, '$.\"sampling-rate\"') AS sampling_rate,
JSON_EXTRACT(HISTOGRAM, '$.\"histogram-type\"') AS hist_type
FROM information_schema.COLUMN_STATISTICS
WHERE TABLE_NAME = 'orders';
히스토그램은 인덱스가 없는 컬럼의 선택도(selectivity)를 옵티마이저에 알려줘서, 조인 순서와 실행 계획 결정에 도움을 준다. 카디널리티가 낮은 컬럼(status, region, type 등)에 특히 효과적이다.
Group Replication 환경 특화 최적화
InnoDB 클러스터에서 추가로 고려해야 할 최적화 포인트가 있다.
쓰기 최적화: Group Replication은 모든 쓰기 트랜잭션을 그룹 전체에서 인증(certification)한다. 트랜잭션이 작을수록 인증 충돌(conflict) 가능성이 낮다. 대량 INSERT는 1000~5000행 단위의 배치로 분할한다.
읽기 분산: MySQL Router의 R/O 포트(6447)를 활용하여 읽기 쿼리를 Secondary로 분산한다. 단,
consistency: 'BEFORE_ON_PRIMARY_FAILOVER'설정 시 페일오버 직후 잠시 읽기 지연이 발생할 수 있다.Multi-Primary 모드의 격리 수준: Multi-Primary 모드를 사용한다면
transaction_isolation=READ-COMMITTED로 설정한다.REPEATABLE READ는 Multi-Primary에서 인증 충돌을 증가시킬 수 있다.핫스팟 테이블 회피: 같은 행에 대한 동시 업데이트는 인증 단계에서 충돌을 발생시킨다. 카운터 테이블 등은 애플리케이션 레벨에서 분산 처리(Redis 등)를 고려한다.
InnoDB 버퍼 풀 튜닝
쿼리 성능에 가장 큰 영향을 미치는 단일 설정은 innodb_buffer_pool_size다.
-- 현재 버퍼 풀 히트율 확인
SELECT
(1 - (
(SELECT VARIABLE_VALUE FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads') /
(SELECT VARIABLE_VALUE FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests')
)) * 100 AS buffer_pool_hit_rate;
-- 99% 이하이면 buffer_pool_size 증가 검토
-- 버퍼 풀 상세 상태
SELECT
POOL_ID,
POOL_SIZE,
FREE_BUFFERS,
DATABASE_PAGES,
MODIFIED_DB_PAGES,
PAGES_MADE_YOUNG,
PAGES_NOT_MADE_YOUNG
FROM information_schema.INNODB_BUFFER_POOL_STATS;
-- 온라인으로 버퍼 풀 크기 변경 (재시작 불필요)
SET GLOBAL innodb_buffer_pool_size = 8 * 1024 * 1024 * 1024; -- 8GB
버퍼 풀 히트율이 99% 이상을 유지하는 것이 목표다. 히트율이 95% 이하로 떨어지면 디스크 I/O가 급증하여 전체 쿼리 성능이 급격히 저하된다.
운영 체크리스트
일상 운영과 장애 대응을 위한 체크리스트다.
일일 점검
-
cluster.status()로 전체 노드 ONLINE 상태 확인 -
replication_group_member_stats에서COUNT_TRANSACTIONS_IN_QUEUE가 비정상적으로 높지 않은지 확인 (100 이상이면 경고) - 슬로우 쿼리 로그에 새로운 패턴이 있는지 확인
- 디스크 사용량 확인 (바이너리 로그, 릴레이 로그, 데이터 디렉토리)
- 버퍼 풀 히트율 99% 이상 유지 확인
주간 점검
-
pt-query-digest로 슬로우 쿼리 트렌드 분석 - 사용되지 않는 인덱스 확인 (
sys.schema_unused_indexes) - 중복 인덱스 확인 (
sys.schema_redundant_indexes) - 테이블 통계 업데이트 (
ANALYZE TABLE) - 바이너리 로그 정리 상태 확인
장애 대응 전 준비
-
forceQuorumUsingPartitionOf사용 절차를 팀 전체가 숙지 -
rebootClusterFromCompleteOutage실행 순서(최신 GTID 노드 우선) 문서화 - MySQL Router 재부트스트랩 절차 숙지
- 물리 백업(xtrabackup)이 최소 일 1회 Secondary에서 수행되는지 확인
- 복구 시간 목표(RTO)에 맞는 복구 절차 리허설
업그레이드 전 확인
-
util.checkForServerUpgrade()로 호환성 검증 - mysql_native_password 의존성 제거 확인
- MASTER/SLAVE 용어 사용 코드 검색 및 교체
- mysqlpump 사용 스크립트를 mysqldump 또는 MySQL Shell dump로 교체
- 비고유 키 FK 사용 여부 확인 (
restrict_fk_on_non_standard_key대비) - Rolling Upgrade 순서: Secondary -> Secondary -> Primary (switchover 후)
성능 벤치마크 참고값
8.4 LTS 환경에서의 대략적인 성능 기대치를 정리한다. 하드웨어와 워크로드에 따라 큰 차이가 있으므로 참고 수준으로만 활용한다.
| 구성 | 단일 인스턴스 | InnoDB Cluster 3노드 (Single-Primary) |
|---|---|---|
| 쓰기 TPS (sysbench oltp_write_only) | ~15,000 | |
| 읽기 QPS (sysbench oltp_read_only) | ~50,000 | ~120,000 (3노드 합산, R/O 분산) |
| 페일오버 시간 | N/A | 5~30초 (expelTimeout + 선출 시간) |
| Clone 복구 시간 (100GB 데이터) | N/A | 10~30분 (네트워크 대역폭 의존) |
Group Replication의 인증 오버헤드로 단일 노드 대비 쓰기 TPS가 20~30% 감소하는 것은 정상이다. 읽기 성능은 노드 추가로 선형에 가깝게 확장할 수 있다.
마무리
MySQL 8.4 InnoDB 클러스터는 운영 복잡도와 가용성 사이의 균형이 잘 잡힌 솔루션이다. 핵심은 세 가지다.
첫째, 클러스터 구성 전에 모든 사전 조건(InnoDB 엔진, PK, GTID)을 완벽히 충족시킨다. 둘째, Group Replication의 특성(인증 기반 합의)을 이해하고 트랜잭션 크기를 제어한다. 셋째, performance_schema와 슬로우 쿼리 분석을 통해 80/20 법칙에 따라 상위 20%의 문제 쿼리를 집중 최적화한다.
8.0에서 8.4로의 업그레이드를 고려하고 있다면, mysql_native_password 의존성 제거와 MASTER/SLAVE 용어 교체를 먼저 진행한 뒤 Rolling Upgrade 방식으로 진행할 것을 권장한다.
References
- MySQL 8.4 Release Notes - MySQL 8.4 LTS 전체 릴리스 노트
- MySQL 8.4 Reference Manual - What Is New in MySQL 8.4 - 8.0 대비 변경 사항 공식 문서
- MySQL Shell 9.5 - InnoDB Cluster - InnoDB Cluster 공식 가이드
- MySQL 8.4 Reference Manual - Group Replication - Group Replication 공식 레퍼런스
- MySQL 8.4 Reference Manual - Optimizing SELECT Statements - 쿼리 최적화 공식 가이드
- MySQL 8.4 - InnoDB vs NDB Cluster Comparison - InnoDB와 NDB 스토리지 엔진 비교
- Percona - InnoDB Cluster Setup: Building a 3-Node HA Architecture - 실전 구축 가이드
- MySQL Replication Best Practices (Percona, 2025) - 복제 운영 베스트 프랙티스