🎮Bộ 2 · System Design ArenaTập 15 / 17

[SYSTEM DESIGN TẬP 15] BÍ MẬT 'ĐỨNG HÌNH 301 VIEWS' CỦA YOUTUBE, CÁI BẪY SPLIT 16 NỔ DATABASE VÀ CẶP SONG SÁT HYPERLOGLOG ($85.00 ★★★)! 🔢🧬

Cái bẫy Split 16 làm nổ Database ở 2,300 req/s, điểm ngọt toán học Split 4 ($85.00 ★★★) và bí mật đứng hình 301 views của YouTube bằng Sharded Counter + HyperLogLog
🎨 Cái bẫy Split 16 làm nổ Database ở 2,300 req/s, điểm ngọt toán học Split 4 ($85.00 ★★★) và bí mật đứng hình 301 views của YouTube bằng Sharded Counter + HyperLogLog 🔍 Phóng to

Màn hình sảnh đấu System Design Arena vừa sáng đèn báo hiệu Màn 24: Video viral, bộ đếm nghẽn (counter_at_scale).

Thằng Đ nhìn lướt qua bảng đề bài rồi phá lên cười ha hả:

  • *"Ôi giời ơi anh Kido ơi! Sau trận ác mộng Flash Sale sóng thần 8.780 req/s ở Màn 13, màn này dễ như ăn kẹo! Đề bài chỉ có vỏn vẹn 700 lượt xem video/s (video_view) và 100 lượt mở dashboard xem tổng view/s (view_count_read). Tổng cộng có 800 HTTP req/s, ngân sách cho hẳn $98.00/tháng. Em chỉ cần kéo 1 con App Server với 1 con Database là lụm 3 Sao trong một nốt nhạc!"*

Tôi nhấp một ngụm trà, mỉm cười nhìn thằng em:

  • "Đừng vội khinh địch, Đ. Mày đọc kỹ yêu cầu nghiệp vụ chưa? Mỗi khi người dùng bấm play video, hệ thống phải cộng trực tiếp +1 vào cột view_count ngay trong request chính vì creator cần nhìn thấy số view tăng tức thì trên dashboard, cấm dùng hàng đợi Write-Behind bất đồng bộ. Mày thử kéo đi xem có ăn kẹo được không."

1. CÚ ĐÂM ĐẦU VÀO BỨC TƯỜNG ROW LOCK: TẠI SAO COUNTER: SINGLE TỬ TRẬN Ở 200 REQ/S? 💥

Thằng Đ tự tin kéo ngay bàn cờ sơ khai:

  • App Server Tier L ($35.00/tháng, công suất 1.000 req/s — thừa sức nuốt trọn 800 HTTP req/s).
  • Relational DB Tier L ($50.00/tháng, công suất 1.500 req/s, flavor Postgres).
  • Thiết lập bộ đếm trong Database: Giữ nguyên mặc định Counter: Single.
  • Chi phí hiển thị: $85.00/tháng (thấp hơn trần $98.00, nằm gọn ở mốc Best Cost).

Nó hùng dũng bấm nút Simulate.

Chưa đầy 10 giây sau, còi báo động réo vang khắp phòng thi. Bảng kết quả đỏ quạch:

[FAILED] Tỷ lệ phản hồi hợp lệ: 28.6% (Yêu cầu: >= 99.0%)
[FAILED] Độ trễ trung bình: 1,842 ms (Vượt trần SLA 300 ms)!
[ALERT - Relational DB Tier L]:
  Lệnh UPDATE videos SET views = views + 1 WHERE video_id = 'mv_viral';
  Hàng đợi Transaction Row Lock bị nghẽn cứng! 71.4% request bị drop do lock timeout!

Thằng Đ trợn tròn mắt, hai tay ôm đầu:

  • "Ủa?! Database Tier L chịu được 1.500 req/s cơ mà anh?! Tổng lưu lượng cả xem lẫn đọc có 800 req/s, mới dùng có hơn 53% công suất, tại sao lại sập hàng loạt thế này?!"

