Harbor

Theo dõi giá sản phẩm

Trung bìnhCrawl theo ưu tiênTime seriesCảnh báo giảm giáBị chặn khi crawlGiống CamelCamelCamel, Keepa, các tiện ích theo dõi giá Shopee/Tiki/Lazada

Hiểu đề

Theo dõi giá sản phẩm là gì? Người dùng dán link sản phẩm trên Shopee, Tiki, Lazada để xem biểu đồ lịch sử giá và đặt ngưỡng; khi giá xuống dưới ngưỡng thì nhận thông báo. Các sàn không đẩy dữ liệu cho mình, nên hệ thống phải tự đi lấy giá của hàng triệu sản phẩm.

Yêu cầu chức năng

  1. Người dùng thêm sản phẩm bằng link (hoặc qua tiện ích trình duyệt) và xem biểu đồ lịch sử giá
  2. Người dùng đặt cảnh báo: giá ≤ X đồng hoặc giảm ≥ Y%
  3. Hệ thống cập nhật giá định kỳ cho mọi sản phẩm đang được theo dõi
  4. Người dùng nhận push/email khi điều kiện thỏa, mỗi đợt giảm giá chỉ báo một lần

Yêu cầu phi chức năng

  • Sản phẩm hot (≥ 10 người theo dõi) cập nhật ≤ 15 phút/lần, sản phẩm ít người theo dõi ≤ 24 giờ/lần
  • Thông báo gửi đi ≤ 5 phút sau khi phát hiện giá giảm
  • Biểu đồ lịch sử 1 năm tải p95 < 300 ms
  • 0 thông báo sai do lỗi đọc giá: giá giảm bất thường (> 50%) phải được kiểm tra lại trước khi báo
  • Tốc độ gọi tới mỗi sàn không vượt trần cấu hình (ví dụ 200 req/s mỗi sàn ở mức 2)

Ngoài phạm vi: Mua hộ, thanh toán, link affiliate trong luồng mua; So sánh cùng một sản phẩm giữa nhiều sàn (ghép sản phẩm); Giá sau voucher cá nhân, phí vận chuyển; Dự đoán giá tương lai.

Thực thể và API

Product
Sản phẩm trên một sàn: (site, site_item_id) duy nhất, URL canonical, tên, ảnh, giá hiện tại, last_checked_at, next_check_at, priority, số người theo dõi.
PricePoint
Một điểm giá: product_id, thời điểm, giá, giá gốc, còn hàng hay không, nguồn (crawl/feed đối tác/tiện ích).
Watch
Cảnh báo của người dùng: product_id, target_price hoặc drop_percent, kênh nhận, trạng thái ARMED/TRIGGERED, giá lúc báo gần nhất.
Notification
Thông báo đã gửi: watch_id, episode (đợt giảm giá), giá, thời điểm; khóa (watch_id, episode) để không gửi trùng.
SiteBudget
Ngân sách crawl theo sàn: số request/s cho phép, tỉ lệ lỗi/bị chặn gần đây, trạng thái circuit breaker.
MethodEndpointGhi chú
POST/v1/productsBody {url}. Chuẩn hóa link về (site, item_id); sản phẩm mới thì xếp lịch lấy giá ngay. Trả sản phẩm + giá hiện tại.
GET/v1/products/{id}/history?range=1y&step=1dLịch sử giá đã gom theo ngày (min/max/cuối ngày). Cache ở CDN đến lần đổi giá tiếp theo.
POST/v1/watchesBody {productId, targetPrice | dropPercent, channels}. Header Idempotency-Key.
GET/v1/me/watchesDanh sách cảnh báo kèm giá hiện tại.
DELETE/v1/watches/{id}Xóa cảnh báo; giảm số người theo dõi của sản phẩm.
POST/v1/observationsTiện ích trình duyệt gửi giá người dùng đang thấy trên trang sản phẩm; chỉ dùng khi nhiều quan sát khớp nhau.

