---
title: "Thiết kế hệ thống - ước lượng tải, load balancer, cache, queue, replication và partitioning"
description: "Khoá học nền về thiết kế hệ thống, đi từ requirement, tải cao điểm và constraint tới kiến trúc. Bạn ước lượng dung lượng, băng thông và số instance, rồi chọn cache, queue, replica hay partition cho đúng workload, tìm bottleneck và so sánh phương án. Mỗi thành phần thêm vào đều gắn với một triệu chứng đo được."
canonical: "https://200lab.io/courses/thiet-ke-he-thong-uoc-luong-tai-load-balancer-cache-queue-replication-va-partitioning"
type: "course"
course_type: "course"
learning_type: "free_form"
published: "2026-10-04T01:23:05Z"
students: 0
---

## Khoá học này mang lại gì cho bạn

Hệ thống của bạn cần những thành phần nào, và cần thêm từ lúc nào? Developer backend nào cũng gặp câu hỏi này khi sản phẩm bắt đầu có người dùng thật. Câu trả lời nằm ở requirement, tải cao điểm và constraint của chính hệ thống đó, nên khoá học đi từ những đầu vào này tới kiến trúc, rồi tới cách tìm bottleneck và so sánh phương án.

- **Viết requirement bằng số liệu** — Bạn phân biệt functional requirement, non-functional requirement và constraint, rồi biến những yêu cầu chung chung về tốc độ và độ ổn định thành số liệu đo được: SLO, error budget, RPO và RTO.
- **Ước lượng trước khi dựng hệ thống** — Từ tải cao điểm theo từng phút, tỉ lệ đọc/ghi và payload, bạn ước lượng nhanh dung lượng lưu trữ, băng thông và số instance cần chạy, chừa headroom và ghi rõ từng giả định để sau này đối chiếu với metric thật.
- **Chỉ định nơi giữ dữ liệu gốc** — Bạn chọn source of truth cho từng loại dữ liệu và bảo đảm cache, search index hay bảng báo cáo đều dựng lại được từ đó.
- **Chọn thành phần theo triệu chứng đo được** — Với load balancer, cache, queue, read replica, partition, object storage và CDN, bạn biết lúc nào cần đến từng thành phần, nó cải thiện được số liệu nào và đi kèm tradeoff gì.
- **Tìm bottleneck trước khi thêm máy** — Bạn xem utilization, saturation và errors ở từng chặng trên request path để tìm tài nguyên bão hòa sớm nhất, rồi chỉ ra lỗi ở một thành phần sẽ lan ra những đâu.
- **So sánh phương án và ghi lại quyết định** — Bạn thiết lập hai phương án từ cùng một đầu vào thiết kế, so sánh theo latency, chi phí mỗi tháng, số thành phần phải vận hành và failure mode, rồi ghi quyết định cùng phương án bị loại vào ADR (Architecture Decision Record, bản ghi quyết định kiến trúc).

Trọng tâm là nhận ra lúc nào hệ thống cần thêm một thành phần và ước lượng thành phần đó cải thiện được bao nhiêu; cách cấu hình từng công cụ nằm ngoài phạm vi. Ví dụ trong bài dùng Postgres, Redis và một cửa hàng online nhỏ, còn phép tính và cách chọn thì không phụ thuộc vào ngôn ngữ, framework hay nhà cung cấp cloud bạn đang dùng.

## Khoá học cung cấp những nền tảng nào

