Anh em làm DevOps hay backend chắc không lạ gì kiến trúc mỗi microservice đi kèm với một database riêng: service A dùng MongoDB vì cần lưu JSON, service B dùng Elasticsearch để làm search, service C lại cần Redis để caching. Kiến trúc này được gọi là Polyglot Persistence, nghe thì rất hiện đại, mỗi việc dùng đúng tool. Nhưng vận hành nó thì đúng là một câu chuyện khác, đủ thứ chuyện từ backup, monitor cho đến đảm bảo consistency dữ liệu.
Dạo gần đây, mình thấy PostgreSQL ngày càng mạnh lên, đặc biệt là các phiên bản mới. Nó làm được nhiều thứ hơn là một relational database đơn thuần. Vậy nên mình mới tự đặt câu hỏi: Liệu có thể dùng một mình PostgreSQL để gánh hết các workload này không? Và nếu được thì performance và chi phí vận hành sẽ ra sao? Cùng mình mổ xẻ nhé.
Để cho trực quan, mình sẽ dựng một kịch bản hệ thống e-commerce với các microservices cơ bản và so sánh hai phương án:
- Stack “Hiện đại”: Mỗi service một database chuyên dụng (MongoDB, Elasticsearch, Redis).
- Stack “Tối giản”: Tất cả service dùng chung một instance PostgreSQL, phân tách bằng schema.
Mình sẽ chạy benchmark trên các máy chủ cấu hình tương đương nhau để đảm bảo công bằng.

