Bỏ qua đến nội dung
DevOps Lab

PostgreSQL — Kiến trúc 9 lớp

Từ client xuống đĩa vật lý — 9 lớp kiến trúc nội bộ (PG18) kèm deep-dive replication.

Database Internals

Kiến trúc nội bộ của một PostgreSQL instance

Từ kết nối client xuống tận đĩa vật lý — chín lớp, mỗi lớp một nhiệm vụ. Tài liệu này mô tả chi tiết từng lớp kèm sơ đồ riêng, đã cập nhật theo PostgreSQL 18 và bối cảnh deploy trên CloudNativePG.

PostgreSQL 18 · cumulative statistics trong shared memory

Sơ đồ tổng quan — 9 lớp

toàn cảnh trước khi đi vào chi tiết

Một request đi từ trên xuống: qua pooler, được postmaster fork thành backend, backend thực thi truy vấn, đọc/ghi qua shared memory, rồi xuống kernel và đĩa. Các tiến trình nền chạy song song để bảo trì. Bấm vào từng lớp bên dưới để xem chi tiết.

L1 · CLIENT APPLICATIONS Product Pods kubectl exec PgDog (RW/RO pooler) L2 · POSTMASTER Postmaster — listen :5432 · auth · fork() process-per-connection (COW) L3 · BACKEND PROCESSES Backend 1 Backend 2 Backend N L4 · BACKGROUND PROCESSES checkpointer · bgwriter autovacuum ×3 WAL writer/sender stats (shared mem) L5 · SHARED MEMORY Shared Buffers WAL Buffers CLOG/SLRU Lock tables ProcArray Buffer desc · free list L6 · LOCAL MEMORY work_mem (per op) maintenance_work_mem temp_buffers L7 · OS KERNEL (Linux) VFS / syscall Page cache (double buffer) FS cache I/O scheduler L8 · STORAGE HARDWARE (= EBS trên cloud) RAID controller / write cache Disk internal cache (SSD/NVMe) L9 · PHYSICAL STORAGE — $PGDATA (PVC / EBS) config (*.conf) base/ — data + index pg_wal/ (16MB seg) pg_xact/ ⚠ pg_replslot/ (giữ WAL) pg_stat/ pg_twophase · pgsql_tmp · pid PG18: KHÔNG còn pg_stat_tmp/ — stats nằm trong shared memory (L5) + REPLICATION (vượt ra ngoài instance) WAL Sender (primary)đọc pg_wal/ Standby physical (byte-for-byte) Standby logical (chọn lọc) Decoder→Slot→Sub wal_level=replica (physical) / logical · synchronous_commit → RPO
i
Sơ đồ này là toàn cảnh và có chuyển động: các gói màu chạy dọc theo đường đi của một request (client → pooler → backend → shared memory → kernel → đĩa), mũi tên spine và WAL stream nhấp nháy theo luồng. Mỗi lớp bên dưới phóng to một phần và giải thích chi tiết. (Tôn trọng cài đặt giảm chuyển động của hệ điều hành.)
L1

Client Applications

điểm vào — kết nối tới database

Ứng dụng (các pod Product Service dùng driver pgx) và dev/ops qua kubectl exec mở kết nối TCP cổng 5432. Trong môi trường có tải, chúng không nối thẳng vào Postgres mà đi qua PgDog ở chế độ transaction — pooler này ghép nhiều kết nối client thành ít kết nối thật, và route read/write về đúng node.

Product Pod 1 Product Pod 2 kubectl exec PgDog Service transaction mode · RW/RO Postmaster :5432 TCP/5432 multiplexed
i
PgDog ở transaction mode trả connection về pool sau mỗi transaction, nên dùng được rất ít kết nối thật cho hàng nghìn client — nhưng phải cẩn thận với session-level state (prepared statements, SET, advisory locks).
L2

Postmaster — daemon giám sát

tiến trình gốc, sinh ra mọi tiến trình con

Postmaster là tiến trình cha duy nhất: nó lắng nghe cổng 5432, xử lý xác thực (pg_hba.conf), ghi PID vào postmaster.pid, và fork một backend riêng cho mỗi kết nối. Đây là đặc trưng quan trọng nhất của PostgreSQL — mô hình process-per-connection, không phải thread.

Postmaster Process listen :5432 · auth · postmaster.pid giám sát mọi child fork() process, not thread copy-on-write → backend mỗi kết nối → background proc lúc khởi động
!
Vì mỗi kết nối = một tiến trình OS thật, max_connections cao (mặc định 100) tốn RAM và context-switch. Đây là lý do connection pooling gần như bắt buộc khi scale — đừng nâng max_connections lên hàng nghìn, hãy đặt pooler phía trước.
L3

Backend Processes

thực thi truy vấn cho từng client

Mỗi backend phục vụ một kết nối, tự mình chạy trọn vòng đời truy vấn: parse → plan → execute, và quản lý transaction của riêng nó. Backend là nơi tiêu thụ work_mem — và lưu ý: work_mem được cấp cho mỗi operation (mỗi sort, mỗi hash), nên một truy vấn phức tạp có thể dùng gấp nhiều lần con số đó.

Backend 1 parse · plan execute · txn Backend 2 parse · plan execute · txn Backend 3 parse · plan execute · txn Backend N max_connections=100 work_mem mỗi op Mỗi backend là một tiến trình độc lập — postmaster fork ra khi có kết nối mới
parse
Kiểm tra cú pháp SQL, tạo parse tree
plan
Planner chọn index scan vs seq scan dựa trên thống kê
execute
Executor (Volcano/pull-based) chạy plan, đọc/ghi page
L4

Background Processes

bảo trì hệ thống, chạy nền liên tục

Đây là các tiến trình postmaster fork lúc khởi động và chạy mãi. Chia thành các nhóm: writer (đẩy dữ liệu xuống đĩa), autovacuum (dọn dead tuple), replication (gửi WAL), và utility (log, archive). Từ PG15 trở đi, thống kê được giữ trong shared memory nên không còn stats collector process.

Writer processes Checkpointer timeout=5min · ghi checkpoint record vào WAL BG Writer delay=200ms WAL Writer fdatasync Autovacuum system Autovacuum Launcher naptime=1min · spawn theo ngưỡng Worker 1 Worker 2 Worker 3 max_workers=3 · mỗi worker xử lý MỘT bảng Utility / Replication (primary) Logger Archiver WAL Sender ×N max_wal_senders=10 · stream → standby Cumulative statistics (PG18) Stats trong SHARED MEMORY KHÔNG còn process / pg_stat_tmp PG15+ đã bỏ stats collector. Autovacuum đọc stats trực tiếp từ shared mem.
Sửa so với diagram gốc: diagram cũ vẽ Worker 1=VACUUM, Worker 2=ANALYZE, Worker 3=FREEZE. Sai. Mỗi worker xử lý một bảng và làm cả vacuum (gồm freeze khi tới ngưỡng) lẫn analyze. autovacuum_max_workers=3 nghĩa là 3 worker chạy song song trên các bảng khác nhau.
!
bgwriter chỉ flush page bẩn — không ghi WAL. Chỉ checkpointer mới ghi checkpoint record vào WAL.
checkpoint_timeout = 5min max_wal_size / min_wal_size bgwriter_delay = 200ms wal_writer_delay = 200ms autovacuum = on autovacuum_naptime = 1min autovacuum_max_workers = 3 max_wal_senders = 10
L5

Shared Memory

vùng nhớ chung — IPC giữa mọi tiến trình

Vì các tiến trình tách biệt nhau, chúng phối hợp qua một vùng shared memory chung. Trái tim là shared buffers (cache các page 8KB), bên cạnh là WAL buffers, cache trạng thái commit (CLOG/SLRU), ProcArray (snapshot cho MVCC), lock table, catalog cache, và metadata buffer. Từ PG15, hệ thống thống kê cũng nằm ở đây.

Shared Buffers shared_buffers 8KB pages · LRU prod: 25-40% RAM WAL Buffers wal_buffers=-1 WAL trước flush CLOG / SLRU cache cache của pg_xact trạng thái commit XID ProcArray backend đang chạy snapshot · MVCC Lock tables table/row/page locks Catalog / Rel cache metadata, layout Buffer descriptors pin · dirty flag · LRU Free buffer list bgwriter duy trì Statistics — cumulative stats system nằm tại đây từ PG15+ · thay cho stats collector process
Lưu ý PG18: box "CLOG/SLRU cache" là cache của pg_xact trong shared memory, không phải bản thân pg_xact trên đĩa. Và hộp Statistics màu lime là điểm mới — trước PG15 nó là một process riêng.
L6

Local Memory

bộ nhớ riêng của từng backend

Khác với shared memory, mỗi backend có vùng nhớ riêng không chia sẻ. Đây là nơi các tham số work_mem, maintenance_work_mem, temp_buffers sống. Hiểu đúng chỗ này cực kỳ quan trọng để ước lượng RAM tổng.

work_mem = 4MB sort/hash · ORDER BY / JOIN / GROUP BY · nhiều lần mỗi query maintenance_work_mem = 64MB VACUUM · CREATE INDEX · ALTER TABLE temp_buffers = 8MB bảng tạm · riêng từng session Per-backend caches catalog cache local · query plan · prepared statement
!
Công thức RAM thực tế: tổng ≈ shared_buffers + (work_mem × số operation đồng thời trên tất cả backend). Vì work_mem cấp per-operation chứ không per-connection, đặt nó quá cao + nhiều kết nối = OOM. Đây là một trong những nguyên nhân OOM phổ biến nhất trên Postgres.
L7

OS Kernel

Linux — cầu nối giữa Postgres và phần cứng

Postgres không ghi thẳng xuống đĩa mà qua syscall của kernel (read, write, fsync, mmap). Kernel có page cache riêng — và đây là nguồn gốc của hiện tượng double buffering: cùng một page có thể nằm cả trong shared_buffers lẫn page cache của OS.

Kernel I/O (VFS) read/write/fsync Kernel Page Cache ⚠ DOUBLE BUFFERING data ở 2 nơi FS cache inode · dentry I/O Sched mq-deadline effective_ cache_size chỉ gợi ý
Đính chính: effective_cache_size không cấp bộ nhớ. Nó chỉ là con số để planner ước lượng chi phí index scan (giả định có bao nhiêu cache khả dụng). Đặt nó không làm Postgres dùng thêm RAM — chỉ ảnh hưởng cách chọn plan.
L8

Storage Hardware

tầng cache phần cứng trước khi chạm đĩa

Trên bare-metal, dưới kernel còn RAID controller (có write cache, thường battery-backed) và cache nội bộ trong ổ đĩa (SSD/NVMe/HDD). Các tầng này tăng tốc nhưng cũng là nơi dữ liệu có thể "mất" nếu mất điện mà chưa flush.

RAID Controller battery-backed write cache · write-back/through Disk internal cache SSD DRAM+NAND · NVMe · write coalescing
i
Trên CNPG / EC2: tầng này thực chất là EBS — bạn không kiểm soát RAID mode hay disk firmware. Durability dựa vào fsync + WAL + độ bền của EBS, không phải battery-backed cache vật lý. Với cloud, tầng L8 hơi "thừa" về mặt vận hành.
L9

Physical Storage — $PGDATA

PVC / EBS — nơi dữ liệu thật sự nằm

Đáy cùng là thư mục dữ liệu trên PVC/EBS. Mỗi loại file có vai trò riêng: config, data (base/), WAL (pg_wal/), trạng thái transaction (pg_xact/), replication slot (pg_replslot/), và nhiều thư mục phụ.

Config files postgresql.conf pg_hba.conf pg_ident.conf Data files base/ base/<db>/<table_oid> 8KB heap pages Table files heap .0/.1/.2 1GB segment TOAST FSM · VM Index files B-tree .0/.1 tách khỏi heap REINDEX WAL pg_wal/ 16MB segments archive_status/ pg_xact/ Commit log 2-bit / XID Replication files pg_logical/ · snapshot · origin ⚠ pg_replslot/ — giữ WAL, MONITOR! Stats / khác pg_stat/ (lưu lúc shutdown) pg_tblspc/ (symlink) Other pg_twophase/ pgsql_tmp/ postmaster.pid ⚠ PG18: KHÔNG còn pg_stat_tmp/ stats đã chuyển vào shared memory — đây là khác biệt chính so với diagram gốc
!
pg_replslot/ giữ WAL lại đến khi consumer (replica/subscriber) xác nhận đã nhận. Nếu consumer chết hoặc chậm, slot không cho xóa WAL → pg_wal phình to đến đầy đĩa. Phải monitor pg_replication_slots (cột wal_status) và alert sớm.
+

Replication — Physical & Logical

vượt ra ngoài một instance

WAL không chỉ để recovery — nó còn là cơ sở của replication. Physical stream WAL byte-for-byte sang standby (cùng version, read-only). Logical decode WAL thành thay đổi mức dòng, gửi chọn lọc sang DB đích độc lập (khác version cũng được).

WAL Sender primary · max=10 Standby (physical) WAL Receiver + Startup replay byte-for-byte · hot standby RO physical Standby (logical) publication/subscription cross-version · DDL không repl. logical Decoder→Slot→Pub→Sub pgoutput đọc WAL qua slot DB đích ghi được
!
WAL Receiver + Startup chỉ chạy trên standby — không cùng node với WAL Sender. Một node tại một thời điểm chỉ đóng một vai trò: primary (gửi) hoặc standby (nhận).
wal_level = replica (physical) | logical (logical replication) synchronous_commit = on / remote_flush / remote_apply → quyết định RPO CNPG: spec.instances = 3 · spec.minSyncReplicas / maxSyncReplicas
Deep dive · Replication
§1

WAL & LSN — nền tảng của mọi replication

tại sao cả hai loại đều bắt đầu từ WAL

Mọi thay đổi trong Postgres được ghi vào WAL trước khi chạm data file. Mỗi byte trong WAL có một địa chỉ duy nhất gọi là LSN (Log Sequence Number) — một con số tăng dần, ví dụ 0/16B3748. LSN là "đồng hồ" chung mà mọi cơ chế replication dùng để biết đã đồng bộ tới đâu.

Dòng WAL (tăng dần theo LSN →) INSERT UPDATE COMMIT DELETE ... flush LSN (đã fsync) replay/apply LSN của replica

Khoảng cách giữa LSN của primary và LSN mà replica đã xử lý chính là replication lag. Có ba mốc LSN bạn sẽ gặp khi monitor: sent_lsn (đã gửi), flush_lsn (replica đã fsync), replay_lsn (replica đã apply xong).

-- xem vị trí WAL hiện tại của primary SELECT pg_current_wal_lsn(); -- xem lag từng replica (chạy trên primary) SELECT client_addr, state, sent_lsn, flush_lsn, replay_lsn, pg_wal_lsn_diff(sent_lsn, replay_lsn) AS replay_lag_bytes FROM pg_stat_replication;
§2

Physical replication — từng bước

streaming WAL byte-for-byte

Physical sao chép nguyên trạng vật lý: standby nhận WAL và replay y hệt, tạo ra bản sao byte-for-byte của primary. Toàn bộ cluster được copy — không chọn bảng được.

  1. Backend trên primary sinh WAL recordMọi thay đổi ghi vào WAL buffer rồi xuống pg_wal/ (fsync khi commit).
  2. WAL Sender đọc WAL mớiMỗi standby kết nối tạo một process walsender trên primary (max_wal_senders). Nó theo dõi LSN và đẩy WAL qua TCP.
  3. Replication slot ghi nhớ vị tríSlot lưu LSN mà standby đã nhận, để primary không xóa WAL standby chưa kịp lấy. Đây là khác biệt so với streaming không slot (dùng wal_keep_size).
  4. WAL Receiver trên standby nhận streamProcess walreceiver ghi WAL vào pg_wal/ local của standby.
  5. Startup Process replay WALĐọc WAL tuần tự, áp dụng từng record lên data file — giống hệt crash recovery, nhưng chạy liên tục.
  6. Hot standby phục vụ readNếu hot_standby=on, standby nhận truy vấn SELECT read-only trong khi vẫn đang replay.
  7. Standby gửi feedback LSN về primaryCho biết đã flush/replay tới đâu — dùng cho synchronous replication và hot_standby_feedback.
!
hot_standby_feedback=on báo cho primary biết replica đang đọc tuple nào, để primary không vacuum sớm tuple đó (tránh "snapshot too old" / query conflict trên replica). Đánh đổi: primary giữ dead tuple lâu hơn → bloat tăng. Cân nhắc theo workload.
§3

Logical replication — từng bước

decode WAL thành thay đổi mức dòng

Logical không copy block vật lý mà giải mã WAL thành thao tác logic ("INSERT dòng này vào bảng users") rồi gửi sang một database đích độc lập, có thể ghi được, khác version, khác schema. Cần wal_level = logical.

  1. Tạo Publication trên nguồnCREATE PUBLICATION mypub FOR TABLE users, orders; — khai báo replicate bảng/cột/hàng nào.
  2. Tạo Subscription trên đíchCREATE SUBSCRIPTION mysub CONNECTION '...' PUBLICATION mypub; — đích kết nối tới nguồn và tạo replication slot ở nguồn.
  3. Initial snapshot (copy_data)Lúc tạo subscription, dữ liệu hiện có được COPY sang đích trước, rồi mới stream thay đổi tiếp theo. Bảng lớn → bước này nặng.
  4. Logical decoder đọc WAL qua slotPlugin pgoutput đọc WAL từ replication slot, lọc theo publication, dịch thành luồng thay đổi row-level.
  5. Stream tới đíchCác thay đổi (INSERT/UPDATE/DELETE) được gửi qua kết nối logical.
  6. Apply worker thực thi trên đíchMột background worker trên đích nhận và chạy các thay đổi đó như câu lệnh thật.
!
3 giới hạn phải nhớ: (1) DDL không replicateALTER TABLE phải chạy tay ở cả hai bên, đúng thứ tự. (2) Sequence không sync giá trị — failover sang đích phải setval lại kẻo trùng ID. (3) Cần REPLICA IDENTITY (thường là primary key) để UPDATE/DELETE biết dòng nào; bảng không có PK phải set REPLICA IDENTITY FULL.
§4

Synchronous vs Asynchronous — quyết định RPO

synchronous_commit điều khiển khi nào COMMIT trả về

Đây là tham số quan trọng nhất về độ bền. Nó quyết định primary chờ tới mức nào trước khi báo COMMIT thành công cho client. Càng chờ xa → mất dữ liệu càng ít khi sự cố (RPO thấp), nhưng latency càng cao.

Mức chờ tăng dần → RPO giảm dần, latency tăng dần off trả về trước fsync on (local) fsync WAL local remote_write replica nhận vào OS (chưa fsync) remote_apply replica replay xong đọc thấy ngay RPO > 0 (có thể mất giao dịch cuối) RPO = 0 remote_flush (= on khi có sync standby): replica đã FSYNC WAL → RPO=0, là mức cân bằng phổ biến. CNPG mặc định async (không có trong synchronous_standby_names) → ưu tiên throughput, chấp nhận RPO>0.
MứcPrimary chờ đến khiRPOLatency
offkhông chờ cả fsync localcó thể mất vài giao dịchthấp nhất
on (không sync standby)WAL fsync trên local0 nếu disk còn, >0 nếu mất nodethấp
remote_writereplica ghi WAL vào OS cache>0 (replica chưa fsync)trung bình
remote_flush / on+syncreplica đã fsync WAL0cao
remote_applyreplica replay xong (query thấy)0 + read-after-writecao nhất
i
Trên CNPG, bạn không sửa synchronous_standby_names trực tiếp mà khai báo spec.minSyncReplicas / maxSyncReplicas — operator tự sinh cấu hình. Đặt minSyncReplicas ≥ 1 để có RPO=0, nhưng nhớ: nếu số sync standby khả dụng tụt dưới mức min, primary sẽ chặn COMMIT để giữ đảm bảo — đánh đổi availability lấy durability.
§5

Failover — khi primary chết

promote standby thành primary mới

Failover chỉ áp dụng cho physical (logical không có khái niệm này). Khi primary chết, một standby được chọn promote. Trên CNPG operator tự lo; trên EC2 thường là Patroni + etcd.

  1. Phát hiện primary chếtCNPG operator (hoặc Patroni qua TTL trong etcd) phát hiện primary không còn phản hồi.
  2. Chọn standby tốt nhấtStandby có replay_lsn mới nhất (lag thấp nhất) được ưu tiên — để mất ít dữ liệu nhất.
  3. Promotepg_promote(): standby thoát recovery mode, mở read-write, trở thành primary mới với một timeline WAL mới.
  4. Đổi routingCNPG đổi label để Service -rw trỏ sang pod mới; Patroni thì HAProxy health check tự phát hiện qua REST API.
  5. Các standby còn lại re-attachCác replica khác đổi sang stream WAL từ primary mới (cần cùng timeline; dùng pg_rewind nếu phân kỳ).
!
RPO khi failover phụ thuộc sync mode: nếu async, các giao dịch đã commit trên primary nhưng chưa kịp stream sẽ mất khi promote standby. Muốn RPO=0 phải dùng sync replication — nhưng đánh đổi latency mỗi write. Đây là quyết định kiến trúc, không có lựa chọn "free".
i
Timeline & split-brain: sau promote, primary cũ nếu sống lại không được tự nối lại làm primary (sẽ thành 2 primary = split-brain). Fencing (CNPG) hoặc etcd quorum (Patroni) ngăn điều này. Primary cũ phải pg_rewind rồi join lại làm standby.
§6

Vận hành & sự cố thường gặp

monitor gì, hỏng ra sao, sửa thế nào
Triệu chứngNguyên nhânXử lý
pg_wal phình to, sắp đầy đĩaReplication slot của một consumer (replica/subscriber) chết hoặc chậm → slot giữ WAL không cho xóaKiểm tra pg_replication_slots.wal_status; nếu slot mồ côi thì pg_drop_replication_slot(); đặt max_slot_wal_keep_size để giới hạn
Replication lag tăng dầnReplica yếu hơn primary, hoặc network nghẽn, hoặc replay bị block bởi query dài (recovery conflict)So flush_lsn vs replay_lsn; tăng tài nguyên replica; cân nhắc max_standby_streaming_delay
Query trên replica bị cancelRecovery conflict: replay cần vacuum tuple mà query trên replica đang đọcBật hot_standby_feedback=on (đổi lấy bloat trên primary), hoặc nới max_standby_streaming_delay
Logical: apply worker dừng, lag tăngXung đột (duplicate key) trên đích, hoặc DDL lệch giữa hai bênXem pg_stat_subscription + log; sửa conflict trên đích; đồng bộ DDL thủ công đúng thứ tự
Sau failover, ID bị trùng (logical)Sequence không được replicate sang đíchsetval() các sequence trên đích trước khi cho ghi
-- monitor cốt lõi (đặt alert) SELECT slot_name, active, wal_status, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained_wal FROM pg_replication_slots; -- slot giữ bao nhiêu WAL SELECT subname, received_lsn, latest_end_lsn FROM pg_stat_subscription; -- logical lag
!
Alert quan trọng nhất cho SRE: retained_wal của bất kỳ slot nào vượt ngưỡng (vd 50% dung lượng pg_wal PVC). Đây là nguyên nhân #1 khiến Postgres chết vì đầy đĩa — và nó âm thầm, không báo cho tới khi hết chỗ.
§7

Chọn loại nào cho use case nào

physical vs logical — không phải thay thế nhau

Physical streaming

  • Toàn bộ cluster, byte-for-byte đơn giản
  • RPO=0 nếu sync, có failover/HA
  • Bắt buộc cùng major version
  • Standby read-only hoàn toàn
  • Không chọn lọc bảng được

Logical replication

  • Chọn lọc bảng/cột/hàng linh hoạt
  • Cross-version, cross-schema
  • Đích ghi được (multi-master, gộp nguồn)
  • DDL không replicate, sequence thủ công
  • Không phải cơ chế HA
PHYSICAL
HA / standby chịu tải đọc
Cluster CNPG primary + replica của bạn — đây chính là physical streaming. Mặc định nên dùng cho mọi setup production.
PHYSICAL
Disaster recovery cùng version
Standby ở region/AZ khác, PITR kết hợp WAL archive (S3).
LOGICAL
Nâng major version không downtime
Dựng PG18 mới, logical replicate từ PG15, đồng bộ xong thì switch over. Physical không làm được vì khác version.
LOGICAL
CDC ra ngoài / data warehouse
Bắn vài bảng sang Kafka (Debezium dùng logical decoding), hoặc sang analytics DB.
LOGICAL
Gộp nhiều DB nguồn vào một đích
Nhiều service, mỗi cái một DB, logical replicate vào một DB tổng hợp để báo cáo.
i
Quan trọng: cluster HA của bạn vẫn dùng physical cho primary/standby. Logical là kênh thêm vào, song song — thường để bắn dữ liệu ra ngoài cluster. Hai cái không loại trừ nhau; một cluster có thể vừa physical-replicate nội bộ vừa logical-replicate ra ngoài cùng lúc.
Tài liệu mô tả kiến trúc nội bộ một PostgreSQL 18 instance trong bối cảnh CloudNativePG. Mỗi sơ đồ trên là một lớp tách từ sơ đồ tổng 9 lớp. Các tham số là giá trị mặc định — điều chỉnh theo workload thực tế.