Backend

System Design — Bài 17: Thiết kế News Feed: push, pull và mô hình hybrid

SSite Admin
10 tháng 10, 2026 6 phút đọc 0 lượt xem

Một bài đăng tới 50 follower khác hẳn bài đăng tới 5 triệu follower. Đẩy sẵn mọi bài giúp đọc nhanh nhưng có thể tạo hàng triệu lượt ghi ít giá trị. News Feed cần quyết định nơi trả chi phí: khi đăng, khi đọc hay kết hợp cả hai theo phân bố người dùng.

Khóa học System Design: mục lục 21 bài — bạn đang đọc bài 17.

Các con số dưới đây là giả định phục vụ học thiết kế, không phải số liệu hay kiến trúc nội bộ của sản phẩm được nhắc tên. Dung lượng dùng đơn vị thập phân; capacity thực tế phải đo theo workload và mục tiêu chất lượng.

Yêu cầu & ước lượng

Phạm vi bài gồm follow, đăng text, xem feed mới nhất trước và tương tác. Giả định 500 triệu tài khoản, 50 triệu người hoạt động mỗi ngày, mỗi người một post và trung bình mười comment trên post. Đây là con số bài tập, không phải số liệu vận hành của Threads.

Với post ba kilobyte và comment 500 byte, mỗi ngày tạo khoảng 400 gigabyte payload. Giữ năm năm theo cách làm tròn 400 ngày một năm cho khoảng 800 terabyte trước replica và index. Chỉ riêng post bảy ngày là 1,05 terabyte, chưa gồm comment, feed entry hay overhead.

Mục tiêu dưới 100 mili giây cần làm rõ đo tại backend hay từ client và percentile nào. Tổng hợp nhiều nguồn dưới ngân sách đó mới là khó khăn chính; vì vậy ta phải quyết định phần công việc nào làm sẵn.

Push hay Pull — trade-off trung tâm

Sơ đồ: Push hay Pull — trade-off trung tâm

Xem sơ đồ đầy đủ: Push hay Pull — trade-off trung tâm

Push, hay fan-out on write, tạo feed entry cho follower khi có bài mới. Khi mở app, người đọc lấy danh sách đã chuẩn bị nên nhanh. Nhưng một người có hàng triệu follower tạo lượng ghi lớn, kể cả cho người không còn hoạt động.

Pull, hay fan-out on read, lấy các bài từ những người được follow khi người đọc yêu cầu. Nó tránh đẩy dư nhưng phải tổng hợp nhiều nguồn, làm đường đọc tốn kém. Có thể cache hoặc xử lý song song, nhưng vẫn cần giới hạn fan-in.

Unfollow, xóa bài và quyền riêng tư cũng ảnh hưởng push. Có thể phải dọn feed entry hoặc lọc tại read path; dọn bất đồng bộ không đủ nếu nội dung đã bị cấm mà vẫn hiển thị. Vì vậy không chọn chỉ dựa trên read/write ratio; cần cả phân bố follower và điều kiện hiển thị.

Mixing model — xử lý latency

Với người đăng thông thường, số follower vừa phải nên có thể push feed entry. Với celebrity, giữ bài ở nguồn riêng rồi pull khi follower đọc, tránh cơn bão ghi ngay lúc đăng.

Read path lấy inbox đã dựng, bổ sung bài celebrity, loại trùng, kiểm tra quyền và sắp xếp theo quy tắc. Nếu dùng phân trang, cursor cần giúp kết quả ổn định khi bài mới xuất hiện. Lưu ID trong feed entry thường tiết kiệm hơn sao toàn bộ nội dung post, nhưng cần batch fetch và cache để không tạo nhiều vòng đọc.

Ngưỡng celebrity cần dựa vào chi phí fan-out, tần suất đăng và lượng follower thực sự đọc. Chỉ push cho user active có thể giảm tải, nhưng cần đường dựng lại khi user lâu ngày quay lại. Mô hình pha vẫn cần recovery, không chỉ một nhánh if đơn giản.

Ta có thể dùng sự kiện mở app hoặc tương tác để cập nhật last_active. Không nhất thiết ghi xuống kho bền trên mọi thao tác; có thể gộp trong một khoảng và chấp nhận độ trễ đã định nghĩa. TTL một ngày là chính sách ví dụ, khác khái niệm “online ngay lúc này”.

Sorted set hoặc timing wheel từ bài liveness giúp quản lý thời hạn hoạt động. Dữ liệu này hỗ trợ quyết định có dựng feed sẵn hay không và phân nhóm người lâu ngày chưa quay lại.

Trending cần một hàm điểm có yếu tố thời gian để bài cũ nhiều tương tác không giữ vị trí mãi. Like, comment và share có thể có trọng số khác, kèm chống spam. Counter cập nhật trễ có thể chấp nhận với xếp hạng, nhưng quyền xem bài vẫn phải kiểm tra theo yêu cầu riêng.

