Cơ sở dữ liệu SQL và NoSQL
Xem thử"Dùng MongoDB cho dễ scale" và cái giá của một câu nói
Trong buổi họp, một thành viên đề xuất: "Đơn hàng mình lưu MongoDB đi, NoSQL scale tốt hơn." Cả nhóm đồng ý. Ba tháng sau, kế toán yêu cầu báo cáo doanh thu theo sản phẩm và theo khách hàng, kèm đối soát với thanh toán. Mỗi báo cáo cần gom dữ liệu từ nhiều tài liệu, nhóm viết hàng trăm dòng code để làm thứ mà một câu JOIN làm được trong relational database. Rồi họ phát hiện hai request cùng trừ kho chạy song song và bán lố số lượng, vì không có giao dịch bao quanh nhiều tài liệu theo cách họ cần.
Câu chuyện không có nghĩa MongoDB tệ. Nó có nghĩa quyết định đã dựa trên một khẩu hiệu thay vì trên cách dùng dữ liệu. Bài này đặt SQL và NoSQL cạnh nhau để bạn quyết định bằng lập luận.
SQL và NoSQL là gì?
- SQL (relational): dữ liệu nằm trong bảng có schema cố định, quan hệ qua khóa ngoại, truy vấn bằng ngôn ngữ SQL. Ví dụ PostgreSQL, MySQL.
- NoSQL: tên chung cho các database không theo mô hình quan hệ truyền thống: key-value, document, wide-column, graph (đã giới thiệu ở lesson trước). Ví dụ Redis, MongoDB, Cassandra.
Điểm cốt lõi cần hiểu là hai khái niệm giúp so sánh công bằng:
- ACID (mô tả giao dịch của relational): Atomicity (nguyên tử: cả giao dịch hoặc thành công hết hoặc không gì cả), Consistency (nhất quán: dữ liệu luôn thỏa các ràng buộc), Isolation (cô lập: các giao dịch đồng thời không giẫm lên nhau), Durability (bền: đã commit thì không mất).
- BASE (cách mô tả triết lý của nhiều hệ NoSQL phân tán): Basically Available (luôn phục vụ được), Soft state (trạng thái có thể thay đổi theo thời gian khi dữ liệu đồng bộ), Eventually consistent (nhất quán sau một khoảng thời gian). Đây là cách nói chung, không phải mọi NoSQL đều như vậy. Nhiều database NoSQL hiện đại có hỗ trợ giao dịch ở các mức khác nhau.
So sánh theo tiêu chí
| Tiêu chí | SQL (relational) | NoSQL |
|---|---|---|
| Mô hình dữ liệu | Bảng, quan hệ qua khóa ngoại | Tùy nhóm: cặp khóa giá trị, tài liệu, cột, đồ thị |
| Schema | Cố định, được database kiểm tra | Linh hoạt, thường do ứng dụng kiểm soát |
| Truy vấn | SQL mạnh, JOIN, tổng hợp linh hoạt | Tối ưu cho các truy vấn đã dự tính, JOIN hạn chế hoặc không có |
| Giao dịch | ACID đầy đủ, nhiều bảng | Tùy sản phẩm, thường mạnh trong phạm vi một tài liệu hoặc một khóa |
| Nhất quán | Mạnh theo mặc định (một node) | Nhiều lựa chọn từ mạnh đến nhất quán sau một khoảng thời gian |
| Mở rộng | Scale up dễ, scale out ghi cần chia dữ liệu và phức tạp | Nhiều hệ thiết kế để chia dữ liệu và mở rộng ngang từ đầu |
| Phù hợp | Dữ liệu có quan hệ, giao dịch, báo cáo | Khối lượng lớn, cấu trúc thay đổi, truy cập theo khóa hoặc theo một mẫu cố định |
Quan sát: không có cột nào thắng tuyệt đối. Điểm mạnh của mỗi bên là điểm yếu của bên kia, và đó chính là trade-off.
Quy trình chọn
Đang tải Animation…
Đang tải sơ đồ…
Đang tải Animation…
Cách đọc sơ đồ
- Bắt đầu bằng việc mô tả dữ liệu và các truy vấn, đúng như quy trình thiết kế lesson trước.
- Có quan hệ chặt và giao dịch nhiều dòng (đơn hàng, thanh toán, kho): chọn SQL trước.
- Nếu đường truy cập đã biết, khối lượng rất lớn và hình dạng dữ liệu linh hoạt (lịch sử hoạt động, danh mục thuộc tính biến đổi): cân nhắc NoSQL cho phần dữ liệu đó, không nhất thiết cho cả hệ thống.
- Không rõ ràng: SQL vẫn là điểm xuất phát an toàn.
- Mọi nhánh đều kết thúc bằng đo đạc dưới tải thật. Lựa chọn dựa trên số liệu, không phải khẩu hiệu.
Nhánh quan trọng nhất cho quyết định ở đầu bài: đơn hàng thuộc nhánh đầu tiên. Quyết định đúng cho gdev-shop thường là PostgreSQL cho đơn hàng và thanh toán, trong khi mô tả sản phẩm có thể dùng một cột JSON trong PostgreSQL hoặc một document database.
Ví dụ: danh mục sản phẩm
Cùng một sản phẩm lưu theo hai cách:
CREATE TABLE products (
id BIGINT PRIMARY KEY,
name TEXT NOT NULL,
price_cents INT NOT NULL,
attributes JSONB
);
{
"id": 7,
"name": "Laptop GDev 14",
"priceCents": 18900000,
"attributes": { "cpu": "8 cores", "ramGb": 16, "screenInch": 14 }
}
Với SQL, cột attributes kiểu JSON cho phép linh hoạt thuộc tính theo loại hàng trong khi vẫn giữ giao dịch và JOIN cho phần còn lại. Với document database, mỗi sản phẩm là một tài liệu như trên. Trong thực tế hai hướng thu hẹp khoảng cách lẫn nhau: nhiều relational database hỗ trợ JSON, nhiều document database hỗ trợ giao dịch. Vì vậy câu hỏi quan trọng không phải "SQL hay NoSQL" mà là "truy vấn và yêu cầu nhất quán của tôi cần gì".
Khi nào chưa cần cân nhắc NoSQL, và ngược lại
- Chưa cần NoSQL khi dữ liệu có quan hệ rõ ràng, quy mô vừa phải, cần báo cáo linh hoạt. Relational database thông thường xử lý được hàng nghìn request mỗi giây trên một máy tốt.
- Chưa cần SQL cho mọi thứ khi có phần dữ liệu khối lượng rất lớn truy cập theo khóa hoặc theo mẫu cố định, như hàng trăm triệu sự kiện hoạt động.
Những hiểu lầm phổ biến
- "NoSQL scale tốt hơn SQL." Một số hệ NoSQL được thiết kế để mở rộng ngang dễ hơn, nhưng đổi lại giới hạn truy vấn và giao dịch. Relational cũng mở rộng được qua replica, partitioning và sharding, chỉ phức tạp hơn khi ghi rất lớn.
- "SQL không scale được." Nhiều hệ thống lớn chạy trên relational. Phần lớn ứng dụng không bao giờ chạm trần của một relational database được thiết kế tốt.
- "NoSQL không có giao dịch." Phụ thuộc sản phẩm. Nhiều hệ có giao dịch ở phạm vi một tài liệu, một số hỗ trợ nhiều tài liệu.
- "Eventual consistency nghĩa là dữ liệu có thể sai mãi mãi." Không. Nó nghĩa là các bản sao sẽ hội tụ về cùng giá trị sau một khoảng thời gian nếu không có thay đổi mới, nhưng trong khoảng đó người đọc có thể thấy dữ liệu cũ.
Must Know, Good to Know, Advanced
- Must Know: ACID là gì; so sánh theo mô hình, schema, truy vấn, giao dịch, nhất quán, mở rộng; không có bên nào thắng tuyệt đối; quyết định theo truy vấn và yêu cầu nhất quán.
- Good to Know: BASE và eventual consistency; JSON trong relational; polyglot persistence.
- Advanced: mức cô lập giao dịch chi tiết, NewSQL. Chỉ cần biết các mức cô lập khác nhau tồn tại.
Tự kiểm tra
- Vì sao quyết định lưu đơn hàng trong MongoDB chỉ vì "NoSQL scale tốt hơn" là thiếu căn cứ?
- Nêu một loại dữ liệu của
gdev-shophợp với NoSQL hơn đơn hàng, và lập luận. - "Eventually consistent" khác "không bao giờ nhất quán" ở điểm nào?
Đáp án gợi ý
- Vì "NoSQL scale tốt hơn" là khẩu hiệu chung, không dựa vào cách truy cập dữ liệu và yêu cầu của đơn hàng. Đơn hàng cần giao dịch nhiều bảng, ràng buộc toàn vẹn và truy vấn linh hoạt (báo cáo, hoàn tiền), những thứ mà relational database với ACID làm rất tốt. Quyết định phải xuất phát từ truy vấn và yêu cầu nhất quán đã đo, không từ danh tiếng.
- Ví dụ: log hoạt động người dùng hoặc sự kiện theo dõi hành vi: khối lượng ghi lớn, truy cập theo khóa và thời gian, ít cần giao dịch nhiều bảng và có thể chấp nhận nhất quán sau một khoảng. Một kho khóa-giá trị hoặc kho cột rộng xử lý tốt tải ghi này.
- Eventually consistent nghĩa là nếu không còn ghi mới, các bản sao sẽ hội tụ về cùng giá trị sau một khoảng thời gian, tuy trong lúc đó có thể đọc thấy giá trị cũ. "Không bao giờ nhất quán" nghĩa là các bản sao lệch nhau mãi mãi. Hội tụ là đảm bảo mạnh hơn nhiều.
Nếu chỉ nhớ 3 điều
- SQL mạnh ở quan hệ, truy vấn linh hoạt và giao dịch ACID. NoSQL gồm nhiều nhóm, thường đổi bớt truy vấn hoặc nhất quán lấy khả năng mở rộng và linh hoạt.
- Không có bên thắng tuyệt đối. Quyết định theo truy vấn, giao dịch và yêu cầu nhất quán.
- Hai bên đang xích lại gần nhau, nên đo đạc dưới tải thật quan trọng hơn khẩu hiệu.