PgBouncer: pool_mode session, transaction và prepared statement
PgBouncer là bộ gom kết nối PostgreSQL: hàng trăm client dùng chung vài chục kết nối thật tới máy chủ cơ sở dữ liệu. Nó không phải proxy HTTP đổi IP. ProxyVN viết bài này theo tài liệu chính thức, kèm bộ quét SQL đã chạy thật và ghi rõ phần chưa thử.
PgBouncer là gì và khác proxy IP thuê ở điểm nào
PgBouncer là một chương trình nhỏ đứng giữa ứng dụng và PostgreSQL. Ứng dụng kết nối tới PgBouncer như thể đó là máy chủ Postgres. Tài liệu FAQ viết rõ: PgBouncer đóng vai máy chủ Postgres, nên chỉ cần trỏ client tới cổng của nó.
Phía sau, PgBouncer giữ một nhóm kết nối thật tới PostgreSQL và cho nhiều client mượn lần lượt. Mục tiêu là tránh việc mỗi tiến trình ứng dụng mở một kết nối riêng rồi chiếm bộ nhớ của máy chủ cơ sở dữ liệu.
Nó nói giao thức PostgreSQL, không nói HTTP
Anh em thuê proxy HTTP hoặc SOCKS5 để website đích thấy một địa chỉ IP khác. Giao thức đi qua là HTTP hay SOCKS. PgBouncer không làm việc đó. Nó nhận gói tin theo giao thức Postgres ở cổng 6432 theo mặc định, và chuyển tiếp cũng bằng giao thức Postgres.
Nó không đổi vị trí của anh em, không có khái niệm IP xoay, và không nên coi là thay thế cho một gói proxy thương mại. Nếu anh em đã đọc định dạng proxy với chuỗi `ip:port:user:pass`, hãy giữ định dạng đó cho proxy HTTP. Chuỗi kết nối của PgBouncer là chuyện khác.
Vì sao bài Proxy.vn lại nói về nó
Nhiều người làm thu thập dữ liệu chạy hàng chục tiến trình cùng ghi vào một cơ sở PostgreSQL. Phía lấy trang đi qua proxy IP. Phía ghi kết quả thì mở kết nối tới cơ sở dữ liệu. Khi số tiến trình tăng, số kết nối ghi tăng theo và máy chủ Postgres bắt đầu nghẹt.
Hai lớp proxy này giải hai bài toán khác nhau. Bài về Redis quản lý proxy xoay nói về hàng đợi và cấp phát IP. Bài này nói về lớp lưu kết quả.
Phạm vi đã thử và chưa thử
Máy mình soạn bài không có PostgreSQL, không có Docker và không có bản phân phối WSL. Tài liệu cài đặt của PgBouncer ghi rằng môi trường biên dịch duy nhất được hỗ trợ trên Windows là MinGW, và mình không dựng được bản đó. Vì vậy mình chưa chạy PgBouncer và chưa chạy PostgreSQL.
Phần mình chạy thật là một script Python quét tệp SQL, đối chiếu với bảng tương thích trong tài liệu. Mọi kết quả về hành vi của PgBouncer trong bài là mô tả theo tài liệu, không phải số đo của mình. Mình đánh dấu rõ ở từng chỗ.
Pooling mode của PgBouncer: session, transaction, statement
Tính năng quan trọng nhất của PgBouncer là chế độ gom kết nối. Đây là chỗ người mới hay chọn sai.
Ba chế độ theo tài liệu
Trang tính năng của PgBouncer mô tả ba chế độ. Với session pooling, khi một client kết nối thì một server connection được gán cho nó suốt thời gian client còn kết nối, và trả về nhóm khi client ngắt. Tài liệu gọi đây là cách lịch sự nhất, hỗ trợ mọi tính năng của PostgreSQL.
Với transaction pooling, server connection chỉ được gán cho client trong lúc có transaction. Khi PgBouncer thấy transaction kết thúc, kết nối quay lại nhóm. Chế độ này phá vỡ một số tính năng gắn với phiên của PostgreSQL, và chỉ dùng được khi ứng dụng chịu không dùng những tính năng đó.
Với statement pooling, kết nối được trả về sau mỗi câu lệnh. Đây là transaction pooling có thêm một ràng buộc: transaction nhiều câu lệnh bị cấm, ép client chạy kiểu autocommit. Tài liệu ghi chế độ này chủ yếu nhắm tới PL/Proxy.
Giá trị mặc định của `pool_mode` là session.
Bảng tương thích tính năng
Tài liệu có bảng "SQL feature map for pooling modes". Mình rút lại các dòng quan trọng nhất.
Tính năng |
Session |
Transaction |
|---|---|---|
Tham số khởi động (client_encoding, DateStyle, TimeZone...) |
Có |
Có |
SET / RESET |
Có |
Không bao giờ |
LISTEN |
Có |
Không bao giờ |
NOTIFY |
Có |
Có |
Cursor WITHOUT HOLD |
Có |
Có |
Cursor WITH HOLD |
Có |
Không bao giờ |
Prepared statement mức giao thức |
Có |
Có, khi bật max_prepared_statements |
PREPARE / DEALLOCATE (mức SQL) |
Có |
Không bao giờ |
Bảng tạm ON COMMIT DROP |
Có |
Có |
Bảng tạm PRESERVE / DELETE ROWS |
Có |
Không bao giờ |
LOAD |
Có |
Không bao giờ |
Advisory lock cấp phiên |
Có |
Không bao giờ |
Cột transaction là cái cần nhớ. Mỗi transaction có thể rơi vào một server connection khác, nên mọi thứ anh em đặt ở cấp phiên có thể biến mất hoặc, tệ hơn, rò sang client khác.
Rò trạng thái: lỗi khó thấy nhất
Tài liệu cấu hình giải thích `server_reset_query`. Mặc định nó là `DISCARD ALL`, chạy khi server connection được trả về, để client sau nhận một phiên sạch. Nhưng ở transaction pooling, câu này không chạy, vì PgBouncer coi như client không được dùng tính năng phiên nào.
Tùy chọn `server_reset_query_always` mặc định 0, cho phép chạy câu reset ở mọi chế độ. Tài liệu nói rõ mục đích: nó chuyển hỏng hóc không đoán trước thành hỏng hóc có đoán trước, vì client luôn mất trạng thái sau mỗi transaction.
Vậy một câu `SET statement_timeout = '7s'` ở transaction pooling có thể không làm gì, hoặc làm client khác chịu giới hạn 7 giây, tùy lúc nào kết nối được tái dùng. Không có thông báo lỗi. Đó là lý do mình viết bộ quét SQL ở phần dưới.
Tham số khởi động được theo dõi riêng
Có một ngoại lệ. Tài liệu `track_extra_parameters` liệt kê các tham số PgBouncer theo dõi sẵn khi chúng đổi: `application_name`, `client_encoding`, `DateStyle`, `default_transaction_read_only`, `IntervalStyle`, `scram_iterations`, `search_path`, `session_authorization`, `standard_conforming_strings`, `TimeZone`.
Một số tham số chỉ được PostgreSQL báo về client ở phiên bản mới. `search_path` chỉ từ PostgreSQL 18, `default_transaction_read_only` từ 14 và `scram_iterations` từ 16. Với `search_path` trên bản trước 18, PgBouncer chỉ biết giá trị client gửi lúc khởi động kết nối. Một câu `SET search_path` giữa chừng có thể không được giữ.
Nếu driver gửi tham số lạ lúc khởi động, PgBouncer báo lỗi trừ khi anh em liệt kê chúng ở `ignore_startup_parameters`. Ví dụ tài liệu đưa ra là `options,extra_float_digits`.
Chọn chế độ nào cho ứng dụng của anh em
Chọn session nếu ứng dụng dùng `LISTEN`, bảng tạm kéo dài, `SET` ở cấp phiên hoặc advisory lock kiểu phiên, hoặc nếu anh em không kiểm soát được mã. Chọn transaction nếu ứng dụng là chuỗi transaction ngắn độc lập, như API web hoặc worker ghi dữ liệu thu thập. Statement mode hiếm khi đúng, trừ khi cố ý dùng autocommit.
Tài liệu cho phép đặt `pool_mode` riêng cho từng database trong khối `[databases]`. Mình dùng điều này ở phần cấu hình để chạy hai chế độ cạnh nhau.
Chuẩn bị lab PgBouncer: topology, phiên bản, listener, backend
Lab tối thiểu có ba phần: một PostgreSQL, một PgBouncer và một client.
Phiên bản
Trang tin của PgBouncer liệt kê bản mới nhất là 1.26.1. Tính năng theo dõi prepared statement ở transaction pooling có từ bản 1.21.0, theo FAQ. Hãy dùng bản từ 1.21 trở lên nếu anh em cần nó, và ghi phiên bản đã thử vào tài liệu nội bộ. Lệnh `SHOW VERSION` trên console in chuỗi phiên bản, tiện để dán vào báo cáo.
Với PostgreSQL, anh em chọn bản đang chạy ở sản xuất. Một vài hành vi, như theo dõi `search_path` ở trên, đổi theo phiên bản Postgres.
Topology
Ba phương án đặt PgBouncer đều hợp lệ. FAQ viết: đặt trên máy web tốt khi kết nối ngắn, vì giảm độ trễ thiết lập. Đặt trên máy cơ sở dữ liệu tốt khi có nhiều máy khách, vì gộp được kết nối của tất cả. Đặt cả hai nơi được, nhưng mỗi hop thêm một chút độ trễ.
Trong lab, mình đặt PgBouncer cùng máy với PostgreSQL: Postgres nghe `127.0.0.1:5432`, PgBouncer nghe `127.0.0.1:6432`.
Listener và quyền truy cập
Tài liệu ghi `listen_addr` mặc định không đặt, nghĩa là chỉ nhận kết nối qua Unix socket. `listen_port` mặc định 6432. Để client TCP kết nối được, anh em phải đặt `listen_addr`. Đặt `*` là nghe mọi địa chỉ, và nên tránh khi chưa có tường lửa.
Tạo người dùng cho PgBouncer
PgBouncer kiểm tra mật khẩu client dựa trên tệp `auth_file`. Định dạng mỗi dòng là tên người dùng và mật khẩu hoặc chuỗi băm, đặt trong dấu nháy kép. Mật khẩu băm MD5 của Postgres có dạng "md5" cộng với md5 của mật khẩu ghép tên người dùng. Chuỗi SCRAM có dạng `SCRAM-SHA-256$<iterations>:<salt>$<storedkey>:<serverkey>`.
Chuỗi trong tệp này có hai việc. Nó kiểm mật khẩu client gửi vào, và nó là mật khẩu PgBouncer dùng để đăng nhập tiếp vào Postgres, trừ khi mật khẩu được ghi thẳng trong chuỗi kết nối của database.
Cấu hình PgBouncer tối thiểu, quyền truy cập và các tuyến cần kiểm tra