Thiết kế tổng thể

Đi lần lượt từng yêu cầu chức năng. Mục tiêu ở đây là chạy đúng, việc tối ưu để dành cho phần đào sâu.

1) Người dùng thêm sản phẩm và xem lịch sử giá

Nhiều link khác nhau (có tham số theo dõi, link rút gọn, link app) trỏ về cùng một sản phẩm, nên phải chuẩn hóa về (site, item_id) để mọi người dùng chung một dòng lịch sử.

  1. 1Client gửi POST /v1/products với link
  2. 2API mở link rút gọn, tách site và item_id, upsert Product
  3. 3Sản phẩm mới thì đặt next_check_at = now() để lấy giá ngay
  4. 4GET /history đọc bảng giá đã gom theo ngày, trả JSON nhỏ (365 điểm)

⚠ Chưa có nguồn trong thư viện: chuẩn hóa URL về (site, item_id) là thiết kế riêng của khoá học

2) Hệ thống cập nhật giá định kỳ

Scheduler chọn sản phẩm đến hạn theo độ ưu tiên và còn ngân sách của từng sàn. Chỉ ghi điểm giá khi giá hoặc tình trạng còn hàng thay đổi.

  1. 1Scheduler lấy Product có next_check_at <= now(), ưu tiên sản phẩm nhiều người theo dõi
  2. 2Token bucket theo sàn quyết định được gửi bao nhiêu task mỗi giây
  3. 3Crawler gọi trang/endpoint sản phẩm, parser lấy giá, giá gốc, còn hàng
  4. 4Trang trả về là captcha hoặc lỗi thì coi là lần lấy thất bại, không ghi giá
  5. 5Giá đổi thì ghi PricePoint và phát sự kiện PriceChanged; luôn cập nhật next_check_at theo priority

Nguồn: Grokking Modern System Design · ch.19 Rate Limiter ↗ · mục "Token bucket algorithm" ; Grokking Modern System Design · ch.23 Distributed Task Scheduler ↗ · mục "Prioritization" (delay tolerance)

3) Người dùng nhận thông báo khi giá giảm

Chỉ những lần giá giảm mới kích hoạt việc tìm watch. Watch được đánh index theo (product_id, target_price) nên một lần giảm giá chỉ đọc đúng những watch thỏa điều kiện.

  1. 1Matcher nhận PriceChanged(product, old, new)
  2. 2Giảm > 50% thì xếp lấy lại giá ngay để xác nhận trước khi báo
  3. 3Query watch WHERE product_id=? AND target_price >= new AND state='ARMED'
  4. 4UPDATE watch SET state='TRIGGERED' có điều kiện state='ARMED'; chỉ hàng đổi thành công mới tạo Notification
  5. 5Notifier gửi push (FCM/APNs) hoặc email; khi giá lên lại trên ngưỡng + 5% thì watch về ARMED

Nguồn: Grokking Modern System Design · ch.17 Distributed Messaging Queue ↗ · mục "Message deletion" (visibility_timeout, dead-letter queue) ; Microservices Patterns ↗ (.zip) · ch.3 · §3.3.6 Handling duplicate messages (tr. 95)

Hạ tầng theo quy mô

Cùng một sản phẩm, ba mức người dùng. Chọn từng mức để xem sơ đồ, phép tính và cấu hình server thay đổi thế nào.

Người dùng
10.000 DAU, 50.000 sản phẩm được theo dõi, 100.000 watch
Tải đỉnh
≈ 23 req/s API, ≈ 7,6 lượt lấy giá/s
Chi phí ước lượng
≈ 40–120 USD/tháng (ước lượng)

Dự án cá nhân hoặc startup nhỏ. Một API, một worker vừa crawl vừa kiểm tra cảnh báo, một Postgres lưu cả lịch sử giá.

