HTTP 200 OK

Memento mori & Carpe diem

DB

데이터베이스 성능 최적화 고찰

sjoongh 2025. 3. 2. 23:29

대규모 서비스를 운영하다 보면 데이터 저장 방식, 쿼리 최적화, 트랜잭션 관리, 대용량 데이터 처리가 서비스 성능에 직접적인 영향을 미치기도 합니다.

 

잘못된 데이터 모델링, 비효율적인 쿼리, 인덱스 활용 부족 등이 문제를 일으킬 수 있습니다.

 

이 글에서는 PostgreSQL과 MySQL을 기반으로 쿼리 최적화, 대용량 데이터 처리, 트랜잭션 설계를 위한 전략을 정리해 봤습니다.

 

1. 성능 최적화 전 선행 작업

 

백엔드 개발자로서 성능 개선을 고민할 때, 단순히 "이 쿼리를 최적화하자" 수준이 아니라 서비스의 데이터 처리 패턴을 먼저 이해해야 한다.

 

1.1 데이터 액세스 패턴 분석

"우리 서비스는 읽기(조회) 비중이 높은가? 쓰기(저장) 비중이 높은가?"

  • 읽기 중심 서비스 (예: 뉴스 피드, 검색 엔진) → 캐싱 전략이 중요
  • 쓰기 중심 서비스 (예: IoT 센서 데이터 저장) → 배치 처리, 비동기 큐 활용 고려

"어떤 쿼리가 가장 자주 실행되며, 어떤 부분에서 병목이 발생하는가?"

  • Slow Query Log 분석 (long_query_time 설정)
  • EXPLAIN ANALYZE 활용
  • APM (Application Performance Monitoring) 툴 연동 (예: New Relic, Datadog)

 

1.2 현재 데이터 모델과 트랜잭션 설계 점검

데이터베이스의 정규화 vs 반정규화 여부를 검토해야 한다.

  • 정규화: 데이터 중복을 방지하지만, 조인이 많아질 경우 성능 저하 가능
  • 반정규화: 성능 최적화를 위해 중복 저장을 허용하지만, 데이터 정합성 관리 필요

트랜잭션 격리 수준도 성능에 큰 영향을 미친다.

  • READ COMMITTED (기본 설정) → 일반적인 서비스에서 무난한 선택
  • REPEATABLE READ (MySQL 기본) → 트랜잭션이 길어지면 데드락 위험
  • SERIALIZABLE → 데이터 정합성은 보장되지만, 성능 저하 가능
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

 

2. 성능 최적화 방법

2.1 인덱스 최적화: 왜 자동 인덱스에 의존하면 안 되는가?

"데이터베이스가 자동으로 인덱스를 만들어주니까, 따로 관리 안 해도 되는 거 아닌가?"

 

아니다. 실무에서는 인덱스가 과하게 걸려서 성능이 오히려 저하되는 경우도 많다.

  • B-Tree 인덱스 → 일반적인 WHERE 검색 조건에 적합
  • GIN 인덱스 → JSONB, Full-Text 검색
  • Hash 인덱스 → 정확한 값 일치 검색
CREATE INDEX idx_user_email ON users(email);

 

 

자주 발생하는 인덱스 실수

  • "모든 컬럼에 인덱스를 걸어야 한다?" → 인덱스가 많으면 쓰기 성능이 오히려 저하됨
  • "LIKE 검색에는 인덱스가 적용된다?" → %keyword% 패턴에서는 인덱스가 무용지물

 

해결 방법중 하나는 "Partial Index + Covering Index" 활용하는 것

CREATE INDEX idx_active_users ON users(is_active) WHERE is_active = true;

 

2.2 쿼리 성능 튜닝: EXPLAIN ANALYZE로 병목 구간 찾기

PostgreSQL과 MySQL 모두 EXPLAIN ANALYZE를 활용하면 실행 계획을 확인할 수 있다.

 
EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = 123;

1. Seq Scan(전체 스캔) vs Index Scan(인덱스 사용) 차이를 확인할 수 있다.
2. Index Scan이 나오지 않는다면? → 인덱스 최적화 필요

 

2.3 대용량 데이터 처리 전략

서비스가 성장하면서 데이터가 몇십만, 몇백만 건을 넘어가면 기존 설계가 한계에 부딪힌다.

 

 

테이블 파티셔닝 (Partitioning) 활용

 

"대용량 데이터를 한 테이블에 몰아넣으면 검색 속도가 느려진다!"

  • PostgreSQL: RANGE, LIST, HASH 파티셔닝 지원
  • MySQL: PARTITION BY RANGE 활용 가능
CREATE TABLE orders_2024 PARTITION OF orders FOR VALUES FROM ('2024-01-01') TO ('2024-12-31');

 

 

캐싱 전략 적용

 

모든 요청을 데이터베이스에서 직접 처리하면 부담이 크다.

  • Redis, Memcached → 자주 조회하는 데이터를 캐싱
  • Materialized View → 정적인 데이터는 미리 생성하여 빠르게 조회
CREATE MATERIALIZED VIEW user_summary AS SELECT user_id, COUNT(*) FROM orders GROUP BY user_id;

 

 

비동기 처리 & 이벤트 큐 활용

  • Kafka, RabbitMQ → 트래픽이 몰릴 때 백그라운드 작업으로 처리
  • Batch Processing → 하루 한 번씩 통계를 계산하여 저장

 

 

2.4 데이터 모델링 & 트랜잭션 설계

정규화 & 반정규화

  • 정규화를 통해 데이터 중복을 방지
  • 하지만 성능이 중요하다면 반정규화를 활용해 특정 데이터를 중복 저장 가능

 

트랜잭션 격리 수준 설정

  • 데이터 정합성을 보장하기 위해 READ COMMITTED, SERIALIZABLE 등의 격리 수준 설정
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

 

3. 모니터링 & 유지보수 전략

실무에서 쿼리 성능을 주기적으로 모니터링하지 않으면 언제 성능이 저하되는지 알 수 없다.

 

 

PostgreSQL 모니터링 도구

  • pg_stat_statements → 가장 느린 쿼리 찾기
  • pgBadger → 실행 계획 자동 분석

 

MySQL 모니터링 도구

  • Performance Schema → 쿼리 성능 분석
  • Slow Query Log → 느린 쿼리 감지

 

자동화 스크립트 적용

  • 주기적으로 실행 계획을 확인하는 스크립트 작성
SELECT * FROM pg_stat_statements ORDER BY total_time DESC LIMIT 10;

 

4. 결론

정리하자면 데이터베이스 성능 개선을 위해서는 다음 순서에 따라 진행하는 것이 좋다!

  • 요구사항 분석 → 현재 시스템 이해 → 최적화 적용 → 성능 검증 → 지속적인 모니터링 필수

 

요약

1. 데이터 액세스 패턴을 분석하여 최적화 방향을 설정
2. 적절한 인덱스 설계와 쿼리 튜닝을 적용
3. 대용량 데이터 처리 기법(파티셔닝, 캐싱, 비동기 처리) 활용
4. 모니터링 시스템을 구축하여 지속적인 성능 개선

 

백엔드 개발자로서 데이터베이스 성능은 단순한 튜닝이 아니라, 시스템 설계 단계부터 고려해야 한다.

 

서비스를 운영하면서 성능 개선이 필요하다면 위의 전략을 하나씩 적용해보자

'DB' 카테고리의 다른 글

PLSQL 프로시저와 트리거 간단 정리  (0) 2022.03.04