Chương tổng hợp của Phần 5. Nếu sáu chương trước là sáu công cụ, chương này là bàn tay cầm chúng.

1. Bảng so sánh theo quyết định gốc

PostgreSQLMySQLMongoDBRedisClickHouseElasticsearch
Cấu trúc lưuHeap + B-treeClustered B-treeB-tree (WiredTiger)RAM structuresColumnar MergeTreeInverted index (Lucene)
Đơn vị nhất quánTransaction đa hàngTransaction đa hàngDocumentLệnh đơnKhông (eventual)Document, không transaction
Trục tối ưuĐọc/ghi điểm + query linh hoạtĐọc/ghi điểm theo PKĐọc/ghi cả objectOps/s + latency µs–msScan + aggregateTìm liên quan + facet
Điểm mù cấu trúcScan tỷ hàng; ghi append cực lớnNhư PG + analytics yếu hơnQuery cắt ngang documentDataset > RAM; query đa chiềuPoint read/updateUpdate dày; đếm chính xác; transaction
Scale ghi1 writer (shard = tự lo)1 writer (Vitess đường sẵn)Sharding tích hợpCluster tích hợpCụm phân tán tốtShard tích hợp
Vai trong kiến trúcNguồn sự thậtNguồn sự thậtNguồn sự thật (đúng domain)Dẫn xuất/phù duDẫn xuấtDẫn xuất
Chi phí vận hành thêmThấp (managed phổ cập)ThấpTrung bìnhThấp–trung bìnhTrung bình–caoCao

Hàng “vai trong kiến trúc” là hàng quan trọng nhất: hai cột đầu giữ sự thật; bốn cột sau phục vụ tốc độ. Nhầm vai là nguồn của các tai nạn lớn nhất (Redis làm nguồn sự thật, ES làm primary store, ClickHouse làm OLTP).

2. Khung quyết định — sáu câu hỏi theo thứ tự

Chọn engine bằng cách trả lời tuần tự, không nhảy cóc:

flowchart TD
    Q1{"1. Mất dữ liệu này có phải<br/>xin lỗi khách hàng không?"} -->|Có| Q2{"2. Nghiệp vụ có chạm nhiều<br/>thực thể trong một giao dịch?"}
    Q1 -->|"Không (dựng lại được / phù du)"| Q4
    Q2 -->|Có| RDBMS["PostgreSQL / MySQL<br/>(ACID đa hàng)"]
    Q2 -->|"Không — đọc/ghi trọn object,<br/>schema biến động, cần shard sớm"| MG["MongoDB<br/>(vẫn phải writeConcern majority)"]
    Q4{"4. Hình dạng truy cập chính?"} -->|"Key→value nóng, đếm, khóa, hàng đợi nhẹ"| RD["Redis"]
    Q4 -->|"Tổng hợp/quét theo thời gian, volume lớn"| CH["ClickHouse"]
    Q4 -->|"Tìm theo nghĩa, facet, autocomplete"| ES["Elasticsearch"]
    RDBMS --> Q3{"3. RDBMS đã có làm nổi việc này<br/>với index/partition/JSONB chưa?"}
    Q3 -->|Rồi| STAY["Ở lại — đừng thêm hệ mới"]
    Q3 -->|"Không — có số đo chứng minh"| Q4

Chú ý hai chốt chặn: câu 1 tách nguồn sự thật khỏi dẫn xuất (quyết định quan trọng hơn mọi so sánh hiệu năng), và câu 3 — cửa kiểm tra “PostgreSQL đã đủ chưa” đứng trước mọi lựa chọn chuyên dụng, đúng bài học xuyên suốt Phần 12: mỗi hệ mới là một nghề vận hành mới, chỉ thêm khi có bằng chứng đo được.

3. Cùng một dữ liệu, bốn hình chiếu — bức tranh polyglot chuẩn

Kiến trúc dữ liệu trưởng thành (VietShop giai đoạn 8) không chọn một engine — nó cho một sự thật chảy qua nhiều hình chiếu:

flowchart LR
    PG[("PostgreSQL<br/>NGUỒN SỰ THẬT<br/>đơn hàng, user, tồn kho")] -->|outbox/CDC| K[(Kafka)]
    K --> RD[("Redis<br/>cache + session<br/>TTL, mất được")]
    K --> ES[("Elasticsearch<br/>search sản phẩm<br/>rebuild được")]
    K --> CH[("ClickHouse<br/>analytics + dashboard<br/>rebuild được")]
    APP[App] -->|"ghi + đọc quyết định"| PG
    APP -->|đọc nóng| RD
    APP -->|search| ES
    BI[Dashboard/BI] --> CH