Tôi gõ cây bút lên bàn hình vẽ:

  • *"Mày bị dính đòn Bức tường Khóa dòng (Row Lock Contention)! Trong cơ sở dữ liệu quan hệ, để đảm bảo tính nhất quán ACID, khi có một câu lệnh UPDATE sửa đổi dữ liệu của một dòng (row), database buộc phải xin một Exclusive Lock trên dòng đó. Một dòng dữ liệu vật lý trên đĩa chỉ có thể mở khóa, ghi chép nhật ký WAL và commit tối đa khoảng 200 lần/giây! Bây giờ cả 700 người cùng lao vào tranh giành giật quyền ghi trên đúng một dòng mv_viral. Trong khi chỉ có 200 người được phục vụ mỗi giây, 500 người còn lại phải xếp hàng chờ. Hàng đợi phình to như quả bóng bay, độ trễ vọt lên gần 2 giây và hệ thống rụng như sung!"*

2. CẠM BẪY LÒNG THAM: TẠI SAO CHẺ 16 MẢNH (SPLIT 16) LẠI LÀM NỔ TUNG DATABASE Ở 2.300 REQ/S? 💣

Nghe tôi giải thích xong, mắt thằng Đ sáng rực lên:

  • "À! Em hiểu rồi! Đề bài mở khóa cho chọn Counter: Split 4 và Counter: Split 16! Nếu 1 dòng bị nghẽn ở 200 writes/s, thì em chẻ cụ nó ra hẳn 16 dòng (Split 16) cho bõ ghét! 700 chia cho 16 thì mỗi dòng chỉ chịu có hơn 43 writes/s, tha hồ mát mẻ!"

Nó liền chuyển cấu hình sang Counter: Split 16, hí hửng bấm Simulate lần hai.

Thế nhưng, kết quả lần này còn thảm khốc hơn lần trước!

[FAILED - DATABASE OVERLOADED]:
  Lưu lượng thực tế nã vào Relational DB: 2,300 req/s! (Vượt 153.3% trần 1,500 req/s!)
  Dashboard View Count Read: 100% Timeout!

Thằng Đ ngã ngửa ra sau ghế, mặt cắt không còn giọt máu:

  • "Trời đất ơi! Tại sao tổng người dùng chỉ có 800 req/s mà Database lại bị nã tới 2.300 req/s?! Có ma ám hả anh Kido?!"

Tôi bật cười, kéo chiếc bảng trắng lại gần:

  • *"Không có con ma nào cả, đó là cái bẫy kinh điển: Hiệu ứng Khuếch đại Đọc SUM() (Read Amplification Trap)! Mày chỉ nhìn thấy chiều Ghi mà mù tịt chiều Đọc!
  1. Lúc Ghi (700 writes/s): Mỗi request xem video bốc ngẫu nhiên 1 slot trong 16 slot để ghi $\implies$ tốn đúng 700 operations/s trên DB.
  2. Nhưng lúc 100 người/s mở Dashboard xem tổng số view: Vì số view bị xé làm 16 mảnh nằm ở 16 dòng khác nhau, Database buộc phải đọc quét cả 16 dòng ra để tính tổng SUM(views): $$\text{Tải đọc DB} = 100\text{ reads/s} \times 16\text{ slots} = \mathbf{1.600\text{ operations/s}}!$$ Cộng cả hai luồng lại: $$\text{Tổng tải DB} = 700\text{ writes} + 1.600\text{ reads} = \mathbf{2.300\text{ req/s}} \gg 1.500\text{ req/s của DB Tier L}!$$ Chính sự tham lam chẻ ra 16 mảnh của mày đã tự nhân tải đọc lên gấp 16 lần, biến con Database thành ngọn đuốc sống!"*

3. ĐIỂM NGỌT TOÁN HỌC: BẤT ĐẲNG THỨC ĐỊNH CỠ KHE VÀ MÀN RINH 3 SAO $85.00 ★★★ 🏆

Thằng Đ run rẩy:

  • "Kinh khủng thật... Ghi nhiều thì nghẽn dòng, mà chẻ nhiều slot quá thì nổ đọc! Vậy làm sao biết chẻ bao nhiêu mảnh là vừa khít hở anh?!"

Tôi cầm bút viết lên bảng công thức tổng quát mà tôi đã đúc kết:

$$\left\lceil \frac{W}{C_{\text{row}}} \right\rceil \le N \le \left\lfloor \frac{C_{\text{db}} - W}{R} \right\rfloor$$

Trong đó:

  • $W = 700\text{ writes/s}$ (Lưu lượng ghi xem video).
  • $R = 100\text{ reads/s}$ (Lưu lượng đọc dashboard).
  • $C_{\text{row}} \approx 200\text{ writes/s/slot}$ (Trần an toàn chống khóa dòng).
  • $C_{\text{db}} = 1.500\text{ req/s}$ (Công suất cực đại của Database Tier L).

Thay số vào bất đẳng thức hai đầu:

  • Cận dưới (Chống kẹt Row Lock): $$N \ge \left\lceil \frac{700}{200} \right\rceil = \lceil 3.5 \rceil = \mathbf{4\text{ slots}}!$$
  • Cận trên (Chống nổ tải đọc tổng DB): $$N \le \left\lfloor \frac{1.500 - 700}{100} \right\rfloor = \left\lfloor \frac{800}{100} \right\rfloor = \mathbf{8\text{ slots}}!$$

$$\implies \mathbf{4 \le N \le 8}$$

  • *"Nhìn vào kết quả đi Đ! Điểm an toàn bắt buộc phải từ 4 đến 8 slot. Kho đồ chơi của hệ thống chỉ có Single (1), Split 4 và Split 16. Lựa chọn duy nhất hoàn hảo ở đây chính là Counter: Split 4!"*

Tôi cùng Đ kiểm tra lại con số khi chọn Counter: Split 4:

  • Tải Ghi: $700\text{ writes/s}$ chia đều cho 4 dòng $\implies$ Mỗi dòng chỉ nhận: $$\frac{700}{4} = \mathbf{175\text{ writes/s}} < 200\text{ writes/s}\text{ (An toàn tuyệt đối, zero lock contention!)}$$
  • Tải Đọc: $100\text{ reads/s} \times 4\text{ slots} = \mathbf{400\text{ reads/s}}$.
  • Tổng tải nã vào Database: $$700 + 400 = \mathbf{1.100\text{ req/s}} < 1.500\text{ req/s của DB Tier L}\text{ (Dư 26.7% trần an toàn!)}$$

Thằng Đ run run bấm chọn Relational DB Tier L · Counter: Split 4 rồi ấn Simulate.

======================================================
KẾT QUẢ MÔ PHỎNG MÀN 24 (counter_at_scale):
- Tỷ lệ thành công Xem Video: 100.0% (Latency: 28 ms)
- Tỷ lệ thành công Đọc Dashboard: 100.0% (Latency: 34 ms)
- Tải Database L: 1,100 / 1,500 req/s (Healthy 73.3%)
- Tổng chi phí thực tế: $85.00 / tháng (Trần: $98.00)
XẾP HẠNG: 3 SAO XUẤT SẮC (★★★ - BEST COST WORLD RECORD)!
======================================================

Thằng Đ nhảy dựng lên reo hò ầm ĩ:

  • "3 SAO RỒI ANH KIDO ƠI! Đúng $85.00/tháng chạm đáy kỷ lục thế giới! Quá vi diệu!"

4. BÍ MẬT 'ĐỨNG HÌNH 301 VIEWS' CỦA YOUTUBE VÀ CẶP SONG SÁT HYPERLOGLOG 🧬📺

Sau khi màn ăn mừng lắng xuống, thằng Đ ngồi trầm ngâm nhìn vào kiến trúc:

  • "Nhưng anh Kido ơi, nếu Sharded Counter đỉnh cao và chính xác 100% như thế này rồi, thì cấu trúc dữ liệu HyperLogLog (HLL) mà các đại sư huynh hay nhắc đến có bị coi là phế phẩm không? Tại sao em thấy các hệ thống lớn như YouTube, Reddit vẫn nhắc tới HyperLogLog liên tục?"

Tôi rót thêm một chén trà, vỗ vai nó:

  • "Câu hỏi đắt giá nhất ngày hôm nay đấy! HyperLogLog không hề vô dụng, mà ngược lại, nó tạo thành cặp song sát sống còn với Sharded Counter! Bởi vì hai thằng này sinh ra để trả lời hai câu hỏi nghiệp vụ hoàn toàn khác nhau:"
