Menu
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Đang tải...

Bài 29 — Database Selection ADR — Template

Architecture Decision-Making Bài 29/60

Mở đầu — vì sao bài này quan trọng

Trong sự nghiệp làm kiến trúc sư, ít quyết định nào "dính" lâu và tốn kém khi sai như quyết định chọn database. Bạn có thể đổi framework frontend trong một quý, thay message broker trong một tháng, nhưng đổi database — nhất là khi dữ liệu đã lên hàng chục triệu bản ghi và cả chục service đang phụ thuộc vào nó — là một cuộc di cư đau đớn có thể kéo dài cả năm, với rủi ro mất dữ liệu và downtime thật.

Chính vì "chi phí đảo ngược" (cost of reversal) cao như vậy, quyết định chọn database là ứng viên số một cho một ADR (Architecture Decision Record) tử tế. Ở các bài trước bạn đã học ADR là gì, khi nào cần viết, và các workload SQL/NoSQL/OLTP/OLAP khác nhau ra sao. Bài này gom tất cả lại thành một thứ bạn dùng được ngay: một template ADR chuyên biệt cho việc chọn database, kèm một checklist quyết định để không bỏ sót yếu tố nào.

Điểm mấu chốt tôi muốn bạn thấm: ADR chọn database không phải là bài văn khen công nghệ bạn thích. Nó là bản ghi trung thực về bối cảnh — ràng buộc — các phương án — trade-off — và lý do bạn chọn phương án này thay vì phương án kia. Sáu tháng sau, khi có người mới vào hỏi "sao hồi đó không dùng Postgres cho luôn?", ADR chính là câu trả lời giúp team không phải tranh cãi lại từ đầu.

Khái niệm cốt lõi

ADR chọn database khác gì ADR thông thường?

Về cấu trúc thì vẫn là các mục quen thuộc: Title, Status, Context, Decision, Consequences. Nhưng phần ContextOptions của ADR database dày hơn nhiều, vì database chạm vào rất nhiều thuộc tính phi chức năng (NFR): consistency, availability, latency, chi phí vận hành, khả năng scale. Một ADR database tốt buộc bạn phải định lượng các thứ này trước khi chọn, chứ không chọn xong rồi tự biện minh.

Decision checklist — 8 câu hỏi phải trả lời trước khi chọn

Đây là phần xương sống. Trước khi viết bất kỳ dòng "Decision" nào, hãy trả lời dứt khoát 8 câu hỏi sau. Nếu chưa trả lời được, nghĩa là bạn chưa đủ dữ kiện để quyết.

1. Workload là gì? OLTP (nhiều giao dịch nhỏ, đọc/ghi lẻ) hay OLAP (quét lớn, phân tích, aggregate)? Hay mixed? Hay đặc thù như time-series (IoT, metrics), graph (mạng quan hệ, đề xuất), full-text search, key-value cache? Câu này quyết định "họ" database, và trả lời sai ở đây thì mọi thứ phía sau vô nghĩa. Một Postgres cân OLTP rất tốt nhưng đưa nó đi quét 500 triệu dòng để làm báo cáo BI là chọn sai họ.

2. Consistency yêu cầu ở mức nào? Strong consistency (đọc luôn thấy ghi mới nhất, ví dụ số dư ví tiền) hay eventual consistency chấp nhận được (ví dụ số lượt like)? Đây là ràng buộc nghiệp vụ, không phải sở thích kỹ thuật. Nhầm ở đây dẫn tới hoặc là bug tiền bạc, hoặc là trả giá hiệu năng vô ích.

3. Data model có hình dạng gì? Quan hệ chặt chẽ nhiều bảng join? Document lồng nhau linh hoạt? Cặp key-value đơn giản? Wide-column? Schema có ổn định hay thay đổi liên tục theo tính năng? Nếu bạn cần join 6 bảng trong một truy vấn thì document store sẽ hành bạn.

4. Quy mô và tăng trưởng (scale)? Hiện tại bao nhiêu GB/TB, bao nhiêu QPS đọc/ghi? 12–24 tháng tới ước tính bao nhiêu? Read-heavy hay write-heavy? Con số cụ thể, không nói "sẽ rất lớn". Một hệ 50 GB và 200 QPS thì gần như database nào cũng gánh được — đừng over-engineer.

5. Latency và throughput mục tiêu? P99 đọc phải dưới bao nhiêu ms? Ghi bao nhiêu? Có yêu cầu real-time không? Gắn với NFR đã định nghĩa ở các bài trước.

6. Availability và độ bền dữ liệu (durability)? SLA bao nhiêu 9? Mất 5 phút dữ liệu gần nhất có chấp nhận được không (RPO)? Cần multi-region không?

