Mở đầu — vì sao bài này quan trọng
Trong suốt 36 bài vừa qua, bạn đã được rèn luyện rất kỹ về tư duy quan hệ (relational): chuẩn hóa, khóa chính, quan hệ, ràng buộc. Đó là nền tảng cốt lõi và sẽ luôn là kỹ năng số một của một BA làm về dữ liệu. Nhưng nếu bạn bước vào một dự án thực tế tại các công ty công nghệ Việt Nam như MoMo, Tiki, VNG hay Giao Hàng Nhanh, bạn sẽ nhanh chóng nhận ra một sự thật: không có hệ thống lớn nào ngày nay chỉ chạy trên một cơ sở dữ liệu quan hệ duy nhất.
Một đơn hàng của Tiki có thể được lưu trong PostgreSQL (quan hệ), giỏ hàng tạm thời nằm trong Redis (key-value), lịch sử tìm kiếm sản phẩm chảy vào Elasticsearch (search engine), còn log hành vi người dùng đổ về một kho NoSQL dạng document hoặc column-family. Mỗi loại dữ liệu được đặt vào đúng "ngôi nhà" phù hợp nhất với nó. Đây chính là Polyglot Persistence — tạm dịch là "lưu trữ đa ngôn ngữ", nghĩa là dùng nhiều loại cơ sở dữ liệu khác nhau trong cùng một hệ thống, mỗi loại cho một mục đích.
Vì sao một BA cần hiểu điều này? Vì bạn là người mô hình hóa dữ liệu và viết tài liệu nghiệp vụ. Nếu bạn chỉ biết tư duy bảng-cột-quan hệ, bạn sẽ vô tình ép mọi yêu cầu vào khuôn quan hệ, kể cả khi nó không phù hợp. Bạn cần biết khi nào nên nói với đội kiến trúc: "Phần dữ liệu này có lẽ không nên đưa vào RDBMS." Bài học này cho bạn đúng tư duy đó: hiểu giới hạn của mô hình quan hệ, biết các họ NoSQL chính, và biết cách phối hợp chúng một cách hợp lý.
Khái niệm cốt lõi
Khi nào mô hình quan hệ "không còn đủ"
Mô hình quan hệ tuyệt vời khi dữ liệu có cấu trúc rõ ràng, ổn định, và bạn cần tính nhất quán giao dịch mạnh (ACID). Nhưng nó bắt đầu đuối sức trong bốn tình huống điển hình:
1. Schema cần linh hoạt (flexibility). Trong RDBMS, mỗi lần thêm một thuộc tính mới bạn phải ALTER TABLE, và mọi dòng đều phải tuân theo cùng một cấu trúc. Nhưng có những loại dữ liệu mà mỗi bản ghi lại khác nhau. Ví dụ: catalog sản phẩm của một sàn thương mại điện tử — điện thoại có "dung lượng pin", áo thun có "size" và "chất liệu", thực phẩm có "hạn sử dụng". Nhồi tất cả vào một bảng quan hệ sẽ tạo ra hàng trăm cột phần lớn là NULL, hoặc buộc bạn dùng mô hình EAV (Entity-Attribute-Value) rất khó truy vấn.
2. Mở rộng theo chiều ngang khổng lồ (massive horizontal scale). RDBMS truyền thống mở rộng theo chiều dọc (vertical scale) — mua server mạnh hơn. Đến một ngưỡng nào đó, bạn không thể mua server đủ mạnh nữa. NoSQL được thiết kế để mở rộng theo chiều ngang (horizontal scale) — thêm nhiều máy bình thường (commodity hardware) và phân tán dữ liệu (sharding) trên đó. Khi Shopee xử lý hàng triệu lượt truy cập trong ngày 9/9, không một server đơn lẻ nào kham nổi.
3. Dữ liệu phi cấu trúc / bán cấu trúc (unstructured / semi-structured). Bình luận, log, sự kiện theo dõi hành vi, payload JSON từ thiết bị IoT, nội dung do người dùng tạo... đều không vừa khít với mô hình bảng cứng nhắc.
4. Yêu cầu hiệu năng đặc thù (specialized access patterns). Một số bài toán cần tốc độ đọc/ghi cực nhanh ở quy mô lớn (đếm lượt xem, cache phiên đăng nhập), hoặc cần tìm kiếm toàn văn (full-text search), hoặc cần đi theo quan hệ nhiều cấp (mạng xã hội, gợi ý bạn bè). RDBMS làm được nhưng không tối ưu bằng các công cụ chuyên dụng.
Bốn họ NoSQL chính
Bạn nên ghi nhớ bốn họ này như bốn "công cụ" trong hộp đồ nghề:
Key-Value Store (ví dụ: Redis, Amazon DynamoDB ở chế độ đơn giản). Đơn giản nhất: một khóa trỏ tới một giá trị. Cực nhanh. Dùng cho cache, session, đếm số, hàng đợi, bảng xếp hạng (leaderboard). Hạn chế: bạn chỉ truy vấn được theo khóa, không truy vấn theo nội dung giá trị.
Document Store (ví dụ: MongoDB, Couchbase). Lưu dữ liệu dưới dạng tài liệu JSON/BSON. Mỗi document có thể có cấu trúc khác nhau. Phù hợp catalog sản phẩm, hồ sơ người dùng, nội dung CMS. Bạn có thể truy vấn theo các trường bên trong document. Đây là họ NoSQL gần gũi nhất với tư duy BA vì một document trông giống một "aggregate" — một thực thể nghiệp vụ trọn vẹn.
Column-Family / Wide-Column (ví dụ: Apache Cassandra, HBase, Google Bigtable). Tối ưu cho ghi lượng lớn và truy vấn theo dải khóa. Dùng cho log, dữ liệu chuỗi thời gian (time-series), IoT, lịch sử giao dịch khổng lồ. Cassandra nổi tiếng vì khả năng ghi cực mạnh và không có điểm chết (no single point of failure).
Graph Database (ví dụ: Neo4j, Amazon Neptune). Lưu trữ nút (node) và cạnh (edge) như những công dân hạng nhất. Tuyệt vời cho mạng quan hệ: bạn bè của bạn bè, phát hiện gian lận (fraud rings), gợi ý sản phẩm theo đồ thị. Truy vấn "tìm đường đi 3 cấp" trên graph nhanh hơn nhiều so với hàng loạt JOIN trong SQL.
Polyglot Persistence là gì
Polyglot Persistence là triết lý: chọn đúng kho dữ liệu cho đúng bài toán, thay vì ép mọi thứ vào một loại. Thuật ngữ này do Martin Fowler và Pramod Sadalage phổ biến. Tư tưởng cốt lõi giống "polyglot programming" — một dự án có thể dùng nhiều ngôn ngữ lập trình; ở đây là nhiều ngôn ngữ lưu trữ.
Cái giá phải trả là độ phức tạp: bạn phải vận hành nhiều hệ thống, đảm bảo dữ liệu nhất quán giữa chúng (eventual consistency), và đội ngũ phải có kỹ năng đa dạng. Vì vậy nguyên tắc vàng là: đừng dùng polyglot vì nó "ngầu", hãy dùng khi có lý do nghiệp vụ rõ ràng.
Một lưu ý quan trọng về thuật ngữ: BASE vs ACID
RDBMS đề cao ACID (Atomicity, Consistency, Isolation, Durability) — nhất quán mạnh. Nhiều hệ NoSQL chấp nhận BASE (Basically Available, Soft state, Eventual consistency) — tức là dữ liệu sẽ nhất quán "cuối cùng", nhưng tại một thời điểm có thể bạn đọc ra dữ liệu hơi cũ. Đây là sự đánh đổi theo định lý CAP: ở quy mô phân tán, bạn không thể đồng thời đạt tối đa cả tính nhất quán (Consistency) lẫn tính sẵn sàng (Availability) khi mạng bị phân vùng (Partition). Là BA, bạn cần đặt câu hỏi: "Dữ liệu này có chấp nhận trễ vài giây không?" Đếm lượt like thì được; số dư tài khoản ngân hàng thì không.
Tình huống thực tế
Tình huống 1 — Sàn thương mại điện tử kiểu Tiki: catalog sản phẩm đa dạng
Bối cảnh: Một sàn TMĐT Việt Nam có 2 triệu SKU thuộc 800 ngành hàng. Ban đầu đội kỹ thuật lưu toàn bộ thuộc tính sản phẩm trong MySQL với một bảng product_attributes theo mô hình EAV. Khi catalog tăng lên, truy vấn lọc sản phẩm ("điện thoại Samsung, RAM 8GB, dưới 10 triệu") phải JOIN bảng EAV năm sáu lần, mỗi truy vấn mất 4–6 giây — không thể chấp nhận trên trang danh mục.
Diễn giải: Đội kiến trúc chuyển phần catalog sang MongoDB. Mỗi sản phẩm là một document chứa toàn bộ thuộc tính đặc thù của nó, không cần biết ngành hàng khác có thuộc tính gì. Đồng thời họ đẩy dữ liệu vào Elasticsearch để phục vụ tìm kiếm và lọc faceted (lọc theo nhiều tiêu chí). Trong khi đó, đơn hàng và thanh toán vẫn nằm trong PostgreSQL vì cần ACID — bạn không thể để "eventual consistency" với tiền của khách. Giỏ hàng tạm dùng Redis với TTL 7 ngày.
Bài học: Cùng một hệ thống nhưng bốn kho dữ liệu, mỗi cái cho một việc. Điểm mấu chốt là họ KHÔNG bỏ RDBMS — phần "tiền và đơn hàng" vẫn quan hệ. Họ chỉ tách phần "catalog linh hoạt" và "tìm kiếm nhanh" ra. Đó là polyglot đúng nghĩa.
Tình huống 2 — Ví điện tử kiểu MoMo: chống gian lận bằng graph
Bối cảnh: Một ví điện tử tại Việt Nam phục vụ hàng chục triệu người dùng. Đội rủi ro cần phát hiện các "vòng gian lận" — ví dụ một nhóm tài khoản chuyển tiền lòng vòng cho nhau, dùng chung thiết bị, chung số điện thoại, chung địa chỉ rút tiền để lạm dụng khuyến mãi. Trong SQL, để tìm "tài khoản A liên quan tới B qua thiết bị, B liên quan C qua số dư rút về cùng ngân hàng, C lại quay về A" đòi hỏi các truy vấn JOIN đệ quy cực nặng, chạy hàng phút.
Diễn giải: Họ dựng một graph database (Neo4j) trong đó node là Người dùng / Thiết bị / Số tài khoản ngân hàng, còn cạnh là các mối quan hệ "đăng nhập bằng", "rút tiền về", "chuyển tiền cho". Một truy vấn Cypher tìm vòng khép kín 3–4 cấp chạy trong vài trăm mili-giây. Lưu ý: dữ liệu giao dịch gốc vẫn nằm trong hệ thống core (RDBMS/ledger); graph chỉ là một lớp phân tích phái sinh được đồng bộ từ dữ liệu gốc.
Bài học: Graph không thay thế sổ cái giao dịch. Nó là một góc nhìn chuyên biệt được xây trên dữ liệu gốc để giải một bài toán mà RDBMS làm rất tệ: truy vết quan hệ nhiều cấp. Là BA, khi bạn nghe yêu cầu có chữ "mạng lưới", "liên quan tới nhau qua nhiều bước", "đề xuất theo quan hệ" — hãy nghĩ tới graph.
Tình huống 3 — Ứng dụng giao đồ ăn: theo dõi vị trí tài xế theo thời gian thực
Bối cảnh: Một nền tảng giao đồ ăn ở Đông Nam Á cần lưu vị trí GPS của 50.000 tài xế đang hoạt động, cập nhật mỗi 3 giây. Đó là khoảng 16.000 lượt ghi/giây liên tục, tăng vọt giờ cao điểm. Nếu ghi vào MySQL, bảng vị trí phình ra hàng tỷ dòng mỗi tuần và ghi không kịp.
Diễn giải: Vị trí hiện tại của mỗi tài xế lưu trong Redis (key-value, ghi đè liên tục, đọc cực nhanh để hiển thị trên bản đồ). Lịch sử di chuyển dùng để đối soát và phân tích lộ trình thì đổ vào Cassandra (wide-column, tối ưu ghi lớn theo time-series). Trong khi đó, thông tin tài xế, hồ sơ, hợp đồng vẫn nằm trong PostgreSQL.
Bài học: "Vị trí hiện tại" và "lịch sử vị trí" là hai bài toán khác nhau dù cùng nói về một dữ liệu. Một cái cần đọc nhanh trạng thái mới nhất (Redis), cái kia cần ghi bền vững lượng lớn (Cassandra). Tách đúng access pattern là chìa khóa.
Hướng dẫn từng bước
Khi bạn — với vai trò BA — đứng trước một yêu cầu và phân vân "cái này nên dùng RDBMS hay NoSQL?", hãy đi theo quy trình sau:
Bước 1 — Mô tả access pattern trước, đừng nghĩ tới công nghệ. Viết ra: Dữ liệu này được ghi như thế nào (số lượng, tần suất)? Được đọc như thế nào (theo khóa? theo điều kiện lọc? theo quan hệ? full-text)? Cần truy vấn linh hoạt hay luôn theo một dạng cố định?
Bước 2 — Hỏi về tính nhất quán. Dữ liệu này có liên quan tới tiền, tồn kho, hợp đồng — những thứ cần ACID không? Hay nó chấp nhận trễ vài giây (like, view, gợi ý)? Nếu cần ACID mạnh, mặc định ưu tiên RDBMS.
Bước 3 — Hỏi về quy mô và độ linh hoạt của cấu trúc. Khối lượng có vượt khả năng một server mạnh không? Cấu trúc mỗi bản ghi có khác nhau nhiều không? Nếu cả hai đều "có" rõ rệt, NoSQL trở nên hợp lý.
Bước 4 — Ánh xạ vào đúng họ NoSQL. Dựa vào access pattern:
- Chỉ tra theo khóa, cần tốc độ → Key-Value.
- Thực thể trọn vẹn, cấu trúc đa dạng, truy vấn theo trường → Document.
- Ghi lớn, time-series, log → Column-Family.
- Đi theo quan hệ nhiều cấp → Graph.
- Tìm kiếm toàn văn / faceted → Search engine (Elasticsearch — về mặt mô hình hóa coi như một họ riêng).
Bước 6 — Mô hình hóa cho NoSQL khác với RDBMS. Với document, bạn thường denormalize có chủ đích — nhúng dữ liệu liên quan vào trong document thay vì tách bảng và JOIN. Bạn thiết kế theo "truy vấn sẽ chạy như thế nào" (query-first design) chứ không theo chuẩn hóa. Hãy tài liệu hóa rõ cấu trúc document mẫu cho đội phát triển.
Bước 7 — Ghi rõ chiến lược đồng bộ và xử lý không nhất quán. Dữ liệu chảy từ kho gốc sang kho phái sinh bằng cách nào (CDC, event, batch)? Nếu hai kho lệch nhau thì xử lý ra sao? Đây là phần tài liệu nghiệp vụ quan trọng nhất của một hệ polyglot.
Lỗi thường gặp & mẹo
Lỗi 1 — Dùng NoSQL vì "trào lưu". Nhiều dự án chọn MongoDB chỉ vì nghe nói "nhanh và hiện đại", rồi nhận ra họ cần JOIN và giao dịch ở khắp nơi. Nếu dữ liệu của bạn có cấu trúc rõ và cần nhất quán mạnh, RDBMS vẫn là lựa chọn đúng. NoSQL không phải bản nâng cấp của SQL — nó là công cụ khác cho bài toán khác.
Lỗi 2 — Coi "NoSQL" là một thứ duy nhất. Như đã thấy, có bốn họ rất khác nhau. Nói "ta sẽ dùng NoSQL" mà không nói loại nào thì vô nghĩa như nói "ta sẽ dùng phương tiện giao thông".
Lỗi 3 — Quên mất tính nhất quán khi denormalize. Khi bạn nhúng tên khách hàng vào trong document đơn hàng, lúc khách đổi tên thì các đơn cũ có cần cập nhật không? Thường là KHÔNG (ta muốn giữ tên tại thời điểm đặt hàng), nhưng bạn phải quyết định và ghi rõ — đừng để nó là tai nạn.
Lỗi 4 — Polyglot quá đà cho hệ thống nhỏ. Một startup với 10.000 người dùng dựng 5 loại database khác nhau sẽ chết vì chi phí vận hành, không phải vì thiếu tính năng. Bắt đầu đơn giản với một RDBMS tốt; PostgreSQL hiện đại còn hỗ trợ kiểu JSONB, full-text search, và mảng — đủ dùng cho rất nhiều bài toán mà không cần thêm kho thứ hai.
Mẹo 1 — Tận dụng JSONB của PostgreSQL trước. Trước khi nhảy sang MongoDB cho "schema linh hoạt", hãy xem PostgreSQL JSONB có giải quyết được không. Bạn có thể lưu phần linh hoạt trong cột JSONB ngay trong bảng quan hệ. Đây thường là "polyglot trong một database" và đơn giản hơn nhiều.
Mẹo 2 — Luôn vẽ sơ đồ luồng dữ liệu (data flow) cho hệ polyglot. Đừng chỉ vẽ schema từng kho. Hãy vẽ mũi tên: dữ liệu sinh ra ở đâu, chảy sang đâu, đồng bộ bằng cơ chế gì. Đây là tài liệu quý nhất bạn để lại cho đội.
Mẹo 3 — Hỏi "source of truth" cho mọi trường dữ liệu quan trọng. Khi một trường tồn tại ở nhiều kho, luôn xác định kho nào là gốc. Quy tắc này giúp tránh hàng loạt bug "số liệu hai nơi không khớp".
Bài tập thực hành
Bài tập 1 — Phân loại kho dữ liệu. Cho ứng dụng đặt vé xem phim (kiểu CGV/Galaxy). Hãy liệt kê 6 loại dữ liệu sau và đề xuất kho phù hợp (RDBMS / Key-Value / Document / Column-Family / Graph / Search) kèm lý do dựa trên access pattern: (a) thông tin đặt vé và thanh toán; (b) sơ đồ ghế còn trống realtime trong lúc nhiều người chọn ghế; (c) catalog phim với metadata đa dạng (poster, trailer, thể loại, diễn viên); (d) tìm kiếm phim theo tên/diễn viên/thể loại; (e) log lượt click banner quảng cáo; (f) gợi ý "phim người khác cùng xem cũng xem".
Bài tập 2 — Thiết kế document. Lấy thực thể "Đơn hàng" của một sàn TMĐT. Hãy thiết kế cấu trúc một document JSON cho đơn hàng trong MongoDB, trong đó bạn quyết định: thông tin nào nên nhúng (embed) vào document, thông tin nào nên chỉ tham chiếu (reference bằng id) sang nơi khác. Giải thích lý do cho ít nhất 3 quyết định nhúng/tham chiếu, đặc biệt với tên khách hàng và giá sản phẩm tại thời điểm đặt.
Bài tập 3 — Xác định source of truth. Cho kiến trúc: PostgreSQL lưu hồ sơ người dùng, Elasticsearch phục vụ tìm kiếm người dùng, Redis cache hồ sơ. Khi người dùng đổi email, hãy mô tả: kho nào là source of truth, dữ liệu cần lan tỏa tới các kho khác theo trình tự nào, và điều gì xảy ra nếu một kho đồng bộ thất bại.
Bài tập 4 — Phản biện polyglot. Một bạn đồng nghiệp đề xuất dùng MongoDB + Cassandra + Neo4j + Redis cho một sản phẩm nội bộ mới có dự kiến 5.000 người dùng. Hãy viết một đoạn phản biện 150–200 từ, chỉ ra rủi ro và đề xuất một kiến trúc khởi đầu đơn giản hơn.
Tóm tắt
Mô hình quan hệ là nền tảng, nhưng nó không phải lời giải cho mọi bài toán dữ liệu. Khi schema cần linh hoạt, khi quy mô vượt một server, khi dữ liệu phi cấu trúc, hoặc khi access pattern quá đặc thù — đó là lúc NoSQL tỏa sáng. Bốn họ NoSQL cần nhớ là Key-Value (cache, session, đếm), Document (thực thể linh hoạt), Column-Family (ghi lớn, time-series) và Graph (quan hệ nhiều cấp), cùng với search engine như một công cụ tìm kiếm chuyên biệt.
Polyglot Persistence là nghệ thuật phối hợp nhiều kho dữ liệu, mỗi cái cho đúng việc — như các tình huống của sàn TMĐT, ví điện tử và ứng dụng giao đồ ăn đã minh họa. Nhưng hãy nhớ ba nguyên tắc của một BA giỏi: (1) mô tả access pattern và tính nhất quán trước khi chọn công nghệ; (2) luôn xác định source of truth và luồng đồng bộ; (3) đừng phức tạp hóa khi chưa cần — một PostgreSQL với JSONB thường đi xa hơn bạn tưởng. Hiểu được khi nào nên rời bỏ mô hình quan hệ — và quan trọng hơn, khi nào nên ở lại — chính là dấu hiệu của một người mô hình hóa dữ liệu trưởng thành.