본문 바로가기

Database/PostgreSQL

처음 사용자를 위한 PostgreSQL 사용 샘플 - 9. Vacuum이란?

"처음 사용자를 위한 PostgreSQL 사용 샘플"이라는 제목의 시리즈로, 저처럼 PostgreSQL을 처음 접하는 사용자를 위한 기본적인 개념, 사용 샘플을 설명드립니다.

이전글

[Database/PostgreSQL] - 처음 사용자를 위한 PostgreSQL 사용 샘플 - 6. Replication - WAL Streaming

 

아래 자료를 참조했습니다.

PostgreSQL Domcumentation
The Internals of PostgreSQL
PostgreSQL Tutorial

Vacuum이란?

참고자료

Understanding of Bloat and VACUUM in PostgreSQL

Optimizing, monitoring, and troubleshooting VACUUM operations in PostgreSQL

 

오라클 데이터베이스에서 로우에 대한 갱신과 삭제가 일어나게 되면 Consistency를 유지하기 위해 로우에 대한 이전 이미지를 담고 있는 DB 인스턴스 레벨의 UNDO를 이용합니다. PostgreSQL 경우에는 각 테이블 레벨에서 로우에 대한 이전 이미지와 최신 이미지를 유지하는 방식을 사용합니다. 이렇게 스냅샷 기반으로 로우에 대한 여러개의 복사본을 유지하는 동시성 제어 메커니즘을 Multiversion Concurrency Control (MVCC)라고 하는데 데이터베이스 마다 조금씩 다르게 사용합니다.

PostgreSQL에서 Vacuum이 하는 일 중의 하나는 MVCC 과정에서 발생한 Dead Tuple (사용하지 않는 버전 또는 스냅샷 이미지)을 정리하는 역할을 합니다. Vacuum에는 표준 VACUUMVACUUM FULL이 있는데, 먼저 ①표준 VACUUM은 SELECT, INSERT, UPDATE, DELETE와 같은 다른 오퍼레이션과 함께 작동할 수 있는 상대적으로 작은 규모의 Vacuum이라고 할 수 있습니다. ②VACUUM FULL은 테이블에 대한 Exclusive Lock이 필요한, 표준 VACUUM 보다 더 많은 공간을 회수하지만 훨씬 더 느린 방식의 Vacuum입니다.

 

Vacuum이 하는 일

다음은 PostgreSQL VACUUM 명령이 하는 대표적인 작업입니다.

갱신, 삭제된 로우가 차지했던 공간 반환/재사용

갱신, 삭제 시 로우의 각 버전이 아직 다른 트랜잭션에게 보여져야 한다면 삭제되어서는 않되지만, 더 이상 참조되지 않을 경우에는 재사용과 디스크 공간이 무한정 커지는 것을 막기 위해 회수되어야 합니다. 이를 담당하는 것이 VACUUM입니다.

표준 VACUUM은 더 이상 참조되지 않는 로우 버전을 테이블과 인덱스에서 삭제하고, 재사용을 위해 해당 공간을 사용 가능하다고 표시합니다. 다만 일부 특수한 경우를 제외하고는 운영 체제에 해당 공간을 반환하지는 않습니다. 이에 반해 VACUUM FULL은 더 이상 참조되지 않는 로우 버전이 없도록 테이블을 완전히 새로운 버전으로 작성하는 방식입니다. 테이블 사이즈를 최소화할 수 있지만 시간이 오래 걸립니다. 또한 VACUUM FULL이 끝날때 까지 테이블의 새로운 복사본이 필요하기 때문에 추가 공간도 필요하게 됩니다. 

따라서 표준 VACUUM을 자주 발생시켜서 VACUUM FULL이 필요한 상황을 가급적 피하는 것이 운영 상황에서 Vacuum 의 목표가 됩니다. Autovacuum 데몬이 이러한 일을 하게 됩니다. 테이블의 사이즈를 최소로 유지하는 것보다는 안정적인 디스크 공간 사용 비율율 유지하는 것이 Autovacuum의 역할이라고 할 수 있습니다.

Vacuum 모니터링을 위한 기초 데이터로서 테이블별 입력, 갱신, 삭제된 로우 수는 pg_stat_all_tables 뷰에서 확인할 수 있습니다.

postgres=# SELECT relname, n_tup_ins, n_tup_upd, n_tup_del
postgres-# FROM pg_stat_all_tables
postgres-# ORDER BY n_dead_tup DESC LIMIT 10;
    relname    | n_tup_ins | n_tup_upd | n_tup_del
