Cảnh báo hiệu năng lúc 2:15 sáng
Các cảnh báo giám sát bắt đầu kêu inh ỏi vào lúc 2:15 sáng thứ Ba. Độ trễ API trên môi trường production đột ngột tăng vọt từ mức 50ms ổn định lên mức 4 giây đầy đau đớn. Trên bảng điều khiển, mức sử dụng CPU của PostgreSQL bị treo ở mức 99%. Bản năng đầu tiên của tôi là tìm đến “giải pháp đám mây”: chỉ cần nâng cấp instance và thêm nhiều vCPU hơn. Tuy nhiên, phần cứng thường chỉ là miếng băng cá nhân tạm thời cho những nút thắt cổ chai sâu xa trong cấu hình hoặc truy vấn.
Việc khắc phục sự cố cần một mức cơ sở (baseline), chứ không chỉ là một máy chủ lớn hơn. Tôi cần biết chính xác hệ thống hiện tại có thể xử lý bao nhiêu giao dịch mỗi giây (TPS – Transactions Per Second) trước khi nó bị sập. Đây là lúc pgbench trở thành công cụ chẩn đoán chính của bạn. Nếu cơ sở dữ liệu của bạn bò ra dưới áp lực dù có 64GB RAM, bạn cần ngừng đoán mò và bắt đầu benchmarking.
Các chỉ số thực sự quan trọng
Khi đo lường hiệu năng PostgreSQL, có hai chỉ số là tiêu chuẩn vàng: TPS và Độ trễ (Latency). Bạn không thể chỉ nhìn vào một cái mà bỏ qua cái còn lại.
- TPS (bao gồm cả kết nối): Chỉ số này đếm số lượng giao dịch mà cơ sở dữ liệu hoàn thành mỗi giây. Nó bao gồm cả thời gian thiết lập kết nối mới cho mỗi yêu cầu.
- TPS (không bao gồm kết nối): Đây là thông lượng “thuần túy”. Nó đo lường tốc độ xử lý dữ liệu của engine sau khi kết nối đã hoạt động.
- Độ trễ (Latency): Đây là thời gian phản hồi khứ hồi cho một giao dịch duy nhất. TPS cao sẽ vô nghĩa nếu độ trễ trung bình lên tới 5 giây, vì người dùng của bạn chắc chắn đã bỏ cuộc từ lâu.
PostgreSQL tích hợp sẵn pgbench theo mặc định. Nó chạy một bài kiểm tra giống như TPC-B bằng cách sử dụng năm lệnh SELECT, UPDATE, và INSERT cho mỗi giao dịch. Tuy nhiên, sức mạnh thực sự của nó nằm ở việc chạy các script tùy chỉnh mô phỏng khối lượng công việc thực tế của ứng dụng của bạn.
Chuẩn bị môi trường kiểm thử
Tránh chạy benchmark trên cơ sở dữ liệu production đang hoạt động. Tôi luôn dựng một môi trường staging mô phỏng chính xác phần cứng của production. Để bắt đầu, chúng ta khởi tạo một cơ sở dữ liệu kiểm thử với một “hệ số quy mô” (scale factor) cụ thể.
# Tạo một cơ sở dữ liệu kiểm thử
createdb benchmark_test
# Khởi tạo pgbench với hệ số quy mô (scale factor) là 50
# Lệnh này tạo ra khoảng 750MB dữ liệu (5.000.000 hàng trong bảng accounts)
pgbench -i -s 50 benchmark_test
Hệ số quy mô (-s) rất quan trọng. Mức quy mô bằng 1 là quá nhỏ đối với các hệ thống hiện đại. Đối với một bài kiểm tra thực tế, tôi hướng tới một tập dữ liệu lớn hơn shared_buffers. Điều này đảm bảo chúng ta đang kiểm tra hiệu năng I/O của đĩa thay vì chỉ truy cập vào RAM.
Thiết lập mức cơ sở (Baseline)
Hãy chạy một bài kiểm tra tiêu chuẩn. Tôi sẽ mô phỏng 10 client đồng thời (-c) sử dụng 2 luồng worker (-j) trong thời gian 60 giây (-T).
pgbench -c 10 -j 2 -T 60 benchmark_test
Khi kết quả hoàn tất, hãy kiểm tra dòng cuối cùng. Bạn có thể thấy tps = 450.23. Đó là ngưỡng sàn của bạn. Nếu bạn tinh chỉnh một cài đặt trong postgresql.conf và con số đó giảm xuống còn 300, bạn có ngay bằng chứng rằng thay đổi đó đã thất bại.
Script tùy chỉnh cho các truy vấn thực tế
Các bài kiểm tra TPC-B tiêu chuẩn rất hữu ích, nhưng ứng dụng của bạn có lẽ không chỉ cập nhật bảng pgbench_accounts. Các ứng dụng thực tế thường gặp khó khăn với các lệnh SELECT join nặng nề hoặc các đợt INSERT khổng lồ. Tôi tạo các tệp .sql để cô lập các nút thắt cổ chai cụ thể này.
Nếu một truy vấn báo cáo bị chậm, tôi sẽ đưa nó vào một tệp có tên read_heavy.sql:
-- read_heavy.sql
BEGIN;
SELECT abalance FROM pgbench_accounts WHERE aid = (random() * 100000 * :scale)::int;
SELECT count(*) FROM pgbench_branches;
END;
Chạy pgbench bằng tệp cụ thể đó với cờ -f:
pgbench -c 20 -j 4 -T 120 -f read_heavy.sql benchmark_test
Tôi thường phải xử lý các bản xuất dữ liệu cũ lộn xộn khi chuẩn bị các script kiểm thử này. Khi cần chuyển đổi CSV sang JSON để nhập dữ liệu nhanh, tôi sử dụng toolcraft.app/vi/tools/data/csv-to-json. Nó chạy hoàn toàn trong trình duyệt, giúp tôi không phải viết script Python cho một bài kiểm tra duy nhất.
Xác thực các tối ưu hóa của bạn
Gần đây tôi đã làm việc trên một máy chủ có 16GB RAM nhưng shared_buffers vẫn để ở mức mặc định 128MB. Đây là một lỗi kinh điển giết chết hiệu năng. Tôi đã tăng shared_buffers lên 4GB và đặt effective_cache_size thành 12GB.
Sau khi khởi động lại dịch vụ, tôi chạy lại lệnh pgbench tương tự. Cải thiện thấy rõ ngay lập tức:
- Trước khi tinh chỉnh: 450 TPS, Độ trễ trung bình 22ms
- Sau khi tinh chỉnh: 1.200 TPS, Độ trễ trung bình 8ms
Bạn cũng có thể kiểm tra Prepared Statements bằng cờ -M prepared. Điều này thường giúp tăng 10-20% hiệu năng vì nó loại bỏ việc cơ sở dữ liệu phải phân tích lại SQL cho mỗi lần thực thi. Những con số này giúp tôi tự tin triển khai các thay đổi cấu hình lên production lúc 3 giờ sáng.
Đừng bỏ qua Jitter
TPS không phải là toàn bộ câu chuyện. Hãy nhìn kỹ vào Độ lệch chuẩn (Standard Deviation) của độ trễ. Nếu độ trễ trung bình của bạn là 10ms nhưng độ lệch chuẩn là 50ms, người dùng của bạn đang gặp phải hiện tượng “jitter” (biến động). Một số yêu cầu rất nhanh, nhưng số khác lại bị chậm nghiêm trọng. Điều này thường chỉ ra sự tranh chấp I/O đĩa hoặc các tiến trình autovacuum đang can thiệp vào các truy vấn của bạn.
Thói quen Benchmarking
Kiểm thử không phải là việc làm một lần. Bạn nên chạy pgbench mỗi khi nâng cấp PostgreSQL, thay đổi loại instance đám mây hoặc triển khai một tính năng lớn mới. Nó chuyển cuộc hội thoại từ “Tôi nghĩ cơ sở dữ liệu đang chậm” thành “Tôi biết instance này xử lý được 1.500 người dùng trước khi độ trễ chạm ngưỡng 100ms.”
Khi bạn thấy mình đang nhìn chằm chằm vào một bảng điều khiển bị treo giữa đêm, đừng đoán mò. Hãy khởi tạo dữ liệu kiểm thử, chạy baseline và sử dụng các script tùy chỉnh để tìm ra điểm nghẽn. Dữ liệu là thứ duy nhất giúp bạn có thể quay lại giấc ngủ ngon.

