PostgreSQL

PostgreSQL: fillfactor

dewstream 2026. 3. 18. 08:00
728x90

※ PostgreSQL: fillfactor.

 

안녕하세요. 듀스트림입니다.

 

fillfactor는 단순히 페이지 저장 밀도를 조정하는 옵션처럼 보일 수 있지만, 실제로는 PostgreSQL의 UPDATE 처리 방식과 밀접하게 연결되어 있습니다.

특히 HOT (Heap-Only Tuple) 업데이트 가능 여부, 인덱스 page split 빈도, bloat 증가 양상, VACUUM 및 autovacuum 효율에 직접적인 영향을 줄 수 있습니다.

결국 fillfactor는 UPDATE가 빈번한 테이블과 인덱스의 성능, 저장 공간 활용, 유지보수 비용까지 함께 좌우하는 중요한 튜닝 파라미터입니다.

이번 글에서는 MVCC와 UPDATE, HOT의 핵심 배경만 필요한 수준으로 짚은 뒤, fillfactor가 실제 성능과 저장 구조에 어떤 차이를 만드는지 단계적으로 정리해보겠습니다.


1. PostgreSQL의 MVCC와 UPDATE 비용

PostgreSQL은 MVCC 구조로 인해 UPDATE 시 기존 튜플을 직접 수정하지 않고 새로운 튜플 버전을 생성하는 방식으로 동작합니다.

 

내부적으로는 다음과 같은 동작이 일어납니다.

UPDATE = 새로운 tuple 생성 + 기존 tuple 무효화(dead tuple)

 

예를 들어 다음과 같은 데이터가 있다고 가정해 보겠습니다.

id | name
---------
1  | kim

 

여기서 UPDATE가 발생하면 실제로는 다음과 같은 상태가 됩니다.

id | name
---------
1  | kim   ← dead tuple
1  | lee   ← new tuple

 

이 방식 때문에 PostgreSQL의 UPDATE는 단순한 수정이 아니라 새로운 row 생성 작업에 가깝습니다.

 

MVCC에 대한 자세한 내용은 아래 포스팅 참고 부탁드립니다.
 

PostgreSQL: MVCC

※ PostgreSQL: MVCC.※ Version: PostgreSQL 17. 안녕하세요. 듀스트림입니다. 아니.. 이걸 포스팅 안했더라고요? 그래서 작성하는 오늘의 포스팅은 PostgreSQL의 MVCC 관련 내용입니다.1. PostgreSQL MVCC란?PostgreSQL

dewstream.tistory.com


2. 일반적인(Non-HOT) UPDATE의 비용

일반적인 UPDATE는 다음과 같은 추가 비용을 발생시킵니다.

 

1️⃣ 새로운 heap tuple 생성

기존 row를 수정하지 않고 새로운 row 버전을 생성합니다.

 

2️⃣ 모든 관련 인덱스에 새로운 index entry 추가

PostgreSQL 인덱스는 (key, tuple 위치) 형태의 엔트리를 저장합니다.

예를 들어,

1 → (page 10, tuple 3)

UPDATE가 발생하면 tuple 위치가 바뀌기 때문에

1 → (page 15, tuple 2)

와 같이 새로운 index entry가 추가됩니다.

 

즉 UPDATE가 발생하면 heap tuple 생성 + 모든 관련 인덱스에 index entry 추가가 수행됩니다.

 

3️⃣ WAL 증가 및 추가 I/O 발생

Non-HOT UPDATE는 heap WAL과 index WAL을 발생시킵니다.

즉 테이블과 인덱스 모두 더티 페이지가 발생합니다.

 

4️⃣ bloat 증가

시간이 지나면 dead tuples (테이블)과 dead index entries (인덱스)가 누적됩니다. 이로 인해 table bloat와 index bloat가 발생합니다.


3. 이러한 문제를 줄이기 위해 등장한 HOT UPDATE

이러한 업데이트 비용을 줄이기 위해 PostgreSQL 8.3에서 도입된 최적화가 HOT(Heap-Only Tuple) 업데이트입니다.

 

HOT Update는 같은 heap 페이지 내에서 새로운 튜플 버전을 생성하면서도 새로운 인덱스 엔트리를 만들지 않도록 하는 메커니즘입니다.

 

PostgreSQL 공식 문서에서도 HOT을 다음과 같이 설명합니다.

Heap-Only Tuple (HOT) updates allow updates of rows without creating new index entries.

 

 

66.7. Heap-Only Tuples (HOT)

66.7. Heap-Only Tuples (HOT) # To allow for high concurrency, PostgreSQL uses multiversion concurrency control (MVCC) to store rows. However, MVCC …

www.postgresql.org

 

 

PostgreSQL: HOT(Heap-Only Tuple)

※ PostgreSQL: HOT(Heap-Only Tuple). 안녕하세요. 듀스트림입니다. 오늘 포스팅은 PosgreSQL 8.3 버전부터 도입된 HOT에 대한 내용입니다. 참고로 저는 HOT 세대입니다.1. PostgreSQL의 MVCC 업데이트 비용과 HOT의

dewstream.tistory.com

 

일반 업데이트와 HOT 업데이트의 동작 방식

일반 UPDATE: 새로운 index entry 생성

index
  ↓
(10,1) old tuple

UPDATE

index
  ↓
(15,2) new tuple

 

HOT Update: 같은 페이지 내부에서 tuple chain 생성

index
  ↓
(10,1)

page 내부
(10,1) → (10,5)
tuple1(dead) → tuple2(dead) → tuple3(live)
이를 HOT chain이라고 합니다.
이 구조 덕분에 index entry 추가가 없습니다.

 