---------------+-----------+-----------+-----------
 pg_attribute  |        68 |         7 |        51
 pg_depend     |        36 |         0 |        27
 pg_class      |        12 |         0 |         9
 pg_sequence   |         4 |         4 |         3
 pg_type       |         8 |         0 |         6
 pg_index      |         4 |         0 |         3
 pg_constraint |         4 |         0 |         3
 pg_attrdef    |         4 |         0 |         3
 pg_authid     |         2 |         1 |         0
 pg_database   |         4 |         0 |         1
(10 rows)

 

pg_stat_all_tables 뷰에서는 Vacuum 대상인 dead 튜플 갯수 역시 확인할 수 있습니다.

postgres=# SELECT relname, n_live_tup, n_dead_tup, n_tup_hot_upd
postgres-# FROM pg_stat_all_tables
postgres-# ORDER BY n_dead_tup DESC LIMIT 10;
    relname    | n_live_tup | n_dead_tup | n_tup_hot_upd
---------------+------------+------------+---------------
 pg_attribute  |         17 |         53 |             6
 pg_depend     |          9 |         27 |             0
 pg_class      |          3 |          9 |             0
 pg_sequence   |          1 |          7 |             4
 pg_type       |          2 |          6 |             0
 pg_index      |          1 |          3 |             0
 pg_constraint |          1 |          3 |             0
 pg_attrdef    |          1 |          3 |             0
 pg_authid     |          2 |          1 |             1
 pg_database   |          3 |          1 |             0
(10 rows)
  • n_live_tup: 트랜잭션에 의해 참조 중인 튜플 수
  • n_dead_tup: 트랜잭션에 의해 더이상 참조되지 않는 튜플 수. dead tuple이 live tupe 보다 많으면 공간에 대한 정리를 고려해야 함
  • n_tup_hot_upd: HOT(Heap Only Tuple) 업데이트 튜플 수. HOT은 PostgreSQL 8.3부터 소개된 이전 버전의 로우가 저장된 동일한 테이블 페이지에 업데이트된 로우가 저장되어 있을 때 인덱스와 테이블 페이지를 효과적으로 사용하는 기능.

 

PostgreSQL query planner가 사용하는 통계 정보 업데이트

PostgreSQL query Planner는 쿼리의 실행 계획을 만들 때 테이블 통계 정보를 이용합니다. 이때 통계 정보를 VACUUM ANALYZE 명령으로 수행합니다. Autovacuum 데몬이 활성화 되어 있는 경우, 테이블 내용의 일정 수준 변경이 발생하면 ANALYZE 명령이 자동으로 수행됩니다. 통계 정보 수집은 특정 테이블 또는 칼럼 레벨로 수행할 수 있으나, 가장 빠르기 때문에 전체 데이터베이스 레벨의 통계 정보 수집이 일반적으로 사용됩니다. ANLAYZE가 실행될 때는 전체 모든 로우를 읽는 것이 아니라 통계적인 샘플링이 사용됩니다.

 

Visibility Map 갱신

Visibility Map은 각 테이블에서 실행 중인 트랜잭션이 참조하는 튜플만 포함하는 페이지를 추적하는 역할을 합니다. Vacuum은 이러한 페이지에 대해서는 정리할 자원이 없기 때문에 해당 페이지에 대해 작업을 건너뛰게 됩니다. 그리고 테이블 참조 없이 인덱스만으로 쿼리에 응답할 수 있도록 (index-only scan) 해 줍니다. PostgreSQL 인덱스에는 튜플을 트랜잭션이 참조하는지 대한 정보를 따로 가지고 있지 않기 때문에, 현재 트랜잭션에 의해 참조되어야 하는 것인지 여부를 인덱스에 매칭되는 메모리 힙 튜플에서 가져오게 됩니다. 반면 index-only scan은 Visibility Map을 우선 체크하게 됩니다. 페이지 상의 모든 튜플이 참조되는 것이라면 해당 힙 페치는 건너뛰게 되어 디스크 스캔을 피할 수 있게 됩니다.

 

Autovacuum

Autovacuum은 VACUUM과 ANALYZE를 자동으로 실행하는 용도로서, PostgreSQL 운영 시 사용이 권고되는 기능입니다. 활성화되면 대량의 입력, 갱신, 삭제된 튜플 발생한 테이블을 체크하게 되는데, 이때 통계정보를 이용합니다. 따라서 테이블과 인덱스에 대해 통계정보를 수집할 지 여부 설정하는 파라미터인 track_counts가 true로 설정되어 있지 않다면 Autovacuum이 실행되지 않습니다. 디폴트로 Autovacuum은 활성화되어 있고, 관련 파라미터도 설정되어 있습니다.

