DB 성능 튜닝 완벽 체크리스트: 느린 쿼리를 10배 빠르게 만드는 방법

데이터베이스 성능을 획기적으로 향상시키는 실전 체크리스트. 인덱스, 쿼리, 설정 최적화까지 모든 것을 다룹니다.

럿지 AI 팀
5분 읽기

DB 성능 튜닝 체크리스트



1단계: 문제 진단



슬로우 쿼리 로그 확인



``sql
-- 슬로우 쿼리 활성화
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1; -- 1초 이상

-- 로그 확인
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log
`

현재 실행 중인 쿼리



`sql
SHOW PROCESSLIST;

-- 상세 정보
SELECT * FROM information_schema.processlist
WHERE command != 'Sleep'
ORDER BY time DESC;
`

2단계: 인덱스 최적화



✅ 체크리스트



- [ ] WHERE 절 컬럼에 인덱스
- [ ] JOIN 컬럼에 인덱스
- [ ] ORDER BY 컬럼에 인덱스
- [ ] 복합 인덱스 순서 최적화
- [ ] 불필요한 인덱스 제거

인덱스 확인



`sql
-- 테이블의 인덱스 목록
SHOW INDEX FROM orders;

-- 사용 안 하는 인덱스
SELECT * FROM sys.schema_unused_indexes;
`

복합 인덱스 순서



`sql
-- ✗ 나쁨
CREATE INDEX idx_bad ON orders(order_date, status);
WHERE status = 'PAID' AND order_date >= '2024-01-01';

-- ✓ 좋음 (선택도 높은 것 먼저)
CREATE INDEX idx_good ON orders(status, order_date);
`

3단계: 쿼리 최적화



SELECT 최적화



`sql
-- ✗ 나쁨
SELECT * FROM orders;

-- ✓ 좋음
SELECT order_id, total_amount FROM orders;
`

JOIN 최적화



`sql
-- ✗ 나쁨 (불필요한 JOIN)
SELECT o.*
FROM orders o
LEFT JOIN members m ON o.member_id = m.member_id;

-- ✓ 좋음
SELECT * FROM orders;
`

WHERE 최적화



`sql
-- ✗ 나쁨 (함수 사용)
WHERE YEAR(order_date) = 2024

-- ✓ 좋음
WHERE order_date >= '2024-01-01' AND order_date < '2025-01-01'

-- ✗ 나쁨 (LIKE 중간 매칭)
WHERE name LIKE '%홍%'

-- ✓ 좋음 (LIKE 앞부분 매칭)
WHERE name LIKE '홍%'
`

LIMIT 활용



`sql
-- ✗ 나쁨
SELECT * FROM orders ORDER BY order_date DESC;

-- ✓ 좋음
SELECT * FROM orders ORDER BY order_date DESC LIMIT 100;
`

4단계: 테이블 설계



정규화



`sql
-- ✗ 나쁨 (데이터 중복)
CREATE TABLE orders (
order_id INT,
member_name VARCHAR(50), -- 중복!
member_address VARCHAR(200) -- 중복!
);

-- ✓ 좋음 (3NF)
CREATE TABLE orders (
order_id INT,
member_id INT,
FOREIGN KEY (member_id) REFERENCES members(member_id)
);

CREATE TABLE members (
member_id INT PRIMARY KEY,
name VARCHAR(50),
address VARCHAR(200)
);
`

데이터 타입 최적화



`sql
-- ✗ 나쁨
status VARCHAR(255)

-- ✓ 좋음
status ENUM('PENDING', 'PAID', 'SHIPPED')

-- ✗ 나쁨
is_active VARCHAR(10)

-- ✓ 좋음
is_active BOOLEAN
`

5단계: 파티셔닝



대용량 테이블



`sql
-- 1억 건 이상 → 파티셔닝 고려

CREATE TABLE orders (
order_id BIGINT,
order_date DATE,
PRIMARY KEY (order_id, order_date)
) PARTITION BY RANGE (YEAR(order_date) * 100 + MONTH(order_date)) (
PARTITION p202401 VALUES LESS THAN (202402),
PARTITION p202402 VALUES LESS THAN (202403),
...
);
`

6단계: 설정 최적화



InnoDB 버퍼 풀



`ini

