(KO) PostgreSQL 시스템 카탈로그와 캐시 — 섹션 개요
목차
이 섹션이 다루는 것
섹션 제목: “이 섹션이 다루는 것”PostgreSQL은 카탈로그 주도 엔진이다. 타입, 연산자, 함수, 접근 방법(access method), 인덱스 전략, 네임스페이스, 그리고 릴레이션 자체가 하드코딩된 값이 아니라 pg_* 시스템 테이블의 행이다(pg_class, pg_attribute, pg_type, pg_proc, pg_am, pg_index 등). 이 한 가지 사실이 엔진의 런타임 확장성을 가능하게 하는 근거다(postgres-architecture-overview.md의 축 7). 새로운 타입이나 연산자를 PostgreSQL에 가르치는 일은 INSERT이지 재컴파일이 아니다.
이 서브카테고리가 존재하는 이유도 거기 있다. 모든 플랜, 모든 표현식, 모든 튜플 디폼이 카탈로그를 힙에서 직접 읽어야 한다면 카탈로그가 병목이 된다. 그래서 PostgreSQL은 카탈로그 앞에 세 단계의 캐시를 두고, 그 캐시들이 백엔드 간에 일관성을 유지하도록 공유 무효화 루프를 운영한다. 이 섹션이 다루는 범위는 정확히 그 스택이다. 거기에 캐시 계층 위에 놓인 두 가지 서비스도 포함된다. 카탈로그를 캐시하는 것이 아니라 읽는 서비스들이다.
- 카탈로그 레이아웃 자체 —
pg_*테이블이 무엇인지, 행이 어떻게 생기는지, 그리고 서버가 SQL을 읽기 전에 카탈로그를 채우는 부트스트랩 경로(pg_*.dat→.bki→initdb)가 어떻게 동작하는지. →postgres-system-catalogs.md - relcache — 백엔드별
Relation디스크립터. 각 항목은 여러 카탈로그(pg_class+pg_attribute+pg_index+ AM 정보 + …)를 조합해 만들고, 테이블 접근 때마다 읽히기 때문에 캐시된다. →postgres-relcache.md - catcache와 syscache — catcache는 개별 카탈로그 튜플을 조회 키로 캐시한다. syscache는 각 캐시에 이름을 부여하는 타입별 디스패치 테이블(
TYPEOID,PROCOID등)이고, lsyscache는 그 위에서 자주 쓰는 조회를 감싸는 편의 접근자다. →postgres-catcache-syscache.md - 캐시 무효화(sinval) — 캐시를 공유 메모리 기반과 연결하는 루프. 카탈로그 변경이 메시지를 큐에 넣고, 모든 백엔드가 그것을 소비해 무효화한다. →
postgres-cache-invalidation.md - 의존성 추적 —
pg_depend/pg_shdepend, 객체 참조 그래프.DROP ... CASCADE,RESTRICT, pg_dump 정렬 순서를 가능하게 한다. →postgres-dependency-tracking.md - 네임스페이스 / search_path — 스키마 해석. 비정규화된 이름
foo를 활성search_path를 사용해 특정 카탈로그 OID로 바꾸는 과정. →postgres-namespace-search-path.md
이 섹션이 다루지 않는 범위:
- 공유 메모리 기반이 아니다.
sinval큐는 고정 공유 메모리 세그먼트 안에 살고 그 구조체 중 하나지만, 세그먼트 자체,PGPROC,procsignal, 무효화 신호를 전달하는 IPC 프리미티브는 server-architecture(postgres-shared-memory-ipc.md)에 속한다. 이 섹션은 캐시 정합성 프로토콜을 소유하고, 전송 계층은 아래로 위임한다. - DDL 실행이 아니다.
CREATE/ALTER/DROP문이 카탈로그를 변경하고 무효화 메시지를 발행하지만, 명령 처리 기계(tcop/utility.c,commands/tablecmds.c,commands/indexcmds.c)는 ddl-schema(postgres-ddl-execution.md,postgres-alter-table.md)에 속한다. 이 섹션은 카탈로그를 자료 구조이자 캐시로 소유하고, ddl-schema는 그 기록자를 소유한다. 의존성 추적이 접합부다. 이 섹션은pg_depend그래프를 설명하고, ddl-schema가 그 위에서performDeletion을 호출한다. - 트랜잭션 힙으로서의 카탈로그가 아니다. 카탈로그 테이블은 평범한 MVCC 힙 릴레이션이다. 가시성, WAL, vacuum 규칙이 사용자 테이블과 동일하게 적용된다. 그 메커니즘은 txn-recovery와 storage-engine(
postgres-mvcc-snapshots.md,postgres-heap-am.md)에 속하고, 이 섹션은 그 바닥 위에 캐시 계층을 쌓는다. - 부트스트랩 도구가 아니다. 이 섹션은 카탈로그 행이
pg_*.dat에서genbki.pl과initdb를 거쳐 온다는 사실을 설명하지만, 코드 생성 파이프라인과initdb바이너리 자체는 utilities(postgres-initdb-bootstrap-genbki.md)에 속한다.
계층 구조
섹션 제목: “계층 구조”카탈로그가 바닥이고, 캐시가 그 위에 쌓이고, sinval이 기록자의 커밋에서 모든 독자의 캐시로 루프를 닫는다. 의존성 추적과 네임스페이스 해석은 캐시 옆에 자리 잡은 서비스로, 같은 카탈로그 행을 읽는다.
flowchart TB
subgraph BOOT["부트스트랩 (initdb, 최초 1회) — utilities로 위임"]
DAT["pg_*.dat / .bki<br/>genbki.pl 코드 생성"]
end
subgraph CAT["카탈로그 (MVCC 힙 위의 pg_* 테이블)"]
direction LR
SYSCAT["postgres-system-catalogs.md<br/>pg_class, pg_attribute, pg_type,<br/>pg_proc, pg_am, pg_index, ..."]
end
DAT -. "initdb 시점에 채움" .-> SYSCAT
subgraph CACHES["백엔드별 캐시 (사적 사본)"]
direction LR
RELC["postgres-relcache.md<br/>릴레이션 디스크립터<br/>(여러 카탈로그를 조합)"]
CATC["postgres-catcache-syscache.md<br/>catcache 튜플 + syscache 디스패치<br/>+ lsyscache 접근자"]
end
SYSCAT --> RELC
SYSCAT --> CATC
subgraph SVC["카탈로그 위의 서비스"]
direction LR
DEP["postgres-dependency-tracking.md<br/>pg_depend / pg_shdepend 그래프<br/>(DROP CASCADE, pg_dump 순서)"]
NS["postgres-namespace-search-path.md<br/>search_path → OID 해석"]
end
SYSCAT --> DEP
SYSCAT --> NS
EXEC["실행기 / 플래너 / 튜플 디폼<br/>(query-processing)"]
RELC --> EXEC
CATC --> EXEC
NS --> EXEC
subgraph LOOP["정합성 루프"]
DDL["DDL / 카탈로그 변경<br/>(ddl-schema 기록자)"]
INVAL["postgres-cache-invalidation.md<br/>CacheInvalidate* → 메시지 등록"]
SINV["sinval 큐<br/>(공유 메모리 — server-architecture)"]
end
DDL --> SYSCAT
DDL --> INVAL
INVAL --> SINV
SINV -. "모든 백엔드가 AcceptInvalidationMessages에서 소비" .-> RELC
SINV -.-> CATC
다이어그램에서 세 가지를 읽어 두면 된다.
- 캐시는 사적이고, 카탈로그는 공유다. 각 백엔드의 relcache와 catcache는 그 백엔드만의 메모리 컨텍스트 안에 있다. 이 서브카테고리에서 공유 상태는
sinval메시지 큐 하나뿐이다. 바로 그래서 무효화는 공유 캐시 퇴거 문제가 아니라 메시징 문제다. - 루프가 이 섹션의 척추다. 기록자(DDL 명령)가 카탈로그 행을 변경하는 동시에 무효화 메시지를 등록한다. 커밋 시점에 그 메시지들이 공유 큐로 들어가고, 다른 모든 백엔드는
AcceptInvalidationMessages에서 큐를 소비해 낡은 relcache / catcache 항목을 버린다.postgres-cache-invalidation.md가 이 루프를 처음부터 끝까지 담당한다. - 의존성 추적과 네임스페이스 해석은 읽기 위주 서비스다. 두 서비스 모두 카탈로그(
pg_depend,pg_namespace)를 참조하지만 캐시 스택의 일부가 아니다. 카탈로그 로직이기 때문에 여기 묶인 것이지, 질의나 스토리지 로직이어서가 아니다.
읽는 순서
섹션 제목: “읽는 순서”다른 문서들이 의존하는 문서를 먼저 읽는 교차 참조 우선 순서다.
postgres-system-catalogs.md— 여기서 시작한다. 이 섹션의 나머지는 모두 카탈로그 위의 캐시이거나 카탈로그를 읽는 서비스다.pg_class/pg_attribute/pg_type이 무엇을 담는지, OID가 어떻게 작동하는지, 행이 어떻게 부트스트랩되는지를 먼저 파악하고 들어온다.postgres-catcache-syscache.md— 더 단순한 캐시(개별 튜플)이고, relcache 자체가 이것에 의존한다. relcache보다 먼저 읽는다.postgres-relcache.md— 더 무거운 캐시(릴레이션 전체 디스크립터, 일부는 syscache 조회로 조합). 버퍼 매니저 다음으로 엔진 안에서 가장 많이 참조되는 문서다. 모든 테이블 접근이 이것을 건드리기 때문이다.postgres-cache-invalidation.md— 무엇이 캐시되는지(2~3번)를 알아야 의미 있다. 두 캐시를 정직하게 유지하는 정합성 루프가 여기 있으며, 전송 계층은postgres-shared-memory-ipc.md로 전달된다.postgres-dependency-tracking.md— 캐시 스택과 독립적이다. DDL 의미론(DROP CASCADE,pg_dump정렬 순서)을 볼 때 읽는다.postgres-namespace-search-path.md— 가장 얇은 문서. 이름 해석 프런트엔드다. 마지막에 읽거나, 질문이 순전히 “search_path가 어떤foo를 고르는가”라면 첫 번째로 읽는다.
세부 문서 요약
섹션 제목: “세부 문서 요약”아래는 전방 참조다. 모듈 문서들이 아직 존재하지 않을 수 있으며, 요약은 각 문서의 계획된 범위를 예측한 것이다.
| 모듈 문서 | 한 줄 범위 |
|---|---|
postgres-system-catalogs.md | 자료 구조로서의 pg_* 시스템 테이블 — pg_class, pg_attribute, pg_type, pg_proc, pg_am, pg_index 등; OID 할당, 공유 카탈로그 대 데이터베이스 로컬 카탈로그, 고정/매핑 릴레이션을 위한 relmapper, 행이 생성(heap_create_with_catalog, InsertPgClassTuple)되고 부트스트랩(pg_*.dat → .bki)되는 방식. |
postgres-relcache.md | 릴레이션 캐시 — 여러 카탈로그로 Relation 디스크립터를 구성하는 RelationBuildDesc, RelationIdGetRelation 핫 패스, “nailed” 시스템 카탈로그 항목, 빠른 백엔드 기동을 위한 init 파일, RelationClearRelation / RelationCacheInvalidate가 sinval에 응답하는 방식. |
postgres-catcache-syscache.md | 시스템 카탈로그 튜플 캐시 — catcache 해시 버킷과 네거티브 캐싱(SearchCatCache), syscache 타입별 디스패치 테이블(SearchSysCache1..4, TYPEOID / PROCOID 캐시 ID), 자주 쓰는 조회를 감싸는 lsyscache 편의 접근자. |
postgres-cache-invalidation.md | 공유 무효화 루프 — 메시지를 등록하는 CacheInvalidateHeapTuple / CacheInvalidateRelcache, CommandEndInvalidationMessages를 통한 트랜잭션별 버퍼링, sinvaladt.c 링(SIInsertDataEntries / SIGetDataEntries)을 통한 브로드캐스트, AcceptInvalidationMessages에서의 소비. |
postgres-dependency-tracking.md | 객체 의존성 그래프 — pg_depend(로컬) · pg_shdepend(공유/전역) 엣지, ObjectAddress 식별, recordDependencyOn, DROP ... CASCADE 대 RESTRICT와 pg_dump 순서를 구현하는 performDeletion / findDependentObjects 재귀. |
postgres-namespace-search-path.md | 스키마 해석 — 활성 search_path, recomputeNamespacePath, 정규화된 이름과 비정규화된 이름 조회(RangeVarGetRelid), 임시 스키마와 pg_catalog 우선순위, 해석된 목록이 필요한 호출자를 위한 fetch_search_path. |
인접 섹션
섹션 제목: “인접 섹션”- server-architecture (
postgres-overview-server-architecture.md) —sinval큐가 사는 공유 메모리 세그먼트, 그리고 백엔드를 깨워 큐를 소비하게 하는procsignal/ latch 기계를 소유한다. 이 섹션의 무효화 루프는 그 기반의 클라이언트다. 접합부는postgres-shared-memory-ipc.md다. - ddl-schema (
postgres-overview-ddl-schema.md) — 카탈로그의 기록자. 모든CREATE/ALTER/DROP이pg_*테이블을 변경하고 이 섹션의 루프가 운반하는 무효화 메시지를 발행한다.DROP ... CASCADE는 이 섹션이 기술하는 의존성 그래프를 따라 걷는다. ddl-schema는 명령을 소유하고, 이 섹션은 명령이 건드리는 자료 구조와 캐시를 소유한다. - txn-recovery (
postgres-overview-txn-recovery.md) — 카탈로그 테이블은 MVCC 힙 릴레이션이다. 읽기는 스냅샷 가시성을 따르고, 쓰기는 WAL에 기록되고, vacuum을 받는다. 카탈로그 접근은 질의 스냅샷과 구별되는 카탈로그 스냅샷(postgres-mvcc-snapshots.md)을 사용한다. 무효화 타이밍을 다룰 때 알아 두어야 할 접합부다. - query-processing (
postgres-overview-query-processing.md) — 이 섹션 캐시들의 주된 독자. 플래너는 relcache에서 통계와 릴레이션 형태를 가져오고, 표현식 평가는 syscache로 함수와 연산자를 해석하고, 분석 단계의 이름 해석은 여기 있는 네임스페이스 해석기를 사용한다. - utilities (
postgres-overview-utilities.md) — 이 섹션이 읽는 카탈로그를 채우는 부트스트랩 도구(genbki.pl,initdb)와 이 섹션의 의존성 그래프에 객체 정렬 순서를 의존하는pg_dump를 소유한다.