Người dùngNgười dùngDịch vụAPI Node.jsDatabasePostgreSQLWorkerCrawler + alertBên ngoàiSàn TMĐTBên ngoàiFCM / EmailHTTPSsản phẩm, watchđến hạn, ghi giálấy giábáo giảm giá

Ước lượng

  • Lấy giá: 5.000 sp hot / 900 s ≈ 5,6 + 45.000 sp thường / 21.600 s (6 giờ) ≈ 2,1 → ≈ 7,6 lượt/s
  • Mỗi ngày: 5.000 × 96 + 45.000 × 4 = 480.000 + 180.000 = 660.000 lượt
  • Lịch sử: giá đổi ở 10% số lượt → 66.000 điểm × 100 B ≈ 6,6 MB/ngày × 365 ≈ 2,4 GB/năm
  • Worker: 50 request song song × 1 s mỗi request = 50 lượt/s, giữ 60% → 30; ceil(7,6 / 30) = 1
  • API: 10.000 × 20 req / 86.400 ≈ 2,3 req/s, đỉnh ×10 ≈ 23 req/s → 1 instance, chạy 2

Nguồn con số: Grokking Modern System Design · ch.5 Back-of-the-envelope Calculations ↗ · mục "Standard numbers to remember" và "Requests estimation" ; Quy tắc ước lượng của khoá học · /hoc/chon-server

Bảng hạ tầng

Thành phầnChọnSố lượngCấu hìnhVì sao
APINode.js (NestJS) trên Cloud Run22 vCPU · 4 GB RAM23 req/s lúc đỉnh, 2 instance để dự phòng.
Crawler + cảnh báoNode.js worker (fetch + cheerio)11 vCPU · 2 GB RAM7,6 lượt/s; chết vài phút chỉ làm giá trễ, lịch nằm trong DB nên không mất.
DatabasePostgreSQL managed12 vCPU · 8 GB · 50 GB SSD2,4 GB lịch sử/năm; bảng giá partition theo tháng, index (product_id, ts).
Thông báoFirebase Cloud Messaging + Amazon SES1Dịch vụ bên ngoàiVài nghìn thông báo/ngày, gửi thẳng từ worker.
Thay đổi so với mức trước
  • Điểm xuất phát: một API, một worker, một Postgres
  • Hai tầng tần suất: 15 phút cho sản phẩm hot, 6 giờ cho phần còn lại
  • Chỉ ghi điểm giá khi giá đổi
Nút thắt tiếp theo

Lên hàng triệu sản phẩm, một worker không lấy kịp và gọi dồn vào một sàn sẽ bị chặn. Bảng giá trong Postgres thường cũng bắt đầu chậm khi vẽ biểu đồ.

Cấu hình và chi phí là ước lượng để học và trình bày trong phỏng vấn. Xem cách tính ở bài Chọn server.

So sánh nhanh ba mức