my.cnf


[mysqld]

전체 RAM의 70-80%


innodb_buffer_pool_size = 8G

CPU 코어 수


innodb_buffer_pool_instances = 8
`

쿼리 캐시 (MySQL 5.7 이하)



`ini
query_cache_type = 1
query_cache_size = 256M
`

커넥션 풀



`ini
max_connections = 500
`

7단계: N+1 문제 해결



문제



`sql
-- 주문 100개 조회
SELECT * FROM orders LIMIT 100;

-- 각 주문마다 회원 정보 (100번!)
SELECT * FROM members WHERE member_id = ?;
`

해결



`sql
-- JOIN으로 한 번에
SELECT o.*, m.*
FROM orders o
LEFT JOIN members m ON o.member_id = m.member_id
LIMIT 100;
`

8단계: 캐싱 전략



Redis 캐싱



`python

자주 조회되는 데이터


product = cache.get(f'product:{product_id}')
if not product:
product = db.query('SELECT * FROM products WHERE id = ?', product_id)
cache.set(f'product:{product_id}', product, ttl=3600)
`

쿼리 결과 캐싱



`python

집계 쿼리 캐싱


stats = cache.get('daily_stats')
if not stats:
stats = db.query('SELECT COUNT(*), SUM(amount) FROM orders ...')
cache.set('daily_stats', stats, ttl=3600)
`

9단계: 모니터링



주요 지표



`sql
-- 슬로우 쿼리 개수
SHOW GLOBAL STATUS LIKE 'Slow_queries';

-- 커넥션 사용률
SHOW STATUS LIKE 'Threads_connected';
SHOW VARIABLES LIKE 'max_connections';

-- 버퍼 풀 사용률
SHOW STATUS LIKE 'Innodb_buffer_pool%';
`

테이블 통계



`sql
-- 테이블 크기
SELECT
table_name,
ROUND(((data_length + index_length) / 1024 / 1024), 2) AS size_mb
FROM information_schema.tables
WHERE table_schema = 'mydatabase'
ORDER BY size_mb DESC;
`

10단계: 정기 유지보수



통계 정보 갱신



`sql
-- 주 1회
ANALYZE TABLE orders;
ANALYZE TABLE members;
`

테이블 최적화



`sql
-- 월 1회
OPTIMIZE TABLE orders;
`

성능 향상 전후 비교



Before



`sql
SELECT * FROM orders
WHERE status = 'PAID' AND order_date >= '2024-01-01';
-- 실행 시간: 5초
-- EXPLAIN: type=ALL, rows=1,000,000
`

After (최적화 적용)



`sql
-- 1. 인덱스 추가
CREATE INDEX idx_status_date ON orders(status, order_date);

-- 2. 필요한 컬럼만 조회
SELECT order_id, total_amount
FROM orders
WHERE status = 'PAID' AND order_date >= '2024-01-01'
LIMIT 100;

-- 실행 시간: 0.01초 (500배 향상!)
-- EXPLAIN: type=ref, rows=100, key=idx_status_date
``

최종 체크리스트



인덱스


- [ ] WHERE 절 인덱스
- [ ] JOIN 인덱스
- [ ] 복합 인덱스 순서
- [ ] 불필요한 인덱스 제거

쿼리


- [ ] SELECT * 제거
- [ ] LIMIT 사용
- [ ] 함수 제거
- [ ] N+1 해결

설계


- [ ] 정규화 (3NF)
- [ ] 데이터 타입 최적화
- [ ] 파티셔닝 (대용량)

운영


- [ ] 슬로우 쿼리 모니터링
- [ ] 정기 ANALYZE
- [ ] 정기 OPTIMIZE

더 배우기



김영한의 실전 데이터베이스
- 성능 튜닝 심화
- 대용량 처리
- 실전 최적화

---

**태그**: #DB성능튜닝 #쿼리최적화 #MySQL성능 #체크리스트 #최적화가이드

L

럿지 AI 팀

AI 기술과 비즈니스 혁신을 선도하는 럿지 AI의 콘텐츠 팀입니다.