Backend

Làm sao YouTube, TikTok phục vụ livestream cho 2–3 triệu người xem cùng lúc?

SSite Admin
6 tháng 08, 2026 8 phút đọc 24 lượt xem
Làm sao YouTube, TikTok phục vụ livestream cho 2–3 triệu người xem cùng lúc?

Trận chung kết Cricket World Cup 2023 có lúc chạm 59 triệu người xem đồng thời trên Hotstar; buổi phát trực tiếp tàu Chandrayaan-3 đáp xuống Mặt Trăng vượt 8 triệu người xem cùng lúc trên YouTube. Nghe qua tưởng vô lý: một server tốt gánh nổi vài chục nghìn kết nối là giỏi — vậy 2–3 triệu kết nối video thì ai gánh? Câu trả lời bất ngờ: không ai gánh cả — vì với kiến trúc đúng, không có 3 triệu kết nối nào chạm tới server gốc. Bài này lần theo đường đi của một livestream, từ camera của streamer tới màn hình của hàng triệu người.

Sơ đồ kiến trúc livestream 3 triệu người xem: streamer → ingest → transcoding → HLS packager → origin shield → edge PoP → người xem, kèm đường chat/counter riêng

Một dòng chảy, hai bài toán rất khác nhau

Nhìn từ trên cao, livestream là một dòng video chảy một chiều — và hệ thống tách nó thành hai bài toán gần như độc lập:

  • Ingest (nhận vào): nhận một dòng video duy nhất từ streamer — ưu tiên ổn định và chất lượng. Quy mô: 1 kết nối.

  • Delivery (phát ra): nhân bản dòng đó tới hàng triệu màn hình — ưu tiên băng thông và chi phí. Quy mô: hàng triệu kết nối.

  • Realtime phụ trợ: chat, tim, bộ đếm người xem — hệ thống riêng, đi đường riêng, không dính gì tới đường video.

Toàn bộ "phép màu triệu người" nằm ở phía delivery — nhưng muốn hiểu nó, phải đi từ ingest.

Ingest & transcoding: từ camera đến "thang bitrate"

Streamer (hoặc đài truyền hình) đẩy video lên điểm ingest gần nhất qua RTMP, SRT hoặc WebRTC — chỉ một kết nối, nhưng là kết nối quan trọng nhất: nó đứt là 3 triệu người cùng nhìn màn hình quay. Vì thế điểm ingest luôn đặt gần streamer về mặt địa lý và có đường dự phòng. Nhận xong, hệ thống transcode (chuyển mã) dòng gốc thành nhiều mức chất lượng:

# "Thang bitrate" (ABR ladder) — transcode MỘT lần, phục vụ tất cả
1080p60   ~ 6.0 Mbps    # mạng xịn, TV / desktop
720p30    ~ 3.0 Mbps    # mặc định phổ biến trên mobile
480p30    ~ 1.2 Mbps    # 4G yếu
360p30    ~ 0.7 Mbps    # mạng chập chờn
audio-only ~ 64 kbps    # tệ đến mấy vẫn nghe được

# Player tự nhảy giữa các bậc theo tốc độ mạng — đó là Adaptive Bitrate (ABR)
  • Transcode một lần tại trung tâm, dùng cho tất cả người xem — không ai transcode lại ở tầng dưới.

  • Ở quy mô YouTube/TikTok, việc này chạy trên GPU hoặc chip mã hóa chuyên dụng (YouTube tự thiết kế chip transcode riêng tên Argos).

  • Các mức chất lượng được canh keyframe thẳng hàng để player đổi bậc giữa chừng mà hình không vỡ, không giật.

Bí mật lớn nhất: video live thực ra là… file tĩnh

Đây là insight quan trọng nhất bài. Các nền tảng lớn không duy trì 3 triệu kết nối video "sống". Thay vào đó, giao thức HLS (HTTP Live Streaming — YouTube, TikTok đều dùng họ này hoặc tương đương như DASH) cắt dòng video thành từng file nhỏ 2–6 giây gọi là segment, kèm một file mục lục gọi là playlist:

# media playlist (.m3u8) — player tải lại file này mỗi vài giây
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:4
#EXT-X-MEDIA-SEQUENCE:1042        # số thứ tự segment đầu tiên trong danh sách

