Câu trả lời đúng ở quy mô này là "đừng vội fan-out". Với vài trăm nghìn user và mỗi người theo dõi vài trăm nguồn, dựng feed lúc đọc (pull) là đủ và ít bộ phận chuyển động nhất.
Data model:
posts(id, author_id, created_at, kind, payload)
follows(follower_id, followee_id, created_at)
-- index: posts(author_id, created_at desc), follows(follower_id)Dựng feed lúc đọc: lấy danh sách followee (cache trong Redis), truy vấn N bài mới nhất của mỗi nguồn, trộn theo thời gian, cắt 20 bài, cache kết quả 30-60 giây. Phân trang bằng con trỏ (created_at, id), không dùng OFFSET — offset vừa chậm vừa nhảy bài khi có bài mới chèn vào.
Khi nào chuyển sang fan-out on write: khi truy vấn trộn vượt ngân sách độ trễ (thường là lúc số nguồn theo dõi lên hàng nghìn, hoặc DAU lên hàng triệu). Lúc đó ghi sẵn feed:{userId} là một Redis sorted set giới hạn ~1000 phần tử, worker đẩy bài mới vào feed người theo dõi.
Vấn đề tài khoản triệu follower: fan-out ghi cho 1 triệu người là không khả thi trong vài giây. Cách chuẩn là hybrid — người thường thì fan-out sẵn, tài khoản lớn thì để pull lúc đọc rồi trộn vào.
Xếp hạng: giai đoạn đầu chỉ cần theo thời gian. Muốn "gợi ý" thì thêm điểm đơn giản: score = w1độ_mới + w2tương_tác + w3*độ_thân. Đừng đưa mô hình học máy vào khi chưa có dữ liệu tương tác.
Điểm nghẽn và đánh đổi:
- Pull tốn CPU lúc đọc nhưng ghi rẻ và không cần backfill khi có người follow mới.
- Push (fan-out) đọc rẻ nhưng tốn bộ nhớ, và mỗi lần follow/unfollow đều phải sửa feed đã dựng.
- Nêu rõ ngưỡng chuyển đổi trong buổi phỏng vấn quan trọng hơn việc chọn sẵn một bên.