postgresql 중 vacuum 이란 것에 대해서 정리
- vaccum 이란 불필요한 데이터를 정리하고 압축해서 디스크공간의 효율성을 높인다
- 오래된 트랜잭션을 정리하는 작업도 수행
Multi-Version Concurrency Control = 동시에 여러 개의 데이터 버전을 제공할수 있는 기법
오라클의 MVCC와 postgresql MVCC의 차이
특성 | PostgreSQL | Oracle |
MVCC 구현 방식 | 같은 row의 여러 버전이 테이블에 함께 저장됨 Dead tuple은 vacuum으로 정리 |
Undo 세그먼트에 이전 버전 저장 자동으로 관리됨 |
공간 관리 | vacuum 작업 필요 관리자의 주기적인 관리 필요 테이블 bloat 발생 가능 |
Undo 세그먼트 자동 관리 별도 관리 작업 불필요 테이블 크기 안정적 |
동시성 처리 | 읽기 작업이 쓰기를 차단하지 않음 높은 읽기 성능 |
Undo 세그먼트 경합 가능 대규모 트랜잭션 시 부하 발생 |
리소스 사용 | Vacuum으로 인한 추가 부하 더 많은 저장 공간 필요 |
Undo 세그먼트 관리 오버헤드 더 효율적인 공간 사용 |
관리 복잡도 | Vacuum 설정과 모니터링 필요 autovacuum 튜닝 필요 |
상대적으로 단순 Undo retention 설정만 필요 |
주요 차이점:
- 구조적 차이:
- PostgreSQL: 테이블 내에 모든 버전 관리 << 이 점이 가장 큰 차이점이면서 vaccum이 필요한 이유
- Oracle: 별도의 Undo 세그먼트에서 관리
- 관리 측면:
- PostgreSQL: Vacuum 작업 필요, 더 많은 관리 필요
- Oracle: 자동 관리, 상대적으로 관리 부담 적음
- 성능 특성:
- PostgreSQL: 읽기 작업에 최적화
- Oracle: 전반적으로 균형 잡힌 성능
Vaccum이 필요한 이유
1. 이전 버전의 데이터를 테이블 블록 내에 저장하기 때문
postgresql 은 테이블내에 모든 버전의 데이터를 저장하므로 공간의 효율성이 떨어진다.
이를 해결하기위해서는 아래 2가지 방법이 존재한다.
--1. 이전 버전 데이터를 삭제 > 공간확보
vaccum (freeze)
--2. 이전 버전 데이터를 삭제 + 공간 압축
vaccum full
2. 레코드별로 트랜잭션 ID를 관리하기 때문
위에 있는 1의 이유로 레코드별로 XID를 관리해야한다.
그래야 시점에 맞는 데이터를 추출할 수 있다.
XID가 레코드별로 존재하기때문에 공간에 대한 비효율이 더 커진다.
그래서 XID는 4byte를 사용하기로 하였다.
하지만 여기서 문제점은 트랜잭션마다 +1이 증가하는데 4byte로 관리할 수 있는 트랜잭션의 갯수는 43억개이다.
50일 후에는 43억을 소진하게된다.
소진 후 다시 1로 돌아가서 트랜잭션을 관리해야하는데 해결방법이 2가지
- 한바퀴 회전할 떄마다 wrap 번호를 증가시키기
- 한바퀴 회전하기전에 이전 xid를 특정값으로 변경, flag 처리 << postgresql가 채택한 방법
Frozen XID에 대해서 미리 알아야함,
postgresql 에는 3개 유형의 XID가 있다.
XID 유형 | 값 | 특징 | 용도 |
Bootstrap XID | 1 | 데이터베이스 초기화 시에만 사용 시스템 카탈로그 생성에 사용 변경 불가능 |
데이터베이스 클러스터 초기화 기본 시스템 객체 생성
|
Frozen XID | 2 | Anti-wraparound Vacuum 영구적으로 유효한 상태 모든 트랜잭션에서 visible vacuum freeze의 결과 |
XID wraparound 방지 오래된 트랜잭션 처리 시스템 안정성 유지 |
Normal XID | 3~ | 일반적인 트랜잭션에 사용 순차적으로 증가 재사용 가능 |
일반적인 데이터베이스 작업 CRUD 작업 트랜잭션 처리 |
-- Frozen 관련 파라미터 확인
SHOW vacuum_freeze_min_age;
SHOW vacuum_freeze_table_age;
SHOW autovacuum_freeze_max_age;
-- 테이블의 relfrozenxid 확인
SELECT relname, age(relfrozenxid) as xid_age,
pg_size_pretty(pg_total_relation_size(relname::regclass)) as size
FROM pg_class
WHERE relkind = 'r'
AND relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'public')
ORDER BY xid_age DESC;
-- 수동으로 freeze 실행
VACUUM (FREEZE, VERBOSE) table_name;
-- freeze 진행 상황 모니터링
SELECT pid, state, wait_event_type, query, xact_start
FROM pg_stat_activity
WHERE query LIKE '%VACUUM%';
-- 데이터베이스별 oldest XID 확인
SELECT datname, age(datfrozenxid) as age
FROM pg_database
ORDER BY age DESC;
-- freeze 카운터 확인
SELECT schemaname, relname, n_live_tup, n_dead_tup,
vacuum_count, autovacuum_count,
last_vacuum, last_autovacuum
FROM pg_stat_user_tables
WHERE n_live_tup > 0
ORDER BY n_dead_tup DESC;
Vaccum
vaccum <테이블명>
vaccum을 하면서 select, dml 작업을 할 수 있다.
하지만 vaccum full을 하면서 select, dml 작업을 할 수 없다. << select 하는 세션도 대기하게 된다.
vaccum 작업시에 redo는 생성하지 않는다.
vaccum freeze 시에 redo가 발생한다.
vaccum full시에 redo가 발생한다.
vaccum freeze와 vaccum full 의 차이
특성 | VACUUM FREEZE | VACUUM FULL |
주요 목적 | - 트랜잭션 ID aging 방지 - XID wraparound 예방 - 오래된 트랜잭션 ID를 Frozen 상태로 변경 |
- 물리적 공간 회수 - 테이블 완전 재작성 |
테이블 잠금 | - ACCESS SHARE lock - 읽기/쓰기 가능 |
- ACCESS EXCLUSIVE lock - 읽기/쓰기 불가 |
디스크 공간 | - OS에 반환하지 않음 - 빈 공간 재사용 가능 |
- OS에 공간 반환 - 테이블 크기 최소화 |
수행 시간 | - 상대적으로 빠름 - 온라인 수행 가능 |
- 매우 오래 걸림 - 전체 테이블 복사 |
부하 | - 중간 정도 - 다른 작업 영향 적음 |
- 매우 높음 - I/O 부하 큼 |
- autovacuum을 적절히 설정
- 주기적인 ANALYZE 실행
- 대규모 DELETE 작업 후 수동 VACUUM 고려
AGE
-- 현재 트랜잭션 ID와의 차이가 age
age = current_xid - frozen_xid
postgresql은 데이터베이스, 테이블, 레코드 별로 나이를 관리한다.
트랜잭션이 발생할 때마다 age값이 +1 이 증가한다.
나이순으로 확인해보면 데이터베이스 > 테이블 > 레코드 이다.
● 관련 파라미터
vacuum_freeze_min_age 파라미터 (deault : 5000000 ) << 이 값의 수치보다 age가 많은 레코드가 xid frozen 대상
vacuum_freeze_table_age 파라미터 (deault : 150000000 )
autovacuum_freeze_max_age 파라미터 (deault : 200000000 )
● Freeze 발생 조건:
- vacuum_freeze_min_age보다 오래된 XID
- vacuum_freeze_table_age에 도달한 테이블
- autovacuum_freeze_max_age 도달 시 강제 실행
-- 데이터베이스 전체의 age 확인
SELECT datname, age(datfrozenxid) as transaction_age
FROM pg_database
ORDER BY transaction_age DESC;
-- 테이블별 age 확인
SELECT schemaname, relname, n_live_tup, n_dead_tup,
age(relfrozenxid) as xid_age,
round(100 * n_dead_tup::float / NULLIF(n_live_tup + n_dead_tup, 0), 2) as dead_ratio
FROM pg_stat_user_tables
ORDER BY xid_age DESC;
-- autovacuum 동작 상태 확인
SELECT relname, last_vacuum, last_autovacuum,
vacuum_count, autovacuum_count,
n_dead_tup, n_live_tup
FROM pg_stat_user_tables;
-- 오래된 XID를 가진 테이블 찾기
SELECT c.oid::regclass as table_name,
greatest(age(c.relfrozenxid),age(t.relfrozenxid)) as age
FROM pg_class c
LEFT JOIN pg_class t ON c.reltoastrelid = t.oid
WHERE c.relkind IN ('r', 'm')
AND greatest(age(c.relfrozenxid),age(t.relfrozenxid)) > 100000000
ORDER BY age DESC;
- autovacuum 파라미터 적절히 설정
- 큰 테이블은 더 낮은 임계값 설정
- 정기적인 모니터링 수행
- age가 높은 테이블 우선 처리
AutoVaccum
참고용
필드 | 크기 | comment |
xmin | 4 bytes | 해당 레코드를 생성한 트랜잭션 ID |
xmax | 4 bytes | 해당 레코드를 삭제하거나 업데이트한 트랜잭션 ID (활성 상태면 0) |
cmin | 4 bytes | 트랜잭션 내에서 생성 명령 ID |
cmax | 4 bytes | 트랜잭션 내에서 삭제 명령 ID |
ctid | 6 bytes | 물리적 레코드 위치 (블록 번호, tuple 인덱스) |
infomask | 2 bytes | 플래그 비트들 (HEAP_XMIN_COMMITTED, HEAP_XMIN_INVALID 등) |
infomask2 | 2 bytes | 추가 플래그 비트들 |
t_bits | variable | NULL 비트맵 |
'Postgresql > 아키텍처' 카테고리의 다른 글
PostgreSQL architecture (Local Memory) (0) | 2024.11.13 |
---|---|
PostgreSQL architecture (shared Memory) (0) | 2024.10.22 |