Hai tên database ảo, hai pool_mode
Cấu hình dưới đây khai báo hai tên database ảo cùng trỏ vào một database thật, mỗi tên một chế độ.
Tệp pgbouncer.ini
[databases]
appdb = host=127.0.0.1 port=5432 dbname=appdb pool_mode=transaction
appdb_session = host=127.0.0.1 port=5432 dbname=appdb pool_mode=session
[pgbouncer]
listen_addr = 127.0.0.1
listen_port = 6432
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt
admin_users = pgb_admin
default_pool_size = 5
max_client_conn = 100
max_prepared_statements = 100
Đọc từng dòng
Khối `[databases]` có dạng tên bằng chuỗi kết nối. Tài liệu ghi tên database `pgbouncer` được dành cho console quản trị, nên anh em không đặt trùng. Tên có ký tự ngoài `_0-9A-Za-z` phải đặt trong nháy kép kiểu SQL.
Hai dòng cùng trỏ tới `appdb` thật nhưng khác `pool_mode`. Client chọn chế độ bằng cách kết nối vào tên database ảo nào.
`auth_type = scram-sha-256` yêu cầu `auth_file` chứa chuỗi SCRAM hoặc mật khẩu thô. Giá trị mặc định là `md5`. Tài liệu ghi rõ nếu `md5` được đặt mà người dùng có chuỗi SCRAM, PgBouncer tự dùng SCRAM.
`default_pool_size = 5` giới hạn số server connection cho mỗi cặp người dùng và database. Giá trị mặc định là 20. Con số 5 chỉ để lab dễ quan sát hiện tượng chờ.
`max_client_conn = 100` giới hạn số client. Tài liệu lưu ý khi tăng, giới hạn file descriptor của hệ điều hành cũng có thể phải tăng, và số file descriptor thực dùng lớn hơn `max_client_conn`.
`max_prepared_statements = 100` bật theo dõi prepared statement ở transaction pooling. Giá trị 0 tắt tính năng này.
`admin_users` liệt kê tài khoản được vào console quản trị. Console là một database ảo tên `pgbouncer`.
Hai bản ghi phải khớp
Tên người dùng trong `auth_file` phải tồn tại trong PostgreSQL với đúng mật khẩu. Nếu không, client đăng nhập qua PgBouncer thành công nhưng PgBouncer không đăng nhập được vào Postgres, hoặc ngược lại. Hãy thử kết nối trực tiếp tới Postgres bằng cùng tài khoản trước khi đổ lỗi cho PgBouncer.
Tuyến cần kiểm tra
Lab có bốn tuyến kết nối cần thử. Cổng 5432 là kết nối thẳng làm mốc. Cổng 6432 với `appdb` là transaction. Cổng 6432 với `appdb_session` là session. Cổng 6432 với `pgbouncer` là console.
psql "host=127.0.0.1 port=5432 dbname=appdb user=app"
psql "host=127.0.0.1 port=6432 dbname=appdb user=app"
psql "host=127.0.0.1 port=6432 dbname=appdb_session user=app"
psql "host=127.0.0.1 port=6432 dbname=pgbouncer user=pgb_admin"
Console chỉ hỗ trợ giao thức truy vấn đơn giản (simple query). Một số driver dùng giao thức mở rộng cho mọi lệnh nên không vào console được. Dùng `psql` cho việc này.
Prepared statement qua PgBouncer ở chế độ transaction