Autovacuum 데몬은 사실상 여러개 프로세스로 구성되어 있습니다. Autovacuum launcher라는 데몬 프로세스가 모든 데이터베이스에 대한 Autovacuum Worker 프로세스를 기동하는 역할을 담당합니다. Autovacuum launcher은 특정 설정 시간에 따라 Worker 프로세스를 각 데이터베이스 내에서 기동해서 작업을 분산합니다.

Autovacuum에 대한 설정은 아래와 같이 pg_settings를 쿼리해서 확인할 수 있습니다.

postgres=# SELECT * from pg_settings where category like 'Autovacuum';
                 name                  |  setting  | unit |  category  |                                        short_desc                                         | ex
tra_desc |  context   | vartype | source  | min_val |  max_val   | enumvals | boot_val  | reset_val | sourcefile | sourceline | pending_restart
---------------------------------------+-----------+------+------------+-------------------------------------------------------------------------------------------+---
---------+------------+---------+---------+---------+------------+----------+-----------+-----------+------------+------------+-----------------
 autovacuum                            | on        |      | Autovacuum | Starts the autovacuum subprocess.                                                         |
         | sighup     | bool    | default |         |            |          | on        | on        |            |            | f
 autovacuum_analyze_scale_factor       | 0.1       |      | Autovacuum | Number of tuple inserts, updates, or deletes prior to analyze as a fraction of reltuples. |
         | sighup     | real    | default | 0       | 100        |          | 0.1       | 0.1       |            |            | f
 autovacuum_analyze_threshold          | 50        |      | Autovacuum | Minimum number of tuple inserts, updates, or deletes prior to analyze.                    |
         | sighup     | integer | default | 0       | 2147483647 |          | 50        | 50        |            |            | f
 autovacuum_freeze_max_age             | 200000000 |      | Autovacuum | Age at which to autovacuum a table to prevent transaction ID wraparound.                  |
         | postmaster | integer | default | 100000  | 2000000000 |          | 200000000 | 200000000 |            |            | f
 autovacuum_max_workers                | 3         |      | Autovacuum | Sets the maximum number of simultaneously running autovacuum worker processes.            |
         | postmaster | integer | default | 1       | 262143     |          | 3         | 3         |            |            | f
 autovacuum_multixact_freeze_max_age   | 400000000 |      | Autovacuum | Multixact age at which to autovacuum a table to prevent multixact wraparound.             |
         | postmaster | integer | default | 10000   | 2000000000 |          | 400000000 | 400000000 |            |            | f
 autovacuum_naptime                    | 60        | s    | Autovacuum | Time to sleep between autovacuum runs.                                                    |
         | sighup     | integer | default | 1       | 2147483    |          | 60        | 60        |            |            | f
 autovacuum_vacuum_cost_delay          | 2         | ms   | Autovacuum | Vacuum cost delay in milliseconds, for autovacuum.                                        |
         | sighup     | real    | default | -1      | 100        |          | 2         | 2         |            |            | f
 autovacuum_vacuum_cost_limit          | -1        |      | Autovacuum | Vacuum cost amount available before napping, for autovacuum.                              |
         | sighup     | integer | default | -1      | 10000      |          | -1        | -1        |            |            | f
 autovacuum_vacuum_insert_scale_factor | 0.2       |      | Autovacuum | Number of tuple inserts prior to vacuum as a fraction of reltuples.                       |
         | sighup     | real    | default | 0       | 100        |          | 0.2       | 0.2       |            |            | f
 autovacuum_vacuum_insert_threshold    | 1000      |      | Autovacuum | Minimum number of tuple inserts prior to vacuum, or -1 to disable insert vacuums.         |
         | sighup     | integer | default | -1      | 2147483647 |          | 1000      | 1000      |            |            | f
 autovacuum_vacuum_scale_factor        | 0.2       |      | Autovacuum | Number of tuple updates or deletes prior to vacuum as a fraction of reltuples.            |
         | sighup     | real    | default | 0       | 100        |          | 0.2       | 0.2       |            |            | f
 autovacuum_vacuum_threshold           | 50        |      | Autovacuum | Minimum number of tuple updates or deletes prior to vacuum.                               |
         | sighup     | integer | default | 0       | 2147483647 |          | 50        | 50        |            |            | f
(13 rows)

<끝>