7. Vận hành và năng lực đội ngũ (operational reality)? Team đã biết công nghệ nào? Tự vận hành hay dùng managed service? Có DBA không? Đây là yếu tố bị bỏ quên nhiều nhất và giết chết nhiều dự án nhất.

8. Chi phí và ràng buộc (cost & constraints)? Ngân sách hạ tầng? Ràng buộc pháp lý (dữ liệu phải lưu tại Việt Nam theo Nghị định 13/2023 về bảo vệ dữ liệu cá nhân)? Ràng buộc cloud provider hiện có (đã trên AWS thì RDS/Aurora có lợi thế tích hợp)?

Cấu trúc template ADR database

Dựa trên checklist trên, một ADR database nên có các mục sau:

  • Title: dạng "ADR-014: Chọn database cho dịch vụ X".
  • Status: Proposed / Accepted / Superseded.
  • Context: tóm tắt bối cảnh nghiệp vụ + trả lời 8 câu hỏi checklist bằng số liệu.
  • Decision drivers: 3–5 yếu tố quyết định quan trọng nhất, có xếp trọng số.
  • Options considered: tối thiểu 2–3 phương án, mỗi phương án nêu ưu/nhược so với các decision drivers.
  • Decision: chọn cái nào, và quan trọng nhất là lý do loại các cái kia.
  • Consequences: hệ quả tích cực, tiêu cực, và những gì phải chấp nhận đánh đổi.
  • Follow-up / revisit trigger: điều kiện nào khiến ta phải xem lại quyết định này (ví dụ: khi vượt 5 TB, khi QPS ghi vượt 20.000).
Mục cuối cùng — "revisit trigger" — là thứ phân biệt ADR nghiệp dư với ADR chuyên nghiệp. Bạn thừa nhận quyết định đúng cho hiện tại và ghi rõ khi nào nó có thể sai.

Tình huống thực tế

Ví dụ 1 — Ví điện tử tại TP.HCM chọn giữa MongoDB và PostgreSQL

Một startup fintech ở TP.HCM (gọi là "PayViet") xây ví điện tử. Đội kỹ thuật trẻ, quen MongoDB từ dự án trước, mặc định muốn dùng Mongo vì "linh hoạt, không cần migration". Kiến trúc sư yêu cầu viết ADR trước khi commit.

Chạy qua checklist: Workload là OLTP thuần (nạp tiền, chuyển tiền, thanh toán). Consistency phải strong — số dư ví tuyệt đối không được đọc sai, không được double-spend. Data model quan hệ chặt: tài khoản, giao dịch, sổ cái (ledger) phải nhất quán trong cùng một transaction. Scale dự kiến 500.000 người dùng trong năm đầu, khoảng 300 QPS ghi giờ cao điểm — không hề lớn.

Khi các con số này lên giấy, "sở thích MongoDB" tự sụp. Yêu cầu strong consistency với multi-document transaction và ledger cân đối chính là sân nhà của PostgreSQL với transaction ACID. Mongo có multi-document transaction từ v4.0 nhưng đội chưa có kinh nghiệm vận hành nó ở mức tài chính, và mọi thao tác tiền bạc sẽ phải tự bọc logic mà Postgres cho sẵn.

Quyết định trong ADR: chọn PostgreSQL managed (AWS RDS). Lý do loại Mongo được ghi rõ: "yêu cầu strong consistency đa bảng cho ledger + đội chưa vận hành Mongo transaction ở mức fintech, rủi ro double-spend cao hơn lợi ích linh hoạt schema". Revisit trigger: "nếu phát sinh module lưu log hành vi phi cấu trúc khối lượng lớn, cân nhắc thêm document store riêng cho module đó."

Bài học: con số trong checklist làm "sở thích công nghệ" bốc hơi. ADR buộc bạn đối chiếu lựa chọn với ràng buộc nghiệp vụ, không phải với vùng an toàn của đội.

Ví dụ 2 — Nền tảng thương mại điện tử và bài toán tìm kiếm sản phẩm

Một sàn TMĐT tầm trung ở Đông Nam Á (tạm gọi "ShopSEA") đang dùng MySQL cho toàn bộ. Bộ phận tìm kiếm phàn nàn: truy vấn tìm sản phẩm theo từ khóa + lọc theo 8 thuộc tính + sắp xếp theo độ liên quan mất P99 tới 1,8 giây trên 12 triệu SKU. Kỹ sư đề xuất "chuyển toàn bộ sang Elasticsearch".

ADR bắt đầu bằng câu hỏi workload — và lộ ra rằng đây là hai workload khác nhau bị gộp làm một. Ghi đơn hàng, tồn kho, thanh toán là OLTP cần consistency (MySQL đang làm tốt). Còn tìm kiếm sản phẩm là workload full-text + faceted search, hoàn toàn khác họ.

