본문으로 건너뛰기

"암호화" 태그로 연결된 2개 게시물개의 게시물이 있습니다.

모든 태그 보기

PostgreSQL 코어의 빈칸

· 약 10분

들어가며

PostgreSQL은 매년 약 200개의 기능과 변경을 더하지만, "이건 당연히 되겠지" 싶은 큰 기능 몇 개는 30년째 코어에 비어 있어요. sharding, connection pooling, 내장 암호화(TDE)처럼 상용 DB라면 체크리스트에 들어가는 항목들인데, PostgreSQL을 쓰다 보면 어느 순간 "여기까지 다 되는데 왜 이건 안 되지" 하는 벽을 만나요.

Bruce Momjian(브루스 모미잔)은 2026년 발표 《What's Missing in Postgres?》에서 이 빈칸들을 정면으로 다뤘습니다. 그가 발표 슬라이드를 쓰면서 깨달은 한 가지가 있습니다. 빠진 기능의 대다수는 기능(functionality)이 없어서가 아니라 성능(performance)을 위한 것이라는 점입니다. 즉 PostgreSQL은 "못 하는" 쪽이라기보다, "더 빨리 하기 위한 장치"가 아직 코어에 없는 쪽에 가깝습니다.

이 글은 그 빈칸을 항목별로 짚습니다. 각 항목마다 무엇이 없는지, 왜 코어에 없는지, 지금은 무엇으로 메우는지, 코어 편입 전망은 어떤지 차례로 봅니다. Momjian의 발표 분류를 따라 단일 호스트(single host) 성능 항목과 다중 호스트(multi-host) 항목으로 나눕니다.

한 장으로 보는 빈칸 지도

먼저 전체 그림을 표로 깔아둡니다.

빈칸분류지금 메우는 도구코어 편입 전망
내장 암호화 (TDE)단일 호스트pg_tde, EDB(상용)논의 단계
내장 connection pooler단일 호스트PgBouncer, Pgpool-II, Supavisor패치 제안 반복
optimizer hints단일 호스트pg_hint_planPostgreSQL 19 1차 도입
columnar storage단일 호스트Citus columnar, Hydra 등미정
global index단일 호스트없음(수동 우회)미정
direct I/O단일 호스트(PostgreSQL 18 AIO 기반)진행 중
server-side threading단일 호스트없음(프로세스 모델)장기 과제
64-bit transaction ID단일 호스트Postgres Pro(fork)장기 논의
sharding다중 호스트Citus, Multigres미정
multi-master replication다중 호스트pgEdge/Spock, BDR(상용)미정
Oracle RAC 동급다중 호스트없음사실상 없음
DDL의 logical replication다중 호스트일부 extension부분 진행

표를 위에서 아래로 훑으면 패턴이 보입니다. 빈칸 대부분에 "지금 메우는 도구"가 이미 하나씩 있다는 점입니다. PostgreSQL은 코어를 작게 유지하고 extension/외부 프로세스로 확장하는 철학을 30년간 지켜 왔고, 빈칸은 그 철학의 그림자이기도 합니다.

코어와 빈칸의 경계

PostgreSQL이 무엇을 코어에 두고 무엇을 바깥에 두는지, 영역을 그림으로 나눠 봅니다.

코어는 쿼리 처리, MVCC, streaming replication, declarative partition까지를 책임집니다. 그 위로 sharding, 암호화, hint 같은 빈칸은 extension이 메우고, connection pooling은 아예 별도 프로세스가 연결 앞단에서 받습니다. 이제 각 빈칸을 풀어씁니다.

빈칸 1. 내장 암호화

첫 번째 빈칸은 TDE입니다.

무엇이 없나

데이터 파일을 디스크에 암호화해 저장하는 Transparent Data Encryption(TDE)이 코어에 없습니다. 디스크나 백업 미디어를 통째로 탈취당해도 키 없이는 못 읽게 막는 data-at-rest 보호인데, Oracle/SQL Server는 오래전부터 내장한 기능입니다.