Xử lý dữ liệu JSON: PostgreSQL JSONB vs. MongoDB
Lâu nay cứ nói đến lưu trữ dữ liệu dạng document là anh em auto nghĩ đến MongoDB. Nhưng thực tế thì kiểu dữ liệu JSONB
của PostgreSQL giờ đã rất mạnh.
Thử một bài toán quen thuộc: truy vấn danh sách user ở một thành phố nhất định và có một skill cụ thể trong profile.
Với PostgreSQL, mình sẽ query thế này:
-- Query JSONB trên PostgreSQL
SELECT
user_id,
profile->>'name' as user_name,
profile->'location'->>'city' as city
FROM user_profiles
WHERE profile->'location'->>'city' = 'Hanoi'
AND profile->'skills' @> '["Golang"]'::jsonb
ORDER BY last_login DESC
LIMIT 50;
Kết quả:
- MongoDB 7.0: Đạt khoảng 3,150 ops/giây.
- PostgreSQL 18: Đạt khoảng 4,520 ops/giây (nhanh hơn ~43%).
Kết quả này có thể làm nhiều anh em bất ngờ. Lý do là các phiên bản PostgreSQL gần đây đã tối ưu rất sâu cho việc xử lý JSONB ở tầng dưới, tận dụng các tập lệnh SIMD của CPU để tăng tốc parsing và query. Rõ ràng PostgreSQL không chỉ “làm được” mà còn “làm tốt” việc này.
Full-Text Search: PostgreSQL có thay thế được Elasticsearch?
Elasticsearch là một tượng đài trong mảng search. Nhưng để vận hành một cluster Elastic cũng không phải chuyện đơn giản. PostgreSQL cung cấp sẵn tính năng full-text search, liệu có đủ dùng?
Mình thử search các bài viết có chứa cụm từ “tối ưu database” và ranking theo độ liên quan.
-- Full-Text Search trên PostgreSQL
SELECT
post_id,
title,
ts_rank(search_vector, query) as relevance
FROM
blog_posts, websearch_to_tsquery('vietnamese', 'tối ưu database') query
WHERE query @@ search_vector
ORDER BY relevance DESC
LIMIT 20;
Kết quả (độ trễ trung bình của query):
- Elasticsearch 8.11: Khoảng 25ms.
- PostgreSQL 18: Khoảng 35ms.
Elasticsearch vẫn nhanh hơn, không có gì phải bàn cãi. Nhưng PostgreSQL chỉ chậm hơn một chút và đổi lại mình không cần phải lo lắng về việc đồng bộ dữ liệu từ database chính sang Elasticsearch. Với nhiều ứng dụng không yêu cầu search quá phức tạp, độ trễ 35ms là hoàn toàn chấp nhận được. Sự đơn giản trong kiến trúc là một điểm cộng rất lớn.
Caching: UNLOGGED TABLE có phải là đối thủ của Redis?
Redis là vua về tốc độ khi nói đến caching hay session store. Nhưng việc quản lý cache lại là một bài toán đau đầu khác, đặc biệt là vấn đề cache invalidation (làm sao để cache và database luôn khớp nhau).
PostgreSQL có một tính năng khá hay là UNLOGGED TABLE
. Dữ liệu trong bảng này không được ghi vào WAL (Write-Ahead Log), giúp tăng tốc độ ghi lên đáng kể, nhưng sẽ bị mất nếu database crash. Điều này rất phù hợp cho dữ liệu tạm thời như cache.
Mình thử tạo một bảng để lưu session người dùng:
-- Dùng UNLOGGED TABLE làm cache trong PostgreSQL
CREATE UNLOGGED TABLE user_session_cache (
session_key TEXT PRIMARY KEY,
user_id INT NOT NULL,
cart_items JSONB,
last_active_at TIMESTAMPTZ DEFAULT NOW(),
expires_at TIMESTAMPTZ
);
CREATE INDEX idx_session_expiry ON user_session_cache (expires_at);
Kết quả (số lượt đọc session mỗi giây):
- Redis 7.2: Đạt khoảng 95,000 ops/giây.
- PostgreSQL 18 (UNLOGGED): Đạt khoảng 71,000 ops/giây.
Redis vẫn thắng về thông lượng. Tuy nhiên, mức hiệu năng của PostgreSQL là rất ấn tượng. Cái hay ở đây là mình loại bỏ được hoàn toàn độ phức tạp của việc đồng bộ cache. Dữ liệu session giờ nằm ngay trong database, có thể JOIN
với các bảng khác và được đảm bảo ACID trong một transaction.
Cross-Service ACID Transactions
Đây là lúc mà stack “tối giản” thể hiện sức mạnh vượt trội. Xét một nghiệp vụ chuyển tiền cần sự tham gia của nhiều service: xác thực user, kiểm tra số dư, trừ tiền, cộng tiền, ghi log.
Với stack nhiều database, anh em sẽ phải dùng các pattern phức tạp như Saga để đảm bảo eventual consistency, đi kèm với đó là độ trễ mạng và rủi ro dữ liệu không nhất quán.
Với PostgreSQL, tất cả chỉ là một transaction ACID duy nhất.
-- Giao dịch ACID trong PostgreSQL
BEGIN;
-- Trừ tiền tài khoản gửi
UPDATE accounts SET balance = balance - 1000.00 WHERE user_id = 'user_A';
-- Cộng tiền tài khoản nhận
UPDATE accounts SET balance = balance + 1000.00 WHERE user_id = 'user_B';
-- Ghi log giao dịch
INSERT INTO transaction_logs (from_user, to_user, amount, status)
VALUES ('user_A', 'user_B', '1000.00', 'completed');
COMMIT;
Kết quả (thời gian hoàn thành giao dịch end-to-end):
- Stack nhiều database: Trung bình 260ms.
- PostgreSQL 18: Trung bình 40ms (cải thiện hơn 84%).
Việc loại bỏ hoàn toàn overhead của mạng và các cơ chế phối hợp phức tạp đã tạo ra sự khác biệt khổng lồ về performance.
Chi phí vận hành và tài nguyên
Chưa cần nói đến performance, chỉ nhìn vào chi phí vận hành thôi đã thấy sự khác biệt.
- Stack “Hiện đại”:
- Phải biết vận hành 4 công nghệ khác nhau.
- Cần 4 quy trình backup, monitoring riêng biệt.
- Quản lý hàng tá file config.
- Chi phí memory tổng cộng (MongoDB + Redis + Elastic): khoảng 19.5GB.
- Stack “Tối giản”:
- Chỉ cần nắm vững một công nghệ là PostgreSQL.
- Một quy trình backup, monitoring duy nhất.
- Consistency được đảm bảo sẵn bởi ACID.
- Chi phí memory: khoảng 11.5GB (giảm hơn 40%).
Sự đơn giản hóa trong vận hành giúp team DevOps tiết kiệm rất nhiều thời gian và công sức, giảm rủi ro xảy ra lỗi.
Kết bài
Qua các bài benchmark trên, có thể thấy PostgreSQL phiên bản mới thực sự là một lựa chọn đáng cân nhắc so với các database chuyên dụng trong nhiều kịch bản phổ biến của kiến trúc microservices hoặc các dự án nhỏ.
Việc hợp nhất database giúp giảm đáng kể độ phức tạp vận hành, tiết kiệm chi phí tài nguyên và tăng tốc vượt trội cho các giao dịch phức tạp.
Tất nhiên, không có giải pháp nào là đúng hoàn toàn. Anh em vẫn nên dùng các tool chuyên dụng trong các trường hợp cụ thể:
- Caching tần suất siêu cao: Redis vẫn là lựa chọn số một.
- Graph database: Neo4j hay các database tương tự vẫn có lợi thế riêng.
- Hệ thống search cực lớn với xử lý ngôn ngữ tự nhiên phức tạp: Elasticsearch có hệ sinh thái và tính năng vượt trội.
- Time-series data ở quy mô khổng lồ: InfluxDB hay TimescaleDB sẽ tối ưu hơn.
Với quan điểm cá nhân của mình thì đừng vội chạy theo trend “mỗi service một database”. Hãy nhìn vào bài toán thực tế của mình. Với phần lớn các hệ thống, một instance PostgreSQL mạnh mẽ, được cấu hình đúng cách hoàn toàn có thể gánh vác nhiều loại workload khác nhau một cách hiệu quả. Sự đơn giản đôi khi lại chính là sức mạnh lớn nhất.