INSIGHTS · Technology
PostgreSQL 대용량 이관 중 데이터 누락·중복 실무 실패 사례와 점검 절차
BLICT · 2026. 9. 12. · 수정 2026. 9. 12.
대용량 데이터 이관 시 실제로 발생한 데이터 누락과 중복 사례를 통해, 사전·사후 무결성 점검 절차와 우선순위를 알 수 있습니다.
실무에서 경험한 대용량 이관 실패: 누락과 중복의 실제 사례
대용량 데이터 이관이란 수십수백 GB에서 테라바이트 단위의 데이터를 PostgreSQL 인스턴스나 테이블 간에 이전하는 작업을 말한다. 단순 복사나 덤프-리스토어로 끝날 것 같지만, 실제 현장에서는 데이터 누락과 중복 문제가 빈번하게 발생한다. 블릭트에서 경험한 한 사례를 예로 들면, 일부 테이블은 총 5억 건 중 3,000여 건이 누락되었고, 다른 테이블에서는 PK 제약조건이 없는 컬럼에 동일 레코드가 23회 중복 삽입된 적도 있다.
이런 문제는 개발 환경에서는 드러나지 않고, QA나 심지어 서비스 오픈 이후에야 발견되는 경우가 많다. 누락은 주로 ETL 파이프라인이 중간에 실패했다가 재시도할 때, 중복은 병렬 처리나 트랜잭션 재시도 로직에서 발생했다. 특히, 원본 데이터와 이관 대상의 스키마가 미세하게 달라질 때, 또는 외래키·고유제약 조건이 임시로 해제된 채로 이관이 이루어지면 문제가 심화된다.
실무에서는 “정상적으로 끝났다고 나오는데, 왜 실제 데이터가 달라?”라는 질문이 반복된다. 이 때 단순히 row count만 비교하면 놓치는 사례가 많다. 예를 들어, 일부 컬럼이 NULL로만 채워진 채 이관되는 바람에 로우 수는 맞지만, 실제로는 정보가 크게 누락된 경우다. 또한, PK가 없는 테이블이나 soft delete를 사용하는 테이블에선 중복 삽입이 흔히 발생했다.
대용량 이관에서 발생하는 데이터 무결성 결함의 원인
데이터 누락은 주로 다음과 같은 상황에서 발생했다. 첫째, 병렬 이관 시 데이터의 일관성 보장이 어려워 트랜잭션 경계에서 일부 데이터가 빠지는 경우가 있었다. 예를 들어, id 범위를 나눠서 병렬로 이관하던 중, 경계값에서 일부 데이터가 중복이거나 누락되어 최종 집계가 맞지 않았다.
둘째, 네트워크 오류나 세션 타임아웃, 임시 테이블 공간 부족 등 시스템 이슈로 인해 중간에 이관 프로세스가 비정상 종료되었지만, 프로세스 자체는 “성공”으로 표기된 사례도 있었다. 예를 들면, pg_dump/pg_restore를 사용할 때, 일부 large object나 TOAST 데이터가 누락되는 경우다.
데이터 중복은 주로 재시도 로직이나 병렬 처리에서 발생한다. 예를 들어, 이관 스크립트가 실패 후 부분적으로 재실행될 때, 이미 이관된 데이터를 중복 삽입하는 경우다. PK, unique 제약조건이 없는 테이블에서 이런 오류는 더욱 치명적이다. 또한, CDC(change data capture) 기반 실시간 이관 도중, 동일한 이벤트가 중복 처리되는 경우도 있었다.
실무에서는 "이관 스크립트는 정상 동작"이라는 개발자의 말만 믿고 넘어가다 큰 사고로 이어진다. 실제로 delta 이관(변경분만 추가 복제) 중에, CDC 로그가 중복 적용되어 데이터가 2배로 불어나거나, 오히려 삭제된 데이터가 복구되지 않은 사례도 있었다.
무결성 점검 절차: 사전(Pre-Migration)과 사후(Post-Migration)의 우선순위
실제 이관 프로젝트에서는 사전·사후 무결성 점검의 우선순위와 절차를 명확히 해야 한다. 사전 점검에서는 원본과 대상 스키마가 1:1 매핑되는지, 모든 제약조건(PK, FK, Unique, Not Null 등)이 일치하는지 반드시 확인해야 한다. 특히, 임시로 제약조건을 해제하고 이관하면, 이관 후 반드시 제약조건을 재적용하고, 위반되는 데이터가 없는지 점검해야 한다.
사전 점검 절차는 다음과 같다.
- 스키마 diff 분석:
pg_dump -s로 원본/대상 스키마 덤프 후, diff 도구로 비교 - 제약조건 목록화: 각 테이블별 PK/FK/Unique/Not Null constraint 목록화 및 불일치 항목 체크
- 데이터 범위 샘플링: row count, min/max/id 값 등 간단한 집계로 원본/대상 데이터 대략적 일치 확인
사후 점검은 더욱 정밀해야 한다. 단순 row count만으로는 실제 데이터 무결성을 보장할 수 없다. 대표적인 실수는, 일부 테이블은 row 수는 맞지만, 특정 컬럼이 모두 NULL이거나 default 값으로만 채워진 경우다. 블릭트 내부에서는 아래와 같은 사후 점검 루틴을 표준화했다.
- Row Count 비교: 전체, 파티션별, 조건별 row count 일치 여부
- Checksum/해시값 비교: 주요 컬럼(또는 전체 row)에 대해 MD5/SHA 해시값 산출 후 비교
- 샘플링 검증: 랜덤 추출 1,000~10,000건에 대해 원본-대상 쌍으로 실제 데이터 값 일치 여부 확인
- 제약조건 재적용 및 위반 검사: Unique, Not Null 등 제약조건을 재적용 후 위반건수 확인
예시로, 아래와 같이 row hash를 검증하는 SQL을 사용할 수 있다.
SELECT md5(string_agg(concat_ws('|', col1, col2, col3), ',' ORDER BY id)) AS data_hash FROM my_table;
이는 중소규모 테이블이나 파티션별 샘플 검증에 적합하다.
현장에서 막히는 지점과, 그 지점을 가르는 점검 순서
블릭트 실무에서는 대량 이관 후 누락/중복을 어떻게 빠르게 검출할 것인가가 가장 큰 난관이었다. 특히, 수억~수십억 건의 테이블 전체를 row-by-row로 검증하는 것은 현실적으로 불가능하다. 이 때, 아래와 같은 점검 순서로 접근하면 오류를 빠르게 좁혀갈 수 있다.
- 우선 row count/범위 체크: 전체/파티션/시간대별 row 수가 일치하는지 비교한다. 예를 들어, 하루 단위 파티션이면 하루별 count를 비교해 차이가 나는 구간을 찾는다.
- 범위별 해시값 산출: 수십만~수백만 건 단위로 해시값을 비교해, 해시값이 다른 구간을 집중적으로 파고든다.
- PK/FK 위반 검사: 제약조건 적용 후 위반 건수를 SQL로 산출한다. 이 단계에서 중복/누락이 구조적으로 드러난다.
- 랜덤 샘플링 검증: 무작위로 추출한 row의 주요 컬럼을 원본/대상에서 직접 비교한다. 실무에서는 외부 스크립트나 SQL을 사용해 샘플을 추출한다.
- 로그 분석: 이관 로그(ETL, CDC 등)에서 실패/재시도/경고 메시지 구간을 집중적으로 본다. 실제 장애가 발생한 시점과 겹치는 데이터 범위부터 재검토한다.
이 과정에서, “전수검증”이 아니라 “차이 나는 지점 위주로 정밀 검증”으로 점검 범위를 극소화해야 한다. 이를 통해 수십억 건 중 실제 오류가 있는 몇 천 건만 집중적으로 파악할 수 있다. 특히, 장애 발생 시점의 로그와 데이터 범위를 매칭하는 것이 실무에서는 가장 효율적이었다.
실무 적용 체크리스트
- 이관 대상 테이블별 스키마/제약조건 diff 결과를 사전에 문서화하고, 이관 후 재적용 여부를 반드시 검증한다.
- 전체 row count, 파티션/조건별 row count, 주요 컬럼 값의 min/max 등 기본 집계값을 원본/대상 모두 산출한다.
- 주요 컬럼 또는 row 전체에 대한 해시값을 (파티션별 또는 구간별로) 산출해, 빠르게 차이 구간을 식별한다.
- 랜덤 또는 조건부 샘플링으로 원본/대상 데이터가 실제 일치하는지 spot check를 수행한다.
- 이관/복구/재시도 로그를 수집·분석해, 오류/재시도 구간의 데이터에 대해 집중 점검한다.