5-year Evolution of Ele.me's Transaction System - Part 2
Ele.me · 2020 · 2. Tăng trưởng · Độ khó: Trung bình — bài gốc
Phần 2/6. Phần 1: Ele.me 2020; tiếp theo: Ele.me 2020.
Bối cảnh (2015)
- DB đơn hàng: 1 primary MySQL + 5 secondary + 1 node cho MHA. Không chịu được tải đột biến: nếu phòng kinh doanh không báo trước khuyến mãi thì secondary sập và chỉ còn biết chuyển qua lại giữa các DB.
- DDL quá đắt: thêm một cột vào bảng đơn hàng (hàng triệu đơn mỗi ngày) bị DBA đánh giá là phải dừng 3–5 giờ và cần CEO duyệt.
- Một số API mở quá "thô" sinh ra SQL rất kém.
Thay đổi chính
-
Shard DB và bảng (9/2015 → 3/2016), do team DAL dẫn dắt: hai chiều, mỗi chiều 120 shard, định tuyến được theo user ID, merchant ID hoặc order ID; ghi ưu tiên theo user ID. Do thiếu tài nguyên, shard user và shard merchant chạy chung máy.
-
Các bước:
- quy tắc sinh order ID mới;
- ghi kép vào DB và cache, đọc dữ liệu cũ, so sánh;
- sửa SQL không tương thích (sắp xếp xuyên shard, thống kê, query không có khoá shard). Team nghiệp vụ mất nhiều thời gian nhất ở bước này;
- ghi kép, đọc dữ liệu mới;
- chuyển hẳn.
Trước Tết 2016 họ còn archive chỉ giữ đơn 15 ngày để qua cao điểm. Ngày chuyển (giữa 3/2016), từ 5 giờ sáng toàn hệ thống chặn ghi khoảng 10 phút; hôm sau nhập lại đơn 3 tháng gần nhất.
-
Message broadcast cho đơn hàng (từ 7/2015): chọn RabbitMQ (cụm 3 broker riêng) vì đội vận hành có kinh nghiệm, dù tác giả thấy RocketMQ hợp hơn cho message có thứ tự.
- Lúc release có sự cố do HAProxy ngắt kết nối mà client không xử lý timeout. Họ bật/tắt bằng công tắc và tăng dần 5% → 10% → 30%.
- Quy tắc: phát sự kiện chứ không phát trạng thái; message chỉ để báo, không để đồng bộ dữ liệu (cần thêm thì gọi API tra ngược); consumer phải idempotent; mỗi queue tối đa 10.000 message tồn.
-
Bản thiết kế viết lại bằng Java bị architecture review bác (9/2015). Câu hỏi của đại diện vận hành: hệ thống có nằm trên đường găng không, và lỗi thì có hạ cấp được không? Nằm trên đường găng mà không hạ cấp được nghĩa là hỏng nó thì hỏng cả công ty. Tác giả về sau đánh giá cao việc bị bác. (Chuyển hẳn sang Java chỉ xong năm 2019.)
Bài học cho SWE
- Shard kéo theo việc sửa mọi SQL không có khoá shard; đó mới là phần tốn công của team nghiệp vụ (Sharding và tách database).
- Ghi kép + so sánh + chuyển đọc rồi chuyển hẳn là khung chuẩn, kể cả khi bước cuối vẫn cần vài phút chặn ghi (Migration không downtime).
- Phát sự kiện, không phát trạng thái; message mỏng, consumer tự tra thêm, giống cách "gửi ID, không gửi dữ liệu" của Atlassian (Realtime và fan-out).
- Câu hỏi review hữu ích nhất: "Nếu nó hỏng, có hạ cấp được không?"