Chương 4. Workload Quyết Định Kiến Trúc

Một sai lầm rất phổ biến khi thiết kế hệ thống là chọn công nghệ trước khi hiểu việc mình cần xử lý thuộc loại gì.

Ta nghe nói Node.js async rất tốt, FastAPI nhanh, Django chắc chắn, Go nhẹ, Java mạnh, Kafka scale, Redis nhanh. Những nhận xét đó có phần đúng, nhưng chưa đủ. Câu hỏi quan trọng hơn là:

> Workload của hệ thống là loại việc gì?

Một request đọc dữ liệu 50ms, một job gọi API ngoài 90 giây, một tác vụ resize ảnh, một quá trình transcode video, một truy vấn báo cáo lớn, một kết nối WebSocket giữ hàng giờ - tất cả đều là "việc hệ thống phải làm", nhưng bản chất rất khác nhau.

Nếu dùng cùng một cách xử lý cho mọi loại workload, hệ thống sẽ hoặc chậm, hoặc tốn tài nguyên, hoặc rất khó vận hành.

---

4.1. Ví dụ quán bánh: không phải việc nào cũng giống nhau

Trong quán bánh, có nhiều loại việc:

  • Khách hỏi giá một chiếc bánh.
  • Nhân viên lấy bánh có sẵn đưa cho khách.
  • Bếp làm một chiếc bánh sinh nhật theo yêu cầu.
  • Nhân viên gọi shipper.
  • Kế toán tổng hợp doanh thu cuối ngày.
  • Quản lý xem báo cáo tháng.

Những việc này không nên xử lý giống nhau.

Hỏi giá bánh cần trả lời ngay. Lấy bánh có sẵn cũng nên nhanh. Làm bánh sinh nhật mất lâu, nên nhận đơn rồi hẹn giờ. Gọi shipper phụ thuộc bên ngoài, có thể chậm hoặc lỗi. Báo cáo cuối ngày có thể chạy nền. Báo cáo tháng có thể chạy batch.

Nếu bắt khách đứng chờ tại quầy 2 tiếng để bếp làm bánh sinh nhật xong rồi mới nhận phiếu, quán sẽ tắc. Cách đúng là nhận đơn nhanh, đưa phiếu hẹn, rồi để bếp xử lý sau.

Hệ thống web cũng vậy. Không phải mọi thứ đều nên nằm trong request.

---

4.2. Workload là gì?

Workload là loại công việc mà hệ thống phải xử lý.

Ví dụ:

  • Request đọc dữ liệu.
  • Request ghi dữ liệu.
  • Upload file.
  • Download file.
  • Gửi email.
  • Gọi API thanh toán.
  • Gọi AI model.
  • Resize ảnh.
  • Transcode video.
  • Import Excel.
  • Generate PDF.
  • Search.
  • Realtime chat.
  • Analytics event.
  • Báo cáo tổng hợp.

Mỗi workload có đặc điểm riêng:

  • Mất bao lâu?
  • Dùng CPU nhiều hay chỉ chờ I/O?
  • Cần trả kết quả ngay không?
  • Có thể retry không?
  • Có thể xử lý song song không?
  • Có cần đúng ngay không?
  • Có chấp nhận dữ liệu chậm vài giây không?
  • Có tốn tiền theo request không?
  • Có giới hạn từ bên thứ ba không?

Kiến trúc tốt bắt đầu từ việc phân loại workload, không bắt đầu từ framework.

---

4.3. I/O-bound: phần lớn thời gian là chờ

I/O-bound là loại việc không tốn nhiều CPU, nhưng mất thời gian vì phải chờ thứ khác.

Ví dụ:

  • Chờ database trả kết quả.
  • Chờ API thanh toán.
  • Chờ email provider.
  • Chờ AI provider.
  • Chờ file upload.
  • Chờ đọc file từ disk.
  • Chờ network.

Trong lúc chờ, CPU thường không làm gì nhiều.

Ví dụ một job gọi API AI:

Gửi request đến AI provider
Chờ 90 giây
Nhận response
Lưu kết quả

Trong 90 giây đó, phần lớn thời gian hệ thống chỉ chờ mạng và provider xử lý.

Với I/O-bound, vấn đề thường không phải CPU yếu. Vấn đề là hệ thống có đủ concurrency để chờ nhiều việc cùng lúc không.

Các cách xử lý I/O-bound:

  • Thread pool.
  • Async/event loop.
  • Greenlet/coroutine.
  • Worker concurrency cao.
  • Queue nếu việc không cần trả ngay.

Sai lầm thường gặp:

  • Dùng ít worker process nên nhiều job phải xếp hàng.
  • Giữ web request trong lúc chờ API ngoài rất lâu.
  • Không có timeout.
  • Không có rate limit.
  • Tăng concurrency nhưng làm cạn database connection hoặc vượt quota provider.

---

4.4. CPU-bound: hệ thống thật sự phải tính toán

CPU-bound là loại việc tốn năng lực tính toán của CPU.

Ví dụ:

  • Nén ảnh lớn.
  • Encode/transcode video.
  • Chạy thuật toán nặng.
  • Render PDF phức tạp.
  • Xử lý dữ liệu lớn trong memory.
  • Machine learning inference trên CPU.
  • Tính toán báo cáo rất nặng.

Với CPU-bound, hệ thống không chỉ "chờ". Nó thật sự đang dùng CPU.

Nếu một tác vụ CPU-bound chiếm 100% một core trong 30 giây, việc tăng async không giúp nhiều. Event loop không làm CPU tự nhiên mạnh hơn.

Các cách xử lý CPU-bound:

  • Tách sang worker process.
  • Tăng số process theo số CPU core.
  • Dùng machine riêng cho xử lý nặng.
  • Dùng queue để không làm nghẽn web request.
  • Dùng GPU nếu workload phù hợp.
  • Tối ưu thuật toán.
  • Batch hoặc precompute nếu có thể.

Sai lầm thường gặp:

  • Chạy CPU-bound trong web server.
  • Dùng thread trong Python cho CPU-bound rồi kỳ vọng tăng tốc mạnh.
  • Cho quá nhiều process khiến CPU context switching cao.
  • Không giới hạn số job nặng chạy cùng lúc.

---

4.5. Memory-bound: nghẽn vì RAM

Một số workload không nghẽn CPU hay network trước, mà nghẽn RAM.

Ví dụ:

  • Import file CSV/Excel lớn và load hết vào memory.
  • Query quá nhiều dữ liệu rồi xử lý trong app.
  • Generate báo cáo lớn.
  • Xử lý ảnh/video giữ nhiều buffer.
  • Load model AI lớn.
  • Tạo quá nhiều worker process nặng.

Dấu hiệu:

  • RAM tăng đều.
  • Server bị OOM kill.
  • Swap tăng.
  • Response chậm dần.
  • Worker chết bất thường.

Cách xử lý:

  • Stream dữ liệu thay vì load hết.
  • Xử lý theo batch nhỏ.
  • Giới hạn file size.
  • Giới hạn concurrency.
  • Dùng process riêng cho job nặng.
  • Theo dõi memory per job.
  • Chọn machine có RAM phù hợp nếu thật sự cần.

Sai lầm thường gặp:

  • Nghĩ chỉ cần tăng timeout là xong.
  • Không đo memory.
  • Cho phép upload/import quá lớn mà không giới hạn.
  • Tăng worker concurrency khiến RAM phình mạnh.

---

4.6. Database-bound: nghẽn ở database

Nhiều hệ thống web chết không phải vì backend code, mà vì database.

Database-bound xảy ra khi:

  • Query chậm.
  • Thiếu index.
  • N+1 query.
  • Transaction giữ lock lâu.
  • Connection pool cạn.
  • Ghi quá nhiều.
  • Đọc quá nhiều.
  • Báo cáo nặng chạy trên database chính.

Dấu hiệu:

  • CPU database cao.
  • Slow query tăng.
  • Backend chờ database.
  • Connection pool timeout.
  • Lock wait.
  • Request p95/p99 tăng.

Cách xử lý:

  • Tối ưu query.
  • Thêm index đúng chỗ.
  • Giảm N+1.
  • Cache read-heavy data.
  • Tách report sang read replica hoặc warehouse.
  • Giới hạn transaction dài.
  • Dùng queue cho write burst nếu phù hợp.
  • Scale database khi đã tối ưu hợp lý.

Sai lầm thường gặp:

  • Thêm backend server khi database mới là bottleneck.
  • Dùng cache để che query sai mà không hiểu dữ liệu.
  • Chạy báo cáo nặng trực tiếp trên database production.
  • Mở quá nhiều worker làm cạn connection pool.

---

