백업과 운영

백업과 운영

1. 백업의 중요성

데이터베이스 백업은 데이터 손실을 방지하는 가장 중요한 작업입니다.

┌──────────────────────────────────────────────────────────┐
│                    백업 전략                              │
├──────────────────────────────────────────────────────────┤
│  • 정기 백업: 매일/매주 전체 백업                         │
│  • 증분 백업: 변경분만 백업 (WAL 아카이빙)               │
│  • 복제: 실시간 복제 서버 구성                           │
└──────────────────────────────────────────────────────────┘

2. pg_dump - 논리적 백업

기본 백업

# 단일 데이터베이스 백업
pg_dump dbname > backup.sql

# 사용자/호스트 지정
pg_dump -U username -h localhost dbname > backup.sql

# 압축 백업
pg_dump dbname | gzip > backup.sql.gz

포맷 옵션

# 평문 SQL (-Fp, 기본값)
pg_dump -Fp dbname > backup.sql

# 커스텀 포맷 (-Fc, 압축됨, 선택적 복원 가능)
pg_dump -Fc dbname > backup.dump

# 디렉토리 포맷 (-Fd, 병렬 백업/복원 지원)
pg_dump -Fd dbname -f backup_dir

# tar 포맷 (-Ft)
pg_dump -Ft dbname > backup.tar

선택적 백업

# 특정 테이블만
pg_dump -t users -t orders dbname > tables.sql

# 특정 테이블 제외
pg_dump -T logs -T temp_* dbname > backup.sql

# 스키마만 (데이터 제외)
pg_dump -s dbname > schema.sql

# 데이터만 (스키마 제외)
pg_dump -a dbname > data.sql

# 특정 스키마만
pg_dump -n public dbname > public_schema.sql

Docker에서 백업

# Docker 컨테이너에서 pg_dump 실행
docker exec -t postgres-container pg_dump -U postgres dbname > backup.sql

# 압축 백업
docker exec -t postgres-container pg_dump -U postgres dbname | gzip > backup.sql.gz

3. pg_dumpall - 전체 클러스터 백업

모든 데이터베이스와 전역 객체(사용자, 권한 등)를 백업합니다.

# 전체 클러스터 백업
pg_dumpall -U postgres > full_backup.sql

# 전역 객체만 (사용자, Role 등)
pg_dumpall -U postgres --globals-only > globals.sql

# 역할만
pg_dumpall -U postgres --roles-only > roles.sql

4. pg_restore - 복원

SQL 파일 복원

# 평문 SQL 복원
psql dbname < backup.sql

# 새 데이터베이스 생성 후 복원
createdb newdb
psql newdb < backup.sql

커스텀/디렉토리 포맷 복원

# 커스텀 포맷 복원
pg_restore -d dbname backup.dump

# 새 데이터베이스로 복원
createdb newdb
pg_restore -d newdb backup.dump

# 특정 테이블만 복원
pg_restore -d dbname -t users backup.dump

# 병렬 복원 (4 작업자)
pg_restore -d dbname -j 4 backup_dir

복원 옵션

# 기존 객체 삭제 후 복원
pg_restore -d dbname --clean backup.dump

# 오류 무시하고 계속
pg_restore -d dbname --if-exists backup.dump

# 데이터만 복원
pg_restore -d dbname --data-only backup.dump

# 스키마만 복원
pg_restore -d dbname --schema-only backup.dump

5. 물리적 백업 (pg_basebackup)

전체 데이터 디렉토리를 백업합니다.

# 기본 백업
pg_basebackup -D /backup/path -U postgres -Fp -Xs -P

# 압축 백업
pg_basebackup -D /backup/path -U postgres -Ft -z -P

# 옵션 설명:
# -D: 백업 디렉토리
# -Fp: 평문 포맷
# -Ft: tar 포맷
# -Xs: WAL 스트리밍
# -z: gzip 압축
# -P: 진행률 표시

WAL 아카이빙 설정

postgresql.conf:

wal_level = replica
archive_mode = on
archive_command = 'cp %p /archive/%f'

6. 자동 백업 스크립트

일일 백업 스크립트

#!/bin/bash
# daily_backup.sh

# 설정
DB_NAME="mydb"
DB_USER="postgres"
BACKUP_DIR="/backup/postgres"
DATE=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=7

# 백업 디렉토리 생성
mkdir -p $BACKUP_DIR

# 백업 실행
pg_dump -U $DB_USER -Fc $DB_NAME > $BACKUP_DIR/${DB_NAME}_${DATE}.dump

# 압축
gzip $BACKUP_DIR/${DB_NAME}_${DATE}.dump

# 오래된 백업 삭제
find $BACKUP_DIR -name "*.dump.gz" -mtime +$RETENTION_DAYS -delete

echo "Backup completed: ${DB_NAME}_${DATE}.dump.gz"

Cron 설정

# crontab -e
# 매일 새벽 2시 백업
0 2 * * * /scripts/daily_backup.sh >> /var/log/backup.log 2>&1

7. 모니터링

데이터베이스 크기

-- 데이터베이스별 크기
SELECT
    datname,
    pg_size_pretty(pg_database_size(datname)) AS size
FROM pg_database
ORDER BY pg_database_size(datname) DESC;

-- 테이블별 크기
SELECT
    schemaname,
    tablename,
    pg_size_pretty(pg_total_relation_size(schemaname || '.' || tablename)) AS total_size
FROM pg_tables
WHERE schemaname = 'public'
ORDER BY pg_total_relation_size(schemaname || '.' || tablename) DESC
LIMIT 10;

연결 상태

-- 현재 연결 수
SELECT COUNT(*) FROM pg_stat_activity;

-- 상태별 연결
SELECT state, COUNT(*)
FROM pg_stat_activity
GROUP BY state;

-- 활성 쿼리
SELECT
    pid,
    now() - query_start AS duration,
    query,
    state
FROM pg_stat_activity
WHERE state != 'idle'
  AND query NOT LIKE '%pg_stat_activity%'
ORDER BY duration DESC;

느린 쿼리

-- 오래 실행 중인 쿼리 (5초 이상)
SELECT
    pid,
    now() - query_start AS duration,
    query
FROM pg_stat_activity
WHERE state = 'active'
  AND now() - query_start > interval '5 seconds';

잠금 상태

-- 잠금 대기 중인 쿼리
SELECT
    blocked.pid AS blocked_pid,
    blocked.query AS blocked_query,
    blocking.pid AS blocking_pid,
    blocking.query AS blocking_query
FROM pg_stat_activity blocked
JOIN pg_stat_activity blocking
    ON blocking.pid = ANY(pg_blocking_pids(blocked.pid));

8. 성능 통계

테이블 통계

-- 테이블 접근 통계
SELECT
    schemaname,
    relname,
    seq_scan,
    seq_tup_read,
    idx_scan,
    idx_tup_fetch,
    n_tup_ins,
    n_tup_upd,
    n_tup_del
FROM pg_stat_user_tables
ORDER BY seq_scan DESC
LIMIT 10;

인덱스 사용률

-- 사용되지 않는 인덱스
SELECT
    schemaname,
    tablename,
    indexname,
    idx_scan,
    pg_size_pretty(pg_relation_size(indexrelid)) AS index_size
FROM pg_stat_user_indexes
WHERE idx_scan = 0
ORDER BY pg_relation_size(indexrelid) DESC;

캐시 히트율

-- 캐시 히트율 (99% 이상이 좋음)
SELECT
    sum(blks_hit) * 100.0 / sum(blks_hit + blks_read) AS cache_hit_ratio
FROM pg_stat_database;

9. 유지보수

VACUUM

불필요한 공간을 정리합니다.

-- 일반 VACUUM
VACUUM;
VACUUM users;

-- VACUUM FULL (테이블 재구성, 잠금 발생)
VACUUM FULL users;

-- VACUUM ANALYZE (통계 갱신 포함)
VACUUM ANALYZE users;

ANALYZE

쿼리 최적화를 위한 통계를 수집합니다.

ANALYZE;
ANALYZE users;

REINDEX

인덱스를 재구성합니다.

REINDEX TABLE users;
REINDEX DATABASE mydb;

자동 VACUUM 설정

postgresql.conf:

autovacuum = on
autovacuum_naptime = 1min
autovacuum_vacuum_threshold = 50
autovacuum_analyze_threshold = 50

10. 로그 설정

postgresql.conf:

# 로그 대상
logging_collector = on
log_directory = 'pg_log'
log_filename = 'postgresql-%Y-%m-%d.log'

# 로그 레벨
log_min_messages = warning
log_min_error_statement = error

# 쿼리 로깅
log_statement = 'ddl'           # none, ddl, mod, all
log_duration = off
log_min_duration_statement = 1000  # 1초 이상 걸리는 쿼리만

# 연결 로깅
log_connections = on
log_disconnections = on

11. 보안 설정

pg_hba.conf

# TYPE  DATABASE    USER        ADDRESS         METHOD

# 로컬 연결
local   all         all                         peer

# IPv4 로컬 연결
host    all         all         127.0.0.1/32    scram-sha-256

# 특정 네트워크 허용
host    mydb        appuser     192.168.1.0/24  scram-sha-256

# 특정 IP 거부
host    all         all         192.168.1.100   reject

SSL 설정

# postgresql.conf
ssl = on
ssl_cert_file = 'server.crt'
ssl_key_file = 'server.key'

12. 실습 예제

실습 1: 백업 및 복원

# 1. 백업
pg_dump -U postgres -Fc mydb > mydb_backup.dump

# 2. 새 데이터베이스 생성
createdb -U postgres mydb_restored

# 3. 복원
pg_restore -U postgres -d mydb_restored mydb_backup.dump

# 4. 확인
psql -U postgres -d mydb_restored -c "SELECT COUNT(*) FROM users;"

실습 2: 모니터링 쿼리 저장

-- 모니터링 뷰 생성
CREATE VIEW v_db_stats AS
SELECT
    datname,
    pg_size_pretty(pg_database_size(datname)) AS size,
    numbackends AS connections
FROM pg_database
WHERE datistemplate = false;

CREATE VIEW v_slow_queries AS
SELECT
    pid,
    now() - query_start AS duration,
    state,
    query
FROM pg_stat_activity
WHERE state = 'active'
  AND now() - query_start > interval '5 seconds';

-- 사용
SELECT * FROM v_db_stats;
SELECT * FROM v_slow_queries;

실습 3: 유지보수 스크립트

-- 정기 유지보수 프로시저
CREATE PROCEDURE run_maintenance()
AS $$
BEGIN
    -- 통계 갱신
    ANALYZE;

    -- 불필요한 공간 정리
    VACUUM;

    RAISE NOTICE '유지보수 완료: %', NOW();
END;
$$ LANGUAGE plpgsql;

-- 실행
CALL run_maintenance();

13. 체크리스트

일일 체크

  • [ ] 백업 성공 확인
  • [ ] 디스크 사용량 확인
  • [ ] 연결 수 확인
  • [ ] 오류 로그 확인

주간 체크

  • [ ] 인덱스 사용률 확인
  • [ ] 느린 쿼리 분석
  • [ ] 테이블 크기 추이

월간 체크

  • [ ] 백업 복원 테스트
  • [ ] 불필요한 데이터 정리
  • [ ] 성능 추이 분석

마무리

이것으로 PostgreSQL 학습 자료를 마칩니다.

학습 순서 복습: 1. 기초 → DB 관리 → 테이블 → CRUD → 조건/정렬 2. JOIN → 집계 → 서브쿼리 → 뷰/인덱스 3. 함수 → 트랜잭션 → 트리거 → 백업/운영

더 깊이 있는 학습을 위해: - PostgreSQL 공식 문서 - PostgreSQL Tutorial

to navigate between lessons