Trong giai đoạn đầu của một sản phẩm, việc tạo một cột bytea (binary data) trong PostgreSQL để lưu trữ avatar người dùng hay hình ảnh sản phẩm có vẻ là một giải pháp nhanh gọn. Nó đảm bảo tính toàn vẹn dữ liệu (ACID) và giúp việc backup trở nên đơn giản vì mọi thứ nằm chung một chỗ.
Tuy nhiên, đối với các Product Manager và Business Analyst, thiết kế hệ thống không chỉ là việc làm cho tính năng chạy được, mà là sự tính toán về khả năng mở rộng (scalability) và hiệu năng. Lưu trữ hình ảnh trực tiếp trong cơ sở dữ liệu quan hệ (RDBMS) là một anti-pattern kinh điển sẽ nhanh chóng bóp nghẹt hệ thống của bạn khi scale.
Để hiểu rõ tại sao, chúng ta cần nhìn xuống tầng kiến trúc vật lý của database: Page Storage.
Kiến Trúc Lưu Trữ Dưới Cùng: Câu Chuyện Của Những "Page" 8KB
Các cơ sở dữ liệu như PostgreSQL không lưu dữ liệu dưới dạng một luồng văn bản vô tận. Thay vào đó, chúng chia nhỏ dữ liệu trên ổ cứng thành các khối cố định gọi là Page (hoặc Block).
Trong Postgres, kích thước mặc định của một Page là 8KB.
Mỗi khi hệ thống cần đọc hoặc ghi dữ liệu, nó không đọc từng byte lẻ tẻ; nó tải toàn bộ Page 8KB đó từ ổ cứng (Disk) lên bộ nhớ RAM (Buffer Pool). Kiến trúc này cực kỳ tối ưu cho dữ liệu có cấu trúc (chuỗi ngắn, số nguyên, boolean) vì hàng trăm bản ghi (rows) có thể nằm gọn lỏn trong một Page duy nhất.
Hình Ảnh Tác Động Thế Nào Đến Page?
Hãy làm một phép toán đơn giản. Giả sử bạn cho phép người dùng upload một bức ảnh nặng 2MB.
Kích thước ảnh: 2MB = 2,048 KB
Kích thước 1 Page: 8 KB
Số Page cần thiết: 2,048 / 8 = 256 Pages
(Lưu ý: Postgres có một cơ chế gọi là TOAST để cắt nhỏ các dữ liệu lớn, nhưng bản chất vật lý vẫn là việc cấp phát hàng trăm pages cho một file).
Thay vì một Page chứa được hàng trăm bản ghi thông tin người dùng (tên, email, trạng thái), giờ đây hệ thống phải dùng tới 256 Pages chỉ để chứa một bức ảnh duy nhất.
Hiệu Ứng Domino: Khi Database Bị "Pollute" (Ô Nhiễm)
Việc tiêu tốn 256 Pages không chỉ là vấn đề về dung lượng ổ cứng (Storage Cost). Hậu quả tồi tệ nhất diễn ra ở bộ nhớ RAM của hệ thống (Shared Buffers / Buffer Pool).
Cơ sở dữ liệu hoạt động nhanh là nhờ nó giữ các dữ liệu thường xuyên truy cập (hot data) và các Index ở trong RAM. Vùng RAM này có giới hạn. Khi một truy vấn (query) được gọi để lấy danh sách người dùng kèm theo hình ảnh, Postgres buộc phải kéo hàng ngàn Pages chứa hình ảnh từ ổ cứng lên RAM.
Quy luật Đánh Đổi (Trade-off): Bộ nhớ Cache là một trò chơi zero-sum. Khi 256 Pages của một hình ảnh được đưa vào Buffer Pool, 256 Pages chứa các Index quan trọng hoặc dữ liệu giao dịch cốt lõi của hàng vạn người dùng khác sẽ bị đẩy ra ngoài (Cache Eviction).
Hiện tượng này gọi là Buffer Pool Pollution. Nó dẫn đến:
Cache Miss Rate Tăng Đột Biến: Hệ thống phải liên tục đọc/ghi từ ổ cứng (Disk I/O) cho các truy vấn đơn giản.
Hiệu Năng Giảm Sút: Độ trễ (latency) của toàn bộ các API khác sử dụng chung database bị tăng vọt.
Database Bloat & Backup Chậm: Các file Write-Ahead Log (WAL) dùng cho việc replicate dữ liệu sẽ phình to khủng khiếp. Quá trình backup có thể kéo dài từ vài phút lên vài giờ.
Giải Pháp: Tại Sao Blob Storage Là Chân Ái?
Blob Storage (Binary Large Object Storage) như Amazon S3, Google Cloud Storage, hoặc Azure Blob được thiết kế với một triết lý hoàn toàn khác: Chúng không quan tâm đến cấu trúc, Index, hay Page. Chúng chỉ là những kho lưu trữ phẳng (flat storage) được tối ưu cực đại cho dữ liệu phi cấu trúc với chi phí rẻ hơn database gấp nhiều lần.
Kiến Trúc Separation of Concerns (Tách Biệt Mối Quan Tâm)
Trong một hệ thống chuẩn mực, chúng ta áp dụng mô hình phân tách:
Database (Postgres): Chỉ lưu trữ siêu dữ liệu (Metadata). Thay vì lưu file, bạn lưu một chuỗi text ngắn gọn mang tên đường dẫn: image_url: "s3://bucket-name/user_123_avatar.jpg". Dữ liệu này chỉ tốn vài chục bytes (thậm chí chưa tới 1% của một Page 8KB).
Blob Storage (S3): Chịu trách nhiệm lưu trữ vật lý file hình ảnh 2MB.
CDN (Content Delivery Network): Đứng trước Blob Storage để cache hình ảnh ở các máy chủ biên (edge servers) gần vị trí địa lý của người dùng nhất, giảm tải hoàn toàn cho hệ thống backend.
Góc nhìn của PM: Không bao giờ dùng một công cụ đắt đỏ, tối ưu cho giao dịch ACID (Database) để làm công việc của một hệ thống lưu trữ tĩnh giá rẻ (Blob Storage). Tối ưu chi phí và tách bạch chức năng là chìa khóa để scale từ 10 ngàn lên 10 triệu người dùng.
Case StudyApr 11, 2026
Bài Toán "Chấm Xanh" Của Slack: Cơn Ác Mộng Kiến Trúc Thời Gian Thực
Việc hiển thị trạng thái online tưởng chừng cơ bản nhưng lại là bài toán hạ tầng khổng lồ ở quy mô lớn. Phân tích cách Slack cấu trúc lại mô hình Pub/Sub để cân bằng giữa độ chính xác realtime và chi phí máy chủ.