Kết luận ADR không phải "thay MySQL bằng Elasticsearch" mà là "giữ MySQL làm source of truth, thêm Elasticsearch làm read model cho search, đồng bộ qua CDC". Phương án "chuyển hết sang ES" bị loại vì ES không phải database giao dịch, đưa dữ liệu đơn hàng vào đó sẽ đánh đổi tính đúng đắn để lấy tốc độ tìm kiếm — sai hoàn toàn.

Consequences được ghi thẳng thắn: chấp nhận thêm độ phức tạp đồng bộ và eventual consistency ở tầng search (sản phẩm mới có thể trễ vài giây mới xuất hiện trong kết quả tìm — nghiệp vụ chấp nhận được). Sau triển khai, P99 search xuống 90 ms.

Bài học: đừng để một điểm đau kéo bạn "thay cả database". Nhiều khi câu trả lời đúng là polyglot persistence — mỗi workload một công cụ phù hợp — và ADR chính là nơi ghi lại quyết định gộp/tách này một cách có kỷ luật.

Ví dụ 3 — Startup IoT nông nghiệp và dữ liệu cảm biến

Một startup nông nghiệp thông minh ở Đồng bằng sông Cửu Long ("AgriSense") thu dữ liệu từ 20.000 cảm biến độ ẩm, nhiệt độ, gửi mỗi 10 giây. Nhóm định dùng PostgreSQL vì "đã có sẵn".

Checklist workload trả về ngay: đây là time-series — ghi rất nhiều (khoảng 2.000 điểm/giây, tăng theo số cảm biến bán ra), đọc chủ yếu là truy vấn theo khoảng thời gian và downsample. Postgres thuần sẽ phình bảng và chậm dần khi tới hàng tỷ dòng.

Options trong ADR: (a) PostgreSQL thuần, (b) TimescaleDB (extension time-series trên chính Postgres), (c) InfluxDB. Quyết định chọn TimescaleDB vì vừa có tối ưu time-series (hypertable, nén, retention policy tự động) vừa giữ được cú pháp SQL và hệ sinh thái Postgres mà đội đã quen — giảm rủi ro vận hành so với học InfluxDB từ đầu. Revisit trigger: "nếu tỷ lệ ghi vượt 50.000 điểm/giây hoặc cần phân phối đa vùng, đánh giá lại giải pháp phân tán chuyên dụng."

Bài học: đặc thù workload (time-series) mở ra những phương án mà nếu chỉ nghĩ "SQL hay NoSQL" bạn sẽ bỏ lỡ. Và đôi khi phương án tốt nhất là một extension giúp bạn ở lại vùng năng lực đội ngũ.

Hướng dẫn từng bước

Đây là quy trình viết một ADR chọn database bạn có thể áp dụng ngay:

Bước 1 — Thu thập dữ kiện, trả lời 8 câu hỏi checklist bằng số. Không viết "lớn", "nhanh", "nhiều". Viết "12 triệu bản ghi, tăng 15%/tháng, P99 đọc mục tiêu 100 ms, 300 QPS ghi cao điểm". Nếu không có số, đi hỏi product và đo đạc trước — đây là bước tốn công nhất và quan trọng nhất.

Bước 2 — Chốt decision drivers và xếp trọng số. Từ checklist, chọn ra 3–5 yếu tố quyết định nhất cho bối cảnh này. Ví dụ với ví điện tử: (1) strong consistency — trọng số cao nhất, (2) độ tin cậy dữ liệu, (3) năng lực vận hành của đội, (4) chi phí. Xếp trọng số ép bạn thừa nhận điều gì thực sự quan trọng.

Bước 3 — Liệt kê tối thiểu 2–3 options thật. Mỗi phương án phải là ứng viên nghiêm túc, không dựng "bù nhìn rơm" chỉ để phương án ưa thích thắng. Chấm mỗi phương án theo từng decision driver, tốt nhất là một bảng đối chiếu.

Bước 4 — Viết Decision, tập trung vào lý do LOẠI. Câu quan trọng nhất của ADR không phải "chúng tôi chọn X" mà là "chúng tôi không chọn Y vì...". Đây là thứ người đọc tương lai cần.

Bước 5 — Ghi Consequences trung thực, gồm cả mặt xấu. Chọn Postgres thì đánh đổi gì về horizontal scale? Chọn Mongo thì mất gì về join? Nếu phần Consequences chỉ toàn điểm tốt, ADR của bạn là quảng cáo chứ không phải quyết định.

Bước 6 — Định nghĩa revisit trigger. Nêu điều kiện định lượng khiến phải xem lại. Điều này giúp quyết định "đúng cho hôm nay" không trở thành gánh nặng bị khóa cứng mãi mãi.

Bước 7 — Review với đội và chốt Status = Accepted. ADR là tài liệu sống có phiên bản; commit vào repo cạnh code để mọi người cùng thấy.

Lỗi thường gặp & mẹo

