⚖️ SQL vs NoSQL: chọn công cụ theo nhu cầu

By Better Wise

Vì sao câu hỏi không phải là "SQL hay NoSQL cái nào tốt hơn"

Bài trước ta đã kiểm tra bản backup bằng cách restore thử, tức là tin vào hành vi thật chứ không tin vào lời hứa. Bài này giữ nguyên tinh thần đó, nhưng chuyển từ "giữ dữ liệu an toàn" sang "chọn công cụ nào". Lý do cần chọn đúng: đổi database về sau đắt hơn đổi index hay đổi migration rất nhiều.

Sau bài này, bạn sẽ:

  • Phân biệt SQL và NoSQL theo mô hình dữ liệu, không theo trend.
  • Nhận ra dấu hiệu dữ liệu và truy vấn nghiêng về bên nào.
  • Đọc được ba ví dụ chọn công cụ và giải thích được lý do.
  • Biết chi phí ẩn khi bỏ JOIN hoặc bỏ schema.

Nhìn vào hình dạng dữ liệu trước

SQL (relational) tổ chức dữ liệu thành bảng có schema cố định, liên kết bằng khóa ngoại. NoSQL là tên gọi chung cho các mô hình khác, gồm bốn nhóm chính: document, key-value, wide-column, graph.

Tiêu chí SQL NoSQL
Mô hình chính Bảng, quan hệ, khóa ngoại Document, key, cột rộng, đồ thị
Schema Chặt, khai báo trước Linh hoạt, kiểm tra ở code
JOIN Có sẵn, tối ưu tốt Thường join trong application code
Nhất quán ACID, đúng ngay Thường eventual consistency (BASE)
Truy vấn báo cáo Linh hoạt, ad-hoc dễ Phải thiết kế trước theo access pattern
Điểm mạnh Ràng buộc, transaction, tính đúng Ghi nhiều, mở rộng ngang, schema đổi nhanh

Một câu để nhớ: SQL giỏi trả lời câu hỏi bạn chưa nghĩ ra, NoSQL giỏi trả lời thật nhanh câu hỏi bạn đã biết trước.

Nhìn vào cách ứng dụng đọc và ghi

Trước khi chọn, trả lời bốn câu, bằng số liệu nếu có:

  1. Truy vấn chính có JOIN mấy bảng? Nếu câu trả lời là 3 đến 5 bảng, đó là tín hiệu SQL.
  2. Mỗi giây ghi bao nhiêu bản ghi? Đây là con số phải đo, không phải đoán.
  3. Có cần đúng ngay lập tức không? Trừ kho, trừ tiền, đặt chỗ thì cần transaction và ACID.
  4. Dữ liệu có một hình dạng ổn định hay mỗi bản ghi một kiểu?

Ba ví dụ mở chậm

Ví dụ 1, shop nhỏ. Bảng users, orders, order_items, payments. Một lần đặt hàng phải tạo order, thêm dòng order_items, trừ kho, ghi payment, tất cả phải cùng thành công hoặc cùng hủy. Chọn PostgreSQL. Ở đây transaction là yêu cầu bắt buộc, và báo cáo doanh thu theo tháng là truy vấn JOIN bạn chưa viết lúc thiết kế. Nếu AI đề xuất nhét items dạng JSON vào một collection để "cho linh hoạt", hãy hỏi lại: báo cáo top sản phẩm bán chạy sẽ query thế nào?

Ví dụ 2, telemetry thiết bị. Ghi 50.000 sự kiện mỗi giây, chỉ đọc theo device_id cộng khoảng thời gian, không JOIN, dữ liệu cũ tự hết hạn. Chọn wide-column hoặc time-series. Nhưng đo trước: nếu thực tế chỉ 2.000 sự kiện mỗi giây và cần JOIN với bảng thiết bị để hiển thị, PostgreSQL có partition theo tháng vẫn chạy tốt. Đừng chọn Cassandra cho một bài toán chưa tới ngưỡng đó.

Ví dụ 3, catalog sản phẩm nhiều loại. Điện thoại có RAM, pin; áo có size, màu. Mỗi loại một bộ thuộc tính. Đọc luôn lấy nguyên một document để render trang chi tiết, không JOIN. Document database hợp lý ở đây. Nhưng nếu cùng catalog đó cần báo cáo tồn kho theo 6 chiều, hãy tách phần báo cáo sang SQL, đó là polyglot persistence: mỗi công cụ một việc.

flowchart TD
  A["❓ Dữ liệu và truy vấn"] --> B{"Nhiều bảng quan hệ<br/>và cần transaction?"}
  B -->|Có| C["🐘 SQL"]
  B -->|Không| D{"Truy cập chủ yếu<br/>theo một key?"}
  D -->|Có| E["⚡ Key-value"]
  D -->|Không| F{"Đọc nguyên một<br/>document lồng nhau?"}
  F -->|Có| G["📄 Document"]
  F -->|Không| H["📊 Wide-column hoặc graph"]

