How Uber Conquered Database Overload: From Static Rate-Limiting to Intelligent Load Management
Bài tóm tắt Uber 2026 4. Điều chỉnh Độ khó: Khó
Uber · 2026 · 4. Điều chỉnh · Độ khó: Khó — bài gốc
Bối cảnh
- Docstore (có transaction, CRUD) và Schemaless (tối ưu append) là hai DB phân tán tự làm trên MySQL: hàng nghìn cụm, hàng chục PB, hàng chục triệu request mỗi giây. Kiến trúc chung: query engine stateless (lập kế hoạch, định tuyến, shard, schema, phân quyền), storage engine stateful (transaction, pool kết nối, Raft; mỗi partition một leader hai follower, MySQL trên NVMe gắn liền), control plane.
- Ở quy mô này quá tải lan dây chuyền (timeout, retry dồn), và trong môi trường nhiều tenant phải công bằng.
Thay đổi chính
- Thử đầu tiên: quota trong query engine, thất bại. Mỗi request tính đơn vị dung lượng theo byte, quota cố định, vượt thì 429; vì routing stateless nên đếm trong Redis tập trung. Theo Uber: thêm một lượt gọi Redis mỗi request (thêm điểm hỏng và một hop); để shed đúng cho một partition quá tải, lớp routing phải biết sức khoẻ của hàng nghìn partition; mô hình chi phí sai (query quét cả bảng mà trả một dòng bị tính như query đọc một dòng, do cách MySQL quét và lọc); quota tĩnh nên các team liên tục xin chỉnh. Bài học: điều khiển quá tải phải nằm sát storage node.
- Tín hiệu đúng là concurrency (số thao tác đang bay; theo định luật Little = throughput × latency), không phải QPS.
- Nền đầu tiên ở storage layer:
- CoDel (Controlled Delay, mượn từ mạng): shed theo thời gian chờ trong hàng đợi chứ không theo độ dài; hàng đợi riêng cho đọc, ghi, chậm (scan, delete, replication); LIFO thích ứng: bình thường FIFO, quá tải thì LIFO để ưu tiên request mới còn cơ hội thành công, bỏ việc cũ mà client có lẽ đã bỏ hoặc retry.
- Scorecard: giới hạn concurrency theo tenant, tất định, chạy cả khi hệ khoẻ, để một tenant không chiếm hết và để khoanh nguồn gây sự cố.
- Regulator gắn thêm, chạy tại node, giữ các bất biến mà concurrency không bắt được: tổng byte ghi đồng thời (một caller QPS thấp gửi payload ghi lớn), khoá partition nóng, bộ nhớ trống của process, số goroutine.
- Hạn chế: CoDel không phân biệt ưu tiên (drop cả traffic người dùng lẫn việc nền); timeout hàng đợi và limit concurrency tĩnh phải chỉnh tay; thời gian chờ cố định làm mọi request bị từ chối retry cùng lúc, thành vòng lặp quá tải–từ chối (thundering herd).
- Thay CoDel bằng Cinnamon (load shedder có ưu tiên do team Delivery làm): nhiều lần quá tải đến từ job nền độ ưu tiên thấp (pipeline, aggregator, dọn rác nội bộ). Ưu tiên theo tier t0 (hạ tầng tối quan trọng) tới t5; không có ưu tiên tường minh thì gán mặc định theo service gọi; shed từ thấp lên. Bỏ hàng đợi "chậm", còn đọc và ghi, việc dài chỉ đánh ưu tiên thấp. Timeout hàng đợi tự chỉnh theo P90 latency; auto tuner chỉnh limit đang bay theo latency và tỉ lệ lỗi; điều khiển PID thay vì từ chối cứng sau thời gian chờ cố định (ví dụ 5 ms), nên ít shed sớm, ít 429, ít retry dồn.
- Hợp nhất thành một engine: quá tải không luôn là cục bộ; leader khoẻ vẫn phải shed nếu follower tụt (commit index lag). Trước đó việc này do thành phần ngoài dùng token bucket lo, dễ quyết định chọi nhau (split-brain) và không tối ưu toàn cục. Cinnamon nhận tín hiệu ngoài gắn thêm ("Bring Your Own Signal") trong cùng đường admission: shed rộng theo ưu tiên nếu áp lực toàn hệ, hay shed đúng caller nếu áp lực do một tác nhân.
- Kết quả dưới quá tải, theo Uber (so với token bucket): throughput +80% (5.400 so với 3.000 QPS), P99 upsert −70% (1,0 so với 3,1 giây), goroutine đỉnh −93% (10.000 so với 150.000), heap −60% (tối đa 1 GB so với đỉnh 5–6 GB); commit lag ổn định mượt thay vì dao động.
- Bài học, theo Uber: ưu tiên là trên hết; từ chối sớm, đừng giữ request trong bộ nhớ tới khi hết hạn; PID cho shed ổn định; đặt điều khiển ở nơi có trạng thái; tránh cấu hình tĩnh; quan sát được cái gì bị shed và vì sao; đơn giản.
Vì sao
- Lớp stateless không có đủ ngữ cảnh (sức khoẻ partition, chi phí thật của query) để shed đúng; storage node thì có.
Bài học cho SWE
- Đo tải bằng việc đang bay, không bằng số request; đếm chi phí theo byte trả về có thể sai hẳn với workload quét.
- Hàng đợi khi quá tải nên ưu tiên request mới (LIFO thích ứng) và từ chối sớm.
- Thời gian chờ cố định làm mọi người bị từ chối cùng lúc rồi cùng retry; điều khiển liên tục (PID) tránh dao động.
- Tách hai bài toán: bảo vệ hệ khi quá tải (theo ưu tiên) và công bằng giữa tenant khi khoẻ (theo tenant); chạy song song.
- Thiết kế bộ shed như một khung nhận tín hiệu mới, vì sẽ luôn có loại quá tải mới (follower lag).
Liên quan
- Multi-tenancy và cô lập tenant
- Load shedding, rate limit và backpressure · Hot partition và thundering herd
- Cùng công ty: Uber 2026 · Uber 2026
- Cùng bài toán: Salesforce 2026 (shed theo queue time, ưu tiên đăng nhập)
Load shedding, rate limit và backpressureHot partition và thundering herdMulti-tenancy và cô lập tenant
#Docstore#Schemaless#MySQL#Raft#load shedding#quota in query engine#Redis#capacity unit#concurrency#Little's Law#CoDel#adaptive LIFO#Scorecard#regulators
Được nhắc tới trong
Chủ đề (3)
Công ty (1)