PostgreSQL에서 Tuple(튜플)은 테이블의 각 행(Row)을 의미하며, MVCC(Multi-Version Concurrency Control)와 VACUUM과 밀접한 관계를 가진다.

 

본 내용에서는 Tuple의 구조, 동작 방식, Dead Tuple 문제, VACUUM과의 관계, 성능 최적화 전략을 다뤄보고자 한다.

 

1️⃣ Tuple이란?

  • PostgreSQL에서 Tuple은 테이블의 한 행(Row)을 의미
  • MVCC(다중 버전 동시성 제어)를 지원하기 위해 하나의 논리적인 행이 여러 개의 버전(Old & New Tuple)을 가질 수 있음
  • 데이터는 Heap Page(8KB 블록) 단위로 저장되며, 하나의 Page에 여러 개의 Tuple이 포함됨

PostgreSQL은 기존 데이터를 직접 수정하지 않고, 새로운 버전을 생성하여 MVCC를 구현함.
이 방식은 동시성을 높이는 대신, Dead Tuple이 증가하여 VACUUM이 필요함.

 

MVCC에 대한 내용은 이전 장에서 확인 →  PostgreSQL MVCC (Multi-Version Concurrency Control)

 

2️⃣ Tuple의 내부 구조

각 Tuple(레코드)에는 사용자가 정의한 데이터 외에도 PostgreSQL이 관리하는 메타데이터가 포함된다.

컬럼 설명
ctid 페이지(Page) 내에서 Tuple의 위치를 나타내는 물리적 주소 (Page ID, Tuple Offset)
xmin 이 Tuple을 생성한 트랜잭션 ID
xmax 이 Tuple을 삭제하거나 업데이트한 트랜잭션 ID
t_xmin_commit_ts xmin 트랜잭션의 커밋 타임스탬프
t_ctid 업데이트된 Tuple의 새로운 위치(UPDATE 발생 시 사용)

 

Tuple의 기본 구조

| ctid | xmin | xmax | column1 | column2 | ...

🔹 Tuple의 위치 정보 (ctid)

PostgreSQL은 테이블의 각 튜플에 ctid라는 물리적인 위치 정보를 저장한다.

SELECT ctid, * FROM user;

 ctid  | id | name   | email
-------+----+--------+---------------
 (0,1) | 1  | Alice  | alice@email.com
 (0,2) | 2  | Bob    | bob@email.com

✅ ctid는 테이블이 업데이트되거나 VACUUM이 실행될 때 변경됨.

 

3️⃣ Tuple의 동작 방식 (INSERT, UPDATE, DELETE)

PostgreSQL에서 INSERT, UPDATE, DELETE가 실행될 때 어떻게 동작하는지를 살펴보자.

 

(1) INSERT (새로운 Tuple 생성)

INSERT INTO user (id, name) VALUES (1, 'Alice');
  • 새로운 **Tuple(레코드)**이 Heap Page 내부에 저장
  • xmin에는 해당 Tuple을 생성한 트랜잭션 ID가 기록됨
ctid id name xmin xmax
(0,1) 1 Alice 1001 NULL

xmin(1001)은 트랜잭션 ID를 의미하며, 이 트랜잭션이 커밋되면 데이터가 확정됨

 

(2) UPDATE (새로운 Tuple 생성 & 기존 Tuple은 Dead Tuple)

UPDATE user SET name = 'Alice Smith' WHERE id = 1;
  • PostgreSQL의 MVCC 특성상 UPDATE는 기존 데이터를 수정하지 않고 새로운 Tuple을 생성
  • 기존 Tuple은 xmax가 설정되어 "Dead Tuple"이 됨
ctid id name xmin xmax
(0,1) 1 Alice 1001 1002
(0,2) 1 Alice Smith 1002 NULL

UPDATE는 DELETE + INSERT와 유사한 방식으로 동작
Dead Tuple이 증가할수록 VACUUM 필요성이 커짐

 

(3) DELETE (Dead Tuple 생성)

DELETE FROM user WHERE id = 1;
  • PostgreSQL에서 DELETE는 데이터를 실제로 삭제하지 않고 xmax 값을 업데이트하여 "삭제됨"으로 표시함.
ctid id name xmin xmax
(0,2) 1 Alice Smith 1002 1003

데이터는 즉시 삭제되지 않으며, VACUUM을 실행해야 완전히 제거됨

 

4️⃣ Dead Tuple 문제와 VACUUM의 필요성

Dead Tuple이 많아지면 다음과 같은 문제가 발생

  1. 테이블 크기 증가 → 디스크 공간 낭비
  2. 쿼리 성능 저하 → SELECT 시 Dead Tuple도 검사해야 함
  3. 인덱스 크기 증가 → 불필요한 인덱스 업데이트로 성능 저하
  4. VACUUM이 필요함 → Dead Tuple을 정리해야 함

Dead Tuple 상태 확인

SELECT relname, n_live_tup, n_dead_tup 
FROM pg_stat_all_tables 
WHERE schemaname = 'public' 
ORDER BY n_dead_tup DESC;
 

Dead Tuple을 정리하는 VACUUM 실행

VACUUM ANALYZE user;

 

5️⃣ Fillfactor와 HOT(Heap-Only Tuple) 최적화

HOT(Heap-Only Tuple)란?

  • UPDATE 시 기존 페이지에서 데이터를 수정하는 최적화 기법
  • 인덱스를 수정하지 않고 Heap 내에서만 변경 가능
  • Fillfactor를 80~90으로 설정하면 HOT 적용 가능

 

 

✅ 결론

  • PostgreSQL의 Tuple은 MVCC를 구현하기 위해 다중 버전(Dead & Live Tuple)으로 관리됨
  • UPDATE 및 DELETE 시 Dead Tuple이 생성되며, 이를 정리하지 않으면 성능 저하 발생
  • VACUUM을 실행하여 Dead Tuple을 제거해야 성능 유지 가능
  • Fillfactor와 HOT Optimization을 활용하면 UPDATE 성능을 향상시킬 수 있음

🚀 PostgreSQL에서 Tuple을 최적화하면 INSERT, UPDATE, SELECT 성능을 극대화할 수 있음!

'PostgreSQL' 카테고리의 다른 글

PostgreSQL - VACUUM  (0) 2025.02.23
PostgreSQL - MVCC (Multi-Version Concurrency Control)  (0) 2025.02.23
PostgreSQL - Memory - Shared Buffer  (0) 2025.02.22
PostgreSQL - Memory - Backend Buffer  (0) 2025.02.22
PostgreSQL - Idle Session' Memory  (0) 2025.02.22

+ Recent posts