The growing pains of database architecture

Bài tóm tắt Figma 2023 2. Tăng trưởng Độ khó: Trung bình

Figma · 2023 · 2. Tăng trưởng · Độ khó: Trung bình — bài gốc

Bối cảnh

  • 2020: thêm tính năng, chuẩn bị ra sản phẩm thứ hai, user tăng. Traffic DB tăng khoảng 3 lần mỗi năm.
  • Phần lớn metadata (quyền, thông tin file, comment) nằm trên một Amazon RDS Postgres. Giờ cao điểm CPU vượt 65%, latency ngày càng khó đoán. Theo Figma: nếu DB bão hoà hoàn toàn thì Figma ngừng hoạt động. Lúc đó còn xa ngưỡng đó, nhưng họ muốn xử lý trước.

Thay đổi chính

1. Mua thêm ~1 năm "đường băng":

  • Lên máy lớn nhất (r5.12xlarge → r5.24xlarge).
  • Thêm read replica.
  • DB mới cho tính năng mới.
  • PgBouncer khi số kết nối lên hàng nghìn.

Giới hạn của các biện pháp này: lượt ghi chiếm phần lớn tải, và nhiều lượt đọc không chuyển sang replica được vì app nhạy với replication lag.

2. Loại shard ngang (lúc đó):

  • Giải pháp managed scale ngang phần lớn không tương thích Postgres.
  • NoSQL hoặc Vitess (MySQL) đòi migration đọc/ghi kép phức tạp, NoSQL còn phải sửa app nhiều.
  • NewSQL tương thích Postgres: Figma sẽ thành một trong những cluster lớn nhất, không muốn là khách đầu tiên gặp lỗi scale.
  • Tự host thì đội chưa có kỹ năng.

3. Chọn tách dọc theo nhóm bảng:

  • Chọn bảng theo hai tiêu chí:
    • Tác động: đo average active sessions bằng cách lấy mẫu pg_stat_activity mỗi 10ms, gộp theo bảng.
    • Mức cô lập: tách bảng sẽ mất transaction, foreign key, JOIN. Vì Ruby/ActiveRecord quá động để phân tích tĩnh, họ gắn validator lúc chạy vào ActiveRecord, gửi thông tin query/transaction về data warehouse rồi tìm nhóm bảng hay đi cùng nhau.
  • Mục tiêu migration: ảnh hưởng availability dưới 1 phút, tự động hoá để lặp lại được, hoàn tác được. Không có sẵn công cụ đáp ứng nên tự xây.
  • Hai pha:
    • Tách PgBouncer trước. Mỗi nhóm bảng có PgBouncer riêng nhưng vẫn trỏ về DB cũ; security group buộc mọi client đi qua PgBouncer. Route sai thì phát hiện được mà chưa hỏng gì.
    • Rồi mới tách dữ liệu.
  • Logical replication (không dùng streaming), vì nó:
    • chỉ chép một tập bảng;
    • chép được sang bản Postgres major khác, nên dùng luôn để nâng cấp;
    • cho phép replication ngược để rollback.
  • Chép ban đầu nhanh hơn: bản đầu tiên rất chậm vì index ở DB đích được cập nhật từng hàng. Họ xoá index ở DB đích rồi dựng lại sau, thời gian chép giảm từ vài ngày/tuần xuống vài giờ.
  • Cutover: PgBouncer tạm dừng kết nối mới → thu quyền query trên bảng ở DB cũ → huỷ query còn chạy (thường dưới 10) → lấy LSN ở DB cũ và chờ DB mới replay qua LSN đó → promote → bật replication ngược → cho traffic chạy lại sang DB mới.

Kết quả

  • Đã chạy nhiều lần trên production: lần đầu 2 bảng nhiều traffic, lần cuối (10/2022) 50 bảng.
  • Mỗi lần có khoảng 30 giây ảnh hưởng một phần (~2% request bị rớt).
  • Partition lớn nhất chỉ còn ~10% CPU.

Vì sao

Theo Figma: giảm rủi ro bất ổn mà không hy sinh độ tin cậy ("không thể đóng cao tốc trong lúc sửa").

Bài học cho SWE

  • Đo theo bảng (AAS) để biết tách gì có tác động nhất.
  • Chèn một lớp proxy rồi tách nó trước dữ liệu, nhờ vậy lỗi routing lộ ra khi vẫn còn an toàn.
  • Thiết kế rollback từ đầu (replication ngược).
  • Hiểu nguyên nhân chậm (index cập nhật từng hàng) thay vì chấp nhận nó.
  • Giới hạn tự nhận: số DB tăng làm routing ở client phức tạp, nên họ thêm query routing service. Bảng tỷ hàng vẫn sẽ chạm trần, dẫn tới shard ngang: Figma 2024.

Liên quan