DynamoDB là gì?
Amazon DynamoDB là dịch vụ cơ sở dữ liệu NoSQL được quản lý hoàn toàn (fully managed) bởi AWS, hỗ trợ mô hình dữ liệu key-value và document. DynamoDB cung cấp hiệu năng ổn định ở mức mili-giây đơn (single-digit millisecond) bất kể quy mô dữ liệu, với khả năng tự động mở rộng (auto scaling) để xử lý hàng triệu request mỗi giây.
Use Cases phổ biến
- Gaming: Leaderboard, player sessions, game state — yêu cầu latency cực thấp
- E-commerce: Shopping cart, product catalog, order history
- IoT: Lưu trữ dữ liệu sensor, telemetry data với throughput cao
- Social Media: User profiles, feeds, likes, comments
- Ad Tech: Real-time bidding, click tracking, impression counting
- Session Management: Lưu session state cho web/mobile applications
- Serverless Backend: Kết hợp API Gateway + Lambda + DynamoDB
So sánh DynamoDB vs RDS (Relational Database)
| Tiêu chí | DynamoDB (NoSQL) | RDS (SQL) |
|---|---|---|
| Mô hình dữ liệu | Key-value / Document (schema-less) | Relational (schema-based, tables, rows, columns) |
| Ngôn ngữ truy vấn | PartiQL, API-based (GetItem, Query, Scan) | SQL (SELECT, JOIN, GROUP BY...) |
| Scaling | Horizontal (tự động, gần như vô hạn) | Vertical (scale up instance) + Read Replicas |
| Hiệu năng | Single-digit ms ở mọi quy mô | Phụ thuộc vào query complexity & instance size |
| JOIN | Không hỗ trợ (denormalize data) | Hỗ trợ đầy đủ |
| Transactions | Có (giới hạn 100 items, 4MB) | Có (ACID đầy đủ) |
| Quản lý | Serverless, fully managed | Managed nhưng vẫn cần chọn instance type |
| Chi phí | Pay-per-request hoặc provisioned capacity | Pay-per-instance (chạy 24/7) |
| Phù hợp cho | High-scale, low-latency, known access patterns | Complex queries, ad-hoc reporting, relationships |
Kiến trúc DynamoDB
DynamoDB tổ chức dữ liệu theo mô hình Tables → Items → Attributes. Mỗi table yêu cầu một Primary Key duy nhất để định danh từng item.
Các thành phần chính
Table (Bảng)
Là đơn vị tổ chức dữ liệu cao nhất. Mỗi table chứa nhiều items và yêu cầu định nghĩa Primary Key khi tạo. Không giới hạn số lượng items trong table.
Item (Bản ghi)
Tương đương một "row" trong SQL. Mỗi item là một tập hợp các attributes. Kích thước tối đa của một item là 400 KB.
Attribute (Thuộc tính)
Tương đương "column" trong SQL, nhưng DynamoDB là schema-less — mỗi item có thể có các attributes khác nhau (trừ Primary Key bắt buộc). Hỗ trợ các kiểu dữ liệu: String, Number, Binary, Boolean, Null, List, Map, Set.
Primary Key — Chìa khóa chính
DynamoDB hỗ trợ 2 loại Primary Key:
- Chỉ có Partition Key (PK)
- PK phải unique cho mỗi item
- Ví dụ:
userId= "USER#001" - Phù hợp khi mỗi entity chỉ có 1 record
- Gồm Partition Key + Sort Key (SK)
- Tổ hợp PK+SK phải unique
- Ví dụ: PK=
USER#001, SK=ORDER#2024-001 - Phù hợp cho one-to-many relationships
Cách DynamoDB phân phối dữ liệu
DynamoDB sử dụng consistent hashing trên Partition Key để phân phối items vào các partition vật lý. Mỗi partition có giới hạn:
- 10 GB dung lượng lưu trữ
- 3,000 RCU (Read Capacity Units) hoặc 1,000 WCU (Write Capacity Units)
Khi vượt giới hạn, DynamoDB tự động split partition. Tuy nhiên, throughput không được chia đều nếu access pattern không đồng đều.
Capacity Modes — Chế độ dung lượng
DynamoDB cung cấp 2 chế độ tính phí và quản lý throughput: Provisioned và On-Demand. Bạn có thể chuyển đổi giữa 2 mode mỗi 24 giờ.
- Bạn chỉ định trước số RCU/WCU cần thiết
- Trả phí theo capacity đã provision (dù dùng hay không)
- Hỗ trợ Auto Scaling để tự điều chỉnh
- Có thể mua Reserved Capacity (giảm đến 77%)
- Phù hợp: traffic ổn định, dự đoán được
- Không cần chỉ định capacity trước
- Trả phí theo số request thực tế (pay-per-request)
- Tự động scale lên/xuống ngay lập tức
- Đắt hơn ~6.5x so với Provisioned (per request)
- Phù hợp: traffic không dự đoán, spiky workloads
RCU & WCU — Cách tính toán
Read Capacity Unit (RCU)
| Loại đọc | Công thức | Ví dụ (item 6 KB, 10 reads/s) |
|---|---|---|
| Strongly Consistent Read | 1 RCU = 1 read/s cho item ≤ 4 KBRCU = ⌈item_size / 4KB⌉ × reads/s |
⌈6/4⌉ × 10 = 2 × 10 = 20 RCU |
| Eventually Consistent Read | 1 RCU = 2 reads/s cho item ≤ 4 KBRCU = ⌈item_size / 4KB⌉ × reads/s / 2 |
⌈6/4⌉ × 10 / 2 = 2 × 5 = 10 RCU |
| Transactional Read | Tốn gấp 2x Strongly ConsistentRCU = ⌈item_size / 4KB⌉ × reads/s × 2 |
⌈6/4⌉ × 10 × 2 = 2 × 20 = 40 RCU |
Write Capacity Unit (WCU)
| Loại ghi | Công thức | Ví dụ (item 2.5 KB, 5 writes/s) |
|---|---|---|
| Standard Write | 1 WCU = 1 write/s cho item ≤ 1 KBWCU = ⌈item_size / 1KB⌉ × writes/s |
⌈2.5/1⌉ × 5 = 3 × 5 = 15 WCU |
| Transactional Write | Tốn gấp 2x Standard WriteWCU = ⌈item_size / 1KB⌉ × writes/s × 2 |
⌈2.5/1⌉ × 5 × 2 = 3 × 10 = 30 WCU |
Auto Scaling (Provisioned Mode)
Khi dùng Provisioned Mode, bạn nên bật Auto Scaling để DynamoDB tự điều chỉnh RCU/WCU dựa trên CloudWatch metrics:
- Target Utilization: Mức sử dụng mục tiêu (ví dụ: 70%)
- Minimum Capacity: Giá trị RCU/WCU tối thiểu
- Maximum Capacity: Giá trị RCU/WCU tối đa
Auto Scaling sử dụng Application Auto Scaling service và tạo CloudWatch alarms tự động. Lưu ý: scale up nhanh (vài phút) nhưng scale down chậm hơn (15 phút cooldown).
Secondary Indexes — Chỉ mục phụ
Secondary Indexes cho phép bạn truy vấn dữ liệu theo các attributes khác ngoài Primary Key. DynamoDB hỗ trợ 2 loại: Global Secondary Index (GSI) và Local Secondary Index (LSI).
| Đặc điểm | GSI (Global Secondary Index) | LSI (Local Secondary Index) |
|---|---|---|
| Partition Key | Khác với base table (bất kỳ attribute nào) | Giống base table |
| Sort Key | Tùy chọn, bất kỳ attribute nào | Bắt buộc, khác với base table SK |
| Thời điểm tạo | Bất kỳ lúc nào (sau khi tạo table) | Chỉ khi tạo table (không thể thêm sau) |
| Giới hạn số lượng | 20 GSI / table | 5 LSI / table |
| Consistency | Chỉ Eventually Consistent | Eventually hoặc Strongly Consistent |
| Capacity | Có RCU/WCU riêng (tách biệt base table) | Dùng chung RCU/WCU với base table |
| Size limit | Không giới hạn | 10 GB per partition key value |
Projections — Chọn attributes cho Index
Khi tạo index, bạn chọn attributes nào sẽ được "project" (copy) vào index:
KEYS_ONLY— Chỉ copy Primary Key của base table + index key. Tiết kiệm storage nhất.INCLUDE— Copy thêm một số attributes cụ thể mà bạn chỉ định.ALL— Copy toàn bộ attributes. Tốn storage nhất nhưng không cần fetch lại base table.
- Chỉ tạo GSI khi thực sự cần query pattern mới — mỗi GSI tốn thêm WCU khi write
- Dùng
KEYS_ONLYhoặcINCLUDEthay vìALLđể giảm chi phí storage - GSI throttling có thể ảnh hưởng base table — đảm bảo GSI có đủ WCU
- Cân nhắc dùng sparse index: chỉ items có attribute đó mới xuất hiện trong index
DynamoDB Streams — Change Data Capture
DynamoDB Streams ghi lại mọi thay đổi (insert, update, delete) trên table theo thứ tự thời gian. Dữ liệu stream được lưu giữ trong 24 giờ và có thể được xử lý bởi Lambda, Kinesis, hoặc ứng dụng tùy chỉnh.
Stream View Types
| View Type | Nội dung record | Use Case |
|---|---|---|
KEYS_ONLY |
Chỉ Primary Key của item bị thay đổi | Trigger nhẹ, chỉ cần biết item nào thay đổi |
NEW_IMAGE |
Toàn bộ item sau khi thay đổi | Sync data sang hệ thống khác |
OLD_IMAGE |
Toàn bộ item trước khi thay đổi | Audit log, rollback |
NEW_AND_OLD_IMAGES |
Cả trước và sau khi thay đổi | So sánh diff, complex event processing |
Lambda Trigger với DynamoDB Streams
Đây là pattern phổ biến nhất: Lambda function tự động được invoke khi có thay đổi trong table.
- Giảm số lượng API calls (1 Query lấy nhiều entity types)
- Giảm latency (không cần multiple round-trips)
- Transactions dễ dàng hơn (cùng table)
- Ít tables = ít quản lý capacity
- Phức tạp hơn khi thiết kế và maintain
- Khó hiểu cho team mới
- Cần biết trước tất cả access patterns
- Khó thay đổi access patterns sau này
2. Partition Key Design — Thiết kế khóa phân vùng
Partition Key tốt phải có high cardinality (nhiều giá trị unique) và uniform distribution (traffic phân bố đều).
| PK kém | PK tốt | Lý do |
|---|---|---|
status (active/inactive) |
userId |
status chỉ có 2 giá trị → hot partition |
date (2024-01-15) |
userId#date |
Tất cả writes trong ngày đổ vào 1 partition |
country (VN, US, JP) |
orderId |
Ít giá trị, traffic không đều giữa các nước |
deviceType (iOS/Android) |
deviceId |
Chỉ 2-3 giá trị → extreme hot partition |
Write Sharding — Kỹ thuật phân tán ghi
Khi PK tự nhiên có low cardinality, thêm random suffix hoặc calculated suffix để phân tán:
# Thay vì PK = "2024-01-15" (hot partition)
# Thêm random suffix: PK = "2024-01-15#3"
import random
def get_sharded_pk(date_str, num_shards=10):
"""Tạo sharded partition key"""
shard = random.randint(0, num_shards - 1)
return f"{date_str}#{shard}"
# Write: PK sẽ là "2024-01-15#0", "2024-01-15#1", ..., "2024-01-15#9"
# Read: Cần query tất cả 10 shards rồi merge kết quả
pk = get_sharded_pk("2024-01-15")
# Scatter-Gather pattern cho read
def query_all_shards(date_str, num_shards=10):
results = []
for shard in range(num_shards):
pk = f"{date_str}#{shard}"
items = dynamodb.query(
TableName='Events',
KeyConditionExpression='PK = :pk',
ExpressionAttributeValues={':pk': {'S': pk}}
)
results.extend(items['Items'])
return results
3. Cost Optimization — Tối ưu chi phí
4. Các Pattern quan trọng khác
- Optimistic Locking: Dùng
ConditionExpressionvới version number để tránh concurrent update conflicts - TTL (Time to Live): Set attribute chứa epoch timestamp → DynamoDB tự xóa item khi hết hạn (miễn phí, không tốn WCU)
- Sparse Index: Chỉ items có attribute được index mới xuất hiện trong GSI → tiết kiệm storage và RCU
- Adjacency List: Dùng PK + SK để model graph relationships (many-to-many)
- Composite Sort Key: Kết hợp nhiều attributes vào SK:
STATUS#SHIPPED#2024-01-15→ hỗ trợ begins_with queries
AWS CLI Cheat Sheet — DynamoDB
Tổng hợp các lệnh AWS CLI thường dùng nhất khi làm việc với DynamoDB.
Quản lý Table
# Tạo table với Composite Primary Key (Provisioned mode)
aws dynamodb create-table \
--table-name Users \
--attribute-definitions \
AttributeName=PK,AttributeType=S \
AttributeName=SK,AttributeType=S \
--key-schema \
AttributeName=PK,KeyType=HASH \
AttributeName=SK,KeyType=RANGE \
--billing-mode PROVISIONED \
--provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5
# Tạo table On-Demand mode
aws dynamodb create-table \
--table-name Events \
--attribute-definitions AttributeName=eventId,AttributeType=S \
--key-schema AttributeName=eventId,KeyType=HASH \
--billing-mode PAY_PER_REQUEST
# Liệt kê tables
aws dynamodb list-tables
# Xem thông tin table
aws dynamodb describe-table --table-name Users
# Xóa table
aws dynamodb delete-table --table-name Users
# Chuyển đổi capacity mode
aws dynamodb update-table \
--table-name Users \
--billing-mode PAY_PER_REQUEST
CRUD Operations
# PutItem — Tạo/ghi đè item
aws dynamodb put-item \
--table-name Users \
--item '{
"PK": {"S": "USER#001"},
"SK": {"S": "PROFILE"},
"name": {"S": "Nguyen Van Minh"},
"email": {"S": "minh@example.com"},
"age": {"N": "28"}
}'
# GetItem — Đọc 1 item (Strongly Consistent)
aws dynamodb get-item \
--table-name Users \
--key '{"PK": {"S": "USER#001"}, "SK": {"S": "PROFILE"}}' \
--consistent-read
# UpdateItem — Cập nhật attributes
aws dynamodb update-item \
--table-name Users \
--key '{"PK": {"S": "USER#001"}, "SK": {"S": "PROFILE"}}' \
--update-expression "SET age = :newAge, updatedAt = :now" \
--expression-attribute-values '{
":newAge": {"N": "29"},
":now": {"S": "2024-01-15T10:30:00Z"}
}'
# DeleteItem — Xóa item
aws dynamodb delete-item \
--table-name Users \
--key '{"PK": {"S": "USER#001"}, "SK": {"S": "PROFILE"}}'
# Conditional Write — Chỉ ghi nếu điều kiện thỏa mãn
aws dynamodb put-item \
--table-name Users \
--item '{"PK": {"S": "USER#003"}, "SK": {"S": "PROFILE"}, "name": {"S": "Test"}}' \
--condition-expression "attribute_not_exists(PK)"
Query & Scan
# Query — Lấy tất cả items của 1 user
aws dynamodb query \
--table-name Users \
--key-condition-expression "PK = :pk" \
--expression-attribute-values '{":pk": {"S": "USER#001"}}'
# Query — Lấy orders của user (SK begins_with)
aws dynamodb query \
--table-name Users \
--key-condition-expression "PK = :pk AND begins_with(SK, :sk)" \
--expression-attribute-values '{
":pk": {"S": "USER#001"},
":sk": {"S": "ORDER#"}
}'
# Query với filter (filter sau khi đọc, vẫn tốn RCU)
aws dynamodb query \
--table-name Users \
--key-condition-expression "PK = :pk AND begins_with(SK, :sk)" \
--filter-expression "total > :minTotal" \
--expression-attribute-values '{
":pk": {"S": "USER#001"},
":sk": {"S": "ORDER#"},
":minTotal": {"N": "100000"}
}'
# Query trên GSI
aws dynamodb query \
--table-name Users \
--index-name email-index \
--key-condition-expression "email = :email" \
--expression-attribute-values '{":email": {"S": "minh@example.com"}}'
# Scan — Đọc toàn bộ table (TRÁNH dùng trong production!)
aws dynamodb scan --table-name Users --max-items 10
# Scan với filter
aws dynamodb scan \
--table-name Users \
--filter-expression "age > :minAge" \
--expression-attribute-values '{":minAge": {"N": "25"}}'
Batch Operations
# BatchWriteItem — Ghi nhiều items (tối đa 25 items, 16 MB)
aws dynamodb batch-write-item --request-items '{
"Users": [
{
"PutRequest": {
"Item": {
"PK": {"S": "USER#010"},
"SK": {"S": "PROFILE"},
"name": {"S": "User 10"}
}
}
},
{
"DeleteRequest": {
"Key": {
"PK": {"S": "USER#005"},
"SK": {"S": "PROFILE"}
}
}
}
]
}'
# BatchGetItem — Đọc nhiều items (tối đa 100 items, 16 MB)
aws dynamodb batch-get-item --request-items '{
"Users": {
"Keys": [
{"PK": {"S": "USER#001"}, "SK": {"S": "PROFILE"}},
{"PK": {"S": "USER#002"}, "SK": {"S": "PROFILE"}}
],
"ProjectionExpression": "PK, #n, email",
"ExpressionAttributeNames": {"#n": "name"}
}
}'
Backup, Streams & TTL
# Bật Point-in-Time Recovery
aws dynamodb update-continuous-backups \
--table-name Users \
--point-in-time-recovery-specification PointInTimeRecoveryEnabled=true
# Tạo On-Demand Backup
aws dynamodb create-backup \
--table-name Users \
--backup-name users-backup-2024-01-15
# Bật DynamoDB Streams
aws dynamodb update-table \
--table-name Users \
--stream-specification StreamEnabled=true,StreamViewType=NEW_AND_OLD_IMAGES
# Bật TTL
aws dynamodb update-time-to-live \
--table-name Sessions \
--time-to-live-specification Enabled=true,AttributeName=expiresAt
# Tạo GSI
aws dynamodb update-table \
--table-name Users \
--attribute-definitions AttributeName=email,AttributeType=S \
--global-secondary-index-updates '[
{
"Create": {
"IndexName": "email-index",
"KeySchema": [{"AttributeName": "email", "KeyType": "HASH"}],
"Projection": {"ProjectionType": "ALL"},
"ProvisionedThroughput": {
"ReadCapacityUnits": 5,
"WriteCapacityUnits": 5
}
}
}
]'
# Export table to S3
aws dynamodb export-table-to-point-in-time \
--table-arn arn:aws:dynamodb:ap-southeast-1:123456789012:table/Users \
--s3-bucket my-dynamodb-exports \
--s3-prefix users/ \
--export-format DYNAMODB_JSON
# SELECT (Query)
aws dynamodb execute-statement \
--statement "SELECT * FROM Users WHERE PK='USER#001' AND begins_with(SK, 'ORDER#')"
# INSERT
aws dynamodb execute-statement \
--statement "INSERT INTO Users VALUE {'PK': 'USER#099', 'SK': 'PROFILE', 'name': 'Test User'}"
# UPDATE
aws dynamodb execute-statement \
--statement "UPDATE Users SET age=30 WHERE PK='USER#001' AND SK='PROFILE'"
# DELETE
aws dynamodb execute-statement \
--statement "DELETE FROM Users WHERE PK='USER#099' AND SK='PROFILE'"