개요
- 2년 넘게 PostgreSQL을 사용하면서 PostgreSQL의 아키텍처에 대해서 전혀 알지 못했다.
- RDBMS와 같은 복잡한 시스템에서는 아키텍처의 이해가 성능 최적화와 효율적으로 관리하는데 큰 도움이 된다.
- 그래서 이번 글에서는 PostgreSQL에 대해서 간단히 정리해보려고 한다.
PostgreSQL 아키텍처
PostgreSQL의 아키텍처는 아래와 같다.
- PostgreSQL 구조는 물리적인 관점에서 보면,
Shared Memory, 프로세스, 데이터 파일
(또는 테이블 스페이스)로 구성된다. - 공유 메모리의 주요 요소는
Shared Buffer
와 WAL 버퍼이다. (Commit Log를 비롯한 나머지 구성요소는 위 그림에서 생략되었다.) - 프로세스는 Postgres 프로세스, 백그라운드 프로세스, Backend 프로세스, Client 프로세스로 구분된다.
- 백그라운드 프로세스는 에러 로그 기록, 버퍼 기록, WAL 버퍼 기록용이 존재하며 autovacuum을 위한 프로세스가 존재한다.
- Backend 프로세스는 Client가 요청하는 쿼리를 수행하며, 작업 수행을 위한 로컬 메모리 영역이 존재한다.
이제 위 그림의 구성요소를 하나하나 정리해 보자
Shared Buffer
PostgreSQL에서 Shared Buffer는 여러 프로세스가 공유하는 메모리 공간이다. Shared Buffer는 데이터베이스 서버의 주 메모리(RAM) 내에 할당된 메모리 영역이고 이 영역에는 데이터베이스 테이블과 인덱스에서 필요한 데이터 페이지들이 캐싱된다.
- Shared Buffer의 목적은 DISK IO를 최소화하는 것이다.
- 메모리 액세스는 디스크 액세스보다 훨씬 빠르므로, 자주 사용되는 데이터를 메모리에 유지함으로써 전체 시스템의 성능을 크게 향상 시킬 수 있다.
- Shared Buffer는 데이터의 쓰기 작업도 관리합니다. 변경된 페이지(더티 페이지)는 일정 시간 후에 디스크에 쓰여진다.
- 이 과정은 백그라운드 프로세스인 Checkpointer와 Background Writer에 의해 수행된다. (백그라운드 프로세스는 아래에서 좀 더 자세히 설명한다.)
- 정리하면
Shared Buffer는 DISK IO를 최소화하기 위해서 데이터를 캐싱하는 공간
이라고 생각하면 된다.
WAL 버퍼
WAL 버퍼는 데이터베이스 변경 사항(예: INSERT, UPDATE, DELETE 연산)을 물리적인 WAL 파일에 기록하기 전에 일시적으로 저장하는 메모리 내 버퍼를 말한다.
- WAL은 Write Ahead Log의 약자로
먼저 로그를 쓴다
라는 의미를 갖는다. 더 정확한 의미는 아래와 같다.데이터 파일의 변경된 내용은 해당 변경 사항이 로깅된 이후에 쓰여져야만 한다.
- 데이터베이스 트랜잭션이 발생할 때, 모든 변경 사항은 먼저 WAL 버퍼에 기록되기 때문에 시스템 장애 발생 시에도 변경 사항을 복구할 수 있다.
- WAL 버퍼를 사용하면 디스크 I/O 작업을 최적화할 수 있다.
- 변경 사항을 즉시 디스크에 쓰는 대신, 버퍼를 통해 효율적으로 관리하고 배치 작업으로 디스크에 기록된다.
- 정리하면
WAL 버퍼를 통해서 특정 시점으로 백업 및 복구가 가능하고 성능 최적화
도 할 수 있다.
프로세스
PostgreSQL에서는 다양한 종류의 프로세스가 시스템 운영과 관리에 중요한 역할을 한다. 프로세스는 크게 3가지 종류의 프로세스로 구분된다. 현재 실행 중인 프로세스는 아래와 같은 명령어로 확인할 수 있다.
ps -fu postgres
- 아래 쿼리를 통해서도 현재 동작중인 프로세스를 확인할 수 있다.
select * from pg_stat_activity
Postgres 프로세스
- PostgreSQL 버전 9.3 이전에는 Postmaster라고 불렸다.
- 하나의 데이터베이스 클러스터는 하나의 Postgres 프로세스에 의해 관리된다.
- PostgreSQL 서버가 구동될 때
가장 먼저 생성
돠어복구 작업, Shared 메모리 초기화 작업, 백그라운드 프로세스 구동 작업
을 수행한다. - 클라이언트로부터 커넥션 생성 요청을 받으면,
백엔드 프로세스를 새로 생성해서 새로운 커넥션에 할당
한다.
Background 프로세스
- 데이터베이스를 운영하기 위해서는 쿼리를 처리하는 것뿐만 아니라 다양한 부가 작업이 필요하다.
- PostgreSQL에서는 아래와 같은 백그라운드 프로세스들이 부가 작업을 수행하고 있다.
logger
: 에러 메시지와 로그를 관리하고, 로그 파일에 기록한다.checkpointer
: 체크 포인트 발생 시, dirty 페이지(수정된 메모리 페이지)를 디스크에 기록한다.background writer
: dirty 페이지를 주기적으로 디스크에 기록하여 메모리와 디스크 간의 데이터를 동기화한다.walwriter
: WAL 버퍼 내용을 WAL 파일에 기록한다. 따라서 데이터베이스의 내구성과 복구에 중요한 역할을 한다.autovacuum launcher
: Vacuum이 필요한 시점에 autovacuum worker를 fork 해서 데이터베이스의 공간 재사용과 성능 최적화한다.archiver
: WAL 파일을 지정된 위치에 복사하여 백업한다.stats collector
: 세션 수행 정보(pg_stat_activity) 와 테이블 사용 통계 정보(pg_stat_all_tables)와 같은 DBMS 사용 통계 정보를 수집한다.
Backend 프로세스
- Backend 프로세스는 자신에게 배정된 커넥션을 통해 요청된 쿼리 또는 명령을 수행하기 위한 일종의 워커 프로세스다.
- 따라서 Backend 프로세스는
Client 프로세스의 쿼리 요청을 수행
한 후, 결과를 전송하는 역할을 수행한다. - Backend 프로세스의 최대 개수는
max_connections
파라미터로 설정하며기본 설정값은 100
이다. - 만약, 개발할 때 아래와 같이 쿼리를 수행하면 백엔드 프로세스가 생성되는 것을 확인할 수 있다.
- 위 사진을 보면, 세션을 하나 더 만들어서 쿼리를 수행하니 추가적인 벡엔드 프로세스가 생성된 것을 확인할 수 있다.
- 또한, 한번 생성된 프로세스는 쿼리를 수행했다고 하더라도 사라지지 않는다.
- postgresql.conf 파일에서
idle_in_transaction_session_timeout
값을 통해서 'idle' 상태로 유지되는 최대 시간을 지정할 수 있다.
- postgresql.conf 파일에서
- 하나의 백엔드 프로세스가 생성될 때 하나의 커넥션을 점유하기 때문에 백엔드 프로세스가 많다는 건 사용중인 커넥션 수도 많다는 의미가 된다.
- 현재 사용중인 커넥션 수 유추할 수 있는 쿼리는 아래와 같다.
-- 현재 사용중인 컨넥션 수
select count(*) - (select count(*) from pg_stat_activity where backend_type = 'client backend') as active_connections
from pg_stat_activity;
-- 사용가능한 커넥션 수
select (select setting::int from pg_settings where name = 'max_connections') -
(select count(*) - (select count(*) from pg_stat_activity where backend_type = 'client backend') as active_connections
from pg_stat_activity) as remaining_connections;
- 백엔드 프로세스는
backend_type이 client backend
이기 때문에 위와 같이 현재 동작중인 백엔드 프로세스의 수를 확인할 수 있다. - 만약 max_connections값 보다 더 많은 백엔드 프로세스를 생성하려고 하면 아래와 같은 오류 메시지를 뱉는다.
- 만약, 실무에서 DataGrip으로 개발한다고 가정했을 때, 위와 같이 무작정 여러개의 세션을 생성하면 백엔드 프로세스가 과도하게 생성될 수 있으니 주의하자.
- 백엔드 프로세스는 쿼리를 수행하기 위해서 몇 가지 메모리 구조가 필요한데 이것을 통칭해서
로컬 메모리
라고 한다. - 백엔드 프로세스 안에는 로컬 메모리가 존재한다. 로컬 메모리의 구성요소는 아래와 같다.
Work Memory
- 정렬 작업, Bitmap 작업, 해시 조인과 Merge 조인 작업 시에 사용되는 공간이다.
- 기본 설정은 4MiB이다.
- postgresql.conf의
work_mem
값으로 설정 가능하다.
Maintenance Work Memory
- 데이터베이스 유지 관리 작업(vacuum, 인덱스 생성, 테이블 변경, 외래키 추가)에 사용되는 메모리 공간이다.
- 기본 설정은 64 MiB이다.
- postgresql.conf의
maintenance_work_mem
값으로 설정 가능하다.
Temp Buffers
- Temporary 테이블을 저장하기 위한 공간이다.
- 기본 설정값은 8 MiB이다.
- postgresql.conf의
temp_buffers
값으로 설정 가능하다.
- Catalog Cache
- 데이터베이스의 카탈로그 정보를 캐싱하는 데 사용된다.
- 조회하는 빈도가 높기 때문에 디스크에서 읽을 경우 성능 저하가 발생할 수 있어 로컬 메모리 영역을 사용된다.
- Optimizer & Executor
- 쿼리를 수행할 최적 플랜을 찾는 과정에서 사용하는 메모리 영역이다.
PostgreSQL의 아키텍처를 제대로 이해하기 위해서는 'Vacuum' 프로세스에 대해서 알아야 한다.
Vacuum
- 진공청소기라는 뜻을 가진 Vaccum은 PostgreSQL에만 존재하는 특별한 개념이다.
- PostgreSQL에서
MVCC
의 구현 방법상의 특징상공간 비효율
이 발생하기 때문에 Vacuum은 필수적으로 필요하다. - Vacuum의 역할은 크게 아래 4가지이다.
테이블 및 인덱스 Dead 블록 제거를 통한 디스크 공간 확보
- PostgreSQL에서 행(또는 투플)이 삭제되거나 업데이트될 때, 그 행은 즉시 제거되지 않고 'dead' 상태가 된다.
- VACUUM은 이러한 dead 투플을 정리하여 FSM (Free Space Map)으로 반환 공간을 반환한다.
Transaction ID Wraparound 방지를 위한 레코드별 XID Frozen
- PostgreSQL은 4바이트 정수를 사용하여 트랜잭션 ID를 관리한다.
- 이 정수가 가득 차면, 'transaction ID wraparound' 문제가 발생할 수 있다.
- VACUUM은 오래된 트랜잭션 ID를 정리하여 이러한 문제를 방지한다.
테이블 및 인덱스 통계 정보 갱신
- VACUUM은 테이블과 인덱스의 사용 패턴에 대한 통계를 갱신합니다. 이 정보는 쿼리 플래너가 더 효율적인 실행 계획을 세우는 데 사용한다.
Visibility Map(VM)을 갱신
- Visibility Map은 각 데이터 페이지에 대한 "visibility" 상태를 추적한다.
- 여기서 "visible"이란 특정 페이지가 모든 활성 트랜잭션에게 보이는 상태, 즉 더 이상 변경되지 않을 것이 확정된 상태를 의미한다.
- VACUUM은 이 맵을 갱신하여, 인덱스 스캔이 필요한 페이지만 읽도록 함으로써 성능을 향상시킨다.
References
- PostgreSQL 9.6 성능이야기, 저자 김시연, 최두원
- https://www.instaclustr.com/blog/postgresql-architecture/
- https://oss.tibero.com/d391cda9-ec4c-4f3c-a730-1149e7b7e4fd
'Database' 카테고리의 다른 글
PostgreSQL의 메모리 레이아웃과 데이터를 읽어오는 과정 (0) | 2023.12.19 |
---|---|
PostgreSQL Logical Structure 이해하기 (0) | 2023.12.11 |
서브쿼리 변환하기 2편 (With Java, QueryDSL) (0) | 2023.11.26 |
서브쿼리 변환하기 1편 (이론) (0) | 2023.11.26 |
PostgreSQL 실행계획 분석하기 6편 (서브쿼리) (0) | 2023.11.25 |