Cây này chỉ để khoanh vùng. Bước cuối vẫn là đo tải thật trên dữ liệu thật.

Bốn chỗ trượt chân khi chọn database

  • Chọn theo trend. "Dự án mới nên dùng NoSQL" không phải lý do kỹ thuật. Lý do phải là access pattern và con số tải.
  • Quên chi phí ẩn của việc bỏ JOIN. Phép join chuyển về application code, nhiều round-trip hơn, và rất dễ tái sinh đúng lỗi N+1 bạn vừa học ở Module 1.
  • Tưởng schema linh hoạt là miễn phí. Kiểm tra dữ liệu không biến mất, nó chỉ chuyển từ database sang code, và dữ liệu rác lọt vào sẽ rất khó query về sau.
  • Bỏ qua nhất quán. Ví dụ trừ tồn kho trong hệ eventual consistency có thể bán quá số lượng. Nếu nghiệp vụ không chịu được, cần SQL hoặc cơ chế bù trừ rõ ràng.

Mẹo của người viết EXPLAIN: viết ra 5 truy vấn quan trọng nhất của ứng dụng trước khi chọn database, rồi tự hỏi mỗi truy vấn sẽ viết thế nào ở cả hai bên. Bên nào viết ngắn hơn và ít bước hơn thường là bên đúng.

Mang theo gì sau bài này

Ba điều cần nhớ:

  • Chọn theo hình dạng dữ liệu và access pattern, không theo nhãn SQL hay NoSQL.
  • Cần transaction và ràng buộc chặt thì nghiêng về SQL; cần ghi cực nhiều, truy cập theo key hoặc theo document thì nghiêng về NoSQL.
  • Đo trước khi đoán, và đừng ngại dùng cả hai.

Điền nhanh để tự kiểm tra:

  1. SQL lưu dữ liệu theo _____ với schema cố định, còn document database lưu theo _____ lồng nhau.
  2. Khi phải trừ kho và tạo order trong cùng một lần ghi, ta cần _____.
  3. Key-value store phù hợp khi truy cập chủ yếu theo _____ và cần độ trễ rất thấp.
  4. Bỏ JOIN trong database thường đẩy phép join về _____, rất dễ sinh truy vấn N+1.

Front: SQL tổ chức dữ liệu theo mô hình nào? Back: Bảng có schema cố định, liên kết bằng khóa ngoại, JOIN để kết hợp.

Front: Bốn nhóm NoSQL chính là gì? Back: Document, key-value, wide-column, graph.

Front: Document database hợp khi nào? Back: Khi mỗi bản ghi là một khối lồng nhau, đọc ghi nguyên document, ít JOIN chéo.

Front: Key-value store hợp khi nào? Back: Truy cập chủ yếu theo key và cần độ trễ rất thấp: session, cache, counter.

Front: Wide-column hợp với bài toán nào? Back: Ghi rất nhiều, truy vấn theo partition key cộng khoảng giá trị, ví dụ telemetry, time-series.

Front: ACID khác BASE ở điểm nào? Back: ACID đúng ngay và nhất quán mạnh; BASE ưu tiên sẵn sàng, nhất quán sau (eventual).

Front: Dấu hiệu rõ nhất để chọn SQL? Back: Nhiều bảng quan hệ, cần transaction và ràng buộc, cần truy vấn báo cáo ad-hoc.

Front: Polyglot persistence là gì? Back: Dùng nhiều loại database trong cùng hệ thống, mỗi loại cho một nhu cầu.

Front: Chi phí ẩn khi bỏ JOIN trong database là gì? Back: Phải join trong application code, nhiều round-trip hơn, dễ thành N+1.

Front: Vì sao schema linh hoạt không miễn phí? Back: Kiểm tra dữ liệu chuyển sang code, dữ liệu rác dễ lọt vào và khó query về sau.

Front: Sai lầm phổ biến nhất khi chọn NoSQL? Back: Chọn vì trend hoặc 'để scale' khi chưa đo tải thật.

Front: Trước khi chọn database nên viết ra gì? Back: Năm truy vấn quan trọng nhất và thử viết chúng ở cả hai bên.

Front: Hệ eventual consistency có rủi ro gì với tồn kho? Back: Có thể bán quá số lượng nếu nghiệp vụ cần nhất quán tức thời.

Bài tiếp theo là checkpoint của module: bạn sẽ gặp các tình huống vừa backup vừa chọn công cụ, và tự quyết định trước khi xem lời giải.