Thành phầnMức 1Mức 2Mức 3
Người dùng10.000 DAU, 50.000 sản phẩm được theo dõi, 100.000 watch1 triệu DAU, 5 triệu sản phẩm, 20 triệu watch10 triệu DAU, 100 triệu sản phẩm, 500 triệu watch (thị trường theo dõi giá nhỏ hơn mạng xã hội; tải do số sản phẩm và ngày sale quyết định)
Chi phí≈ 40–120 USD/tháng (ước lượng)≈ 1.500–4.000 USD/tháng (ước lượng, chưa tính chi phí đối tác dữ liệu)≈ 25.000–60.000 USD/tháng (ước lượng)
API
Node.js (NestJS) trên Cloud Run
2 × 2 vCPU · 4 GB RAM
Node.js (NestJS), container autoscale
2–4 (autoscale) × 2 vCPU · 4 GB RAM
Node.js trên Kubernetes, 3 AZ
20 pod (HPA) × 2 vCPU · 4 GB mỗi pod
Crawler + cảnh báo
Node.js worker (fetch + cheerio)
1 × 1 vCPU · 2 GB RAM
Chưa cầnChưa cần
Database
PostgreSQL managed
1 × 2 vCPU · 8 GB · 50 GB SSD
PostgreSQL + TimescaleDB (managed)
1 primary + 1 read replica × 4 vCPU · 16 GB · 500 GB SSD
Chưa cần
Thông báo
Firebase Cloud Messaging + Amazon SES
1 × Dịch vụ bên ngoài
Chưa cầnChưa cần
CDNChưa cần
Cloudflare / CloudFront
1 × Cache JSON biểu đồ, ảnh, web app
CloudFront / Cloudflare
1 × JSON biểu đồ, ảnh, web app
Crawl schedulerChưa cần
Node.js, khóa leader trong Postgres
2 (1 chạy, 1 chờ) × 1 vCPU · 2 GB RAM
Service chia theo sàn, khóa leader qua Kubernetes lease
5 pod (1 mỗi sàn) × 2 vCPU · 4 GB mỗi pod
QueueChưa cần
Amazon SQS
3 (task crawl, giá đổi, thông báo) × Managed, có dead-letter queue
Chưa cần
CrawlerChưa cần
Node.js worker
13 (autoscale) × 1 vCPU · 2 GB RAM, 50 request song song
Chưa cần
RedisChưa cần
ElastiCache / Memorystore
1 primary + 1 replica × 2 vCPU · 4 GB
Chưa cần
NotifierChưa cần
Node.js consumer + FCM/APNs + SES
2 × 1 vCPU · 2 GB RAM
Node.js consumer + FCM/APNs + SES
4–31 pod (HPA) × 2 vCPU · 4 GB mỗi pod
Crawler + feed đối tácChưa cầnChưa cần
Node.js trên Kubernetes
123 pod (HPA) × 1 vCPU · 2 GB mỗi pod
KafkaChưa cầnChưa cần
MSK / Confluent
3 broker × Topic observations, price-changed 48 partition theo product_id, RF 3
Lịch sử giáChưa cầnChưa cần
ClickHouse (ReplicatedMergeTree)
3 shard × 2 replica × 8 vCPU · 32 GB · 1 TB mỗi node
Sản phẩm + watchChưa cầnChưa cần
DynamoDB (on-demand)
1 bảng sản phẩm + 1 bảng watch × Watch: partition key product_id, sort key target_price
Redis ClusterChưa cầnChưa cần
ElastiCache Redis Cluster
3 shard × (1 primary + 1 replica) × 2 vCPU · 13 GB mỗi node

Đào sâu

Mỗi câu hỏi đi từ cách đơn giản đến cách người phỏng vấn mong đợi ở cấp senior.

1) Có hàng triệu sản phẩm nhưng chỉ được gọi mỗi sàn vài trăm lần mỗi giây. Lấy giá cái nào trước?

Cách chưa tốt
Mỗi giờ lấy giá toàn bộ sản phẩm

Cron chạy mỗi giờ, đẩy hết sản phẩm vào queue. Sản phẩm không ai xem cũng được lấy dày như sản phẩm đang sale.

Đánh đổi: 5 triệu sản phẩm mỗi giờ là gần 1.400 req/s, vượt xa mức sàn chấp nhận; sản phẩm quan trọng phải xếp hàng sau sản phẩm vô dụng.

⚠ Chưa có nguồn trong thư viện: lấy giá toàn bộ là nhận định chung

Cách tốt
Chia tầng theo số người theo dõi + next_check_at + token bucket theo sàn

Sản phẩm hot 15 phút, ấm 2 giờ, lạnh 24 giờ. Scheduler đọc index next_check_at, mỗi sàn có token bucket riêng (Grokking ch.19) nên không sàn nào bị gọi quá trần.

Đánh đổi: Tầng cố định bỏ lỡ sản phẩm ít người theo dõi nhưng giá đổi liên tục, và không biết sản phẩm nào sắp chạm ngưỡng của ai đó.

Nguồn: Grokking Modern System Design · ch.19 Rate Limiter ↗ · mục "Token bucket algorithm" ; Grokking Modern System Design · ch.23 Distributed Task Scheduler ↗ · mục "Prioritization" (delay tolerance)

Cách tốt nhất
Điểm ưu tiên động + ngân sách theo sàn + dùng nguồn rẻ hơn crawl

Điểm = số người theo dõi × độ biến động giá gần đây × độ gần ngưỡng (giá chỉ còn cách target ≤ 5% thì lấy dày hơn), cộng thêm cho các ngày sale đã biết lịch (9.9, 11.11, 12.12). Ngân sách mỗi sàn chia theo điểm, sản phẩm lạnh vẫn có suất tối thiểu 1 lần/ngày. Trước khi crawl, kiểm tra xem đã có giá mới từ feed đối tác hoặc từ tiện ích trình duyệt chưa để bỏ qua.

Đánh đổi: Scheduler phức tạp và cần dữ liệu để tinh chỉnh trọng số; phải giám sát để sản phẩm lạnh không bị bỏ quên.

Nguồn: Grokking Modern System Design · ch.23 Distributed Task Scheduler ↗ · mục "Prioritization" (delay tolerance) ; Chưa có nguồn trong thư viện: điểm ưu tiên động là thiết kế của khoá học

2) Lưu lịch sử giá của hàng chục triệu sản phẩm thế nào để biểu đồ vẫn nhanh?

Cách chưa tốt
Mỗi lần crawl ghi một hàng vào bảng Postgres thường

Ghi cả khi giá không đổi, bảng không partition. Vẽ biểu đồ 1 năm là quét hàng nghìn hàng mỗi sản phẩm.

Đánh đổi: Hàng tỉ hàng mỗi năm, phần lớn trùng nhau; index phình to, xóa dữ liệu cũ khóa bảng.

⚠ Chưa có nguồn trong thư viện: ghi mỗi lần crawl là nhận định chung

Cách tốt
Chỉ ghi khi giá đổi + partition theo tháng + index (product_id, ts)

Giá đổi khoảng 10% số lần lấy nên dữ liệu giảm 10 lần. Partition theo tháng để drop dữ liệu cũ; biểu đồ đọc theo index một khoảng thời gian.

Đánh đổi: Ổn tới vài trăm GB; biểu đồ vẫn phải gom theo ngày mỗi lần đọc và một Postgres sẽ chật khi dữ liệu lên TB.

⚠ Chưa có nguồn trong thư viện: partition theo tháng là kỹ thuật DB chung

Cách tốt nhất
Time-series store có nén + bảng gom theo ngày tính sẵn + cache biểu đồ

Dùng TimescaleDB hoặc ClickHouse: dữ liệu theo (product_id, ts) nén theo cột, thường nhỏ đi khoảng 10 lần. Continuous aggregate gom min/max/giá cuối theo ngày nên biểu đồ 1 năm chỉ đọc 365 hàng. JSON biểu đồ của sản phẩm hot cache ở Redis/CDN và bị xóa khi có giá mới. Dữ liệu thô quá 2 năm chuyển ra S3.

Đánh đổi: Thêm một loại database phải vận hành; dữ liệu gom theo ngày trễ vài phút so với điểm giá mới nhất.

⚠ Chưa có nguồn trong thư viện: TimescaleDB/ClickHouse không có trong sách; xem /hoc/time-series-database

3) Một sản phẩm có 200.000 người đặt cảnh báo vừa giảm giá. Báo thế nào cho nhanh mà không trùng?

Cách chưa tốt
Sau mỗi lần crawl, duyệt toàn bộ bảng watch

Đọc mọi watch rồi so với giá mới trong code.

Đánh đổi: Chi phí tỉ lệ với tổng số watch cho mỗi lần đổi giá; giá dao động quanh ngưỡng thì người dùng nhận thông báo liên tục.

⚠ Chưa có nguồn trong thư viện: duyệt toàn bảng là nhận định chung

Cách tốt
Index (product_id, target_price) + queue thông báo

Khi giá giảm, query đúng các watch có target_price >= giá mới của sản phẩm đó, đẩy từng thông báo vào queue để notifier gửi.

Đánh đổi: Message bị giao lại (at-least-once) hoặc hai matcher cùng xử lý thì vẫn gửi trùng; sản phẩm 200.000 watch tạo một lô khổng lồ trong một lần.

Nguồn: Grokking Modern System Design · ch.17 Distributed Messaging Queue ↗ · mục "Message deletion" (visibility_timeout, dead-letter queue) ; Chưa có nguồn trong thư viện: index (product_id, target_price) là kỹ thuật DB chung

Cách tốt nhất
Sự kiện theo product_id + máy trạng thái ARMED/TRIGGERED có độ trễ + khóa chống trùng

PriceChanged đi qua Kafka phân vùng theo product_id nên một sản phẩm chỉ do một matcher xử lý theo thứ tự. Watch chuyển ARMED sang TRIGGERED bằng UPDATE có điều kiện, chỉ về lại ARMED khi giá lên trên ngưỡng + 5% (tránh báo liên tục khi giá dao động). Notification có khóa (watch_id, episode) nên gửi lại vẫn chỉ ra một thông báo (Microservices Patterns §3.3.6). Sản phẩm nhiều watch thì chia thành lô 500 và rải qua queue, giới hạn tốc độ theo kênh gửi.

Đánh đổi: Thêm trạng thái trên mỗi watch; người ở lô cuối của sản phẩm cực hot nhận chậm hơn vài phút.

Nguồn: Microservices Patterns ↗ (.zip) · ch.3 · §3.3.6 Handling duplicate messages (tr. 95) ; Grokking Modern System Design · ch.17 Distributed Messaging Queue ↗ · mục "Ordering of messages"

4) Sàn bắt đầu trả 403, captcha hoặc giá giả khi phát hiện crawler. Xử lý thế nào?

Cách chưa tốt
Gọi dồn dập, đổi user-agent và xoay proxy để né chặn

Tăng tốc độ khi bị chặn, giả trình duyệt và thuê dàn proxy để lách.

Đánh đổi: Vi phạm điều khoản của sàn, rủi ro pháp lý, bị chặn mạnh hơn; dữ liệu lẫn cả trang captcha nên ra giá sai.

⚠ Chưa có nguồn trong thư viện: né chặn không phải cách làm nên dùng, không có trong sách

Cách tốt
Tự giới hạn tốc độ, backoff khi gặp 429/403, cache và request có điều kiện

Mỗi sàn có trần request/s; gặp 429 hoặc 403 thì giảm tốc và lùi thời gian theo cấp số nhân. Phát hiện trang captcha thì đánh dấu lần lấy thất bại, không ghi giá. Không lấy lại sản phẩm vừa có giá mới.

Đánh đổi: Khi bị chặn thì độ tươi của dữ liệu giảm cho mọi người; vẫn phụ thuộc hoàn toàn vào việc crawl.

Nguồn: Grokking Modern System Design · ch.19 Rate Limiter ↗ · mục "Token bucket algorithm" ; Grokking Modern System Design · ch.35 Design a Web Crawler ↗ · mục "Self-throttle at a domain while crawling" và "High priority to robots.txt"

Cách tốt nhất
Ưu tiên nguồn chính thức và quan sát từ người dùng, crawl chỉ là phương án bù

Dùng feed sản phẩm của chương trình đối tác/affiliate do sàn cung cấp cho phần lớn sản phẩm phổ biến. Tiện ích trình duyệt gửi giá người dùng đang xem; giá chỉ được chấp nhận khi nhiều quan sát độc lập khớp nhau. Crawl lấp phần còn thiếu với ngân sách tự động co lại theo tỉ lệ lỗi (circuit breaker theo sàn). Parser có kiểm tra sức khỏe: tỉ lệ đọc giá thất bại tăng đột ngột thì báo động vì trang có thể vừa đổi giao diện.

Đánh đổi: Phụ thuộc vào điều khoản của chương trình đối tác; dữ liệu từ tiện ích có thể bị gian lận nên cần kiểm tra chéo.

Nguồn: Building Microservices ↗ (.zip) · ch.11 · mục "Circuit Breakers" ; Chưa có nguồn trong thư viện: feed đối tác và tiện ích trình duyệt là thiết kế của khoá học

Trong phỏng vấn

Nên nói
  • “Ngân sách gọi tới mỗi sàn là tài nguyên khan hiếm nhất, nên tôi xếp hạng sản phẩm theo số người theo dõi, độ biến động và độ gần ngưỡng.”
  • “Tôi chỉ ghi điểm giá khi giá đổi và gom sẵn theo ngày, nên biểu đồ 1 năm chỉ đọc 365 điểm.”
  • “Cảnh báo chạy theo sự kiện giá giảm, watch có trạng thái ARMED/TRIGGERED và khóa chống trùng cho mỗi đợt giảm.”
  • “Tôi ưu tiên feed đối tác và dữ liệu từ tiện ích, crawl chỉ là phương án bù và luôn tôn trọng giới hạn của sàn.”
Bẫy hay gặp
  • Lấy giá mọi sản phẩm với cùng tần suất.
  • Ghi một hàng cho mỗi lần crawl kể cả khi giá không đổi.
  • Gửi lại thông báo mỗi lần crawl khi giá vẫn dưới ngưỡng.
  • Tin ngay giá giảm 90% mà không kiểm tra lại (thường là lỗi parse hoặc trang captcha).

Đọc thêm

Time-series database
Time-series database lưu dữ liệu đo theo thời gian như metric server, giá cổ phiếu, vị trí GPS. Bài này giải thích đặc điểm của loại dữ liệu này, cách nén, downsampling và retention, so sánh TimescaleDB, InfluxDB, Prometheus, ClickHouse, và bẫy cardinality.
Kafka
Kafka là log phân tán bền vững: topic chia partition, consumer đọc theo offset và có thể đọc lại. Bài này đi qua thứ tự theo partition, consumer group, replication với ISR và acks, giới hạn của exactly-once, cách chọn số partition và so sánh với SQS, Pub/Sub, RabbitMQ.
Redis
Redis là kho key-value trong RAM với nhiều cấu trúc dữ liệu: dùng làm cache, bộ đếm, leaderboard, hàng đợi, GEO và pub/sub. Bài này đi qua mô hình dữ liệu, persistence, replication, Cluster, Lua và cách tính RAM.
DynamoDB
DynamoDB là key-value và document database serverless của AWS: độ trễ vài ms ở mọi quy mô nếu bạn chọn đúng partition key. Bài này đi qua partition key, sort key, single-table design, GSI và LSI, hai chế độ capacity, hot partition, Streams, TTL, transaction và cách nghĩ về chi phí.
Tác vụ chạy lâu
Cách xử lý việc mất vài giây tới vài giờ mà không giữ request: nhận job, trả 202 kèm job id, worker xử lý, báo kết quả qua polling, webhook hoặc SSE. Kèm hàng đợi, retry có backoff và jitter, DLQ, idempotency, heartbeat, lập lịch, workflow engine và cách tính số worker.
Caching
Đặt cache ở đâu (client, CDN, app, Redis), các chiến lược đọc ghi cache-aside, write-through, write-back, write-around, eviction LRU/LFU/TTL, invalidation, chống stampede và hot key, cách tính RAM Redis và toán hit ratio.

Sách trong thư viện của bạn. Số chương có thể lệch nhẹ so với bản PDF.