Nguyên tắc xuyên suốt là ghi lại **đầu vào thiết kế** trước khi vẽ sơ đồ: requirement, constraint và workload, trong đó phần nào đo được thì ghi bằng số liệu. Sau đó, mỗi thành phần chỉ được thêm vào khi nó giải quyết một triệu chứng đo được. Phần mô tả tải và đo hiệu năng bám theo chương đầu của sách [Designing Data-Intensive Applications](https://dataintensive.net/) của Martin Kleppmann, phần SLO và error budget bám theo chương [Service Level Objectives](https://sre.google/sre-book/service-level-objectives/) trong sách SRE của Google, còn cách tìm bottleneck dùng [phương pháp USE](https://www.brendangregg.com/usemethod.html) của Brendan Gregg.

Nội dung chia thành bảy nhóm, nhóm sau dùng lại những gì nhóm trước đã trình bày. Các nhóm 5, 6 và 7 kết thúc bằng một bài tình huống, nơi bạn áp dụng những gì vừa học vào một nghiệp vụ cụ thể và đo kết quả trước và sau khi sửa.

### 1. Request đi qua những đâu, state nằm ở đâu và đọc dashboard thế nào

Bài nền đưa ra cách vẽ hệ thống mà cả khoá học dùng chung: request path đi từ client tới database, mọi chỗ đang giữ state được đánh dấu riêng. Bạn phân biệt tầng stateless với tầng stateful, chuyển session ra một nơi lưu chung để có thể thêm instance app an toàn, và thấy app stateless không có nghĩa là state đã biến mất khỏi hệ thống. Chặng nào trên request path cũng có thể chậm hoặc hỏng, nên các bài sau đều quay lại hình vẽ này.

Bài thứ hai mở bằng một dashboard có ba panel đều xanh, trong khi chỉ trong một phút đã có **90 lần** bấm đặt hàng nhận lỗi 503. Bạn đọc latency theo p50, p95 và p99 thay cho số trung bình, vì trong gần **18.000 request** thành công của phút đó, **180 request** chờ khoảng **2,5 giây** chỉ kéo số trung bình từ **100 ms** lên **124 ms**. Throughput cần được đọc cùng latency, vì API xử lý xong nhiều request hơn chưa chắc khách đã chờ ít hơn.

Error rate thì phải được tính trên request của đúng thao tác cần theo dõi: cùng **90 lỗi**, chia cho mọi request của API thì được **0,5%**, chia riêng cho request đặt hàng thì được **7,5%**. Cuối bài, bạn phân biệt utilization với saturation: CPU mới **45%**, nhưng connection pool đã dùng hết và khoảng **25 request** đang xếp hàng chờ connection.

### 2. Các loại requirement và cách biến chúng thành số liệu

API xuất hóa đơn PDF qua hết mười hai test chức năng, nhưng tối ngày cuối tháng p99 của nó tăng lên **40 giây**, vì chưa ai hỏi có bao nhiêu cửa hàng sẽ xuất hóa đơn cùng lúc. Từ tình huống đó, bạn phân biệt functional requirement, non-functional requirement và constraint (ngân sách, team, công nghệ sẵn có). Nhóm bài cũng đưa ra tám câu hỏi về non-functional requirement nên hỏi trước khi thiết kế.

Ở trang thanh toán, yêu cầu mơ hồ về tốc độ và độ ổn định được viết lại thành hai dòng SLO: với SLO **99,9%** trong **30 ngày**, chỉ **15 phút** sập vào giờ cao điểm đã dùng hết error budget của cả tháng. Phần cuối nhóm trình bày RPO, RTO và cách xếp ưu tiên khi hai requirement mâu thuẫn nhau.

### 3. Mô tả workload, ước lượng nhanh và xác định dữ liệu gốc

Bạn mô tả workload bằng tải cao điểm theo từng phút, burst, tỉ lệ đọc/ghi, payload và tốc độ tăng của tải và dữ liệu, lấy từ access log thay vì dựa vào số trung bình theo ngày. Phần ước lượng nhanh (back-of-the-envelope) tính dung lượng lưu trữ và băng thông cho một tính năng chat chưa ra mắt, rồi dùng Little’s Law để chọn số instance: với cùng **500 request/giây**, khi mỗi request mất **400 ms** thay vì **80 ms**, số request đang xử lý cùng lúc tăng gấp năm lần. Bài cuối nhóm trình bày cách xác định source of truth cho từng loại dữ liệu, để cache, search index hay bảng báo cáo luôn dựng lại được từ đó.

### 4. Các thành phần cơ bản: khi nào cần và tradeoff

Sáu bài, mỗi bài một thành phần, cùng xét ba điểm: khi nào cần, cải thiện được số liệu nào và tradeoff là gì. Load balancer chia tải và loại instance đã chết khỏi danh sách nhận request, nhưng tự nó có thể trở thành single point of failure. Với cache-aside và hit ratio **88%**, CPU của Postgres giảm từ **85%** xuống khoảng **16%**, đổi lại người dùng có thể thấy dữ liệu cũ trong khoảng thời gian TTL.

Queue đưa việc gửi email ra khỏi request đăng ký; read replica nhận những truy vấn đọc mà dữ liệu cũ vài giây vẫn chấp nhận được; partition theo ngày thay job xóa dữ liệu cũ chạy suốt đêm bằng một câu lệnh drop partition. Bài về object storage và CDN đưa ra một kết quả nhiều người không ngờ: khi CDN phục vụ **95%** số byte ảnh, chi phí băng thông chỉ giảm khoảng **28%**, còn thời gian backup database được rút ngắn từ **5 giờ** xuống khoảng **30 phút**.

### 5. Hệ thống lớn lên theo từng nấc

Một cửa hàng online nhỏ chạy tám tháng đầu chỉ với một app và một Postgres, tới năm thứ ba đã có bảy thành phần, và hóa đơn hạ tầng tăng từ khoảng **3 triệu đồng** lên **24 triệu đồng** mỗi tháng. Ở mỗi nấc, bạn thấy triệu chứng đo được nào báo hiệu đã tới lúc lên nấc, thành phần nào được thêm vào và vì sao, cùng những thành phần chưa cần dù hay xuất hiện trong sơ đồ của các hệ thống lớn.

Nhóm kết thúc bằng bài tình huống về dịch vụ rút gọn link: một link chiến dịch bị share mạnh làm p99 của redirect tăng từ **12 ms** lên **1,8 giây**. Từ requirement và bản ước lượng tải, bạn đặt cache trước database để đưa p99 về dưới **50 ms**, rồi so sánh hai cách sinh mã ngắn không trùng nhau.

### 6. Tìm bottleneck và xem lỗi lan ra như thế nào

Bottleneck đầu tiên là tài nguyên bão hòa sớm nhất trên request path, và thêm tài nguyên ở chỗ khác không làm throughput tăng. Bạn tìm nó bằng cách xem utilization, saturation và errors ở từng chặng, rồi ước lượng throughput tối đa mà tài nguyên đó chịu được để biết bottleneck sẽ chuyển sang đâu sau khi sửa.

Bài tiếp theo giải thích vì sao lỗi ở một service phụ lan sang cả những endpoint không gọi tới nó, và cách tính availability khi request phải đi qua nhiều thành phần nối tiếp hoặc khi một tầng có instance dự phòng. Trong ví dụ, số phút tầng app không dùng được mỗi tháng giảm từ khoảng **216 phút** xuống khoảng **1 phút** chỉ nhờ thêm một instance. Ở bài tình huống cuối nhóm, bạn lần ngược từ một triệu chứng lặp lại đúng giờ mỗi sáng để tìm ra tài nguyên mà API đặt hàng và job báo cáo đang dùng chung, rồi chuyển báo cáo sang một replica riêng và partition bảng `orders` theo tháng.

### 7. Thiết lập, so sánh phương án và ghi lại quyết định thiết kế

Khách gõ sai chính tả thì trang tìm kiếm không trả về sản phẩm nào. Từ cùng requirement, constraint và workload, bạn thiết lập hai phương án thật sự khác nhau: tìm gần đúng ngay trong Postgres bằng extension trigram, hoặc thêm Elasticsearch đồng bộ dữ liệu từ Postgres.

Bảng so sánh thay những dòng ưu nhược điểm chung chung bằng giá trị đo ở cùng một workload: latency, chi phí mỗi tháng, số thành phần phải vận hành, failure mode và độ trễ đồng bộ. Quyết định được ghi vào ADR cùng bối cảnh có số liệu, phương án bị loại và điều kiện xem lại, để một năm sau team không phải mất hai tuần đo lại từ đầu. Bài tình huống cuối khoá thêm một đợt flash sale với tải dự kiến **8.000 request/giây** vào cửa hàng đang chạy ổn ở **200 request/giây**, và bạn so sánh hai kiến trúc ghép từ những thành phần đã học bằng cùng một kịch bản load test k6.

## Vì sao 200Lab tạo ra khoá học này

Ngày nay, dựng thêm một thành phần chỉ mất vài phút. Redis, queue hay read replica đều có sẵn dưới dạng managed service, còn coding agent vẽ được một sơ đồ có đủ load balancer, cache, queue và replica chỉ từ một đoạn mô tả tính năng.

Vì việc dựng đã rẻ, câu hỏi khó dồn về phía bạn: với tải cao điểm, ngân sách và số người trong team hiện tại, hệ thống có cần thành phần đó không, và cần từ lúc nào. Muốn trả lời, bạn cần những đầu vào đó được ghi lại thành văn bản và số liệu, không chỉ là ước chừng của từng người trong team.

Khi thiếu số liệu, sai lầm thường đi theo một trong hai hướng, và người gánh hậu quả là bạn. Dựng thừa thì bạn phải vận hành cả những thành phần mà team chưa từng xử lý sự cố với chúng: một cụm Cassandra hỏng lúc **3 giờ** sáng có thể mất hơn **4 giờ** mới chạy lại vì cả team chỉ quen Postgres, trong khi hóa đơn tháng đầu đã gần gấp ba ngân sách. Dựng thiếu thì dashboard báo mọi thứ ổn suốt nhiều tháng, rồi hệ thống trả lỗi đúng vào mấy phút khuyến mãi, lúc nhiều người mua nhất. Còn khi sự cố đã xảy ra, bạn dễ thêm máy vào chặng không phải bottleneck, hoặc ngồi trong một buổi họp mà ai cũng có lý nhưng không ai có số liệu để chốt.

Thứ giúp bạn tránh cả hai hướng sai là vài phép tính có đơn vị, làm được bằng giấy bút hoặc một bảng tính: tải cao điểm theo phút, số request đồng thời theo Little’s Law, hit ratio cần đạt để database còn headroom, số phút downtime mà SLO cho phép. Có những phép tính này, bạn trả lời được hệ thống có chịu nổi đợt tải sắp tới hay không trước khi sự cố xảy ra, giải thích được vì sao chọn phương án này mà loại phương án kia, và biết thành phần nào chưa cần thêm. Trước mỗi mùa Black Friday Cyber Monday, Shopify cũng bắt đầu bằng dự báo tải và load test, tức là cùng loại phép tính này, chỉ ở quy mô lớn hơn nhiều.

Sau khoá học, khi marketing báo một đợt khuyến mãi hay product manager hỏi cần bao nhiêu máy, bạn trả lời bằng một phép tính có giả định thay vì cảm giác khi nhìn biểu đồ. Với mỗi thành phần thêm vào, bạn chỉ ra được triệu chứng đã dẫn tới nó, và ADR bạn để lại đủ để người vào team một năm sau hiểu vì sao hệ thống được thiết kế như hiện tại.

## Những vấn đề thường gặp khi thiết kế hệ thống

Nhiều sự cố về tải bắt đầu từ một quyết định đưa ra trước khi có số đo: thêm máy theo thói quen, tin vào số trung bình, hoặc lấy kết quả benchmark của một endpoint làm sức chịu tải của cả tính năng. Mỗi dòng dưới đây ghép một điều bạn thấy trên dashboard với nguyên nhân thật của nó.

- Tăng số instance của app từ 3 lên 6, throughput vẫn giữ ở **1.200 request/giây** còn p99 tăng từ **180 ms** lên **650 ms** — CPU của Postgres primary đã gần chạm giới hạn, nên thêm instance app chỉ dồn thêm truy vấn vào đúng chặng đang bão hòa.
- Dashboard báo trung bình **40 request/giây** nhưng nginx trả 502 lúc **20:00** mỗi tối — số trung bình theo ngày che mất **3 phút** tải tăng lên **900 request/giây** sau mỗi lần gửi push notification khuyến mãi, gấp ba mức app chịu được.
- Benchmark đạt **2.000 request/giây** nên một instance được coi là dư sức cho **500 request/giây** — benchmark đo trên `/health` với **5 ms** mỗi request, còn API chat mất **80 ms** nên mỗi instance chỉ chịu khoảng **125 request/giây**.
- Chạy app trên ba instance thay vì một, instance nào cũng dưới **40%** CPU mà người dùng liên tục bị đăng xuất — session nằm trong RAM của từng instance, và load balancer gửi request tới instance không có session đó.
- Checkout trả 504 trong khi CPU của app mới **15%** — latency của service gợi ý sản phẩm tăng từ **40 ms** lên **8 giây**, trang chi tiết sản phẩm gọi nó đồng bộ và giữ hết **200 thread** của app trong lúc chờ.
- Đúng **8:00** mỗi sáng, p99 của API đặt hàng tăng từ **80 ms** lên **2,5 giây** dù số request mỗi giây không đổi — job báo cáo doanh thu quét bảng `orders` trên cùng Postgres primary và tranh chấp đĩa với API đặt hàng.
- Search index và bảng báo cáo lệch với Postgres sau một sự cố đồng bộ, không ai nói được bản nào đúng — team chưa chỉ định source of truth cho từng loại dữ liệu, nên không ai biết phải dựng lại các bản dẫn xuất từ đâu.

Các bài trong khoá lần lượt phân tích những tình huống này, từ số liệu trên dashboard tới nguyên nhân và cách xử lý.

## Bạn sẽ học theo cách nào

- **Bắt đầu từ triệu chứng thật** — Bài nào cũng mở bằng một dashboard, một đoạn log hay một hóa đơn cloud có số liệu cụ thể; thành phần hay khái niệm chỉ xuất hiện khi bạn đã thấy nó giải quyết triệu chứng nào.
- **Phép tính kèm hình vẽ** — Các phép tính đều ghi rõ đơn vị và giả định, còn ảnh minh họa vẽ request path, chỗ giữ state, chặng bão hòa và thành phần được thêm vào sau khi sửa.
- **Quiz ở các bài cần tính toán** — Các bài về cách đọc dashboard, workload, ước lượng, sáu thành phần, bottleneck và cách lỗi lan ra có quiz ngắn, trong đó nhiều câu yêu cầu bạn làm phép tính trước khi chọn đáp án.
- **Áp dụng cho hệ thống của bạn** — Phần lớn các bài khép lại bằng một bài tập trên hệ thống bạn đang có: vẽ request path, lập bảng constraint, viết hai dòng SLO hay một ADR.
- **Ba demo kit chạy bằng Docker** — Mỗi bài tình huống kèm một kit để coding agent của bạn dựng lại hệ thống trên laptop, chạy k6 ở mức tải cố định và cho bạn thấy triệu chứng của bài; khi bạn yêu cầu, agent áp dụng cách xử lý mà bài trình bày rồi chạy lại cùng kịch bản để so sánh.

## Khoá học dành cho ai

1. **Developer backend** — Bạn đã đưa lên production một ứng dụng web có database phía sau, và giờ phải trả lời những câu hỏi như có cần cache không, cần mấy instance, hệ thống có chịu nổi đợt khuyến mãi tới không.
2. **Người viết đề xuất kiến trúc cho team** — Bạn là tech lead hoặc developer được giao chọn giữa các phương án thiết kế, và cần so sánh chúng bằng số liệu rồi ghi lại quyết định cho người vào team sau.
3. **Developer vận hành một hệ thống đang lớn dần** — Bạn phụ trách một app có tải, dữ liệu và hóa đơn hạ tầng tăng theo từng tháng, và cần biết triệu chứng nào báo hiệu đã tới lúc thêm thành phần, thành phần nào vẫn chưa cần.

## Những điều cần lưu ý

- **Không đi vào cách từng thành phần hoạt động bên trong** — Cache invalidation, consistency model của replica hay delivery semantics của queue nằm ngoài khoá học; mỗi bài dừng ở mức đủ để bạn quyết định có thêm thành phần đó hay chưa.
- **Không có bước cài đặt hay cấu hình** — Khoá học không đi vào Kubernetes, cách phân loại load balancer theo tầng mạng (L4/L7) hay cách cấu hình một nhà cung cấp cloud cụ thể; bạn có phép tính và tiêu chí để quyết định trước khi mở bất kỳ trang cấu hình nào.
- **Số liệu trong bài là số minh họa** — Các ví dụ dùng số tự đặt để các phép tính khớp với nhau, không phải sức chịu tải chung của Postgres hay Redis; bạn học cách lập phép tính kèm giả định, rồi thay bằng số đo từ hệ thống của mình.
- **Demo kit không bắt buộc** — Mọi bài đều đọc hiểu được mà không cần chạy Docker; kit dành cho lúc bạn muốn tận mắt thấy p99 tăng vọt trên máy mình rồi giảm xuống sau khi áp dụng cách xử lý.

## Bắt đầu từ đâu

Bài mở đầu đặt hai bản thiết kế cạnh nhau. Bản thứ nhất có bảy thành phần cho một cửa hàng chỉ nhận **30 request/giây** lúc cao điểm; bản thứ hai chỉ có một app và một Postgres, và Postgres quá tải trong đêm khuyến mãi khi tải tăng lên **2.000 request/giây**. Bài chỉ ra cách biết một bản thiết kế đang thừa hay thiếu so với tải cao điểm, và vì sao hai bản ngược nhau lại sai ở cùng một chỗ: cả hai được vẽ khi đầu vào thiết kế còn chưa được ghi lại. Từ bài này trở đi, đầu vào thiết kế là điểm xuất phát của mọi quyết định.

## Chương trình

- Thiết kế hệ thống bắt đầu từ requirement, tải cao điểm và constraint (miễn phí)
- Request đi qua những chặng nào và state đang nằm ở đâu (miễn phí)
- Đọc p99, throughput, error rate và saturation trên dashboard (miễn phí) · quiz
- Tính năng qua hết test vẫn không dùng được vì thiếu non-functional requirement
- Ghi rõ constraint để loại bớt phương án trước khi so sánh
- Thay yêu cầu “nhanh và luôn chạy” bằng SLO và error budget
- Mô tả workload bằng tải cao điểm theo từng phút, không chỉ bằng số trung bình · quiz
- Ước lượng số máy, dung lượng và băng thông trước khi làm tính năng · quiz
- Source of truth: dữ liệu nào là gốc, dữ liệu nào dựng lại được
- Khi nào cần đặt load balancer, reverse proxy hay API gateway trước app · quiz
- Khi nào cần cache và nó gánh bớt được bao nhiêu tải đọc cho database · quiz
- Dùng queue để API không phải chờ những việc xử lý lâu · quiz
- Read replica gánh bớt tải đọc cho database primary · quiz
- Partition khi một bảng quá lớn và sharding khi một máy không ghi kịp · quiz
- Đưa file và ảnh ra khỏi database bằng object storage và CDN · quiz
- Hệ thống lớn dần thì thêm thành phần nào, khi nào và vì sao
- Thiết kế dịch vụ rút gọn link từ requirement tới kiến trúc · kit cho agent
- Thêm instance mà throughput không tăng thì bottleneck nằm ở đâu · quiz
- Lỗi ở một service phụ lan ra cả hệ thống như thế nào · quiz
- Lưu hàng triệu đơn hàng mỗi ngày mà báo cáo không làm chậm việc đặt hàng · kit cho agent
- Thiết lập hai phương án thiết kế khác nhau cho cùng một yêu cầu
- So sánh hai phương án thiết kế bằng số liệu thay vì ưu nhược điểm
- Ghi lại quyết định kiến trúc để một năm sau vẫn biết vì sao
- Thiết kế lại hệ thống khi flash sale đẩy tải lên gấp 40 lần · kit cho agent
