반응형

PostgreSQL 오류: cannot merge attstreams with duplicate TIDs — 원인·분석·조치
특정 테이블에서 쿼리/인덱스 작업/테이블 리라이트 시 cannot merge attstreams with duplicate TIDs 오류가 발생할 수 있습니다. 이는 TID(튜플 식별자) 충돌 또는 테이블 접근 방법(Access Method, AM)과 저장 포맷 간 불일치·손상으로 인해 열 스트림(TOAST/압축·컬럼 스트림 등)을 병합할 때 중복 TID가 검출되는 상황에서 보고됩니다.
핵심 요약
- 주로 비-heap AM(예: 실험적 columnar/zheap/서드파티 AM)로 생성된 테이블에서 발생 → heap AM로 전환 시 해결되는 사례 다수
- 인덱스/TOAST 손상·이상 포맷, 반쪽짜리 리라이트(중단)로도 재현 가능 → REINDEX / CTAS 리빌드 필요
- 주로 비-heap AM(예: 실험적 columnar/zheap/서드파티 AM)로 생성된 테이블에서 발생 → heap AM로 전환 시 해결되는 사례 다수
- 인덱스/TOAST 손상·이상 포맷, 반쪽짜리 리라이트(중단)로도 재현 가능 → REINDEX / CTAS 리빌드 필요
1️⃣ 원인
- 접근 방법(AM) 불일치: 확장/실험적 AM로 만들어진 테이블을 일반 연산(ANALYZE/VACUUM/인덱스 스캔/병합)이 처리하다가 내부 스트림 병합 로직과 충돌.
- 중복 TID 검출: 잘못된 리라이트·복제·도중 중단으로 동일 TID를 가리키는 스트림이 생김(TOAST/압축 조각 포함).
- 손상/부적합 인덱스: 인덱스가 손상되어 잘못된 TID를 반환 → 병합 시 중복으로 간주.
2️⃣ 증상/로그 예시
# 예시 (서버 로그) ERROR: cannot merge attstreams with duplicate TIDs CONTEXT: while reading column streams for relation "public.sales_hist" STATEMENT: SELECT ... FROM public.sales_hist WHERE ...;
3️⃣ 진단 절차
- 테이블의 Access Method 확인
SELECT c.relname, a.amname FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace JOIN pg_am a ON a.oid = c.relam WHERE n.nspname = 'public' AND c.relname = '테이블명';
결과가
heap이 아니라면(예:zheap,columnar등) AM 전환 고려. - TOAST/인덱스 상태 점검
-- 인덱스 재구성 필요 여부 SELECT relname, relkind FROM pg_class WHERE relkind IN ('i') AND relnamespace = 'public'::regnamespace AND relname LIKE '테이블명%';해당 인덱스들에 대해
REINDEX수행 계획 수립. - 최근 리라이트/확장 사용 여부 확인
논리복제/확장 모듈/서드파티 AM 사용 이력, 중단된
VACUUM FULL/CLUSTER로그 확인.
4️⃣ 조치 (권장 순서)
4-1. PostgreSQL 15 이상: AM 전환 (다운타임 필요)
주의:
ALTER TABLE ... SET ACCESS METHOD는 테이블 리라이트를 수행합니다. 대용량 테이블은 충분한 디스크 공간과 점검 창 확보가 필요합니다.
BEGIN; ALTER TABLE public."테이블명" SET ACCESS METHOD heap; COMMIT; -- 전환 후 필수 점검 VACUUM (FULL, ANALYZE) public."테이블명"; REINDEX TABLE public."테이블명";
4-2. PostgreSQL 12~14: CTAS 리빌드(대체 절차)
- 새 테이블(HEAP) 생성 후 데이터 이관
CREATE TABLE public."테이블명_heap" (LIKE public."테이블명" INCLUDING ALL); -- 필요 시 제약조건/인덱스는 이후 재생성 INSERT INTO public."테이블명_heap" SELECT * FROM public."테이블명";
- 인덱스·제약조건 재생성
-- 예시 CREATE INDEX ON public."테이블명_heap"(pk_col); ALTER TABLE public."테이블명_heap" ADD PRIMARY KEY (pk_col);
- 스왑(다운타임 구간)
BEGIN; ALTER TABLE public."테이블명" RENAME TO "테이블명_bak"; ALTER TABLE public."테이블명_heap" RENAME TO "테이블명"; COMMIT;
- 검증 후 백업 테이블 삭제
DROP TABLE public."테이블명_bak";
4-3. 인덱스/TOAST 손상 가능성 대응
-- 인덱스 재구성 REINDEX TABLE CONCURRENTLY public."테이블명"; -- 가능 시 -- 통계 갱신 ANALYZE public."테이블명";
4-4. 트랜잭션/복제 환경 주의
- 논리복제: 리라이트 시 리플리카 지연/중단 유의. 점검 창에 맞춰 스왑.
- 물리복제: 대용량 리라이트는 WAL 폭증 → 아카이브 공간 확보.
5️⃣ 결과 확인
-- 오류 재현 쿼리 재실행 EXPLAIN (ANALYZE, BUFFERS) SELECT ... FROM public."테이블명" ...; -- 테이블·TOAST·인덱스 무결성 점검(샘플) VACUUM VERBOSE public."테이블명";
에러가 재발하지 않고, 실행 계획이 정상화(Seq/Index Scan)되며, VACUUM에서 추가 경고가 없다면 복구 성공으로 판단합니다.
6️⃣ 예방 가이드
- 표준 AM(HEAP) 우선: 실험적/서드파티 AM 도입 시는 성능·무결성 검증 및 롤백 플랜 포함.
- 리라이트 작업의 중단 방지:
VACUUM FULL,CLUSTER, 대규모ALTER TABLE시 충분한 공간·시간 확보. - 주기적 REINDEX: 대형/핫 테이블은
REINDEX (CONCURRENTLY)주기 운영으로 인덱스 일탈 방지. - 백업·리커버리 훈련:
pg_dump+ PITR 조합으로 신속 복구 가능한 절차 점검.
현장 Tip
운영 중 즉시성 요구가 크면: CTAS 스왑 방식이 가장 예측 가능하고 안전합니다. PostgreSQL 15+ 환경은 ALTER TABLE ... SET ACCESS METHOD heap가 단순하지만, 동일하게 리라이트이므로 WAL·디스크 여유를 반드시 확인하세요.
운영 중 즉시성 요구가 크면: CTAS 스왑 방식이 가장 예측 가능하고 안전합니다. PostgreSQL 15+ 환경은 ALTER TABLE ... SET ACCESS METHOD heap가 단순하지만, 동일하게 리라이트이므로 WAL·디스크 여유를 반드시 확인하세요.
반응형
LIST
'경험 공유 > DBMS' 카테고리의 다른 글
| DBMS의 종류와 특징 — 관계형부터 객체관계형까지 (1) | 2025.11.10 |
|---|---|
| [Oracle] 아카이브 풀(Archive Full) - ORA-16014, ORA-00257, ORA-19809 (5) | 2025.11.09 |
| [Oracle] 오라클 LogMiner — 실습 케이스 (시간/SCN 기반) & 자동 복구 SQL 생성 스크립트 (0) | 2025.11.02 |
| [Oracle] ORA-06512와 함께 발생하는 대표 에러 종합 가이드 (2) | 2025.11.01 |
| [Oracle] Flashback 복구 가이드 및 ORA-38706 오류 조치 (4) | 2025.11.01 |