Ba quy tắc giữ cho bức tranh này không sụp:

  1. Mọi quyết định ghi đọc từ nguồn sự thật — cache/index/projection chỉ phục vụ hiển thị (4.2, 12.8 §7).
  2. Mọi hình chiếu có SLO độ tươi + cách rebuild đã tập dượt — lag phải đo được (13.3), rebuild phải đo thời gian.
  3. Mỗi engine thêm vào phải có chủ sở hữu vận hành — “ai bị đánh thức khi nó ốm?” chưa có tên người là chưa được thêm.

4. Bài tập tình huống — áp khung vào các quyết định của VietShop

“Catalog sản phẩm nên để PG hay Mongo?” — Câu 1: mất catalog = thảm họa → nguồn sự thật. Câu 2: sửa sản phẩm ít khi chạm thực thể khác → không cần ACID đa hàng → Mongo hợp lệ. Nhưng câu 3: PG + JSONB cho thuộc tính động đã đủ, và team đang vận hành PG → ở lại PG. Mongo chỉ thắng nếu catalog là trung tâm workload + cần shard sớm (marketplace 100M SKU).

“Đếm lượt xem sản phẩm?” — Câu 1: mất được → dẫn xuất. Hình dạng: increment cực dày, đọc xấp xỉ → Redis INCR + flush định kỳ về ClickHouse/PG cho lịch sử. Đặt vào PG là tự tạo hotspot.

“Trang ‘doanh thu theo giờ, 90 ngày’?” — Dẫn xuất, hình dạng aggregate-theo-thời-gian, volume triệu event/ngày → ClickHouse (qua Kafka). Dưới trăm nghìn event/ngày? — câu 3 nói: bảng tổng hợp trong PG là đủ.

“Lịch sử giao dịch ví (FinTech)?” — Câu 1: tuyệt đối không mất. Câu 2: chuyển tiền chạm 2 ví + ledger → ACID đa hàng → PostgreSQL/MySQL, append-only ledger (13.2 — hotspot §khắc phục). Không có cuộc tranh luận NoSQL nào ở đây cả.

5. Anti-patterns của việc lựa chọn (khác với anti-pattern của từng engine)

  • Chọn theo hype/CV: “dự án mới nên thử Mongo/ClickHouse cho biết” — engine là hạ tầng 5–10 năm, không phải sandbox học tập (1.1 — resume-driven).
  • Một engine ôm mọi vai vì “đỡ phức tạp”: đúng ở năm 1 (12.1), thành bóp nghẹt ở năm 3 — sự đơn giản thật nằm ở ít bộ phận đúng vai, không phải một bộ phận sai vai.
  • Polyglot ngày đầu tiên: 5 engine cho 500 user — mỗi cái một nghề vận hành, đội 3 người (12 bài học 1).
  • So sánh bằng benchmark của vendor: mọi engine đều nhất thế giới trong benchmark tự chọn workload. Benchmark duy nhất có giá trị: workload của bạn, dữ liệu cỡ thật của bạn (1.4 §3).
  • Migration engine như thuốc chữa chậm: 90% “PG chậm quá chắc phải sang X” kết thúc bằng một index bị thiếu (1.5 — thứ tự thử). Đổi engine là phương án cuối, sau khi profile chứng minh vấn đề nằm ở mô hình lưu trữ chứ không ở cách dùng.

6. Tóm tắt một đoạn — nếu chỉ nhớ một điều

Bắt đầu bằng PostgreSQL cho sự thật. Thêm Redis khi số đo chỉ vào đọc lặp (12.2). Thêm Elasticsearch khi search thành tính năng trung tâm. Thêm ClickHouse khi analytics đè lên OLTP (12.8). Cân nhắc MongoDB khi domain thật sự là document + shard sớm. Giữ MySQL khi tổ chức đã giỏi MySQL. Và mỗi lần thêm, trả lời đủ năm câu hỏi của chương 00: tại sao, không thì sao, trade-off, lựa chọn khác, chi phí vận hành.


Hết Phần 5. Tiếp theo trong lộ trình viết sâu: Phần 6 — Communication.