PostgreSQL XX002 오류 원인과 해결 방법 완벽 가이드

XX002
2026년 07월 30일 | DBMS Error 가이드

이 글에서 다루는 내용

XX002 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.

XX002 index corrupted 는?

PostgreSQL 에러 코드 XX002 (index corrupted)는 데이터베이스 인덱스의 내부 구조가 손상되어 더 이상 정상적으로 읽거나 사용할 수 없는 상태를 의미합니다. 이 에러는 쿼리 실행 중 인덱스 페이지를 탐색할 때, 또는 VACUUM, ANALYZE 같은 유지보수 작업 도중에 발생할 수 있습니다. 인덱스 손상은 데이터 자체의 손실로 이어지지 않을 수 있지만, 해당 인덱스를 사용하는 모든 쿼리가 실패하거나 잘못된 결과를 반환할 수 있어 즉각적인 조치가 필요합니다.


주요 발생 원인

1. 하드웨어 장애 및 스토리지 오류

디스크 불량 섹터, SSD 펌웨어 버그, RAID 컨트롤러 오작동 등 하드웨어 수준의 오류는 인덱스 페이지를 물리적으로 손상시키는 가장 흔한 원인입니다. PostgreSQL은 운영체제와 스토리지 레이어를 신뢰하기 때문에, 하드웨어가 잘못된 데이터를 기록하면 인덱스 파일 자체가 오염됩니다. 특히 fsync가 비활성화된 환경이나 UPS 없이 운영되는 서버에서 갑작스러운 전원 차단이 발생할 경우 이 문제가 더욱 빈번하게 발생합니다.

2. 비정상적인 PostgreSQL 프로세스 종료 (Crash)

SIGKILL 시그널, OOM Killer, 또는 서버 강제 재부팅으로 인해 PostgreSQL 프로세스가 Write-Ahead Log(WAL)에 변경 사항을 완전히 기록하기 전에 종료되면 인덱스 페이지가 불완전한 상태로 남을 수 있습니다. PostgreSQL은 재시작 시 WAL을 재생(replay)하여 데이터 파일을 복구하지만, 특정 상황(버그, 버전 불일치, 파일시스템 장애)에서는 인덱스가 일관성 없는 상태로 남을 수 있습니다. 이 경우 pg_log 또는 journalctl에서 비정상 종료 흔적을 반드시 확인해야 합니다.

3. PostgreSQL 버그 또는 익스텐션 충돌

오래된 PostgreSQL 버전의 알려진 버그나, 서드파티 익스텐션(예: PostGIS, pg_trgm 등)의 버전 불일치가 인덱스 손상을 유발할 수 있습니다. 특히 B-Tree 인덱스의 페이지 분할(page split) 로직에 버그가 있는 버전을 장기간 운영한 경우, 트랜잭션이 많은 테이블에서 점진적으로 인덱스가 손상될 수 있습니다. PostgreSQL 릴리즈 노트와 CVE 목록을 정기적으로 확인하고 패치 버전으로 업그레이드하는 것이 중요합니다.


해결 방법

1단계: 손상된 인덱스 확인

먼저 어떤 인덱스가 손상되었는지 파악합니다. amcheck 익스텐션을 사용하면 인덱스 내부 구조를 검증할 수 있습니다.

-- amcheck 익스텐션 설치 (PostgreSQL 10+)
CREATE EXTENSION IF NOT EXISTS amcheck;

-- 특정 인덱스 유효성 검사 (일반 검사)
SELECT bt_index_check('your_index_name'::regclass);

-- 힙(테이블)과의 일관성까지 검사 (더 철저한 검사)
SELECT bt_index_parent_check('your_index_name'::regclass, true);
-- pg_catalog에서 인덱스 목록 조회
SELECT
    schemaname,
    tablename,
    indexname,
    indexdef
FROM pg_indexes
WHERE schemaname = 'public'
ORDER BY tablename, indexname;

2단계: 손상된 인덱스 재생성

인덱스 손상이 확인되면 해당 인덱스를 삭제하고 재생성하는 것이 가장 확실한 방법입니다.

-- 방법 1: 인덱스 삭제 후 재생성 (서비스 중단 발생 가능)
DROP INDEX IF EXISTS your_index_name;
CREATE INDEX your_index_name ON your_table (your_column);

-- 방법 2: REINDEX 명령 사용 (권장)
-- 단일 인덱스 재구성
REINDEX INDEX your_index_name;

-- 테이블의 모든 인덱스 재구성
REINDEX TABLE your_table_name;

-- 데이터베이스 전체 인덱스 재구성 (대형 DB에서는 주의)
REINDEX DATABASE your_database_name;