왜 코어에 없나

암호화 자체는 어렵지 않습니다. 어려운 건 키 관리와 성능, 그리고 WAL, 임시 파일, 통계까지 빠짐없이 덮는 일관성입니다. 코어에 넣으려면 KMS 연동 모델까지 표준화해야 하는데, 이 합의가 더딥니다. Momjian은 cluster file encryption을 단일 호스트 항목으로 분류하면서, 진행은 있되 코어 합의에는 이르지 못한 상태로 짚습니다.

지금은 무엇으로 메우나

Percona의 pg_tde extension이 2025년에 production 궤도에 올랐습니다. 2025년에 WAL 암호화가 GA에 도달했고, 2025년 11월에는 pg_tde 2.1이 릴리스되며 PostgreSQL 18.1과 asynchronous I/O까지 지원합니다. HashiCorp, Thales, Fortanix, OpenBao 같은 KMS 연동도 붙었습니다. pg_tde는 구독 뒤에 숨기지 않은 오픈소스라는 점을 내세웁니다. 한편 EDB도 TDE를 제공하지만, 이쪽은 EDB Postgres Advanced Server/Extended Server의 라이선스 제품에서만 쓸 수 있습니다.

pg_tde를 쓸 때 백업 도구와의 궁합은 따로 검증이 필요합니다. 이 주제는 암호화된 PostgreSQL은 pgBackRest로 백업될까 글에서 한 편 다뤘습니다.

코어 편입 전망

당장은 어렵습니다. pg_tde가 사실상의 오픈소스 표준 자리를 먼저 굳히는 중이고, 코어 편입은 그 다음 논의가 될 가능성이 큽니다.

빈칸 2. 내장 connection pooler

무엇이 없나

PostgreSQL은 17까지도 내장 connection pooler가 없습니다. PostgreSQL은 연결 하나당 프로세스 하나(process-per-connection) 모델이라, 연결 수가 늘면 메모리와 context switch 비용이 가파르게 오릅니다. 수천 개의 짧은 연결을 받는 웹 백엔드에서 특히 아픕니다.

왜 코어에 없나

프로세스 모델 자체가 발목을 잡습니다. 내장 pooler를 제대로 넣으려면 연결 처리 구조를 손봐야 하고, 이는 server-side threading 같은 더 깊은 과제와 얽힙니다. 패치 제안은 여러 번 올라왔지만 코어 합의까지 가지 못했습니다.

지금은 무엇으로 메우나

외부 프로세스가 연결 앞단에서 받습니다. 가장 널리 쓰이는 건 PgBouncer로, transaction/session 단위 풀링을 지원하는 경량 도구이자 사실상의 표준입니다. Pgpool-II는 풀링에 더해 load balancing/query routing까지 묶은 무거운 도구고, Supavisor는 Supabase가 Elixir로 만든 멀티테넌트 pooler로 클라우드 규모를 노립니다.

클라우드 사업자들은 이걸 관리형으로 흡수했습니다. Azure Database for PostgreSQL은 PgBouncer를 서버 단위 옵션으로 켤 수 있게 내장했습니다.

코어 편입 전망

논의는 살아 있습니다. 다만 process-per-connection 구조와 threading 과제가 함께 풀려야 본격적인 내장 pooler가 가능해집니다. 단기간에 PgBouncer를 대체할 그림은 아닙니다.

빈칸 3. optimizer hints

무엇이 없나

쿼리 planner에게 "이 인덱스를 써라", "이 조인 순서로 가라" 식으로 강제하는 optimizer hint가 코어에 없었습니다. Oracle 사용자가 PostgreSQL로 옮길 때 가장 먼저 당황하는 지점 중 하나입니다.

왜 코어에 없나