4.7. Network-bound: nghẽn vì đường truyền và khoảng cách

Network-bound xảy ra khi thời gian chủ yếu mất ở đường truyền.

Ví dụ:

  • Người dùng ở xa server.
  • Tải ảnh/video lớn.
  • API gọi service ở region khác.
  • Upload file lớn.
  • Download file lớn.
  • Service gọi nhau qua network quá nhiều.

Cách xử lý:

  • CDN cho static asset/file.
  • Nén response.
  • Giảm kích thước payload.
  • Đặt server gần người dùng hơn.
  • Dùng object storage/CDN cho file.
  • Tránh chatty API giữa services.
  • Batch request nếu hợp lý.

Sai lầm thường gặp:

  • Cho file lớn đi qua backend dù có thể upload trực tiếp object storage.
  • Service A gọi service B rất nhiều lần trong một request.
  • Không để ý region của database, app và external service.

---

4.8. Request/response workload

Đây là loại workload phổ biến nhất: người dùng gửi request và cần response ngay.

Ví dụ:

  • Xem trang chi tiết.
  • Lấy danh sách sản phẩm.
  • Đăng nhập.
  • Kiểm tra quyền.
  • Thêm sản phẩm vào giỏ.
  • Đặt lịch.

Đặc điểm:

  • Cần latency thấp.
  • Không nên làm việc lâu.
  • Cần timeout rõ.
  • Cần trả lỗi dễ hiểu.
  • Cần bảo vệ bằng auth/rate limit nếu public.

Phù hợp với:

  • HTTP API.
  • REST/GraphQL/gRPC tùy bài toán.
  • Cache nếu read-heavy.
  • Database transaction nếu ghi dữ liệu quan trọng.

Không phù hợp để:

  • Gửi email lâu.
  • Transcode video.
  • Chờ API ngoài 90 giây.
  • Generate report lớn.
  • Import file lớn.

Tư duy:

> Request path nên ngắn. Việc gì có thể làm sau thì đưa ra sau.

---

4.9. Background job workload

Background job là việc xử lý nền.

Ví dụ:

  • Gửi email.
  • Xử lý file.
  • Gọi API AI.
  • Sync dữ liệu sang search index.
  • Tính báo cáo.
  • Dọn dữ liệu cũ.
  • Retry webhook.

Đặc điểm:

  • Không cần trả ngay trong request.
  • Có thể mất nhiều giây/phút.
  • Cần retry.
  • Cần theo dõi trạng thái.
  • Cần idempotency.
  • Cần monitoring queue/worker.

Phù hợp với:

  • Message queue.
  • Worker.
  • Scheduler.
  • Workflow engine nếu nhiều bước.

Không nên:

  • Chạy âm thầm mà không có trạng thái.
  • Retry vô hạn.
  • Không giới hạn concurrency.
  • Không có dead letter.
  • Không đo queue wait time.

---

4.10. Realtime workload

Realtime workload là loại cần cập nhật liên tục hoặc gần như ngay lập tức.

Ví dụ:

  • Chat.
  • Presence.
  • Tracking vị trí.
  • Collaborative editing.
  • Game.
  • Tiến độ xử lý job.
  • Dashboard realtime.

Không phải realtime nào cũng giống nhau.

Nếu chỉ cần cập nhật trạng thái mỗi vài giây, polling có thể đủ.

Nếu server cần đẩy trạng thái một chiều, SSE có thể phù hợp.

Nếu client và server cần nói chuyện hai chiều liên tục, WebSocket phù hợp hơn.

Đặc điểm:

  • Nhiều connection mở lâu.
  • Cần quản lý disconnect/reconnect.
  • Cần fan-out nếu gửi cho nhiều người.
  • Cần Pub/Sub hoặc broker nội bộ khi scale nhiều server.
  • Cần chú ý memory và connection limit.

Sai lầm thường gặp:

  • Dùng WebSocket cho mọi thứ.
  • Không có chiến lược reconnect.
  • Không nghĩ đến scale nhiều instance.
  • Không tách message persistence khỏi realtime delivery.

---

4.11. File/media workload

File/media workload có đặc điểm khác request dữ liệu thông thường.

Ví dụ:

  • Upload ảnh.
  • Upload video.
  • Download file.
  • Transcode video.
  • Generate thumbnail.
  • Scan virus.
  • Extract metadata.

Đặc điểm:

  • File có thể lớn.
  • Upload/download tốn bandwidth.
  • Xử lý media có thể CPU-bound.
  • Người dùng cần progress.
  • File nên nằm ở object storage.
  • Delivery nên qua CDN nếu nhiều người tải.

Kiến trúc thường dùng:

Client -> Object Storage
Backend -> lưu metadata
Queue -> Worker xử lý media
CDN -> phục vụ file

Sai lầm thường gặp:

  • Đẩy file lớn qua backend không cần thiết.
  • Lưu file trong database.
  • Không giới hạn file size.
  • Không scan file nguy hiểm.
  • Không tách xử lý media sang worker.

---

4.12. Analytics workload

Analytics workload khác workload nghiệp vụ.

Ví dụ:

  • Ghi nhận user click gì.
  • Đếm số lượt xem.
  • Tính conversion rate.
  • Tạo dashboard.
  • Phân tích hành vi.
  • Training recommendation.

Đặc điểm:

  • Số lượng event có thể rất lớn.
  • Không phải event nào cũng cần ghi vào database nghiệp vụ.
  • Có thể xử lý batch hoặc stream.
  • Thường chấp nhận chậm vài giây/phút/giờ.
  • Cần hệ lưu trữ phù hợp phân tích.

Phù hợp với:

  • Event collector.
  • Queue/event streaming.
  • Data warehouse.
  • OLAP database.
  • Batch processing.
  • Stream processing nếu cần realtime analytics.

Sai lầm thường gặp:

  • Nhét mọi event click vào PostgreSQL chính.
  • Chạy dashboard nặng trên database production.
  • Không có schema cho event.
  • Không nghĩ đến data quality.

---

4.13. External API workload

Nhiều hệ thống phụ thuộc API bên ngoài.

Ví dụ:

  • Payment.
  • Shipping.
  • Email.
  • SMS.
  • AI.
  • Map.
  • Identity provider.

Đặc điểm:

  • Không kiểm soát latency.
  • Không kiểm soát uptime.
  • Có quota/rate limit.
  • Có thể tính phí.
  • Có thể timeout.
  • Có thể trả lỗi tạm thời.

Cần có:

  • Timeout.
  • Retry có backoff.
  • Rate limit.
  • Idempotency nếu có side effect.
  • Logging.
  • Alert.
  • Fallback hoặc trạng thái pending nếu cần.

Với external API mất lâu, nên cân nhắc:

  • Đưa vào background job.
  • Tăng concurrency nếu I/O-bound.
  • Không giữ web request.
  • Có trạng thái cho người dùng.
  • Có cơ chế reconcile nếu kết quả không rõ.

---

4.14. Bảng chọn nhanh theo workload

| Workload | Đặc điểm | Cách xử lý thường hợp | |---|---|---| | Đọc dữ liệu nhanh | Cần trả ngay | API + database/cache | | Ghi dữ liệu quan trọng | Cần đúng | Transaction + validation | | Gửi email/SMS | Có thể làm sau | Queue + worker | | Gọi API ngoài lâu | I/O-bound | Queue/worker concurrency hoặc async service | | Xử lý ảnh/video | CPU/memory/file-heavy | Object storage + queue + worker | | Tìm kiếm phức tạp | Read/search-heavy | Search engine | | Realtime chat | Nhiều connection | WebSocket + Pub/Sub + persistence | | Analytics event | Volume lớn, không cần sync ngay | Event pipeline/warehouse | | Báo cáo lớn | Batch/read-heavy | Background job/read replica/warehouse | | File download lớn | Network-heavy | Object storage + CDN |

Bảng này không phải luật cứng. Nó là điểm bắt đầu để suy nghĩ.

---

4.15. Một tính năng có thể chứa nhiều workload

Một tính năng nhìn đơn giản có thể chứa nhiều loại workload.

Ví dụ: người dùng đăng bài bán bánh có ảnh.

Các việc bên trong:

1. Gửi form tạo bài: request/response. 2. Lưu thông tin bài: database transaction. 3. Upload ảnh: file workload. 4. Resize ảnh: CPU-bound background job. 5. Lưu ảnh: object storage. 6. Cập nhật search index: background job/event. 7. Gửi notification: queue. 8. Ghi analytics event: event pipeline.

Nếu gom tất cả vào một request, người dùng sẽ chờ lâu và hệ thống dễ lỗi.