HOT Update 발생 조건

HOT Update는 다음 조건을 만족해야 합니다.

 

1️⃣ 업데이트된 컬럼이 어떤 인덱스 key에도 포함되지 않을 것

HOT 업데이트 가능 HOT 업데이트 불가능
index(id) update name update id

 

2️⃣ 같은 heap 페이지에 충분한 free space가 존재할 것

same page update가 가능해야 합니다. 여기서 중요한 설정이 바로 fillfactor입니다.


4. Table fillfactor란?

fillfactor는 테이블 페이지를 얼마나 채울지 결정하는 저장 파라미터입니다.

기본값: fillfactor = 100
  • 즉 페이지를 꽉 채웁니다.

 

예로 이 상태에서는 업데이트 시 같은 페이지에 공간이 없기 때문에 HOT 업데이트가 불가능합니다.

PAGE
+--------+
|  row1  |
|  row2  |
|  row3  |
|  row4  |
|  row5  |
+--------+

 


5. fillfactor를 낮추면 발생하는 효과

fillfactor를 80으로 낮추면, 페이지 구조는 다음과 같습니다.

PAGE
+--------+
|  row1  |
|  row2  |
|  row3  |
|  free  |
|  free  |
+--------+
  • UPDATE가 발생하면 같은 페이지에 새 tuple 생성이 가능해집니다. (HOT 업데이트 가능성이 증가합니다.)
  • 결과적으로 index update 감소, WAL 감소, index bloat 감소효과가 발생합니다.

6. Index fillfactor의 목적

테이블 fillfactor는 HOT update를 위한 공간 확보가 목적입니다.
하지만 인덱스 fillfactor는 완전히 다른 목적을 가집니다.

기본값: index fillfactor = 90
  • 즉 인덱스 페이지를 90%만 채웁니다.

 

B-tree 인덱스에서 가장 비용이 큰 작업은 page split입니다.

 

예로 아래와 같은 인덱스 페이지가 있다고 가정해 보겠습니다.

INDEX PAGE
+--------+
10
20
30
40
50
+--------+

 

여기에 25를 삽입하게 되면 페이지가 가득 찼기 때문에, 페이지 공간 균형을 고려한 균등 분할이 되도록 page split이 발생합니다.

INDEX PAGE 1
+--------+
10
20
+--------+

INDEX PAGE 2
+--------+
30
40
50
+--------+
B-tree page split은 다음 작업을 동반합니다.
  1. 새로운 index page 생성
  2. 기존 페이지의 일부 키를 새 페이지로 이동
  3. 부모 노드(parent page)에 새로운 downlink 추가
  4. WAL 기록 증가
  5. 페이지 재작성

 

정리하자면 INDEX fillfactor 값이 낮을수록 각 인덱스 페이지에 더 많은 여유 공간(free space)이 남게 됩니다.

이 여유 공간은 이후 발생하는 INSERT 작업이나 indexed column이 변경되는 UPDATE에 의해 생성되는 새로운 index entry를 페이지 내부에 수용할 수 있도록 해줍니다.

그 결과 다음과 같은 효과가 발생합니다.

  • Page Split 발생 감소
  • Page Split에 따른 WAL 기록 감소
  • 페이지 재작성 및 부모 노드 수정 감소
  • 인덱스 구조의 안정성 유지
  • 장기적으로 index bloat 및 logical fragmentation 완화

 

즉, Index fillfactor는 인덱스 페이지의 초기 저장 밀도를 조절하여 이후 발생할 수 있는 page split 비용을 줄이고 인덱스 구조의 안정성을 유지하기 위한 설정이라고 볼 수 있습니다.

 

특히 지속적인 INSERT가 발생하거나 특정 키 범위에 데이터가 집중되는 워크로드(예: monotonic key, timestamp index)에서는 적절한 index fillfactor 설정이 page split 발생 빈도를 줄이는 데 도움이 됩니다.

7. Table fillfactor와 Index fillfactor의 차이

구분 Table fillfactor Index illfactor
목적 HOT update 공간 확보 page split 감소
효과 index update 감소 index maintenance 감소
관련 기능 HOT update B-tree split

8. 튜닝 기준

UPDATE 많은 테이블

fillfactor = 70~90

 

INSERT 위주 테이블

fillfactor = 100

 

INDEX

fillfactor = 90 (기본값 유지)
 

특별한 경우가 아니라면 인덱스 fillfactor는 조정하지 않습니다.


9. 정리

PostgreSQL의 fillfactor는 단순한 공간 설정이 아니라 UPDATE 처리 방식과 인덱스 유지 비용에 직접적인 영향을 주는 중요한 튜닝 파라미터입니다.

 

Table fillfactor

  • 페이지 내부에 여유 공간 확보
  • HOT update 발생 가능성 증가
  • 불필요한 index update 감소

Index fillfactor

  • 인덱스 페이지에 여유 공간 확보
  • Page split 발생 감소
  • WAL 및 index maintenance 비용 감소

 

아래 내용만 외워두시면 됩니다.

  • Table fillfactor → UPDATE 처리 최적화 (HOT update 유도)
  • Index fillfactor → Page split 감소 및 인덱스 유지 비용 최적화

오늘은 여기까지~

 

 

728x90

'PostgreSQL' 카테고리의 다른 글

PostgreSQL: pgvector  (0) 2026.03.03
PostgreSQL: pg_vacman  (0) 2026.01.25
PostgreSQL: DEFAULT PRIVILEGES  (0) 2025.12.31
PostgreSQL: pg_mooncake  (0) 2025.12.24
PostgreSQL: PgBouncer  (0) 2025.12.17