
시나리오
데이터베이스: 1백만 건의 사용자 테이블
테이블 구조:
sqlCopyCREATE TABLE users (
id INT PRIMARY KEY,
username VARCHAR(50),
email VARCHAR(100),
last_name VARCHAR(50),
city VARCHAR(50),
created_at TIMESTAMP
);
초기 쿼리 성능 확인
sqlCopyEXPLAIN ANALYZE
SELECT * FROM users
WHERE last_name = 'Kim' AND city = 'Seoul';
결과 예시:
실행 시간: 1.5초
전체 테이블 스캔 (비효율적)
성능 저하 원인: 인덱스 부재
인덱스 생성
sqlCopyCREATE INDEX idx_last_name_city ON users(last_name, city);
인덱스 후 성능 비교
sqlCopyEXPLAIN ANALYZE
SELECT * FROM users
WHERE last_name = 'Kim' AND city = 'Seoul';
개선 결과:
실행 시간: 0.05초 (약 30배 성능 향상)
인덱스 스캔으로 변경
디스크 I/O 최소화
복잡한 조인 시나리오
sqlCopy-- 비효율적인 원본 쿼리
SELECT o.id, c.name, p.product_name, o.total_price
FROM orders o
JOIN customers c ON o.customer_id = c.id
JOIN products p ON o.product_id = p.id
WHERE c.city = 'Seoul' AND o.order_date > '2023-01-01';
성능 분석
실행 시간: 2.3초
3개 테이블 풀 스캔
복잡한 조인 연산
조인 순서 최적화
인덱스 추가
서브쿼리로 변환
최적화된 쿼리
방법 1: 조인 순서 및 인덱스 최적화
CREATE INDEX idx_customer_city ON customers(city);
CREATE INDEX idx_order_date ON orders(order_date);
SELECT /*+ USE_NL(c o) */
o.id, c.name, p.product_name, o.total_price
FROM customers c
JOIN orders o ON c.id = o.customer_id
JOIN products p ON o.product_id = p.id
WHERE c.city = 'Seoul' AND o.order_date > '2023-01-01';
-- 방법 2: 서브쿼리 최적화
SELECT o.id,
(SELECT name FROM customers WHERE id = o.customer_id) as customer_name,
(SELECT product_name FROM products WHERE id = o.product_id) as product_name,
o.total_price
FROM orders o
WHERE o.customer_id IN (
SELECT id FROM customers WHERE city = 'Seoul'
) AND o.order_date > '2023-01-01';
성능 비교
원본 쿼리: 2.3초
최적화 쿼리: 0.3초 (약 7.6배 성능 향상)
Redis 기반 쿼리 캐싱 예시
pythonCopyimport redis
import json
redis_client = redis.Redis(host='localhost', port=6379, db=0)
def cached_database_query(query, params, expire_time=3600):
# 캐시 키 생성
cache_key = f"query:{hash(query)}:{hash(str(params))}"
# 캐시된 결과 확인
cached_result = redis_client.get(cache_key)
if cached_result:
return json.loads(cached_result)
# 데이터베이스 쿼리 실행
result = execute_database_query(query, params)
# 결과 캐싱
redis_client.setex(cache_key, expire_time, json.dumps(result))
return result
캐싱 효과
반복 쿼리 응답 시간: 10ms 이내
데이터베이스 부하 감소
캐시 적중률 80% 이상
대규모 테이블 파티셔닝
sqlCopyCREATE TABLE sales (
sale_id INT,
product_id INT,
sale_date DATE,
amount DECIMAL(10,2)
) PARTITION BY RANGE (YEAR(sale_date)) (
PARTITION p2022 VALUES LESS THAN (2023),
PARTITION p2023 VALUES LESS THAN (2024),
PARTITION p2024 VALUES LESS THAN (2025),
PARTITION p_future VALUES LESS THAN MAXVALUE
);
파티셔닝 성능 이점
특정 연도 데이터 쿼리 속도 50-70% 향상
불필요한 데이터 스캔 방지
관리 용이성 증가
확실히 쿼리를 짜다보면 성능개선 부분에 대해 생각하게 된다..
이번에도 energy_Resource가 1만개인데 join을 남발해서 혼나지 않았는가..
그래서 생각한 답은 2개다. indexing을 해보자, partitioning을 해보자.
그 결과를 어떻게 봐야할까 하다가 해본 결과이다.
확실히 자주 검색하는 부분에 대해서는 캐싱을 해두거나 indexing을 조건으로 만들어두는게 좋겠다..