Prepared statement mức giao thức và mức SQL
Đây là chỗ nhiều người báo lỗi nhất, nên mình dành riêng một phần.
Hai loại prepared statement
Có hai thứ cùng tên. Prepared statement mức giao thức do driver tạo qua gói Parse, Bind, Execute của giao thức Postgres. Prepared statement mức SQL là lệnh `PREPARE` và `EXECUTE` gõ thẳng bằng SQL.
Bảng tương thích đối xử với hai loại khác nhau. Loại mức giao thức được hỗ trợ ở transaction pooling nếu bật `max_prepared_statements`. Loại mức SQL ghi "không bao giờ".
PgBouncer làm gì khi bật tính năng này
Tài liệu mô tả khá chi tiết. PgBouncer xem mọi câu client chuẩn bị, rồi đặt cho mỗi chuỗi truy vấn duy nhất một tên nội bộ dạng `PGBOUNCER_{unique_id}`. Hai client chuẩn bị cùng một chuỗi dùng chung tên nội bộ.
Khi client gửi lệnh dùng prepared statement, PgBouncer thay tên do client đặt bằng tên nội bộ trước khi chuyển lên server. Nếu server connection hiện tại chưa có câu đó, vì client vừa bị chuyển sang kết nối khác, PgBouncer tự chuẩn bị nó rồi mới chạy.
Tài liệu nói rõ phần này không áp dụng cho lệnh `PREPARE`, `EXECUTE`, `DEALLOCATE` mức SQL. Chúng đi thẳng xuống Postgres. Ngoại lệ là `DEALLOCATE ALL` và `DISCARD ALL`, hai lệnh này hoạt động như mong đợi.
Giá trị của max_prepared_statements
Con số này là kích thước bộ nhớ đệm LRU các prepared statement trên mỗi server connection. Tài liệu khuyên đặt lớn hơn số câu hay dùng của ứng dụng. Càng cao thì mỗi kết nối Postgres giữ càng nhiều bộ nhớ, và PgBouncer tốn thêm RAM cho chuỗi truy vấn.
Tài liệu đưa ví dụ ước lượng: 1000 client, 200 truy vấn riêng, mỗi truy vấn 5 kB, `pkt_buf` mặc định 4096. Kết quả khoảng 17 MB. Nó cũng cảnh báo việc theo dõi tốn thêm CPU, và gợi ý chạy nhiều tiến trình PgBouncer cùng cổng bằng `so_reuseport`, vì PgBouncer chỉ dùng một lõi cho mỗi tiến trình.
Driver cần tắt prepared statement
FAQ nêu hai ví dụ. Với JDBC, thêm `prepareThreshold=0` vào chuỗi kết nối để tắt. Với PHP/PDO, tài liệu ghi chỉ tương thích khi dùng PHP 8.4 trở lên cùng libpq 17. Với bản cũ hơn, nên nâng cấp hoặc đặt `PDO::ATTR_EMULATE_PREPARES` là true để tắt prepared statement phía máy chủ.
Session pooling thì sao
Ở session pooling, câu hỏi khác. FAQ viết rằng phải để `server_reset_query` làm sạch prepared statement cũ, bằng `DISCARD ALL` hoặc ít nhất `DEALLOCATE ALL`. Mặc định đã là `DISCARD ALL`, nên thường không cần sửa.
Bài thử với pgbench, chưa chạy
Cách thử nhanh nhất là dùng `pgbench` đi kèm PostgreSQL ở chế độ prepared. Mình chưa chạy được vì không có Postgres. Kết quả mong đợi dưới đây theo tài liệu, không phải số đo.
pgbench -i -s 1 "host=127.0.0.1 port=5432 dbname=appdb user=app"
pgbench -M prepared -c 20 -T 10 "host=127.0.0.1 port=6432 dbname=appdb user=app"
Với `max_prepared_statements = 0`, tài liệu ghi hỗ trợ bị tắt. Anh em có thể gặp lỗi kiểu prepared statement không tồn tại khi nhiều client dùng chung ít kết nối thật, nhưng đó là suy luận của mình từ tài liệu, chưa kiểm. Với 100, các lệnh nên chạy. Ghi lại phiên bản PgBouncer và Postgres, vì hành vi này gắn với bản từ 1.21.0.
Chạy ca đúng và sai với PgBouncer, đọc log và bảng SHOW
Phần này gồm bộ quét SQL mình đã chạy thật và kịch bản console mà anh em chạy khi có lab.
Bộ quét SQL mình đã chạy