Lỗi 1 — Chọn database theo sở thích rồi viết ADR để hợp thức hóa. Dấu hiệu: phần Options chỉ có một ứng viên nghiêm túc, còn lại là bù nhìn. Mẹo: bắt buộc mỗi ADR database có ít nhất một phương án đối trọng mà bạn thật sự phải cân nhắc nghiêm túc.

Lỗi 2 — Bỏ qua yếu tố vận hành và năng lực đội. Nhiều team chọn Cassandra vì "scale vô hạn" rồi chết vì không ai biết tune compaction. Mẹo: xem năng lực vận hành như một decision driver hạng nhất. Một database "kém tối ưu về lý thuyết" mà đội vận hành thành thạo thường thắng database "hoàn hảo" mà không ai biết chăm.

Lỗi 3 — Over-engineer cho quy mô chưa tồn tại. Chọn kiến trúc sharding đa vùng cho hệ 50 GB. Mẹo: chọn cho quy mô 12–24 tháng tới, không phải cho giấc mơ 10 năm. Ghi revisit trigger thay vì xây sẵn.

Lỗi 4 — Nhầm "một điểm đau" thành "phải thay cả database". Như ví dụ ShopSEA. Mẹo: luôn hỏi "vấn đề này là workload riêng cần công cụ riêng, hay là cả hệ sai chỗ?".

Lỗi 5 — Quên ràng buộc pháp lý và chủ quyền dữ liệu. Ở Việt Nam, Nghị định 13/2023 và các quy định về lưu trữ dữ liệu cá nhân có thể loại thẳng một số lựa chọn managed service đặt máy chủ ngoài nước. Mẹo: đưa ràng buộc pháp lý vào Context ngay từ đầu, đừng để phát hiện sau khi đã build.

Mẹo tổng quát: giữ template ADR database dưới dạng file mẫu trong repo. Mỗi lần chọn database mới, copy template ra và điền — sự nhất quán này giúp cả team đọc và so sánh các quyết định qua thời gian.

Bài tập thực hành

Đề bài: Một startup giao đồ ăn ở Hà Nội cần chọn database cho tính năng theo dõi vị trí tài xế real-time. Dữ liệu: 5.000 tài xế online lúc cao điểm, mỗi tài xế gửi tọa độ GPS mỗi 3 giây. Ứng dụng khách hàng cần xem vị trí tài xế cập nhật gần như tức thời. Vị trí cũ hơn 1 phút không cần lưu lâu dài. Đội có 4 backend engineer quen Node.js và PostgreSQL, chưa ai dùng Redis nâng cao.

Yêu cầu:

  • Trả lời đầy đủ 8 câu hỏi checklist bằng số liệu (ước lượng hợp lý QPS ghi, dạng dữ liệu, yêu cầu consistency, latency, durability).
  • Đề xuất ít nhất 3 options (gợi ý cân nhắc: PostgreSQL, Redis, một time-series/geo-spatial store).
  • Viết một ADR hoàn chỉnh theo template: Context, Decision drivers có trọng số, Options, Decision (nêu rõ lý do loại), Consequences, và Revisit trigger.
Gợi ý tự chấm: Hãy để ý rằng dữ liệu vị trí này có tính "ephemeral" (không cần bền lâu), write-heavy, cần đọc real-time — đặc điểm rất hợp với in-memory store. Nhưng đội chưa biết Redis. ADR của bạn sẽ cân bằng "công cụ lý thuyết tối ưu" với "năng lực vận hành thực tế" ra sao? Một câu trả lời tốt sẽ thảo luận thẳng thắn đánh đổi này thay vì chọn đại rồi biện minh.

Tóm tắt

  • Quyết định chọn database có chi phí đảo ngược cực cao, nên gần như luôn xứng đáng một ADR tử tế.
  • Trước khi chọn, hãy trả lời 8 câu hỏi checklist bằng số liệu cụ thể: workload, consistency, data model, scale, latency, availability/durability, năng lực vận hành, và chi phí/ràng buộc pháp lý.
  • Template ADR database gồm: Title, Status, Context (giàu số liệu), Decision drivers có trọng số, Options (2–3 phương án thật), Decision (nhấn mạnh lý do loại các phương án khác), Consequences (gồm mặt xấu), và Revisit trigger định lượng.
  • Ba tình huống cho thấy ba bài học: con số làm sở thích công nghệ bốc hơi (PayViet), một điểm đau không có nghĩa phải thay cả database mà thường là polyglot persistence (ShopSEA), và đặc thù workload mở ra phương án như extension để ở lại vùng năng lực đội (AgriSense).
  • Giá trị thật của ADR database không nằm ở việc bạn chọn đúng công nghệ hôm nay, mà ở chỗ nó ghi lại vì sao, để tương lai không phải tranh cãi lại từ đầu và biết khi nào cần xem lại.