Kiến trúc tốt tách các workload bên trong cùng một tính năng:

  • Việc cần đúng ngay thì làm trong transaction.
  • Việc có thể làm sau thì đưa vào queue.
  • File lớn đi object storage.
  • Tìm kiếm dùng search index.
  • Analytics đi pipeline riêng.

---

4.16. Cách hỏi trước khi chọn kiến trúc

Trước khi chọn framework, queue, database hay microservice, hãy hỏi:

Việc này có cần trả kết quả ngay không?

Nếu cần, tối ưu request path.

Nếu không, cân nhắc queue/job nền.

Việc này mất bao lâu?

Nếu dưới vài trăm ms, API trực tiếp có thể ổn.

Nếu vài giây, cần cẩn thận timeout và UX.

Nếu hàng chục giây/phút, thường nên là background job.

Nó chờ I/O hay dùng CPU?

I/O-bound cần concurrency.

CPU-bound cần process/core/machine phù hợp.

Nó có side effect không?

Ví dụ:

  • Trừ tiền.
  • Tạo đơn.
  • Gửi email.
  • Ghi điểm.

Nếu có side effect, retry phải đi cùng idempotency.

Nó có thể chạy trùng không?

Nếu chạy trùng gây lỗi, cần lock/idempotency/unique constraint.

Nó có cần đúng ngay không?

Nếu cần đúng ngay, dùng transaction mạnh.

Nếu chấp nhận đồng bộ sau, có thể dùng event/queue.

Nếu nó lỗi thì sao?

  • Retry?
  • Báo user?
  • Đưa vào dead letter?
  • Cho phép làm lại?
  • Cần người vận hành can thiệp?

---

4.17. Những lỗi tư duy phổ biến

Lỗi 1: Chọn framework trước khi hiểu workload

Framework không tự giải quyết workload sai cách. Một tác vụ CPU-bound chạy trong event loop vẫn làm nghẽn. Một tác vụ I/O-bound chạy với quá ít worker vẫn chậm.

Lỗi 2: Xử lý mọi thứ trong request

Request nên ngắn. Nếu việc lâu không cần trả ngay, đưa ra queue.

Lỗi 3: Dùng queue cho mọi thứ

Queue tốt cho việc xử lý sau, nhưng không nên biến mọi logic thành bất đồng bộ nếu nghiệp vụ cần kết quả ngay và transaction rõ.

Lỗi 4: Nhầm external API chậm với backend yếu

Nếu backend chậm vì chờ provider, đổi backend framework chưa chắc giúp. Cần timeout, concurrency, queue, rate limit, fallback.

Lỗi 5: Không tách workload trong cùng một tính năng

Một endpoint có thể vừa ghi database, vừa upload file, vừa gửi email, vừa gọi API ngoài. Cần tách phần nào phải làm ngay và phần nào làm sau.

Lỗi 6: Không nghĩ đến vận hành

Thêm queue, worker, search engine, object storage, event pipeline đều cần monitoring và xử lý lỗi. Nếu không vận hành được, kiến trúc "đúng" trên giấy vẫn thất bại.

---

4.18. Kết luận của chương

Workload quyết định kiến trúc.

Trước khi hỏi "dùng công nghệ gì", hãy hỏi "việc này thuộc loại gì":

  • Cần trả ngay hay xử lý sau?
  • I/O-bound hay CPU-bound?
  • Có dùng nhiều RAM không?
  • Có nghẽn database không?
  • Có phụ thuộc network không?
  • Có phải file/media không?
  • Có cần realtime không?
  • Có phải analytics không?
  • Có gọi API ngoài không?

Mỗi loại workload có cách xử lý phù hợp. Kiến trúc tốt không phải là chọn một công nghệ thật mạnh cho mọi thứ, mà là đặt từng loại việc vào đúng nơi nó nên được xử lý.

Khi hiểu workload, ta sẽ thấy nhiều quyết định trở nên rõ ràng:

  • Request nhanh để trong API.
  • Việc lâu đưa vào queue.
  • File lớn đưa vào object storage.
  • Nội dung tĩnh đưa qua CDN.
  • Tìm kiếm phức tạp dùng search engine.
  • Analytics tách khỏi database nghiệp vụ.
  • I/O-bound cần concurrency.
  • CPU-bound cần tài nguyên tính toán thật.

Đó là nền tảng để các chương sau bàn sâu hơn về monolith, queue, microservices, cache, database và hạ tầng mà không bị lạc vào tranh luận công nghệ thuần túy.