Home Knowledge base Skyline Cloud MySQL 데이터베이스 성능 튜닝 기초 KNOWLEDGE BASE

MySQL 데이터베이스 성능 튜닝 기초

Linux VPS 또는 클라우드 서버에서 MySQL 8.0을 튜닝하는 실전 단계별 가이드: 느린 쿼리 로그로 먼저 측정하고, InnoDB 버퍼 풀 크기를 올바르게 설정하며, EXPLAIN 출력을 읽고, 적절한 인덱스를 추가하는 방법을 다룹니다.

MySQL 데이터베이스 성능 튜닝 기초

Linux VPS 또는 클라우드 서버에서 MySQL 8.0을 튜닝하는 실전 단계별 가이드입니다. 느린 쿼리 로그로 먼저 측정하고, InnoDB 버퍼 풀 크기를 올바르게 설정하며, EXPLAIN 출력을 읽고, 적절한 인덱스를 추가하는 방법을 다룹니다.

MySQL 데이터베이스 성능 튜닝 기초

느린 데이터베이스는 애플리케이션이 느려지는 가장 흔한 원인입니다. 서버를 증설하거나 코드를 다시 작성하기 전에, 몇 시간의 체계적인 MySQL 튜닝만으로도 잃어버린 성능의 상당 부분을 되찾는 경우가 많습니다. 이 가이드는 MySQL 8.0을 실행하는 일반적인 Linux VPS 또는 클라우드 서버를 기준으로 기본기를 다룹니다. 측정하고, 메모리 크기를 설정하고, 쿼리 실행 계획을 읽고, 적절한 인덱스를 추가하는 과정입니다.

이 단계들은 모든 표준 MySQL 8.0 설치에 적용됩니다. 여기에는 Skyline Cloud VPS 또는 클라우드 서버에서 사우디아라비아 국내에 호스팅되는 데이터베이스도 포함되며, 데이터를 사우디아라비아 내에 상주시키면 PDPL 및 NCA 규정 준수에 도움이 됩니다.

사전 준비 사항

  • MySQL 8.0이 설치된 Linux 서버(Ubuntu 22.04/24.04, Debian, 또는 RHEL 계열).
  • sudo 권한을 가진 사용자와 관리 권한을 가진 MySQL 계정.
  • 실제 또는 대표성 있는 워크로드 — 유휴 상태의 데이터베이스를 대상으로 튜닝하면 아무것도 알 수 없습니다.

항상 한 번에 하나의 변수만 변경하고 다시 측정하세요. 측정 없는 튜닝은 추측에 불과합니다.

1단계 — 느린 쿼리 로그로 먼저 측정하기

측정하지 않는 것은 튜닝할 수 없습니다. 느린 쿼리 로그는 시간 임계값을 초과하는 모든 구문을 기록하며, 이는 가장 심각한 문제 쿼리를 찾는 가장 빠른 방법입니다.

런타임에 활성화하기(재시작 불필요):

SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;            -- log queries slower than 1 second
SET GLOBAL log_queries_not_using_indexes = 'ON';

영구적으로 적용하려면 MySQL 설정 파일(Ubuntu/Debian에서는 /etc/mysql/mysql.conf.d/mysqld.cnf, RHEL에서는 /etc/my.cnf)에 다음을 추가하세요:

[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/mysql-slow.log
long_query_time = 1
log_queries_not_using_indexes = 1

대표성 있는 워크로드를 실행한 다음 로그를 분석하세요. Percona Toolkit의 pt-query-digest는 동일한 쿼리를 핑거프린트별로 그룹화하고 소비된 총 시간 기준으로 순위를 매깁니다:

sudo apt install percona-toolkit   # Debian/Ubuntu
pt-query-digest /var/log/mysql/mysql-slow.log | less

보고서 상단의 쿼리에 집중하세요. 단일 실행 시간이 가장 긴 쿼리가 아니라 총 실행 시간이 가장 긴 쿼리입니다. 분당 10,000번 실행되는 쿼리를 최적화하는 것이 야간에 한 번 실행되는 쿼리를 최적화하는 것보다 낫습니다.

2단계 — InnoDB 버퍼 풀 크기 설정하기

InnoDB는 기본 스토리지 엔진이며, 그 버퍼 풀은 테이블과 인덱스 데이터를 RAM에 캐싱합니다. 작업 집합(working set)이 버퍼 풀에 들어가면 MySQL은 대부분의 읽기를 디스크 대신 메모리에서 처리하는데, 이것이 보통 가장 큰 단일 성능 향상 요인입니다.

현재 크기와 적중률(hit ratio)을 확인하세요:

SHOW VARIABLES LIKE 'innodb_buffer_pool_size';

SELECT
  (1 - (SELECT VARIABLE_VALUE FROM performance_schema.global_status
        WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads')
      / NULLIF((SELECT VARIABLE_VALUE FROM performance_schema.global_status
        WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests'), 0)) * 100
  AS hit_ratio_pct;

적중률은 99% 이상을 목표로 하세요. Oracle 문서에 따르면, 전용 데이터베이스 서버에서는 버퍼 풀을 **전체 RAM의 50~75%**로 설정하여 OS와 MySQL의 다른 버퍼를 위한 공간을 남겨 두어야 합니다(InnoDB 자체는 설정된 크기보다 약 10% 더 많은 메모리를 예약합니다). 소규모 공유 서버에서는 50%에 가깝게 유지하고 OS를 위해 최소 1~2 GB를 남겨 두세요.

8 GB 전용 서버라면 약 5~6 GB가 합리적인 시작점입니다:

[mysqld]
innodb_buffer_pool_size = 5G

MySQL을 재시작하고 워크로드가 캐시를 워밍업한 후 적중률을 다시 확인하세요. 너무 크게 설정하지 마세요. 버퍼 풀에 다른 프로세스를 더한 값이 RAM을 초과하면 OS가 디스크로 스왑(swap)하면서 성능이 무너집니다.

3단계 — EXPLAIN으로 쿼리 실행 계획 읽기

어떤 쿼리가 느린지 파악했다면, 이제 그 _이유_를 알아내세요. EXPLAIN은 MySQL이 구문을 어떻게 실행하려는지 보여줍니다:

EXPLAIN SELECT * FROM orders WHERE customer_id = 42 AND status = 'shipped';

가장 중요한 컬럼:

컬럼 주의해서 볼 점
type ALL은 풀 테이블 스캔을 의미하며 보통 좋지 않습니다. ref, range, 또는 const가 더 낫습니다.
key 사용된 인덱스. NULL은 사용된 인덱스가 없음을 의미합니다.
rows 검사할 것으로 추정되는 행 수. 낮을수록 좋습니다.
Extra Using filesort 또는 Using temporary는 비용이 큰 정렬 단계를 나타냅니다.

MySQL 8.0에서는 EXPLAIN ANALYZE로 한 걸음 더 나아갈 수 있습니다. 이 명령은 실제로 쿼리를 실행하여 추정치와 함께 실제 소요 시간을 보고합니다:

EXPLAIN ANALYZE SELECT * FROM orders WHERE customer_id = 42 AND status = 'shipped';

이는 인덱스 변경이 실제로 도움이 되었는지 확인하는 가장 신뢰할 수 있는 방법입니다.

4단계 — 적절한 인덱스 추가하기

대용량 테이블에서의 풀 테이블 스캔(type: ALL)은 느린 쿼리의 전형적인 원인입니다. WHERE, JOIN, ORDER BY 절에서 사용되는 컬럼에 인덱스를 추가하세요.

위 쿼리의 경우, 필터링되는 두 컬럼을 모두 포함하는 복합 인덱스를 사용하면 MySQL이 일치하는 행으로 곧장 이동할 수 있습니다:

CREATE INDEX idx_orders_customer_status
  ON orders (customer_id, status);

컬럼 순서가 중요합니다. 가장 선택도가 높은 컬럼 — 또는 동등(equality) 매칭에 사용되는 컬럼 — 을 먼저 두세요. 복합 인덱스는 선행 컬럼(customer_id)만으로 필터링하는 쿼리에도 사용되므로, 이를 위한 별도의 단일 컬럼 인덱스는 거의 필요하지 않습니다.

EXPLAIN ANALYZE를 다시 실행하여 type이 개선되었는지(예: ALL에서 ref로) 그리고 rows가 크게 줄었는지 확인하세요.

인덱스는 공짜가 아닙니다. 디스크 공간을 소비하고 각 인덱스를 유지 관리해야 하므로 쓰기(INSERT/UPDATE/DELETE)를 느리게 만듭니다. 신중하게 인덱스를 설계하고, sys.schema_unused_indexes 뷰를 통해 식별할 수 있는 사용되지 않는 인덱스는 제거하세요:

SELECT * FROM sys.schema_unused_indexes;

5단계 — 검증한 다음 반복하기

튜닝은 일회성 작업이 아니라 반복 과정입니다. 각 변경 후에:

  1. 영향을 받은 쿼리를 EXPLAIN ANALYZE로 다시 실행하고 소요 시간을 비교하세요.
  2. 느린 쿼리 로그를 다시 실행시키고 다시 다이제스트(digest)하세요.
  3. 버퍼 풀 적중률이 계속 높게 유지되는지 확인하세요.

도움이 되지 않은 변경은 모두 되돌리세요. 무엇을 왜 변경했는지 메모해 두세요. 미래의 당신이 현재의 당신에게 고마워할 것입니다.

흔한 함정

  • 단일 쿼리만 따로 떼어 튜닝하기. 느린 로그 다이제스트가 드러내는 총 영향(빈도 × 비용)을 기준으로 최적화하세요.
  • 블로그에서 my.cnf 값 그대로 복사하기. 버퍼 풀 크기는 당신의 RAM과 데이터셋에 따라 달라집니다. 복사하지 말고 측정하세요.
  • 인덱스된 컬럼을 함수로 감싸기. WHERE DATE(created_at) = '2026-06-08'created_at의 인덱스를 사용할 수 없습니다. 범위 조건으로 다시 작성하세요: WHERE created_at >= '2026-06-08' AND created_at < '2026-06-09'.
  • 넓은 테이블에서 SELECT * 사용하기. 필요한 컬럼만 가져와야 MySQL이 커버링 인덱스(covering index)를 사용할 수 있습니다.

결론

MySQL 성능 튜닝은 마법이 아니라 체계적인 작업입니다. 느린 쿼리 로그로 측정하고, InnoDB에 충분한 RAM을 주고, EXPLAIN ANALYZE로 쿼리 실행 계획을 읽고, 신중하게 인덱스를 추가하세요. 이 네 가지 기본기만으로도 하드웨어를 업그레이드하기 전에 실제 세계의 성능 저하 대부분이 해결됩니다.

확장성, 예측 가능한 지연 시간, 또는 아랍어 현지 지원이 필요한 워크로드라면 데이터베이스를 사우디아라비아 국내에 호스팅하세요. Skyline Cloud VPS 및 클라우드 서버를 살펴보고, 비즈니스 이메일 호스팅과 결합하여 완전한 스택을 구성한 다음, Skyline Cloud 계정을 생성하여 몇 분 만에 튜닝된 MySQL 서버를 배포하세요.

SKYLINE Engineering

@skyline

The engineering team at SKYLINE Industrial Solutions. We publish field-tested guides drawn from real KSA and GCC deployments.

See author profile
SKYLINE engineering services

Need this implemented for you?

Reading is free — building it right takes a team. SKYLINE engineers ship Skyline Cloud for Aramco vendors, banks, hospitals and government agencies across Saudi Arabia. Talk to us before you start.

Aramco Approved Contractor ISO 9001 · ISO 27001 SAMA CSF aligned NCA ECC ready 247+ KSA clients

Comments

0 total · 0 threads
Be the first to leave a comment.