Tiêu Chí So Sánh 🔢 Sharded Counter (SlottedCounter) 🧬 HyperLogLog (PFADD / PFCOUNT)
Bản chất đo lường TỔNG LƯỢT XEM (Total Views) SỐ NGƯỜI XEM DUY NHẤT (Unique Viewers)
Kịch bản 1 user cày F5 1.000 lần Cộng ngây thơ đủ 1.000 views! Nhận diện đúng 1 người duy nhất (cardinality = 1)!
Độ chính xác Chính xác tuyệt đối 100% Xác suất thống kê (Sai số $\approx 0.81% - 1.04%$)
Chi phí bộ nhớ RAM Tăng tuyến tính theo số slot dữ liệu Cố định đúng 1 KB – 12 KB RAM cho hàng tỷ người!

Tôi ghé sát tai Đ, bật mí:

  • "Mày có nhớ ngày xưa mỗi khi có một MV ca nhạc cực hot vừa ra lò, số lượt xem trên YouTube luôn bị đứng hình cứng đơ ở con số 301 views suốt mấy tiếng đồng hồ không? Đó chính là thời khắc YouTube kích hoạt cặp song sát Sharded Counter + HyperLogLog!"
[Người dùng xem video]
         │
         ├──(Ghi nhận tức thì)──> [Sharded Counter (Split 4)] ──> rawTotalViews
         └──(Ghi nhận định danh)─> [HyperLogLog (1 KB RAM)]  ──> estimatedUniqueViewers
                                              │
                      ┌───────────────────────┴───────────────────────┐
                      ▼                                               ▼
         rawTotalViews <= 300                            rawTotalViews > 300
                      │                                               │
             Hiển thị trực tiếp                               Kiểm tra tỷ lệ Spam:
             (Chưa bật kiểm toán)                        Ratio = rawTotal / uniqueViewers
                                                                      │
                                                     ┌────────────────┴────────────────┐
                                                     ▼                                 ▼
                                                Ratio <= 10                       Ratio > 10
                                            (Người xem tự nhiên)              (Phát hiện Botnet F5!)
                                                     │                                 │
                                            Cập nhật view công khai           ĐÓNG BĂNG Ở 301 VIEWS
                                                                              Chuyển sang Audit ngầm!
  • "Ở quy mô ban đầu ($< 300\text{ views}$), YouTube cộng thẳng từ Sharded Counter lên màn hình. Nhưng khi vượt qua ngưỡng 300 views, YouTube bật cờ kiểm duyệt: Lấy rawTotalViews từ Sharded Counter chia cho estimatedUniqueViewers từ HyperLogLog. Nếu tỷ lệ này nhảy vọt lên quá cao — ví dụ Sharded Counter báo 5.000 lượt xem nhưng HyperLogLog chỉ ghi nhận có 2 người xem duy nhất (tức là 2 con bot đang cắm tool F5 cày view ảo) — hệ thống lập tức khóa chặt bộ đếm công khai ở mốc 301 views! Bộ đếm bị đóng băng để đội ngũ bảo mật và thuật toán Machine Learning rà soát tính hợp lệ của IP, user-agent và thời lượng xem (watch time), bảo vệ hàng tỷ đô la tiền quảng cáo của các nhãn hàng!"

Thằng Đ há hốc mồm thán phục:

  • "Trời đất... Một bên đếm số lượt click bằng Sharded Counter, một bên đếm định danh duy nhất bằng HyperLogLog 1 KB RAM. Hai mảnh ghép khớp lại tạo thành một lá chắn chống gian lận hoàn hảo!"

Tôi cười:

  • "Toàn bộ tinh hoa này — từ hàm giải bất đẳng thức calculateOptimalSlotCount, bộ đếm HyperLogLogCounter cho đến cỗ máy YouTubeHybridViewEngine — anh em mình đã đóng gói sạch sẽ và viết đủ 48 bài kiểm thử trong @kido/hot-counter. Đóng máy lại, viết ngay một bài blog kiến trúc chuyên sâu để chia sẻ cho cộng đồng thôi em!"
🔬
Kido Engineering Blog • Deep-Dive Architecture

Giải Phẫu Kỹ Thuật & Kiến Trúc Thực Chiến Của Tập 215

blog.khanhdp.com ↗

Muốn xem chi tiết mã nguồn, bảng so sánh kiến trúc Sơ khởi ➔ Điểm nghẽn khi Scale ➔ Chuẩn Big Tech Production và phân tích kỹ thuật đằng sau câu chuyện ở tập này? Đọc ngay bài viết chuyên sâu: