Mười một phần trước mỗi phần dựng một tình huống và đếm: dòng mất, giây chờ, byte WAL, lần vacuum. Phần này không dựng thêm gì. Nó gom những con số đó thành thứ người vận hành thật sự cần lúc 3 giờ sáng: một runbook ngắn — làm gì, bao lâu một lần, vì sao — và một bó truy vấn trả lời "cụm này hôm nay có gì cần tay" trong mười giây. Để bó truy vấn không là lý thuyết, nó chạy trên chính cụm thí nghiệm của sê-ri — cụm đã qua năm timeline, hai lần kill, một lần rewind, và 39 GB WAL — và kết quả chỉ ra ba việc.

Runbook: bốn mục, mỗi dòng một con số đứng sau

Ảnh chụp bảng nền tối runbook vận hành PostgreSQL rút từ 11 phần đo thật mỗi dòng một việc nhịp làm và con số trong sê-ri đứng sau P là phần, sao lưu câu hỏi đúng là khôi phục về khi nào mất bao lâu gồm basebackup cộng lưu trữ WAL liên tục nhịp theo tốc độ WAL RTO bằng chép base cộng WAL chia 250 tới 440 MB mỗi giây P4, pg_dump cộng pg_dumpall globals-only hằng ngày dump chụp lúc bắt đầu thiếu role là 1 lỗi P1 P2, tập khôi phục thật tới một đích giờ hằng tháng lệch 1 giây 1500 hay 100 dòng P4, pg_create_restore_point trước mỗi migration mốc có tên không đoán giờ P4, sao chép slot bảo vệ replica bằng đĩa của primary gồm replica qua slot cộng max_slot_wal_keep_size luôn không slot 16 segment mất dựng lại P5, synchronous_commit theo bảng từ 2 sync khi RPO 0 async mất 22 trên 144k 1 sync chết là treo 9 phút P7, wal_log_hints on từ đầu pg_rewind 2,5 giây thay vì chép lại P7, replica báo cáo feedback on cộng timeout cấu hình không thì huỷ sau 2 tới 30 giây có thì bloat primary P6, logical DDL ở subscriber trước setval quy ước xung đột PK kẹt tới khi SKIP lsn P8, sức khoẻ tám con số không phải tám mươi gồm pg_stat_archiver failed_count 0, pg_wal_lsn_diff sent replay không dùng now trừ last_xact P5, pg_replication_slots active wal_status, age datfrozenxid chia freeze_max_age P9, n_dead_tup theo bảng răng cưa hay leo thẳng, pg_stat_activity Lock idle in transaction xact dài nhất P10, kết nối chia max_connections cl_waiting, checkpoints_req chia timed buffers_backend so với checkpoint P3, thay đổi thời gian khoá không phải thời gian lệnh gồm SET lock_timeout cộng retry trước mọi ALTER 1 giao dịch mở cộng ALTER 1 ms là đọc về 0 trong 5 giây P10, CONCURRENTLY NOT VALID VALIDATE CHECK rồi SET NOT NULL default volatile là viết lại bảng P10, SET LOCAL qua pool bật max_prepared_statements một SET dính pool cho 50 trên 50 client sau P11, autovacuum per-table cho bảng lớn mặc định 0 lần trong 60 giây tải trên 3M dòng P9

Hình 1: Bốn mục của runbook. Mỗi dòng là một việc, một nhịp, và con số đo được trong sê-ri khiến dòng đó có mặt.

Sao lưu. Câu hỏi đúng không phải "tệp bao nhiêu MB" mà là khôi phục về khi nào, mất bao lâu. Phần 1 đo: dump chụp lúc bắt đầu, basebackup nhất quán lúc kết thúc, và chỉ basebackup cộng WAL lưu trữ mới về được một giây bất kỳ. Phần 4 đo giá của một giây: 1.500 dòng hay 100 dòng, và RTO là chép basebackup cộng WAL chia 250–440 MB/s — nên nhịp basebackup là hàm của tốc độ sinh WAL, không phải của lịch. Phần 2 nhắc hai việc dump không làm: pg_dumpall --globals-only trước và ANALYZE sau. Và một việc duy nhất không có công cụ nào thay được: tập khôi phục thật, hằng tháng, tới một đích thời gian — vì "ready to accept read-only connections" chưa phải xong và đích trước basebackup không báo lỗi.

Sao chép. Phần 5 cho thấy slot là khác biệt giữa "bắt kịp sau 5 giây" và "dựng lại từ đầu", và cũng là cách replica chết kéo primary đầy đĩa — nên slot đi kèm max_slot_wal_keep_size. Phần 7 đo cái giá của đồng bộ: async mất 22 commit đã xác nhận, sync mất 0 nhưng một replica sync chết làm primary treo chín phút mà statement_timeout không cắt — nên synchronous_commit đặt theo bảng, và ít nhất hai replica sync. wal_log_hints = on phải bật trước ngày cần pg_rewind. Phần 6 cho replica báo cáo một công thức: hot_standby_feedback on cộng statement_timeout, biết rằng bloat chuyển sang primary. Phần 8 cho logical replication ba quy ước thay cho ba cơ chế không có.

Sức khoẻ. Tám con số, không phải tám mươi: archiver không lỗi; lag đo bằng LSN chứ không bằng now() − pg_last_xact_replay_timestamp() (tăng tới 20 giây khi primary rảnh); slot active và wal_status; age(datfrozenxid) so với ngưỡng; n_dead_tup theo bảng — răng cưa là khoẻ, leo thẳng là bệnh; pg_stat_activity với Lock, idle in transaction, giao dịch dài nhất; kết nối so với max_connections và cl_waiting ở pool; tỉ lệ checkpoint bị ép và buffers_backend so với buffers_checkpoint.

Thay đổi. Phần 10 đo thứ làm migration "vô hại" thành sự cố: không phải ALTER mà là giao dịch quên đóng ở chỗ khác — nên SET lock_timeout cộng retry là dòng đầu tiên của mọi migration; CONCURRENTLY, NOT VALID → VALIDATE, và CHECK rồi SET NOT NULL tách khoá ngắn khỏi quét dài; default volatile viết lại bảng còn now() thì không. Phần 11 thêm: qua pool, SET dính vào kết nối cho người sau, nên SET LOCAL. Phần 9: autovacuum mặc định không theo kịp bảng lớn theo thiết kế, chỉnh per-table.

Bó truy vấn trên cụm thật, và ba việc nó chỉ ra

Ảnh chụp output thật nền tối tám con số sức khoẻ trên cụm vừa đi qua 11 phần với một bó truy vấn chạy trên pg-prim lúc kết thúc sê-ri cụm đã qua 5 timeline 39 GB WAL, bó truy vấn sức khoẻ chạy một lần đọc 10 giây gồm 1 pg_stat_archiver archived 319 failed 0 last 0000000500000009000000AE chuỗi WAL liền PITR còn khả thi, 2 pg_stat_replication walreceiver streaming async lag 0 B replay_lag trống đo bằng LSN, replica now trừ pg_last_xact_replay_timestamp bằng 268,7 giây không phải lag primary đang rảnh, 3 pg_replication_slots rep1 physical active t wal_status reserved retained 0 B, 4 wraparound age datfrozenxid 1191094 bằng 0,60 phần trăm của autovacuum_freeze_max_age 200M, 5 bloat top 3 n_dead_tup pgbench_accounts 92988 dead trên 3,0M 3,1 phần trăm chưa autovacuum lần nào 600k mới tới ngưỡng docs 4 dead autovacuum 1 lần events 0, 6 khoá giao dịch chờ Lock 0 idle in transaction 0 xact dài nhất 00 00 00, 7 kết nối 1 trên 100 1 phần trăm, 8 checkpoint timed 3 requested 10 77 phần trăm ép bởi max_wal_size buffers checkpoint 47909 so với backend 1534554, 9 WAL tổng đã ghi từ đầu 39 GB pg_wal hiện 1040 MB, 10 kích thước lab 643 MB 6 bảng timeline 5, đọc ra việc phải làm ngay từ bó này gồm 8 77 phần trăm checkpoint bị ép và backend tự ghi gấp 32 lần checkpointer tăng max_wal_size và shared_buffers 128MB trước P3, 5 bảng 3M dòng chờ 600k dead mới được dọn autovacuum_vacuum_scale_factor riêng cho pgbench_accounts P9, 2 268,7 giây trên replica sẽ kêu báo động giả lúc nửa đêm đổi chỉ số sang LSN P5, 1 3 4 6 7 khoẻ không cần làm gì và đó là lý do chỉ theo dõi 8 con số nhìn một lần biết chỗ nào cần tay, sê-ri trong một bảng gồm P1 sao lưu dump 69 MB 2,9 giây base 651 MB 0,9 giây tải trừ 9 tới 15 phần trăm dump chụp lúc bắt đầu base lúc kết thúc, P2 khôi phục COPY 80 phần trăm j 1,3 lần 1 bảng 73 phần trăm ANALYZE sửa rows 5000 sang 943 role thiếu 1 lỗi, P3 WAL 1 dòng sau checkpoint 6160 B so với 120 B FPW 84 phần trăm WAL max_wal_size 64MB tps trừ 39 phần trăm WAL nhân 2, P4 PITR T trừ 1 giây 1500 dòng T cộng 1 giây 100 replay 250 tới 440 MB mỗi giây đích trước base không lỗi, P5 replica dựng 1,7 giây lag p50 9 KB tắt 20 giây slot giữ 476 MB bắt kịp 5,2 giây không slot chết, P6 đọc rep huỷ sau 2,2 hoặc 30,9 giây trừ 1 lag 454 MB feedback 400k dead ở primary RYW trượt 299 trên 300, P7 failover promote 179 ms async mất 22 trên 144k sync 0 thừa 1 sync chết treo 9 phút rewind 2,5 giây, P8 logical 1M dòng 1,2 giây 300k một txn áp 1,2 giây ADD COLUMN giết worker seq không sang SKIP lsn, P9 vacuum 10 UPDATE 27 sang 296 MB VACUUM không trả đĩa autovacuum mặc định 0 trên 60 giây txn mở 600k kẹt, P10 schema ADD COLUMN 1 ms so với random 739 ms hàng đợi khoá đọc 0 trong 5 giây CIC 1,6 ms so với 113 ms, P11 pool 1000 client sang 20 backend 70k so với 235k tps SET bẩn pool 50 trên 50 pool cạn chờ 2,9 giây, nói thẳng mọi con số là của một cụm 643 MB trên một máy ảo mang sang cụm thật là hình dạng và tỉ lệ không phải giá trị bó truy vấn này chạy 10 giây và trả lời được cụm này có gì cần tay hôm nay đó là toàn bộ việc theo dõi cần làm

Hình 2: Mười truy vấn trên cụm thí nghiệm lúc kết thúc. Năm chỉ số khoẻ, ba chỉ số chỉ ra việc phải làm, và một chỉ số là báo động giả nếu đo sai cách.

chỉ số giá trị trên cụm đọc ra
archiver 319 segment, 0 lỗi chuỗi WAL liền, PITR còn khả thi
replication lag (LSN) 0 B khoẻ
replica now() − last_xact_replay 268,7 s báo động giả: primary rảnh
slot active, reserved, 0 B giữ khoẻ
age(datfrozenxid) 1,19 triệu = 0,60% ngưỡng khoẻ
dead tuple pgbench_accounts 92.988 (3,1%), chưa autovacuum chỉnh scale_factor per-table
lock / idle in txn / xact dài 0 / 0 / 0 khoẻ
kết nối 1/100 khoẻ
checkpoint 77% requested, backend 1,53M vs checkpointer 48k buffer tăng max_wal_size, shared_buffers
WAL đã ghi 39 GB cho CSDL 643 MB giá của 11 phần thí nghiệm

Năm chỉ số nói "không cần làm gì", và đó là giá trị của chúng. Archiver 319 segment không lỗi — kho WAL liền mạch kể từ lần dọn cuối, PITR của phần 4 vẫn khả thi. Lag 0 byte, slot active với 0 byte giữ, không ai chờ khoá, không idle in transaction, một kết nối trên trăm, age 0,6% ngưỡng. Một bảng theo dõi tốt phần lớn thời gian trông đúng như thế này, và lý do để nó chỉ có tám dòng là để dòng khác thường nổi lên ngay.

Ba chỉ số chỉ ra việc phải làm — trên một cụm vừa được chăm sóc suốt mười một phần. Thứ nhất: 77% checkpoint bị ép bởi max_wal_size (10 requested trên 3 timed) và backend tự ghi 1,53 triệu buffer so với 48.000 của checkpointer — đúng hình dạng phần 3 đã đo ở shared_buffers 128 MB mặc định. Việc: tăng shared_buffers rồi max_wal_size, theo thứ tự đó. Thứ hai: pgbench_accounts ba triệu dòng có 92.988 dead tuple và chưa được autovacuum lần nào — vì scale_factor 0.2 nghĩa là chờ 600.000, y như phần 9. Việc: ALTER TABLE … SET (autovacuum_vacuum_scale_factor = 0.01). Thứ ba: replica báo 268,7 giây kể từ giao dịch cuối cùng được replay, trong khi lag thật là 0 — chỉ số này sẽ kêu lúc nửa đêm vì hệ thống rảnh, như phần 5 cảnh báo. Việc: đổi cảnh báo sang pg_wal_lsn_diff trên primary hoặc replay_lag.

Cụm thí nghiệm trả giá 39 GB WAL cho 643 MB dữ liệu. Con số cuối không phải để sửa mà để nhớ: mười một phần với pgbench, UPDATE toàn bảng, basebackup và failover đã sinh WAL gấp 60 lần kích thước CSDL. WAL là giá của mọi thứ — sao lưu, sao chép, khôi phục — và cũng là thứ đầy đĩa trước tiên khi slot kẹt hay archive hỏng. Tám con số ở trên có bốn con liên quan tới nó.

Sê-ri trong một bảng

phần con số đáng nhớ
1 Sao lưu dump 69 MB / 2,9 s, basebackup 651 MB / 0,9 s; dump chụp lúc bắt đầu, basebackup lúc kết thúc
2 Khôi phục dump COPY 80%; -j 1,3× khi một bảng 73%; ANALYZE sửa rows 5000 → 943; role thiếu = 1 lỗi
3 WAL & checkpoint 1 dòng sau checkpoint 6.160 B vs 120 B; FPW 84% WAL; max_wal_size 64 MB: TPS −39%, WAL ×2
4 PITR T−1 s: 1.500 dòng, T+1 s: 100; replay 250–440 MB/s; đích trước basebackup không lỗi
5 Streaming replication dựng 1,7 s; lag p50 9 KB; tắt 20 s: slot giữ 476 MB, bắt kịp 5,2 s; không slot: dựng lại
6 Đọc trên replica huỷ sau 2,2 / 30,9 s; −1: lag 454 MB; feedback: 400k dead ở primary; đọc-sau-ghi trượt 299/300
7 Failover promote 179 ms; async mất 22/144k; sync 0 thừa 1; sync chết: treo 9 phút; rewind 2,5 s
8 Logical replication 1M dòng 1,2 s; 300k dòng/1 txn áp 1,2 s; ADD COLUMN giết worker; sequence không sang
9 VACUUM 10 UPDATE 27 → 296 MB; VACUUM không trả đĩa; autovacuum mặc định 0/60 s; txn mở kẹt 600k
10 Đổi schema ADD COLUMN 1 ms vs random() 739 ms; hàng đợi khoá: đọc 0 trong 5 s; CIC 1,6 ms vs 113 ms
11 PgBouncer 1.000 client → 20 backend; 70k vs 235k TPS; SET bẩn pool 50/50; pool cạn chờ 2,9 s

Phạm vi của cả sê-ri

Mọi con số đến từ một cụm 643 MB trên một máy ảo 10 CPU, PostgreSQL 16, các container cùng một mạng Docker; shared_buffers 128 MB mặc định của image làm backend-write lấn át ở mọi phần có tải ghi. Thứ mang sang cụm thật là hình dạng và tỉ lệ — dump chụp lúc bắt đầu, lệch một giây là khác hẳn, slot giữ WAL, SET dính pool, ALTER chờ sau giao dịch mở — không phải giá trị tuyệt đối. Sê-ri không chạm: nâng cấp phiên bản lớn, Patroni hay repmgr cho failover có trọng tài, pg_repack, TOAST, và theo dõi bằng Prometheus (phần observability của một sê-ri khác đã làm).

Ba ý mang về

  1. Runbook là bốn câu hỏi, không phải bốn mươi lệnh: khôi phục về khi nào và mất bao lâu; slot và sync trả giá ở đâu; tám con số nào phải xanh; thay đổi này giữ khoá bao lâu. Mỗi câu có một con số đo được đứng sau.
  2. Tám con số đủ để biết hôm nay cần tay ở đâu: chạy trên cụm thí nghiệm, năm con xanh, ba con chỉ việc (checkpoint bị ép 77%, bảng lớn chưa vacuum, chỉ số lag sai) — đúng ba thứ phần 3, 9, 5 đã dạy.
  3. Đo bằng cách đúng, không phải bằng con số có sẵn: lag bằng LSN chứ không bằng timestamp cuối; "xong" bằng pg_is_in_recovery() chứ không bằng cổng mở; thời gian khoá chứ không thời gian lệnh. Sai cách đo là báo động giả lúc nửa đêm và im lặng lúc sự cố.

Nguồn