#EXTINF:4.000,
seg-1042.ts                        # mỗi segment = MỘT FILE TĨNH ~4 giây video
#EXTINF:4.000,
seg-1043.ts
#EXTINF:4.000,
seg-1044.ts                        # segment mới nhất — "mép sống" của stream

# Xem live = lặp vô hạn: tải playlist mới → thấy segment mới → tải segment

Xem live, hóa ra, chỉ là tải file lặp đi lặp lại qua HTTP — đúng thứ trình duyệt và Internet đã làm giỏi nhất suốt 30 năm. Và vì mỗi segment là file tĩnh bất biến (segment 1042 mãi mãi là segment 1042 — nghe quen không? đúng tinh thần immutable của String Ngày 09!), nó cache được ở mọi tầng. Bài toán "3 triệu kết nối video" vừa biến thành bài toán "phục vụ file tĩnh có cache" — bài toán mà nhân loại đã giải sẵn bằng CDN.

CDN fan-out: nhân bản theo hình cây

Trước hết, làm phép tính để thấy vì sao một datacenter là không tưởng:

  • 3 triệu người × ~5 Mbps (720p–1080p) = ~15 Tbps — gấp nhiều lần băng thông trục của một datacenter lớn.

  • Giải pháp duy nhất: trải tải ra hàng nghìn PoP (điểm hiện diện) toàn cầu — mỗi edge server chỉ phục vụ vài chục nghìn người gần nó.

Các edge xếp thành hình cây: origin (nguồn) → origin shield (tấm khiên) → tầng khu vực → edge. Chìa khóa là request coalescing (gộp yêu cầu): khi 50.000 người sau cùng một edge cùng cần segment 1044, edge chỉ hỏi tầng trên một lần, nhận về, rồi phát cho cả 50.000. Lặp lại logic đó qua từng tầng: 3 triệu request ở rìa co lại còn vài trăm request chạm origin — origin chỉ "cảm nhận" số edge, không bao giờ cảm nhận số người xem.

Thực tế mỗi nền tảng một kiểu: YouTube đứng trên Google Global Cache — server cache đặt thẳng bên trong mạng của các ISP (VNPT, Viettel…), nên segment tới bạn có khi chưa từng ra khỏi Việt Nam; TikTok kết hợp CDN nhà (BytePlus) với multi-CDN thuê ngoài, đo chất lượng theo thời gian thực để dồn người xem sang CDN đang khỏe.

Độ trễ: cái giá của quy mô

Cắt video thành file 2–6 giây thì phải đợi đủ một file mới phát được — cộng thêm buffer của player là bạn xem "quá khứ" của streamer vài giây tới nửa phút. Đây là đánh đổi tam giác kinh điển: quy mô — độ trễ — chi phí, chọn hai:

  • HLS cổ điển: trễ 15–30s — scale vô hạn, rẻ, nhưng "chậm tin" nhất.

  • LL-HLS / CMAF chunked: cắt segment thành "mảnh" vài trăm ms, đẩy sớm — trễ còn 2–5s; là chuẩn hiện nay của livestream triệu người.

  • WebRTC: trễ dưới 1 giây — nhưng mỗi viewer là một kết nối stateful server phải nuôi riêng, không cache được. Hợp gọi video và stream nghìn người; không hợp 3 triệu.

Vì thế YouTube/TikTok chọn họ HTTP + CDN và chấp nhận vài giây trễ. Đây cũng là lời giải cho bí ẩn muôn thuở: bình luận "sao bạn xem tới trước tôi?" — hai người ở hai nhánh CDN khác nhau, cách nhau vài giây là chuyện thiết kế, không phải mạng nhà ai lag.

So sánh độ trễ ba giao thức: HLS cổ điển 15–30s, LL-HLS/CMAF 2–5s (chuẩn của YouTube/TikTok Live), WebRTC dưới 1s nhưng không scale tới triệu người

Chat, tim và bộ đếm 3 triệu người