PostgreSQL 커뮤니티는 hint를 의도적으로 거부해 왔습니다. planner가 통계로 최적해를 찾게 두는 편이 장기적으로 낫고, hint는 잘못된 플랜을 영구히 고착시키는 부채가 된다는 철학입니다. "hint가 필요하면 그건 planner나 통계를 고칠 신호"라는 입장이 오래 유지됐습니다.

지금은 무엇으로 메우나

pg_hint_plan extension이 그 자리를 메워 왔습니다. 주석 형태로 hint를 심어 planner 동작을 강제합니다.

코어 편입 전망

여기서 흐름이 바뀌었습니다. optimizer hint의 1차 형태가 PostgreSQL 19에 들어옵니다. 오래 거부하던 기능이 코어에 발을 들이는 사례라, 빈칸 목록에서 가장 먼저 지워질 항목입니다.

빈칸 4. 단일 호스트의 나머지 성능 항목

Momjian이 단일 호스트 묶음으로 짚은 나머지를 한 번에 정리합니다. 이들의 공통점은 기능 자체의 결핍이라기보다 더 빠르게 하기 위한 장치라는 점입니다.

columnar storage는 분석 워크로드용 열 지향 저장인데, 코어에 없고 Citus의 columnar나 Hydra 같은 extension이 메웁니다. 코어 작업은 아직 널리 알려진 움직임이 없습니다. global index는 partition 테이블 전체를 가로지르는 인덱스로, 여러 partition에 걸친 unique 보장을 한 인덱스로 처리하려는 것인데 지금은 코어에 없어 수동 우회에 의존합니다. direct I/O는 OS 페이지 캐시를 우회하는 I/O로, PostgreSQL 18이 asynchronous I/O(AIO) 서브시스템을 들이며 기반이 깔렸고 그 위에서 진행 중입니다. server-side threading은 프로세스 모델을 thread 모델로 바꾸는 장기 과제라 connection pooler 빈칸과 뿌리가 같습니다.

64-bit transaction ID는 조금 더 설명이 필요합니다. 32-bit XID는 wraparound 위험을 안고 삽니다. PostgreSQL은 epoch을 포함한 xid8 타입을 이미 갖췄지만, 내부 XID를 통째로 64-bit로 넓히는 작업은 on-disk 호환성 때문에 코어에 못 들어왔습니다. Postgres Pro fork는 내부 64-bit XID를 상용으로 돌리고 있어, 가능은 하되 코어 편입의 벽이 높다는 걸 보여줍니다.

빈칸 5. sharding

무엇이 없나

데이터를 여러 노드에 수평 분산하는 sharding이 코어에 없습니다. 단일 서버 용량을 넘어서는 순간 부딪히는 벽입니다. MySQL 진영은 Vitess라는 검증된 sharding 시스템을 오래 가졌지만, PostgreSQL은 비교 대상이 없었습니다.

왜 코어에 없나

sharding은 distributed transaction, 분산 plan, 노드 간 일관성까지 묶인 거대한 과제입니다. PostgreSQL은 declarative partition으로 단일 노드 안의 분할까지는 코어에 들였지만, 노드를 가로지르는 분산은 extension/미들웨어의 몫으로 남겨 뒀습니다.

지금은 무엇으로 메우나

Citus는 분산 PostgreSQL extension입니다. Microsoft가 인수해 Azure로 들어갔고, Azure의 Elastic Clusters가 이 오픈소스 기술 위에서 row/schema 단위 sharding을 제공합니다. Citus 14는 PostgreSQL 18을 지원합니다. Multigres는 2025년 6월 Supabase가 Vitess 공동 창시자 Sugu(수구)를 영입해 시작한 "PostgreSQL용 Vitess"입니다. PostgreSQL 앞단에 놓이는 proxy로 표준 PostgreSQL 호환을 최우선에 두며, Vitess와 같은 Apache 2.0 라이선스 오픈소스입니다.

Multigres는 분량이 커서 PostgreSQL에도 Vitess가 온다 글에서 따로 다뤘습니다.

