본문으로 건너뛰기
15.6 corruption 대응

15.6 corruption 대응

데이터 corruption은 PostgreSQL 운영의 가장 무서운 사고입니다. 디스크 비트 부패, 메모리 오류, 잘못된 운영 명령, 정전 시 fsync 미보호 등 원인 다양. corruption의 감지·격리·복구 절차를 정리합니다.

corruption의 신호

메시지의미
ERROR: invalid page in block N of relation ...페이지 헤더 손상
WARNING: page verification faileddata_checksums 검출
ERROR: missing chunk number N for toast value NTOAST 손상
WARNING: relation "..." page N is uninitialized페이지 미초기화
PANIC: WAL contains references to invalid pagesWAL과 데이터 불일치
could not read block N: read only 0 of 8192 bytes파일 끝 잘림

여러 번 발생하거나 이전엔 잘 돌던 SELECT가 갑자기 ERROR면 corruption 의심.

1단계 — 격리

추가 손상 방지

-- 영향 받는 테이블에 쓰기 금지
ALTER TABLE bad_table SET (autovacuum_enabled = false);
REVOKE INSERT, UPDATE, DELETE ON bad_table FROM PUBLIC;

read-only 데이터로 만들어 추가 손상 확산 막음.

스냅샷·백업

# 현재 PGDATA 즉시 스냅샷 (LVM·EBS·VM)
sudo lvcreate --snapshot --name pgdata-snap --size 50G /dev/vg0/pgdata

# 또는 cold 디스크 dump
sudo systemctl stop postgresql-17
sudo dd if=/dev/sdX of=/backup/bad_dump.img bs=4M

corruption 원인 추적 + 미래의 forensic 자료.

2단계 — 진단

data_checksums 검증

SHOW data_checksums;
-- on이면 페이지 단위 CRC 검출 가능
# 클러스터 정지 후
sudo -u postgres pg_checksums --check -D /var/lib/pgsql/17/data

손상 페이지·블록 식별.

OS 측 진단

# 디스크 health
sudo nvme smart-log /dev/nvme0n1
sudo smartctl -a /dev/sda

# ECC 메모리 오류
sudo edac-util -v

# kernel 메시지
sudo dmesg -T | grep -iE 'corruption|i/o error|read error|sector'

하드웨어 사고가 원인이면 그 호스트 자체 교체 검토합니다.

영향 범위 식별

-- 영향 받는 페이지·row 식별
SELECT ctid, * FROM bad_table WHERE ctid >= '(100,1)' AND ctid < '(101,1)';
-- 어떤 ctid에서 오류 나는지 binary search

3단계 — 복구 옵션

A. 백업에서 PITR (안전, 권장)

11.7의 표준 시나리오 — 새 호스트에 PITR 후 영향 받은 부분만 운영에 patch.

sudo -u postgres pgbackrest --stanza=main restore \
  --type=time --target='<corruption 이전 시점>' \
  --pg1-path=/var/lib/pgsql/staging

corruption 발생 전 시점으로 복원해 깨끗한 데이터를 가져옴.

B. 영향 row만 제외

-- 우회: 깨진 ctid 제외하고 새 테이블에
CREATE TABLE bad_table_new AS
SELECT * FROM bad_table WHERE ctid::text NOT LIKE '(100,%)';

복구 가능한 부분만 살림. 깨진 row는 별도로 수동 복구합니다.

C. zero_damaged_pages (위험)

SET zero_damaged_pages = on;
-- 깨진 페이지를 0으로 채워 통과
SELECT * FROM bad_table;

데이터 손실 — 마지막 수단. 백업이 없는 경우만. 백업 가능 시점에서 일단 데이터를 뽑아내는 용도.

D. pg_resetwal (매우 위험)

WAL 자체가 손상되어 클러스터 부팅 안 됨:

sudo -u postgres pg_resetwal -f /var/lib/pgsql/17/data

WAL을 강제 reset해 부팅 가능하게 만듦 — 일관성 보장 없습니다. 데이터 부분 손실 거의 확정. 전문가의 마지막 옵션.

4단계 — 정상화 후

원인 분석

원인 후보점검
디스크 비트 부패SMART, ECC, vendor support
메모리 오류memtest, 호스트 교체
fsync=off 또는 synchronous_commit=off 사고 직전 정전설정 점검
OS·storage 펌웨어 버그벤더 공지
다른 프로세스가 PGDATA 수정dmesg, audit
PostgreSQL 자체 버그 (드물지만)메이저·마이너 버전, 재현 가능성

예방

항목권장
data_checksums = oninitdb 시 결정 — 운영 표준
ECC 메모리서버급 표준
RAID 10 또는 ZFS디스크 redundancy
fsync = on, synchronous_commit = on절대 변경 금지
정기 백업 검증분기 PITR 시뮬레이션
디스크 health 모니터링SMART·NVMe 정기

TOAST corruption

ERROR:  missing chunk number 5 for toast value 12345

TOAST 청크 손상. 영향 받는 row 식별:

-- chunk_id로 역추적
SELECT * FROM bad_table WHERE big_col_oid IN (
  SELECT oid FROM pg_class WHERE relname = 'pg_toast_<oid>'
);

복구 매우 어려움 — 백업 또는 zero_damaged + 큰 컬럼 수동 복구합니다.

인덱스 corruption

ERROR:  could not find item N in index "..."

인덱스만 손상이면 데이터는 무사. REINDEX로 해결합니다.

REINDEX INDEX CONCURRENTLY bad_index;

REINDEX 실패하면 DROP INDEX → CREATE.

체크리스트 — corruption 대응

  1. 쓰기 중지 (테이블 권한·application 정지)
  2. 즉시 스냅샷 (PGDATA, OS 레벨)
  3. 영향 범위 식별 (data_checksums, ctid 추적)
  4. 백업 시점 확인 (마지막 안전한 백업)
  5. PITR 검토 — 깨끗한 시점까지
  6. 운영 patch (필요 row만 복원)
  7. 원인 분석 (HW·OS·운영·버그)
  8. 예방 조치 (모니터링·정책)
  9. 사후 보고서
zero_damaged_pages·pg_resetwal은 절대 첫 옵션이 아님. 데이터 손실 + 추가 손상 위험 큽니다. 백업이 있다면 PITR이 항상 답입니다. 백업 없으면 그 자체가 운영 사고입니다.

후속 작업

  • DR(재해 복구) 절차서 업데이트
  • 복원 시뮬레이션 일정 강화
  • 모니터링 강화 — pg_stat_database conflicts·errors

정리

  • corruption은 운영의 가장 무서운 사고
  • data_checksums = on이 초기 발견에 결정적
  • 대응: 격리 → 진단 → PITR → 영향 부분 patch
  • zero_damaged_pages·pg_resetwal은 마지막 수단
  • 예방 = HW redundancy + fsync 보호 + 정기 백업 검증

Part XV 트러블슈팅이 끝났습니다. 다음 Part XVI에서는 PostgreSQL의 확장성 — 확장과 응용을 봅니다.