Có dashboard đẹp là một chuyện; biết cảnh báo khi nào lại là chuyện khác — và quan trọng hơn nhiều. Một cảnh báo đúng, đến đúng lúc, có thể cứu bạn khỏi một sự cố mất dữ liệu; một trăm cảnh báo giả sẽ khiến bạn phớt lờ cả cái thứ 101 thật sự nghiêm trọng. Bài này không liệt kê mọi chỉ số có thể đo, mà tập trung vào những thứ thực sự cần cảnh báo trên PostgreSQL production, đo giá trị thật của từng cái trên một database đang chạy, và giải thích ngưỡng.

Cảnh báo số một: wraparound transaction ID

Nếu chỉ được đặt một cảnh báo, hãy đặt cái này. PostgreSQL đánh số mỗi giao dịch bằng một số nguyên 32-bit — tối đa khoảng 2,1 tỷ. Cơ chế MVCC dựa vào việc so sánh các số này để biết dòng nào "thấy được". Nếu autovacuum không kịp đóng băng (freeze) các dòng cũ và tuổi giao dịch chạm đỉnh, PostgreSQL sẽ từ chối mọi lệnh ghi để tự bảo vệ khỏi hỏng dữ liệu — sự cố tệ nhất có thể xảy ra, chỉ cứu được bằng VACUUM ở chế độ đơn người dùng.

SELECT datname, age(datfrozenxid) AS tuoi_xid
FROM pg_database ORDER BY age(datfrozenxid) DESC;
-- Báo động khi tuoi_xid > ~1 tỷ (nửa giới hạn 2^31)

Ảnh chụp đoạn mã SQL nền tối minh hoạ cần cảnh báo những gì trên PostgreSQL production đặt ngưỡng báo động trước khi sự cố thành thảm hoạ các truy vấn giám sát chính, 1 wraparound transaction ID cảnh báo số một chạm 2 tỷ database dừng SELECT datname age datfrozenxid AS tuoi_xid FROM pg_database ORDER BY age datfrozenxid DESC báo động khi tuoi_xid lớn hơn 1 tỷ 50 phần trăm của giới hạn 2^31 nếu chạm đỉnh PostgreSQL từ chối ghi để tự bảo vệ, 2 kết nối gần cạn max_connections SELECT count current_setting max_connections FROM pg_stat_activity báo động khi lớn hơn 80 phần trăm cạn kết nối ứng dụng không nối được, 3 giao dịch dài idle-in-transaction SELECT pid state now trừ xact_start FROM pg_stat_activity WHERE now trừ xact_start lớn hơn 5 min giữ khoá cộng giữ xmin chặn VACUUM phình bảng báo khi lớn hơn vài phút, 4 replication lag slot giữ WAL SELECT replay_lag FROM pg_stat_replication lag lớn hơn vài giây SELECT pg_wal_lsn_diff pg_current_wal_lsn restart_lsn FROM pg_replication_slots slot chết giữ WAL đầy đĩa, 5 còn nữa disk đầy cache hit thấp deadlock autovacuum tụt pg_database_size vs disk blks_hit deadlocks n_dead_tup temp_files temp_bytes tăng thiếu work_mem sort tràn đĩa

Hình 1: Các truy vấn giám sát chính — wraparound (số một), kết nối gần cạn, giao dịch dài, replication lag/slot giữ WAL, cùng disk/cache/deadlock. Mỗi cái kèm ngưỡng nên báo động.

Đo thật các chỉ số trên một database

Tôi chạy các truy vấn giám sát trên database lab để lấy giá trị thật:

Ảnh chụp bảng kết quả đo thật nền tối các chỉ số cảnh báo trên pg-lab PostgreSQL 16, chỉ số kết nối đang dùng max 6 trên 100 6 phần trăm ngưỡng lớn hơn 80 phần trăm, tuổi xid lớn nhất wraparound 856409 ngưỡng lớn hơn 1 tỷ giới hạn 2^31 khoảng 2,1 tỷ, so với ngưỡng autovacuum 200M 0,428 phần trăm lớn hơn 100 phần trăm autovacuum bắt buộc chạy, cache hit ratio 76,04 phần trăm nhỏ hơn 95 phần trăm OLTP đáng xem, giao dịch idle-in-transaction pid 421 2 giây lớn hơn vài phút, deadlocks 0 tăng đột biến, replication slot giữ WAL 0 slot slot chết giữ WAL lớn dần, kích thước database 958 MB đĩa lớn hơn 85 phần trăm đầy, temp files temp_bytes 7 file 226 MB tăng nhiều thiếu work_mem, vì sao wraparound là cảnh báo số một PostgreSQL đánh số giao dịch bằng số 32-bit 2,1 tỷ nếu autovacuum không đóng băng freeze các dòng cũ kịp và tuổi xid chạm đỉnh database từ chối mọi lệnh ghi để tránh hỏng dữ liệu ở đây tuoi_xid 856409 rất xa mức nguy nhưng đây là thứ phải giám sát, nguyên tắc đặt cảnh báo báo sớm còn thời gian xử lý ít cảnh báo giả kẻo bị phớt lờ mỗi cảnh báo phải kèm hành động rõ ràng

Hình 2: Giá trị thật đo được: 6/100 kết nối, tuổi xid 856.409 (0,428% ngưỡng autovacuum, rất an toàn), cache hit 76%, một idle-in-transaction 2 giây, 0 deadlock, 0 slot, DB 958 MB, 7 temp file 226 MB. Kèm ngưỡng nên báo động cho từng cái.

Đọc bảng theo mức độ ưu tiên:

Wraparound: age(datfrozenxid) lớn nhất là 856.409 — cực kỳ an toàn (0,428% của ngưỡng autovacuum 200 triệu, và chỉ 0,04% của giới hạn cứng 2,1 tỷ). Nhưng đây chính là thứ phải giám sát: nó tăng chậm và âm thầm, chỉ thành thảm hoạ nếu autovacuum bị hỏng lâu ngày mà không ai để ý.

Kết nối: 6/100 (6%). Báo động khi vượt 80% — cạn kết nối nghĩa là ứng dụng không nối được nữa, thường do rò rỉ kết nối hoặc thiếu connection pooler (bài PgBouncer đã bàn).

Giao dịch dài / idle-in-transaction: đo được một phiên idle-in-transaction 2 giây. Báo động khi vượt vài phút — nó giữ khoá và giữ xmin (chặn VACUUM), làm phình bảng như các bài trước đã đo.

Cache hit ratio: 76% — thấp ở lab này do các seq scan lớn tôi chạy trước đó. Với workload OLTP thật, tỷ lệ dưới 95-99% đáng xem (thiếu RAM). Đây là cảnh báo "mềm" — cần xem xu hướng chứ không báo động ngay.

Replication slot: 0 slot. Nếu có slot mà standby chết, slot sẽ giữ WAL vô hạn và làm đầy đĩa — cần cảnh báo khi WAL bị giữ vượt ngưỡng. Deadlocks và temp_files (7 file, 226 MB từ các sort tràn đĩa) là các chỉ số phụ nên theo dõi xu hướng.

Nguyên tắc đặt cảnh báo

Ba nguyên tắc phân biệt hệ thống cảnh báo tốt với hệ thống gây nhiễu:

Báo sớm, khi còn thời gian xử lý. Cảnh báo "đĩa đã đầy 100%" là vô dụng — lúc đó đã sự cố rồi. Báo ở 85% để còn kịp dọn. Wraparound báo ở 1 tỷ (còn cả tỷ giao dịch để chạy VACUUM), không đợi tới 2 tỷ.

Ít cảnh báo giả. Một cảnh báo kêu suốt vì ngưỡng quá nhạy sẽ bị team tắt tiếng, rồi bỏ lỡ cái thật. Đặt ngưỡng đủ cao và thêm điều kiện thời gian (ví dụ "cache hit dưới 90% liên tục 15 phút") để tránh báo động vì một đỉnh nhất thời.

Mỗi cảnh báo kèm hành động. Đừng cảnh báo thứ bạn không làm gì được. Mỗi cảnh báo nên trả lời được "khi cái này kêu, tôi phải làm gì?" — nếu không có câu trả lời, đừng đặt nó.

Đánh đổi cần cân nhắc

Không có ngưỡng đúng cho mọi hệ thống. Cache hit 76% là báo động với một OLTP nhỏ nhưng bình thường với một data warehouse chuyên quét lớn. max_connections 80% nguy với setup không pooler nhưng OK nếu có PgBouncer đệm. Ngưỡng phải hiệu chỉnh theo chính hệ thống của bạn — dùng con số trong bài làm điểm khởi đầu, không phải luật cứng.

Giám sát tốn tài nguyên và cần bảo trì. Mỗi truy vấn giám sát chạy định kỳ tốn một chút; một số (như đếm bloat trên bảng khổng lồ) khá nặng. Cân tần suất kiểm với chi phí. Và bản thân hệ thống giám sát cũng cần được giám sát — một collector chết âm thầm khiến bạn "mù" mà không biết.

Cảnh báo là lớp cuối, không thay cho thiết kế đúng. Cảnh báo wraparound quan trọng, nhưng tốt hơn là đảm bảo autovacuum khoẻ mạnh để nó không bao giờ gần ngưỡng. Cảnh báo kết nối cạn quan trọng, nhưng tốt hơn là có pooler. Cảnh báo bắt sự cố; thiết kế đúng ngăn sự cố.

Ba ý mang về

  1. Wraparound transaction ID là cảnh báo số một: nếu tuổi giao dịch chạm giới hạn 32-bit (~2,1 tỷ) mà autovacuum không đóng băng kịp, PostgreSQL từ chối mọi lệnh ghi — đo thật age(datfrozenxid) = 856.409 (rất an toàn) nhưng phải giám sát vì nó tăng âm thầm, báo động ở ~1 tỷ.
  2. Các chỉ số cần cảnh báo còn lại: kết nối gần cạn max_connections (>80%), giao dịch idle-in-transaction quá lâu (giữ khoá + chặn VACUUM), replication lag/slot giữ WAL (đầy đĩa), disk đầy, cùng cache hit thấp/deadlock/temp file như cảnh báo mềm theo xu hướng — tất cả đo được bằng các view pg_stat_*.
  3. Ba nguyên tắc: báo sớm (còn thời gian xử lý), ít cảnh báo giả (kẻo bị phớt lờ), mỗi cảnh báo kèm hành động rõ ràng — và nhớ ngưỡng phải hiệu chỉnh theo từng hệ thống, cảnh báo là lớp cuối chứ không thay cho thiết kế đúng (autovacuum khoẻ, có pooler).

Phần sau ta tổng hợp mọi công cụ đã học vào một quy trình thực chiến: Phần sau chẩn đoán một truy vấn chậm từ A đến Z — từ phát hiện, đọc EXPLAIN, tìm nguyên nhân, tới sửa và xác nhận, dùng đúng các công cụ cả series đã dựng.