Kết quả quét SQL: 6 cảnh báo, 2 ghi chú
Script `kiem_tra_sql_pgbouncer.py` đọc một tệp SQL, mỗi câu kết thúc bằng dấu chấm phẩy. Nó cảnh báo những câu dùng tính năng cấp phiên mà bảng tài liệu ghi "không bao giờ" ở transaction pooling. Nó không kết nối Postgres, không chạy PgBouncer.
Mình đưa vào 13 câu SQL giả định của một ứng dụng và chạy bằng Python 3.12. Kết quả: 6 cảnh báo, 2 ghi chú, 5 câu không có vấn đề, thoát mã 1.
Câu |
Kết quả |
Lý do |
|---|---|---|
`SET LOCAL statement_timeout` |
Ghi chú |
Bảng chỉ liệt kê SET và RESET. SET LOCAL chỉ sống trong một transaction |
`SET search_path` |
Ghi chú |
PgBouncer theo dõi từ Postgres 18, bản trước có thể mất giá trị |
`LISTEN` |
Cảnh báo |
Không bao giờ ở transaction pooling |
`PREPARE` và `EXECUTE` mức SQL |
Cảnh báo |
Không được theo dõi |
`CREATE TEMP TABLE` không có `ON COMMIT DROP` |
Cảnh báo |
PRESERVE/DELETE ROWS bị "không bao giờ" |
`pg_advisory_lock` |
Cảnh báo |
Lock cấp phiên. Bản `pg_advisory_xact_lock` được qua |
`DECLARE ... WITH HOLD` |
Cảnh báo |
Cursor WITH HOLD |
Hai câu đáng chú ý là `CREATE TEMP TABLE ... ON COMMIT DROP` và `pg_advisory_xact_lock`. Chúng được qua, vì vòng đời của chúng nằm gọn trong một transaction.
Giới hạn của script cần nói thẳng. Nó dựa vào mẫu chuỗi, nên có thể sót câu viết lạ hoặc nằm trong hàm. Nó chỉ cho anh em danh sách câu cần xem lại, không chứng minh ứng dụng chạy được. Câu `SET` giữa chừng trong mã ORM thường không hiện trong nhật ký SQL mà anh em dán vào.
Bảng SHOW trên console
Sau khi chạy lab, anh em kết nối console và xem các bảng sau. Danh sách này lấy từ tài liệu sử dụng, chưa chạy.
- `SHOW POOLS` hiện mỗi cặp database và người dùng một dòng. Cột `cl_active` là client đang gắn với server connection hoặc rảnh. Cột `cl_waiting` là client đã gửi truy vấn nhưng chưa có kết nối. Cột `sv_active` là server connection đang gắn với client.
- `SHOW CLIENTS` và `SHOW SERVERS` cho biết client nào đang dùng server connection nào. FAQ hướng dẫn nối chúng bằng cột `ptr` và `link`.
- `SHOW STATS` có tổng số transaction và truy vấn đã gom, cùng lưu lượng nhận và gửi. Số trung bình cập nhật theo `stats_period`, mặc định 60 giây.
- `SHOW CONFIG` liệt kê giá trị đang chạy, giá trị mặc định và cột `changeable` cho biết đổi được lúc chạy hay không.
- `SHOW VERSION` in phiên bản, nên chạy đầu tiên để ghi vào báo cáo.
Một thí nghiệm rò trạng thái, chưa chạy
Đặt `default_pool_size = 1`. Mở hai phiên `psql` vào `appdb` ở transaction pooling. Phiên X chạy `SET statement_timeout = '7s';` rồi thoát transaction. Phiên Y chạy `SHOW statement_timeout;`.
Theo cách tài liệu mô tả, vì chỉ có một server connection và `server_reset_query` không chạy ở chế độ này, Y có thể thấy `7s`. Lặp lại trên `appdb_session`, kết nối được dọn bằng `DISCARD ALL` nên Y thấy giá trị mặc định. Anh em ghi lại cả hai kết quả. Nếu thấy khác, kết quả thật là thứ đúng và tài liệu nội bộ phải theo nó.
Đọc log
Cấu hình `logfile` cho PgBouncer. Tài liệu ghi `log_connections` mặc định 1, nên mỗi lần đăng nhập thành công được ghi. Dòng thống kê định kỳ xuất hiện theo `stats_period`. Sau khi xoay vòng log, gửi `kill -HUP` hoặc chạy `RELOAD;` trên console. Trên Windows, tài liệu nói phải dừng rồi khởi động lại dịch vụ.
Đưa PgBouncer vào hệ thống thật: hoàn nguyên, giới hạn, bảo mật