코어 편입 전망

가까운 시일에는 어렵습니다. 코어가 분산 트랜잭션까지 흡수하기보다, Citus/Multigres 같은 미들웨어가 각자 자리를 잡는 그림이 현실적입니다.

빈칸 6. 다중 호스트의 나머지 항목

다중 호스트 묶음의 나머지를 정리합니다. multi-master replication은 여러 노드가 동시에 write를 받는 구성인데, 코어의 replication은 단일 primary 기준입니다. pgEdge의 Spock이 multi-master logical replication을 제공해 지리적으로 분산된 배치에 쓰이고, EDB의 BDR이 상용으로 그 자리를 채웁니다. Oracle RAC 동급은 공유 스토리지 위에서 여러 인스턴스가 같은 DB를 동시에 여는 RAC 모델을 말하는데, PostgreSQL에는 이에 직접 대응하는 코어 기능도, 널리 쓰이는 대체재도 사실상 없습니다. 빈칸 중 가장 비어 있는 자리입니다. DDL의 logical replication은 결이 조금 다릅니다. logical replication이 DML은 나르지만 CREATE TABLE 같은 DDL은 자동으로 나르지 못하는데, PostgreSQL 19에서 sequence 복제 같은 주변부가 채워지며 부분적으로 전진하고 있습니다.

왜 비어 있는가

빈칸들을 한 발 떨어져 보면 공통된 이유가 보입니다. 첫째는 철학입니다. 코어를 작게 두고 extension/외부 프로세스로 확장하는 노선인데, optimizer hint를 오래 거부한 것이 대표적입니다. 둘째는 구조입니다. process-per-connection 모델이 connection pooler와 threading을 동시에 막고, on-disk 포맷 호환성이 64-bit XID를 막습니다. 셋째는 합의 비용입니다. TDE의 키 관리나 sharding의 분산 트랜잭션처럼 표준화 합의가 비싼 과제는 코어 진입이 더딥니다.

그리고 Momjian의 결론처럼, 이 빈칸들은 대부분 "PostgreSQL이 못 하는 일"이라기보다 "더 빠르게/더 크게 하기 위한 장치"에 가깝습니다. 기능의 결핍보다는 성능과 규모의 천장에 걸리는 문제라, 대부분의 빈칸 옆에는 이미 그 천장을 뚫는 도구가 하나씩 서 있습니다.

닫으며

PostgreSQL의 빈칸 목록은 약점 목록이라기보다 지도에 가깝습니다. 어디까지가 코어이고 어디부터 extension/미들웨어의 영역인지, 그리고 다음 5년 동안 어느 칸이 먼저 채워질지를 보여줍니다. optimizer hint가 PostgreSQL 19에서 코어로 들어오는 것처럼, 빈칸은 고정된 게 아니라 천천히 메워집니다.

DBA 입장에서 실무적으로 남는 건 단순해요. 벽에 부딪히기 전에 어느 칸이 비어 있는지 미리 알아 두면 돼요. sharding이 필요하면 Citus나 Multigres를 일찍 검토하고, 연결 폭증이 보이면 PgBouncer를 처음부터 설계에 넣고, 규제 요건이 있으면 pg_tde를 미리 검증하면 돼요. 빈칸은 막다른 길이라기보다 무엇을 곁들여야 하는지 알려주는 표지판에 가깝습니다.

1차 출처

Momjian 발표:

암호화(TDE):

connection pooling:

optimizer hints:

sharding / multi-host:

64-bit XID:

pgBackRest와 pg_tde 백업

· 약 5분

데이터를 디스크에 암호화해서 저장하는 클러스터를 백업 도구가 제대로 다룰 수 있을까요? 결론부터 말하면, pg_tde로 암호화한 PostgreSQL 클러스터를 pgBackRest로 백업하고 복구하는 과정은 거의 대부분 투명하게 작동하지만, 암호화된 데이터를 백업 도구가 해석할 수 없다는 본질적 한계 때문에 몇 가지 검증/압축 옵션은 꺼야 해요.

