• 1
  • 394
Sử dụng PostgreSQL “đúng chuẩn” cho ứng dụng production
| January 18, 2024 | 5 min read

PostgreSQL (Postgres) là hệ quản trị cơ sở dữ liệu quan hệ (RDBMS) mã nguồn mở mạnh mẽ, ổn định và giàu tính năng (ACID, MVCC, JSONB, extension phong phú…). Tuy nhiên, “dùng Postgres” cho môi trường production không chỉ là CREATE TABLE rồi kết nối từ app: bạn cần tư duy bảo mật, cô lập, vận hành, backup/restore, migration, quan sátquy ước ngay từ ngày đầu, để hệ thống phát triển lâu dài mà không “nợ kỹ thuật”.

 

Bài hướng dẫn này tập trung vào cách triển khai Postgres sẵn sàng production theo tư duy microservices, bao gồm:

 

Thiết kế schemaquyền: tránh public, tách role theo nhiệm vụ (owner/migrator/rw/ro), cố định search_path.

Migration có versioning, idempotent, rollback.

Bảo mật & kết nối: phân quyền tối thiểu, pooling (RDS Proxy/pgBouncer), quản lý thông số kết nối.

Sao lưu & khôi phục: default value vs dữ liệu cũ, PITR (trên RDS/Aurora), backup logic từng service.

Hiệu năng & quan sát: kế hoạch index, pg_stat_statements, log chậm, autovacuum.

Extensions: bật/tắt có chủ đích (vd: pg_trgm, uuid-ossp), tách schema ext.

Thói quen tốt: chuẩn hoá URL/slug, kiểu dữ liệu tiền tệ (NUMERIC), ràng buộc CHECK, not-null + default.

 

 

Nếu bạn đang vận hành microservices dùng một cluster RDS/Aurora: khuyến nghị mỗi service = một database riêng, mỗi DB có schema riêng (không dùng public), và role tách biệt. Điều này giảm “blast radius”, quản trị quyền dễ, migrate/rollback độc lập.

 

1) Khóa public

 

Không dùng schema public cho dữ liệu ứng dụng.

 

Tạo schema theo service (vd: post, course, account), thêm schema ext cho extensions.

Chặn quyền public:

 

-- Khóa hoàn toàn create/use bừa bãi trên public
REVOKE ALL ON SCHEMA public FROM PUBLIC;
-- (optional) vẫn có thể cho USAGE nếu cần resolve vài thứ cũ
-- GRANT USAGE ON SCHEMA public TO PUBLIC;

 

2) Tạo schema riêng + roles tách bạch

 

Tách role theo nhiệm vụ:

*_owner (NOLOGIN) – sở hữu schema/objects, không dùng cho runtime.

*_migrator – chỉ dùng cho migration/DDL.

*_rw – read/write từ ứng dụng.

*_ro – chỉ đọc (dashboards, reporting, sidecar).

 

-- Owner (không dùng cho app runtime)
CREATE ROLE post_owner NOLOGIN;

-- App roles
CREATE ROLE post_rw LOGIN PASSWORD '...';  -- app ghi/đọc
CREATE ROLE post_ro LOGIN PASSWORD '...';  -- app chỉ đọc
CREATE ROLE post_migrator LOGIN PASSWORD '...'; -- tool migration

-- Schema cho service
CREATE SCHEMA post AUTHORIZATION post_owner;

-- (tuỳ chọn) schema cho extensions
CREATE SCHEMA ext AUTHORIZATION post_owner;

 

3)search_path ràng buộc về schema của service

-- Mặc định cho cả DB
ALTER DATABASE api_post SET search_path = post, ext, pg_catalog;

-- Ràng buộc cho từng role (an toàn hơn)
ALTER ROLE post_rw SET search_path = post, ext, pg_catalog;
ALTER ROLE post_ro SET search_path = post, ext, pg_catalog;
ALTER ROLE post_migrator SET search_path = post, ext, pg_catalog;

 

4) Quyền chi tiết + default privileges

-- Cho phép app truy cập schema post
GRANT USAGE ON SCHEMA post TO post_rw, post_ro, post_migrator;

-- Migrator được tạo/sửa cấu trúc
GRANT CREATE ON SCHEMA post TO post_migrator;

-- Quyền mặc định cho đối tượng mới tạo về sau
ALTER DEFAULT PRIVILEGES IN SCHEMA post GRANT SELECT ON TABLES TO post_ro;
ALTER DEFAULT PRIVILEGES IN SCHEMA post GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO post_rw;
ALTER DEFAULT PRIVILEGES IN SCHEMA post GRANT USAGE, SELECT ON SEQUENCES TO post_rw, post_ro;

-- (nếu đã có bảng rồi, cấp quyền hiện hữu)
GRANT SELECT ON ALL TABLES IN SCHEMA post TO post_ro;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA post TO post_rw;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA post TO post_rw, post_ro;

 

5) Extensions (không lẫn vào dữ liệu app)

-- Cài extension vào schema ext
CREATE EXTENSION IF NOT EXISTS pg_trgm SCHEMA ext;
CREATE EXTENSION IF NOT EXISTS uuid-ossp SCHEMA ext;

 

 

6) Thiết kế bảng “an toàn”:

 

Dùng NUMERIC(12,2) cho tiền tệ (tránh float).

NOT NULL + DEFAULT nhất quán; nếu sửa schema, update dữ liệu cũ trước khi bật NOT NULL.

Ràng buộc CHECK cho tính đúng đắn (vd giá không âm):

ALTER TABLE course
  ALTER COLUMN price SET DEFAULT 0,
  ALTER COLUMN price SET NOT NULL;

ALTER TABLE course
  ADD CONSTRAINT chk_price_non_negative CHECK (price >= 0);

 

Migration có kỷ luật:

 

Mọi thay đổi DB qua migration có version, review, CI/CD (goose/migrate/flyway…).

Migration idempotent khi có thể; có script rollback tương ứng.

Tách migration schema/role/index rõ ràng.

 

Kết nối & pooling:

 

Luôn dùng connection pool (RDS Proxy/pgBouncer) cho mỗi service.

Giới hạn max_connections phù hợp; thiết lập statement_timeout trên role/DB để tránh truy vấn treo:

ALTER ROLE post_rw SET statement_timeout = '5s';

 

Quan sát & hiệu năng:

 

Bật pg_stat_statements, log chậm (log_min_duration_statement).

Tạo index theo workload thực tế; đo và kiểm tra plan (EXPLAIN (ANALYZE, BUFFERS)).

Theo dõi autovacuum; tránh bloat (đặc biệt bảng ghi nhiều).

 

Extensions có kiểm soát:

Cài vào schema ext, không pha trộn dữ liệu app:

CREATE SCHEMA IF NOT EXISTS ext AUTHORIZATION post_owner;
CREATE EXTENSION IF NOT EXISTS pg_trgm SCHEMA ext;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp" SCHEMA ext;

 

Sao lưu & khôi phục:

 

Trên RDS/Aurora: tận dụng snapshot/PITR ở cấp cluster, nhưng tổ chức backup logic per service (dump logical) nếu cần khôi phục cục bộ.

Khi sửa default cho cột, đừng quên update dữ liệu cũ để tránh lỗi NOT NULL (điển hình: price từ NULL → 0).

 

Note

Schema đề xuất: post cho DB api_post.

Tuyệt đối không dùng public cho data app.

Tách roles: *_migrator (DDL), *_rw (R/W), *_ro (read-only).

Bắt buộc set search_path để tránh lẫn schema.

Dùng default privileges để không quên cấp quyền cho bảng mới.

 

 

 

Share on: