15.6 corruption 대응
데이터 corruption은 PostgreSQL 운영의 가장 무서운 사고입니다. 디스크 비트 부패, 메모리 오류, 잘못된 운영 명령, 정전 시 fsync 미보호 등 원인 다양. corruption의 감지·격리·복구 절차를 정리합니다.
corruption의 신호
| 메시지 | 의미 |
|---|---|
ERROR: invalid page in block N of relation ... | 페이지 헤더 손상 |
WARNING: page verification failed | data_checksums 검출 |
ERROR: missing chunk number N for toast value N | TOAST 손상 |
WARNING: relation "..." page N is uninitialized | 페이지 미초기화 |
PANIC: WAL contains references to invalid pages | WAL과 데이터 불일치 |
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=4Mcorruption 원인 추적 + 미래의 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 search3단계 — 복구 옵션
A. 백업에서 PITR (안전, 권장)
11.7의 표준 시나리오 — 새 호스트에 PITR 후 영향 받은 부분만 운영에 patch.
sudo -u postgres pgbackrest --stanza=main restore \
--type=time --target='<corruption 이전 시점>' \
--pg1-path=/var/lib/pgsql/stagingcorruption 발생 전 시점으로 복원해 깨끗한 데이터를 가져옴.
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/dataWAL을 강제 reset해 부팅 가능하게 만듦 — 일관성 보장 없습니다. 데이터 부분 손실 거의 확정. 전문가의 마지막 옵션.
4단계 — 정상화 후
원인 분석
| 원인 후보 | 점검 |
|---|---|
| 디스크 비트 부패 | SMART, ECC, vendor support |
| 메모리 오류 | memtest, 호스트 교체 |
fsync=off 또는 synchronous_commit=off 사고 직전 정전 | 설정 점검 |
| OS·storage 펌웨어 버그 | 벤더 공지 |
| 다른 프로세스가 PGDATA 수정 | dmesg, audit |
| PostgreSQL 자체 버그 (드물지만) | 메이저·마이너 버전, 재현 가능성 |
예방
| 항목 | 권장 |
|---|---|
data_checksums = on | initdb 시 결정 — 운영 표준 |
| ECC 메모리 | 서버급 표준 |
| RAID 10 또는 ZFS | 디스크 redundancy |
fsync = on, synchronous_commit = on | 절대 변경 금지 |
| 정기 백업 검증 | 분기 PITR 시뮬레이션 |
| 디스크 health 모니터링 | SMART·NVMe 정기 |
TOAST corruption
ERROR: missing chunk number 5 for toast value 12345TOAST 청크 손상. 영향 받는 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 대응
- 쓰기 중지 (테이블 권한·application 정지)
- 즉시 스냅샷 (PGDATA, OS 레벨)
- 영향 범위 식별 (data_checksums, ctid 추적)
- 백업 시점 확인 (마지막 안전한 백업)
- PITR 검토 — 깨끗한 시점까지
- 운영 patch (필요 row만 복원)
- 원인 분석 (HW·OS·운영·버그)
- 예방 조치 (모니터링·정책)
- 사후 보고서
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의 확장성 — 확장과 응용을 봅니다.