본문 바로가기
Database2026년 8월 16일5분 읽기

fillfactor — UPDATE 많은 테이블을 살리는 저장 옵션

YS
김영삼
조회 5
fillfactor — UPDATE 많은 테이블을 살리는 저장 옵션

fillfactor는 PostgreSQL이 테이블·인덱스 페이지(기본 8KB)를 처음 채울 때 몇 %까지만 채우고 나머지를 비워둘지 정하는 저장 파라미터입니다. 기본값은 테이블 100(꽉 채움), 대부분 인덱스 90입니다. 이 한 숫자가 UPDATE가 많은 테이블의 성능을 좌우하는 이유는, 비워둔 공간이 바로 HOT 업데이트가 앉을 자리이기 때문입니다.

처음엔 "왜 이렇게 사소한 옵션이 따로 있지?" 싶었습니다. 그런데 쓰기 많은 테이블 하나를 fillfactor 조정만으로 눈에 띄게 살려낸 뒤로 생각이 바뀌었습니다.

빈 공간이 왜 성능이 되는가

MVCC 때문에 UPDATE는 제자리 수정이 아니라 새 튜플을 만드는 일입니다. 이 새 튜플이 원본과 같은 페이지에 들어갈 수 있으면, 인덱스만 바뀌지 않았다는 조건 하에 HOT 업데이트가 성립해 인덱스 쓰기를 통째로 건너뜁니다. 그런데 페이지가 이미 꽉 차 있으면(fillfactor 100) 새 튜플은 다른 페이지로 밀려나고, HOT은 깨지며, 모든 인덱스가 새 위치를 기록해야 합니다.

즉 fillfactor를 조금 낮춰 페이지마다 여유를 남겨두면, 반복되는 UPDATE가 그 여유 공간을 재활용하며 같은 페이지 안에서 순환합니다. 인덱스 쓰기 감소 → WAL 감소 → 블로트 감소로 이어집니다.

-- 생성 시 지정
CREATE TABLE sessions (
  id     bigint primary key,
  data   jsonb,
  seen_at timestamptz
) WITH (fillfactor = 85);
-- 나중에 변경 (새로 쓰이는 페이지부터 적용됨)
ALTER TABLE sessions SET (fillfactor = 85);
-- 기존 페이지에도 반영하려면 재작성 필요
VACUUM FULL sessions;   -- 강한 락. 운영 중이면 pg_repack 권장

얼마로 잡을까 — 감이 아니라 원칙

정답은 워크로드에 달렸습니다. 저는 대략 이렇게 나눕니다.

테이블 성격권장 fillfactor이유
읽기 전용 / append-only100 (기본)UPDATE 없음 → 여유 공간 낭비
가끔 UPDATE90약간의 여유로 HOT 확률 확보
UPDATE 헤비(카운터·상태·세션)70~85HOT 순환 공간 넉넉히

너무 낮추면 부작용이 옵니다. 같은 데이터가 더 많은 페이지에 흩어지니 테이블이 커지고, 순차 스캔·전체 집계가 느려지며, 버퍼 캐시 효율이 떨어집니다. 그래서 저는 70 밑으로는 웬만하면 안 갑니다. 극단은 항상 대가가 있습니다.

효과를 숫자로 검증하기

바꿨으면 확인해야죠. HOT 비율이 실제로 올랐는지 봅니다.

SELECT relname,
       n_tup_upd,
       n_tup_hot_upd,
       round(100.0*n_tup_hot_upd/NULLIF(n_tup_upd,0),1) AS hot_pct
FROM pg_stat_user_tables
WHERE relname = 'sessions';
-- 테이블 물리 크기 추이도 함께 관찰
SELECT pg_size_pretty(pg_relation_size('sessions'));

fillfactor를 낮추고 재작성한 뒤 hot_pct가 30%대에서 90%대로 뛰는 걸 확인하면, 그게 제대로 먹혔다는 신호입니다. 반대로 hot_pct가 안 오르면 원인은 fillfactor가 아니라 바꾸는 컬럼에 걸린 인덱스일 확률이 높습니다. 그건 fillfactor로 못 고칩니다.

인덱스에도 fillfactor가 있다

B-tree 인덱스의 기본 fillfactor는 90입니다. 값이 단조 증가하는 키(예: bigserial, 시간순 삽입)라면 항상 오른쪽 끝에만 삽입되므로 여유 공간이 별 의미가 없어 100에 가깝게 둬도 됩니다. 반대로 중간에 마구 끼어드는(랜덤 UUID 같은) 키라면 90 이하가 페이지 분할(page split)을 줄여줍니다.

자주 묻는 질문

fillfactor를 바꾸면 즉시 적용되나요?

아닙니다. ALTER TABLE ... SET (fillfactor=...)는 앞으로 새로 쓰이는 페이지에만 적용됩니다. 기존 데이터에 반영하려면 VACUUM FULL이나 pg_repack, 또는 CLUSTER로 테이블을 다시 써야 합니다.

fillfactor만 낮추면 UPDATE 성능이 무조건 좋아지나요?

HOT 조건 중 "같은 페이지" 쪽만 해결됩니다. "바꾸는 컬럼이 인덱스에 없을 것"이라는 다른 조건이 안 맞으면 소용없습니다. 두 조건을 함께 봐야 합니다.

기본값이 왜 하필 100인가요?

대다수 테이블에서 저장 공간을 최대로 쓰고 순차 읽기를 빠르게 하는 게 무난한 기본이기 때문입니다. UPDATE 패턴은 테이블마다 다르니, 필요한 테이블만 골라서 낮추라는 설계 철학입니다.

파티션 테이블에도 적용되나요?

네, 각 파티션(자식 테이블)에 개별적으로 지정합니다. 부모에 걸어도 자동 상속되지 않는 버전이 있으니, 파티션 생성 템플릿에 fillfactor를 명시해 두는 편이 안전합니다.

댓글 0

아직 댓글이 없습니다.
Ctrl+Enter로 등록