Partitioning GitHub's relational databases to handle scale
Bài tóm tắt GitHub 2021 3. Quy mô lớn Độ khó: Trung bình
GitHub · 2021 · 3. Quy mô lớn · Độ khó: Trung bình — bài gốc
Bối cảnh
- Hơn 10 năm trước GitHub bắt đầu bằng Rails + một MySQL.
- Các bước tiến hoá trước 2019: tách vài tính năng (ví dụ statuses) sang DB riêng; thêm read replica; dùng ProxySQL giảm số kết nối tới primary.
- Nhưng lõi vẫn là một cluster
mysql1chứa user, repo, issue, PR. Họ liên tục phải chuyển sang máy to hơn (scale dọc), và mọi sự cố ởmysql1đều ảnh hưởng mọi tính năng trên đó. - 2019:
mysql1trung bình 950.000 query/giây (900K trên replica, 50K trên primary).
Thay đổi chính
1. Tách ảo trước (virtual partitions):
- Schema domain: nhóm các bảng thường dùng chung trong JOIN/transaction, khai báo trong
db/schema-domains.yml. Linter bắt buộc mọi bảng phải thuộc một domain. - Query linter (bật ở dev/test và CI): báo lỗi khi một query chạm bảng của nhiều domain. Query vi phạm được đánh dấu bằng comment
cross-schema-domain-query-exempted, tạo thành backlog cần sửa. - Cách gỡ JOIN: tách thành nhiều query (
preload); thêm tuỳ chọndisable_joinschohas_many :through(đã đưa ngược vào Rails 7); JOIN ở phía app. Join ở app đôi khi còn nhanh hơn, vì query planner của MySQL có lúc chọn kế hoạch tệ. - Mỗi thay đổi đi qua Scientist: chạy cả code cũ lẫn mới trên một phần request để so sánh.
- Transaction linter chạy trên production với sampling. Nếu transaction xuyên domain thực sự cần nhất quán, họ tách dữ liệu ra bảng mới thuộc cùng domain (hay gặp với bảng đa hình như
reactions).
2. Dời vật lý không downtime, hai công cụ độc lập để giảm rủi ro:
- Vitess (vertical sharding qua VTGate/VTTablet/VReplication).
- Quy trình write-cutover tự viết: cluster mới replicate từ cluster cũ; ProxySQL của cluster mới ban đầu vẫn trỏ về primary cũ. Script cutover: bật read-only → đọc GTID cuối → chờ cluster mới nhận đủ → ngắt replication → đổi routing ProxySQL → tắt read-only. Mất vài chục mili giây cho cả những bảng bận nhất; chạy lúc traffic thấp nhất nên chỉ có vài lỗi ghi thấy được.
- Cách này đã tách
mysql1: 130 bảng bận nhất (repo, issue, PR) được dời một lần. Họ không dùng Vitess cho mọi trường hợp vì topology deploy và yêu cầu read-your-writes.
Kết quả (2019 → 2021)
Cùng các bảng đó giờ nằm trên nhiều cluster, tổng 1,2 triệu query/giây (traffic vẫn tăng), nhưng tải trung bình mỗi host giảm một nửa; số sự cố liên quan DB giảm rõ.
Vì sao
Theo GitHub: tăng trưởng và availability. Một cluster dùng chung là điểm hỏng chung của mọi tính năng lõi.
Bài học cho SWE
- Tách logic trước, tách vật lý sau. Linter biến các phụ thuộc ẩn thành backlog nhìn thấy được.
- Có hai đường migrate độc lập để không phụ thuộc vào một công cụ duy nhất.
- Chọn công nghệ "nhàm chán" đã kiểm chứng; độ tin cậy là ưu tiên số một.
- Sau tách dọc là shard ngang (GitHub hẹn kể trong bài sau).
Liên quan
Được nhắc tới trong
Chủ đề (5)
Công ty (1)