-- 방법 3: CONCURRENTLY 옵션으로 운영 중단 없이 재구성 (PostgreSQL 12+)
REINDEX INDEX CONCURRENTLY your_index_name;
REINDEX TABLE CONCURRENTLY your_table_name;

3단계: 테이블 전체 무결성 검사

인덱스 손상이 발생한 경우 테이블 데이터 자체도 확인하는 것이 좋습니다.

-- 테이블에 대한 전체 시퀀셜 스캔으로 데이터 무결성 기본 확인
SELECT COUNT(*) FROM your_table_name;

-- 특정 컬럼 값의 유효성 확인
SELECT * FROM your_table_name WHERE your_column IS NULL OR your_column < 0;

-- pg_class에서 테이블/인덱스 상태 확인
SELECT
    relname,
    relkind,
    relpages,
    reltuples,
    relallvisible
FROM pg_class
WHERE relname IN ('your_table_name', 'your_index_name');

4단계: 백업에서 복구 (심각한 경우)

만약 REINDEX로도 해결되지 않거나 데이터 손상이 의심된다면 백업에서 복구를 고려합니다.

-- 현재 손상된 데이터 덤프 시도 (가능한 데이터만 추출)
-- 인덱스 스캔을 강제로 비활성화하여 Sequential Scan으로 데이터 추출
SET enable_indexscan = OFF;
SET enable_bitmapscan = OFF;

-- 이 상태에서 데이터 조회 및 pg_dump 수행
SELECT * FROM your_table_name;

-- 설정 복원
RESET enable_indexscan;
RESET enable_bitmapscan;

예방 방법

1. pg_amcheck를 이용한 정기적인 인덱스 무결성 점검 자동화

운영 환경에서는 인덱스 손상을 조기에 발견하기 위해 정기적인 점검 스크립트를 cron 또는 pgAgent로 예약 실행해야 합니다. 아래 스크립트를 주 1회 이상 실행하면 손상을 조기에 발견할 수 있습니다.

-- 모든 B-Tree 인덱스에 대한 일괄 무결성 점검
CREATE EXTENSION IF NOT EXISTS amcheck;

DO $$
DECLARE
    idx RECORD;
BEGIN
    FOR idx IN
        SELECT c.oid, c.relname
        FROM pg_class c
        JOIN pg_am a ON c.relam = a.oid
        WHERE a.amname = 'btree'
          AND c.relkind = 'i'
          AND c.relnamespace NOT IN (
              SELECT oid FROM pg_namespace WHERE nspname = 'pg_toast'
          )
    LOOP
        BEGIN
            PERFORM bt_index_check(idx.oid);
            RAISE NOTICE 'Index % is OK', idx.relname;
        EXCEPTION WHEN OTHERS THEN
            RAISE WARNING 'Index % may be corrupted: %', idx.relname, SQLERRM;
        END;
    END LOOP;
END;
$$;

2. checksums 활성화 및 스토리지 모니터링 강화

PostgreSQL 데이터 클러스터 초기화 시 --data-checksums 옵션을 반드시 활성화하여 페이지 수준의 체크섬 검증을 활성화해야 합니다. 이를 통해 스토리지 레이어의 조용한 데이터 손상(silent data corruption)을 조기에 감지할 수 있습니다. 기존 클러스터에는 PostgreSQL 12+에서 제공하는 pg_checksums 유틸리티로 오프라인 상태에서 활성화할 수 있습니다.

-- 현재 데이터 체크섬 활성화 여부 확인
SHOW data_checksums;

-- 또는 pg_settings에서 확인
SELECT name, setting FROM pg_settings WHERE name = 'data_checksums';
# 신규 클러스터 초기화 시 체크섬 활성화
initdb --data-checksums -D /var/lib/postgresql/data

# 기존 클러스터에 체크섬 활성화 (PostgreSQL 12+, DB 중단 필요)
pg_checksums --enable -D /var/lib/postgresql/data

관련 에러

  • XX001 (heap corrupted): 인덱스가 아닌 테이블 힙 파일 자체가 손상된 경우로, XX002와 함께 발생하는 경우가 많습니다. 스토리지 장애가 원인인 경우 두 에러가 동시에 나타날 수 있습니다.
  • 53300 (too_many_connections) / 58030 (io_error): I/O 오류(에러코드 58030)는 인덱스 손상의 전조 증상일 수 있으므로, 해당 에러 발생 시 즉시 amcheck로 인덱스 무결성을 확인하는 것을 권장합니다.
  • P0001 (raise_exception): 커스텀 함수나 트리거에서 인덱스 손상 감지 시 명시적으로 발생시키는 예외와 혼동될 수 있으므로 에러 컨텍스트를 정확히 확인해야 합니다.

DBMS 에러 코드 시리즈

주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.

본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.

댓글 남기기