5-year Evolution of Ele.me's Transaction System - Part 4
Bài tóm tắt Ele.me 2020 3. Quy mô lớn Độ khó: Trung bình
Ele.me · 2020 · 3. Quy mô lớn · Độ khó: Trung bình — bài gốc
Phần 4/6. Trước: Ele.me 2020; sau: Ele.me 2020.
Bối cảnh
Sau khi shard (đầu 2016), nút cổ chai dời từ DB sang Redis (khoảng 6/2016). Twemproxy và Codis gặp nhiều vấn đề, nên Ele.me tự viết proxy Redis corvus có đủ metric (bộ nhớ, kết nối, hit rate, số key). Code dùng Redis cho ba việc: cache (bảng, API), distributed lock, sinh số thứ tự cho merchant.
Thay đổi chính
- Chuyển cụm cache thì "sốc": cụm cũ 55 GB, cụm mới 100 GB đầy sau khoảng 10 phút. Nghĩa là cụm cũ thực ra đã quá tải từ trước, chỉ bị che đi nhờ chính sách thu hồi bộ nhớ khác.
- Thủ phạm là cache của
query_order(khoảng 7.000 QPS) vàcount_order(khoảng 10.000 QPS): merchant poll đơn theo khoảng thời gian, tham số đổi liên tục nên key tăng vọt, TTL ngắn, cache gần như vô dụng. - Họ bỏ hẳn hai cache đó, tách cache cấp API và cấp bảng ra hai cụm. Hit rate lên 88–95% rồi trên 98%.
- Thủ phạm là cache của
- Đường cong bộ nhớ không khớp đỉnh kinh doanh (trưa, tối) mà tăng dần trong ngày: poll "đơn 3 ngày" và "đơn hôm nay" lấy nhiều hơn một trang. Theo tác giả, latency trung bình ở QPS cao không nói lên gì.
- Gọi API bất thường lúc 1–2 giờ sáng: hệ thống đối soát mới gọi
query_orderhàng loạt. Gốc là quản trị service còn non: ai trong mạng nội bộ cũng gọi được mọi API. - Pipeline cập nhật cache (RabbitMQ để giảm đỉnh + Redis để gộp) ổn định tới mức bị quên. Khi nó hỏng vì một logic sai ở phía merchant thì không ai nhận ra, và merchant thấy dữ liệu cũ. Họ rút gọn pipeline, thêm luồng giám sát và bù.
- Thay polling bằng long connection + push/pull dựa trên message broadcast. Gặp tình huống push qua mạng public về nhanh hơn dữ liệu kịp sang replica, nên client tra không thấy đơn mới. Họ đã từ chối đề xuất "poll thẳng primary". Họ dùng dead-letter queue để trễ message, nhưng topology đó phải duy trì tay và mất khi node RabbitMQ chết. Sau đó họ khai báo topology trong config của service và dựng tự động khi khởi động.
Bài học cho SWE
- Cache cho dữ liệu có tham số query đổi liên tục (polling theo khoảng thời gian) chỉ tốn bộ nhớ, không có hit (Cache và invalidation).
- Một hệ thống chạy ổn tới mức không ai nhìn vẫn cần cảnh báo và luồng bù.
- Push nhanh hơn replication thì client đọc replica sẽ không thấy dữ liệu: bài toán thứ tự giữa kênh realtime và kho dữ liệu (Realtime và fan-out).
Liên quan
Được nhắc tới trong
Công ty (1)
Bài tóm tắt (3)