Checklist trước khi đổi sang transaction pooling
Lab chạy được chưa phải điều kiện đủ để đưa lên sản xuất.
Hoàn nguyên
Console có lệnh `RELOAD` để nạp lại tệp cấu hình và `auth_file`. Tài liệu ghi rằng nếu thông số kết nối của một database đổi, kết nối cũ sẽ đóng khi được trả về nhóm lần kế tiếp, và kết nối mới dùng ngay thông số mới. Muốn quay lại, sửa lại tệp rồi `RELOAD`.
Khi cần khởi động lại Postgres, dùng `PAUSE` để PgBouncer ngắt server connection theo từng chế độ, rồi `RESUME` sau đó. Client mới kết nối vào database đang tạm dừng sẽ chờ.
Muốn nâng cấp PgBouncer mà không làm rớt client, FAQ dẫn tới quy trình rolling restart với `SHUTDOWN WAIT_FOR_CLIENTS`.
Cân bằng tải và chuyển đổi dự phòng nằm ngoài PgBouncer
FAQ ghi rõ PgBouncer không có cấu hình nhiều máy chủ nội bộ và không có phát hiện chuyển đổi dự phòng. Muốn cân bằng, dùng DNS round-robin hoặc bộ cân bằng TCP như LVS hay HAProxy, và nên giảm `server_lifetime` cùng bật `server_round_robin`. Bài HAProxy là gì giải thích loại cân bằng tải này, còn proxy failover nói về chuyển đổi dự phòng cho proxy IP.
Giới hạn tài nguyên
Các giá trị mặc định đáng biết: `server_lifetime` 3600 giây, `server_idle_timeout` 600 giây, `query_wait_timeout` 120 giây, `reserve_pool_size` 0. Giá trị `query_wait_timeout` quyết định bao lâu một truy vấn chờ kết nối rảnh trước khi client bị ngắt. Nếu đặt 0, client xếp hàng vô thời hạn.
`max_db_connections` và `max_user_connections` chặn tổng số server connection theo database và theo người dùng. Tài liệu lưu ý khi chạm giới hạn, đóng client của một pool không mở ngay kết nối cho pool khác, vì server connection cũ vẫn còn mở cho tới khi hết thời gian chờ.
Mình không đo mức dùng RAM hay CPU nên không có con số riêng. Con số 2 kB cho mỗi kết nối mà trang tính năng nêu là mặc định của tài liệu. Hãy đo bằng tải thật của anh em.
Bảo mật
Đừng mở `listen_addr = *` ra Internet. Dùng `auth_type` mạnh, tách `admin_users` khỏi tài khoản ứng dụng và giữ `auth_file` có quyền hạn chế. Tài liệu có kiểu xác thực `cert` yêu cầu chứng chỉ khách qua TLS, tên người dùng lấy từ CommonName.
PgBouncer đứng giữa nên thấy mọi truy vấn. Máy chạy nó cần được bảo vệ như máy chạy cơ sở dữ liệu. Nếu ứng dụng nằm ở máy khác, hãy cân nhắc mã hóa kết nối giữa hai máy. Bài proxy SSL là gì giải thích lớp mã hóa nói chung.
Chạy trên Windows
Trang cài đặt cho biết môi trường biên dịch duy nhất được hỗ trợ trên Windows là MinGW, và PgBouncer chạy được như một dịch vụ Windows. Mình không thử, nên không khuyên cụ thể. Với hệ thống thật, phần lớn người dùng chạy nó trên Linux.
Câu hỏi thường gặp về PgBouncer
PgBouncer có thay được proxy IP không. Không. Nó nói giao thức PostgreSQL, không đổi IP khi truy cập website và không có IP xoay. Hai loại proxy phục vụ hai lớp khác nhau của một hệ thống.
Có nên dùng transaction pooling cho mọi ứng dụng không. Không. Nếu mã dùng `LISTEN`, bảng tạm giữ lâu, `SET` cấp phiên hoặc advisory lock cấp phiên, hãy dùng session pooling. Nếu không chắc, chạy bộ quét SQL, rồi thử bằng bộ kiểm thử của ứng dụng.
Prepared statement có chạy được ở transaction pooling không. Có, từ PgBouncer 1.21.0, nếu đặt `max_prepared_statements` khác 0 và dùng prepared statement mức giao thức. Lệnh `PREPARE` mức SQL vẫn không được hỗ trợ.
PgBouncer có tự chuyển sang máy dự phòng không. Không. FAQ nói nó không có phát hiện chuyển đổi dự phòng. Cần DNS, HAProxy hay công cụ khác.
Vì sao client báo lỗi tham số khởi động không hỗ trợ. Driver gửi một tham số mà PgBouncer không theo dõi. Thêm tên tham số đó vào `ignore_startup_parameters` nếu anh em chắc nó không ảnh hưởng, hoặc vào `track_extra_parameters` nếu cần giữ giá trị.
Một PgBouncer dùng được bao nhiêu lõi CPU. Tài liệu ghi PgBouncer là đơn luồng, mỗi tiến trình dùng một lõi. Muốn dùng nhiều lõi, chạy nhiều tiến trình cùng cổng với `so_reuseport`, tùy hệ điều hành.
Ứng dụng thu thập dữ liệu của tôi mở quá nhiều kết nối Postgres. Đặt PgBouncer ở transaction pooling, giới hạn `default_pool_size` theo số lõi Postgres, kiểm tra SQL trước. Phần lấy trang đi qua proxy thuê vẫn tách riêng, như đã nói ở đầu bài. Bài giám sát proxy bằng Prometheus và Grafana cho cách nhìn tương tự về đo đạc.
Cần thêm IP, anh em có thể mua proxy tại ProxyVN, có proxy dân cư tĩnh Viettel, VNPT, FPT, datacenter Việt Nam và proxy xoay, hỗ trợ HTTP và SOCKS5 với tài khoản user và mật khẩu.
PgBouncer: những điều cần nhớ
PgBouncer gom kết nối PostgreSQL, và transaction pooling phá các tính năng cấp phiên như SET, LISTEN, PREPARE mức SQL. Bật max_prepared_statements cho prepared statement mức giao thức, quét SQL trước khi đổi chế độ. Cần proxy IP ổn định cho thu thập dữ liệu, liên hệ Proxy.vn - Nhà cung cấp dịch vụ proxy chất lượng hàng đầu Việt Nam cho PgBouncer.