Chúng ta đã biết ưu điểm của DynamoDB qua bài viết trước, tuy nhiên nếu kết hợp sử dụng Single Table Design (STD) nó như "hổ mọc thêm cánh" vậy. Đại khái là: “Dùng một bảng DynamoDB để lưu cả thế giới” yes he he, đó là STD! Thay vì chia dữ liệu thành chục bảng như MySQL, STD nhét mọi thứ vào một bảng duy nhất, vừa tiết kiệm, vừa siêu nhanh. Nghe điên vãi =))) Đúng, nhưng điên kiểu thiên tài! Cùng “mổ xẻ” để xem bí mật này là gì nhé.
Single Table Design là gì?
Trong MySQL, bạn quen chia dữ liệu thành nhiều bảng: Users, Orders, Pets, mỗi bảng một nhiệm vụ. STD với Dynamo thì bảo: “Nhiều bảng làm gì cho mệt? Một bảng là đủ!”. STD là cách thiết kế để tất cả dữ liệu liên quan – khách hàng, đơn hàng, thú cưng – sống chung trong một bảng, được truy vấn siêu tốc nhờ Primary Key (partition key, sort key) và Index (GSI, LSI).
Tại sao nên sử dụng STD?
- Tối ưu hiệu suất:
- DynamoDB truy vấn nhanh nhất khi dùng khóa chính. STD đảm bảo mọi thứ bạn cần đều nằm trong một bảng, giảm số lần gọi API.
- Với app PetShop, bạn muốn lấy thông tin khách hàng và tất cả đơn hàng của họ. Nếu dùng nhiều bảng, bạn phải query riêng bảng Users, rồi bảng Orders – tốn 2 API calls, chậm hơn chút. Với STD, một Query duy nhất vào bảng PetShop lấy hết cả hai.
Gọi bánh tráng + trà sữa 2 nơi thì bạn tốn 2 lần phí ship. Gọi cùng 1 chổ thì chỉ tốn 1 lần thôi =))
- Tiết kiệm chi phí: Một bảng tốn ít storage và RCU/WCU hơn nhiều bảng. Ai mà không thích tiết kiệm tiền?
- Trong PetShop, nếu dùng ba bảng (Users, Orders, Pets), bạn phải trả tiền cho storage và throughput của cả ba. Với STD, chỉ một bảng PetShop, nhìn chung thì centralize lại sẽ ít tiết kiệm hơn.
- Linh hoạt: STD xử lý ngon lành dữ liệu phức tạp, kể cả khi app phát triển thêm tính năng.
- PetShop ban đầu chỉ bán mèo, giờ muốn thêm dịch vụ “thuê thú cưng”? Với STD, bạn chỉ cần thêm item mới như {PK: "USER#U001", SK: "RENTAL#R001", pet_id: "P001"} vào bảng PetShop, không cần tạo bảng mới. Nếu dùng MySQL, bạn phải tạo bảng Rentals, lại đau đầu migration.
Với PetShop, STD giúp bạn lưu mọi thứ – từ thông tin khách, đơn hàng, đến thú cưng – trong bảng PetShop. Muốn lấy dữ liệu? Chỉ cần một truy vấn, nhanh gọn lẹ! Nhưng để đạt được mức 1 truy vấn lấy đầy đủ data cũng không phải đơn giản nhé.
Làm sao để thiết kế một bảng “bao sân”?
STD không phải kiểu nhét bừa mọi thứ vào một “sọt rác” dữ liệu. Bạn cần thiết kế dựa trên mô hình truy vấn (Access pattern) – tức là nghĩ trước xem người dùng sẽ hỏi gì và tối ưu bảng để trả lời nhanh nhất. Các bước cơ bản:
- Xác định truy vấn chính (ví dụ: lấy đơn hàng của khách, lấy thú cưng theo loại).
- Chọn partition key và sort key để hỗ trợ các truy vấn đó.
- Dùng chỉ mục (GSI) để xử lý các truy vấn phụ nếu cần.
- Denormalize dữ liệu – tức là lặp lại thông tin để tránh phải “JOIN kiểu NoSQL”.
Ví dụ với PetShop, bạn muốn hỗ trợ các truy vấn:
- Lấy thông tin khách hàng theo userid.
- Lấy tất cả đơn hàng của một khách hàng.
- Lấy danh sách thú cưng theo loại (mèo, chó).
Thay vì ba bảng, bạn tạo bảng PetShop với:
- Partition key: PK (ví dụ: USER#<user_id> cho khách, PET#<pet_id> cho thú cưng).
- Sort key: SK (ví dụ: INFO cho thông tin chính, ORDER#<order_id> cho đơn hàng).
Mỗi item trong bảng có thể trông như sau:
PK | SK | name | order_id | user_id | total | type | price | |
---|---|---|---|---|---|---|---|---|
USER#U001 | INFO | Nam | nam@gmail.com | |||||
USER#U001 | ORDER#O001 | O001 | U001 | 500000 | ||||
PET#P001 | INFO | Miu | cat | 500000 |
Giờ muốn lấy đơn hàng của Nam? Chỉ cần Query: PK = "USER#U001" AND SK begins_with "ORDER#". Muốn query info của Nam? PK = "USER#U001" AND SK begins_with "INFO".
Bí kíp denormalization: Sao chép "thông minh"
MySQL dạy bạn tránh lặp dữ liệu để tiết kiệm chỗ, nhưng DynamoDB thì bảo: “Sao chép đi, đừng ngại!”. Denormalization là cách lặp lại thông tin để truy vấn nhanh hơn, vì DynamoDB không có JOIN.
Ở PetShop, bạn muốn hiển thị danh sách đơn hàng kèm tên khách hàng mà không cần query thêm. Thay vì chỉ lưu user_id trong đơn hàng, bạn sao chép cả tên khách luôn:
PK | SK | order_id | user_id | user_name | total |
---|---|---|---|---|---|
USER#U001 | ORDER#O001 | O001 | U001 | Nam | 500000 |
Giờ lấy đơn hàng, bạn có ngay tên “Nam” mà không cần chạy Query đến item user với id "U001" nữa. Nhược điểm? Nếu tên user đổi, bạn phải cập nhật cả ở đơn hàng của user nhé – nhưng truy vấn nhanh hơn. Có đáng để trade-off không thì bạn là người quyết =)))
Dùng GSI để “hack” truy vấn linh hoạt
Partition key và sort key đôi khi không đủ. Global Secondary Index (GSI) là cứu tinh, giống như một “góc nhìn khác” của bảng chính, cho phép truy vấn theo cách mới.
PetShop muốn tìm tất cả thú cưng là mèo. Bạn thêm GSI1 cho bảng và thêm 2 field GSI1PK + GSI1SK như sau:
PK | SK | name | type | price | GSI1PK | GSI1SK |
---|---|---|---|---|---|---|
PET#P001 | INFO | Miu | cat | 500000 | TYPE#CAT | INFO |
Muốn lấy tất cả mèo? Query GSI1: GSI1PK = "TYPE#CAT", GSI1SK = "INFO". Kết quả trả về danh sách mèo ngay.
STD có phải silver bullet không?
STD nghe đỉnh, nhưng không phải lúc nào cũng hoàn hảo.
- Ưu điểm: Tiết kiệm chi phí, truy vấn nhanh, quản lý dễ. Với PetShop, một bảng xử lý từ khách hàng đến đơn hàng, bớt đau đầu.
- Nhược điểm: Thiết kế ban đầu cần tư duy kỹ, thay đổi sau hơi mệt. Nếu cần báo cáo phức tạp như “doanh thu mèo so với chó theo tháng”, STD có thể khiến bạn toát mồ hôi hơn MySQL. Đây cũng là lúc tận dụng DynamoDB Stream, nhưng đó là 1 câu chuyện khác =))
PetShop chạy ngon với STD cho truy vấn như xem đơn hàng, tìm thú cưng. Nhưng nếu muốn phân tích “tỷ lệ mua mèo vào thứ Bảy”, có thể cần export dữ liệu sang Redshift, vì STD không mạnh về báo cáo phức tạp.
Kết luận: STD – Một thiết kế đáng để thử
Single Table Design là một trong những lý do khiến DynamoDB phổ biến – nhanh, rẻ, mạnh. Với PetShop, thay vì đau đầu với ba bảng, bạn dùng một bảng để xử lý mọi thứ, từ tìm mèo đến xem đơn hàng. Nhưng nhớ, STD đòi hỏi bạn nghĩ trước, thiết kế kỹ, và sẵn sàng denormalize một cách thông minh.