Sơ đồ cuối

Sơ đồ: Sơ đồ cuối

Xem sơ đồ đầy đủ: Sơ đồ cuối

Theo đường ghi: Post service lưu bài, sau đó event đi tới các bên xây feed và thông báo theo cơ chế phát tin đáng tin cậy đã chọn. Feed builder đọc quan hệ follow, phân nhánh người đăng thông thường hoặc celebrity rồi cập nhật feed entry phù hợp.

Theo đường đọc: Feed service lấy danh sách đã dựng, trộn phần cần pull, lấy nội dung và kiểm tra điều kiện hiển thị. Reaction đi theo nhánh counter và có thể cập nhật cache hoặc dữ liệu xếp hạng bất đồng bộ. Liveness cung cấp thông tin hoạt động để giảm công việc không cần thiết.

Các sản phẩm được cân nhắc cho mô hình này là lựa chọn minh họa, không mô tả kiến trúc nội bộ của sản phẩm thật. Neo4j chỉ đáng trả chi phí nếu truy vấn quan hệ cần nó; lookup follow đơn giản có thể dùng cách khác. Hãy chỉ rõ nguồn sự thật, độ trễ chấp nhận và đường rebuild khi projection hoặc cache mất.

Hybrid có hai đường rõ ràng

Ghi: Post DB + Outbox -> Event -> Feed Builder
  tác giả thường -> push post_id tới inbox của follower
  celebrity      -> lưu ở nguồn để pull khi đọc

Đọc: inbox đã dựng + bài celebrity
  -> merge -> dedup -> kiểm tra quyền -> sort -> cursor

Payload/ngày = 50M x (3 KB + 10 x 500 B) = 400 GB
5 năm làm tròn = 400 GB x 400 x 5 = 800 TB
Chỉ post 7 ngày = 50M x 3 KB x 7 = 1,05 TB

Đây là mô hình học tập, không mô tả kiến trúc nội bộ Threads. Dung lượng chưa có replica, index, feed entry và metadata. Ngưỡng celebrity phải được đo; user lâu không hoạt động cần đường dựng lại feed khi quay lại.

Thuật ngữ cần nhớ

  • DAU — Daily Active Users: Số người dùng hoạt động trong một ngày theo định nghĩa của sản phẩm; nhân với số hành động mỗi người để ước lượng tổng request/ngày.

  • TTL — Time To Live: Thời gian sống còn hiệu lực của dữ liệu, bản ghi tên miền hoặc lease. Hết hạn không tự thực hiện nghiệp vụ như hoàn tồn kho; cần luồng xử lý tương ứng.

  • CQRS — Command Query Responsibility Segregation: Tách trách nhiệm ghi và đọc; có thể dùng mô hình đọc tối ưu riêng, đồng thời phải xử lý độ trễ đồng bộ và cách phục hồi projection.

Bài tập tự thực hành

Celebrity có năm triệu follower đăng liên tục.

Gợi ý kiểm tra lời giải

So push với pull bằng fan-out; mô hình pha có merge, dedup, quyền xem và recovery.

Tự kiểm tra sau bài học

  • Mô tả chi phí fan-out theo phân bố follower.

  • Unfollow, xóa bài và đổi quyền được xử lý tại đường đọc phù hợp.

  • Projection có thể rebuild từ nguồn sự thật và lịch sử cần thiết.

Tiếp tục lộ trình

Bài trước — Thiết kế URL Shortener: sinh mã, cache và ánh xạ 1-1

Bài tiếp — Thiết kế ứng dụng chat: WebSocket, ordering và reconnect

Xem toàn bộ khóa học System Design 21 bài

S

Site Admin

Engineer and writer. Building things with TypeScript and distributed systems.

Bình luận (0)

Bạn cần đăng nhập bằng Google để bình luận.

Hãy là người bình luận đầu tiên.

Bài viết liên quan

99 Ngày Spring — Ngày 76: Circuit Breaker với Resilience4j

CLOSED, OPEN, HALF_OPEN, fallback, bulkhead và rate limiter để giới hạn lỗi dây chuyền.

10 thg 10, 20262 phút5
99 Ngày Java — Ngày 76: Soft, Weak và Phantom Reference

Bốn mức tham chiếu, WeakHashMap, ReferenceQueue và cách chọn cache có giới hạn trên Java 21.

10 thg 10, 20262 phút6
K

Học System Design qua 21 bài: yêu cầu, capacity, database, cache, hệ phân tán và 6 bài thực hành, kèm bài tập, sơ đồ luồng và mục lục đầy đủ.

10 thg 10, 20266 phút3