Đường video giải xong vẫn còn một bài toán realtime khác: 3 triệu người cùng chat. Đường này tách hẳn khỏi đường video và có mẹo riêng:

  • Fan-out theo pub/sub: phòng chat chia mảnh (shard) trên nhiều server; tin nhắn đi qua message broker rồi tỏa xuống các kết nối WebSocket — nhẹ hơn video hàng trăm lần vì chỉ là text.

  • Không ai đọc nổi 3 triệu tin: hệ thống lấy mẫu — mỗi người chỉ nhận một dòng tin đã lọc/xếp hạng, không phải toàn bộ. Gửi hết vừa vô dụng vừa tự đánh sập mình.

  • Tim và view: đếm cục bộ ở edge, gộp theo lô (batch) mỗi 1–2 giây mới đẩy về trung tâm; con số bạn thấy là xấp xỉ có chủ đích — chính xác tuyệt đối ở quy mô này vừa bất khả thi vừa vô nghĩa.

Khi 3 triệu người bấm vào cùng một giây

Kịch bản đáng sợ nhất không phải 3 triệu người đang xem — mà là 3 triệu người cùng bấm vào khi trận đấu bắt đầu (thundering herd — bầy thú giẫm đạp):

  • Pre-warm: đẩy sẵn playlist + segment đầu tới các edge trước giờ G với sự kiện đã biết lịch.

  • Chặn cache-miss storm: request coalescing + giữ bản cache cũ trong lúc chờ bản mới (kiểu stale-while-revalidate) để origin không bao giờ hứng đủ triệu miss cùng lúc.

  • Suy giảm có kiểm soát (graceful degradation): quá tải thì hạ bậc bitrate mặc định, giãn nhịp tải playlist, retry có jitter — thà 3 triệu người xem 480p còn hơn 0 người xem 1080p.

  • Multi-CDN failover: một CDN hắt hơi là điều hướng người xem sang CDN khác trong vài giây.

Góc phỏng vấn

"Thiết kế hệ thống livestream cho hàng triệu người xem" là câu system design kinh điển. Khung trả lời ăn điểm:

  • Mở bài bằng việc tách ingest khỏi delivery — hai bài toán, hai độ khó, hai kiến trúc.

  • Nêu insight then chốt: HLS biến live thành file tĩnh cache được → delivery quy về CDN.

  • Làm phép tính băng thông (viewers × bitrate) để chứng minh vì sao bắt buộc phải fan-out hình cây + request coalescing.

  • Chủ động nêu trade-off độ trễ HLS vs LL-HLS vs WebRTC — và giải thích vì sao triệu người thì WebRTC bị loại.

  • Đừng quên nói chat/counter là hệ realtime riêng với sampling + batch — điểm cộng lớn vì đa số quên.

Kết luận

Phục vụ 3 triệu — hay 59 triệu — người xem cùng lúc không phải là chuyện mua server khỏe hơn, mà là chuỗi quyết định kiến trúc: transcode một lần thành thang bitrate, cắt video thành file tĩnh bất biến, để CDN nhân bản theo hình cây với request coalescing, chấp nhận vài giây trễ làm giá của quy mô, và tách chat thành hệ riêng biết "đếm xấp xỉ". Từng mảnh ghép ở đây — CDN, caching, pub/sub, trade-off latency — đều là chủ đề được mổ xẻ từng ngày trong series 50 Ngày System Design trên blog. Hẹn gặp bạn ở đó!

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 14: Bean Validation — @Valid & bộ constraint chuẩn

Ngày 14 của 99 Ngày Spring: Bean Validation khai báo luật ngay trên DTO — @Valid kích hoạt, bộ constraint chuẩn (@NotBlank vs @NotEmpty vs @NotNull, @Size, @Email, @Min/@Max), bẫy int vs Integer, validate object lồng nhau không tự lan, và lỗi 400 gom một lượt.

10 thg 8, 20265 phút0
99 Ngày Java — Ngày 14: Kế thừa (Inheritance)

Ngày 14 của 99 Ngày Java: extends trao gia tài từ lớp cha, override với @Override và super., thứ tự khai sinh cha trước con sau qua super(...), protected trả nợ Ngày 13, và phép thử is-a vs has-a — khi nào nên composition thay vì kế thừa.

10 thg 8, 20266 phút0
99 Ngày Spring — Ngày 13: @RequestBody & DTO — đừng công khai entity ra API

Ngày 13 của 99 Ngày Spring: @RequestBody + Jackson deserialize JSON thành object, DTO là hợp đồng API — chặn lỗ hổng mass assignment chiều vào và rò rỉ dữ liệu chiều ra, request/response DTO khác nhau là bình thường, và Jackson annotations tinh chỉnh hợp đồng JSON.

9 thg 8, 20266 phút9