Data Egret의 Stefan Fercot(스테판 페르코)가 pgBackRest and pg_tde 글에서 이 조합을 직접 검증했습니다. 이 블로그에서 다뤄 온 백업 시리즈의 곁가지로, 백업 도구와 암호화 extension의 궁합을 운영 관점에서 정리해 봐요.

pg_tde가 암호화하는 것

pg_tde는 Percona가 개발 중인 투명 데이터 암호화(Transparent Data Encryption) extension입니다. 이름 그대로 애플리케이션은 암호화 여부를 신경 쓰지 않고, 디스크에 저장되는 데이터(data at rest)만 암호화합니다. 암호화 대상은 크게 두 가지입니다.

  • 테이블 데이터(heap): tde_heap 접근 방식(access method)으로 만든 테이블. 기존 테이블은 ALTER TABLE ... SET ACCESS METHOD tde_heap으로 전환합니다. heap 테이블뿐 아니라 거기 딸린 index, TOAST, sequence까지 암호화됩니다.
  • WAL: pg_tde.wal_encrypt GUC를 켜면 WAL 세그먼트도 암호화됩니다. 이 설정은 서버 전역이라 재시작이 필요하고, 켠 시점 이후의 WAL write부터 암호화가 적용됩니다.

다만 모든 게 암호화되지는 않습니다. 시스템 카탈로그와 통계 데이터 같은 메타데이터는 아직 암호화 대상이 아닙니다. 이름 그대로 "데이터" 암호화에 초점이 맞춰져 있습니다.

키 구조는 2계층입니다. 실제 데이터를 암호화하는 internal key는 $PGDATA/pg_tde 아래에 로컬로 저장되고, 이 internal key를 다시 암호화하는 principal key는 외부 KMS에 둡니다. principal key는 데이터베이스당 하나입니다. 검증에서는 KMS로 OpenBao를 Docker로 띄워 썼고, root 자격증명이 아니라 secret/ 경로에만 읽기/쓰기 권한을 준 최소 권한 토큰을 발급해 사용했습니다.

pgBackRest가 암호화된 클러스터를 다루는 흐름

pgBackRest 입장에서 보면, pg_tde가 이미 암호화해 놓은 데이터 파일과 WAL을 그대로 받아 저장소로 옮깁니다. 백업 도구는 그 안을 들여다보지 않습니다. 암호화는 PostgreSQL 쪽에서 끝나 있고, pgBackRest는 암호화된 바이트 덩어리를 운반하는 역할입니다.

중요한 지점은 복구 이후입니다. pgBackRest가 복구해 놓은 PGDATA는 여전히 암호화된 상태입니다. 이걸 PostgreSQL이 다시 읽으려면 internal key를 풀 principal key가 필요하고, principal key는 외부 KMS에 있습니다. 즉 백업 파일만 들고 있어서는 복구한 클러스터를 기동할 수 없고, KMS 접근이 함께 살아 있어야 합니다.

검증 결과와 주의점

Fercot는 pgbench로 부하를 주면서 full 백업과 incremental 백업을 뜨고, pgbench_tellers에서 1,000건을 삭제한 뒤 named restore point로 시점 복구(PITR)를 실행해 삭제된 1,000건이 되살아나는 것까지 확인했습니다. WAL 세그먼트와 데이터 파일은 백업 저장소 안에서도 암호화된 채 유지됐고, 복구 과정에서 별도의 복호화 래퍼 없이 named restore point에 깔끔하게 도달했습니다.

다만 암호화 특성 때문에 꺼야 하는 설정들이 있습니다. 정리하면 다음과 같습니다.

설정이유
archive-header-checkn암호화된 WAL 헤더를 pgBackRest가 해석하지 못합니다.
checksum-pagen암호화된 page의 checksum을 검증할 수 없습니다.
compress-typenone암호화된 데이터는 무작위에 가까워 압축 이득이 거의 없습니다.
repo1-blocknblock 단위 incremental의 효율이 암호화로 무력화됩니다.

표의 항목들은 모두 같은 원인에서 나옵니다. pgBackRest는 암호화된 내용을 들여다볼 수 없으므로, 데이터 내부를 읽어야 성립하는 검증과 최적화가 작동하지 않습니다. 그래서 page checksum 검증과 WAL 헤더 검증을 끄고, 압축도 끄는 편이 낫습니다. 압축을 켜 두면 줄어들지도 않을 데이터를 압축하느라 CPU만 씁니다.

한 가지 더, incremental 백업 크기가 예상보다 컸습니다. block 단위 incremental은 변경된 블록만 골라 담아 용량을 아끼는데, 암호화된 데이터에서는 이 절감 효과가 크게 줄어듭니다.

WAL 암호화 관련: 과거 Percona 문서는 암호화 환경에서 비동기 아카이빙(archive-async=y)을 권하지 않았습니다. 그러나 이번 검증에서는 비동기 아카이빙이 문제없이 작동했습니다. 운영에서 적용하기 전에는 사용하는 pg_tde 버전 기준으로 한 번 더 확인하는 편이 안전합니다.

키 관리 관점: 백업과 복구가 투명하게 돈다고 해서 백업만으로 복구가 끝나는 것은 아닙니다. 복구된 PGDATA는 암호화 상태 그대로이므로, principal key를 보관한 KMS에 접근하지 못하면 클러스터를 기동할 수 없습니다. 백업 파일과 KMS 접근 권한을 함께 보존하고, KMS 자체의 가용성과 백업도 별도로 챙겨야 합니다. 백업 저장소 한쪽만 살아남는 시나리오도 복구 절차에 반드시 포함해 두는 편이 좋습니다.

저장소 암호화 중복: pgBackRest 자체에도 저장소 암호화 옵션(repo1-cipher-type)이 있습니다. pg_tde가 이미 데이터를 암호화한 뒤 넘겨주므로 저장소 암호화는 선택 사항입니다. 다만 시스템 카탈로그처럼 pg_tde가 암호화하지 않는 영역이 백업에 포함되는 점을 고려하면, 저장소 암호화를 한 겹 더 두는 선택도 합리적입니다.

검증에는 Percona Server for PostgreSQL이 쓰였습니다. pg_tde가 특정 패치에 의존하기 때문에 표준 PostgreSQL이 아닌 Percona 배포판이 필요하고, pgBackRest가 버전을 제대로 인식하도록 pg-version-force로 버전을 강제해야 했습니다.

운영 관점과 백업 시리즈 연결

백업 도구를 고를 때는 도구 자체의 기능만 보기 쉽지만, 실제 운영에서는 암호화, 복제, 버전 같은 주변 환경과의 궁합이 더 자주 발목을 잡습니다. 이번 검증의 의미는 "pg_tde를 켜도 pgBackRest를 그대로 쓸 수 있다"는 점을 확인했다는 데 있습니다. 암호화 때문에 백업 도구를 새로 고민할 필요는 없고, 검증/압축 옵션 몇 개를 끄는 선에서 정리됩니다.

대신 복구 절차의 무게중심이 옮겨 갑니다. 평소처럼 백업 무결성만 점검하는 데서 그치지 말고, principal key를 가진 KMS까지 포함한 복구 리허설을 정기적으로 돌려야 합니다. 암호화를 도입하는 순간, 백업 전략은 "데이터 백업"에서 "데이터 백업 + 키 관리"로 확장됩니다.

pgBackRest를 운영에 들이는 과정 자체가 처음이라면, 이 블로그의 pgBackRest 도입기에서 기본 stanza 구성과 아카이빙 흐름을 먼저 잡고 오는 편이 좋아요. 암호화 궁합은 그 위에 얹는 한 겹이에요.

참고