How Figma's databases team lived to tell the scale

Bài tóm tắt Figma 2024 3. Quy mô lớn Độ khó: Nâng cao

Figma · 2024 · 3. Quy mô lớn · Độ khó: Nâng cao — bài gốc

Bối cảnh

  • Stack DB của Figma tăng gần 100 lần từ 2020. Cuối 2022 đã có cache, read replica và khoảng 12 DB tách dọc (Figma 2023).
  • Họ đo trần theo nhiều chiều (CPU, IO, kích thước bảng, số hàng ghi) bằng dữ liệu lịch sử + load test. Kết quả: một số bảng vài TB, tỷ hàng đã quá lớn cho một DB:
    • vacuum của Postgres bắt đầu gây ảnh hưởng độ tin cậy;
    • bảng ghi nhiều nhất sắp vượt IOPS tối đa của RDS.
  • Tách dọc hết tác dụng, vì đơn vị nhỏ nhất của tách dọc là một bảng.

Mục tiêu

Developer bị ảnh hưởng ít nhất; scale-out về sau không phải sửa app; không backfill đắt; làm từng bước; không có migration một chiều (luôn rollback được); nhất quán mạnh, không ghi kép; tận dụng chuyên môn đang có.

Thay đổi chính

  • Loại CockroachDB, TiDB, Spanner, Vitess và NoSQL:
    • migration giữa hai kho dữ liệu phức tạp;
    • mất chuyên môn RDS Postgres tích luỹ nhiều năm;
    • chỉ còn vài tháng đường băng;
    • mô hình quan hệ phức tạp không hợp NoSQL.
  • Tự xây shard ngang trên RDS Postgres, nhưng chỉ một tập tính năng hẹp. Ví dụ không hỗ trợ transaction nguyên tử xuyên shard.
  • Khoá shard: không có một khoá chung cho mọi bảng, nên chọn vài khoá (UserID, FileID, OrgID). Colo là nhóm bảng chung khoá shard và layout: trong một colo vẫn JOIN và transaction đầy đủ nếu giới hạn trong một giá trị khoá.
  • Hash khoá shard để route: nhiều khoá là auto-increment hoặc ID tiền tố timestamp kiểu Snowflake, nếu chia theo khoảng thì dồn dữ liệu vào một shard. Hash giải quyết điều đó. Đánh đổi: range scan trên khoá shard kém hiệu quả, nhưng hiếm gặp trong code của họ.
  • Shard logic tách khỏi shard vật lý:
    • Shard logic bằng Postgres view (WHERE hash(shard_key) trong một khoảng), mỗi view có connection pooler riêng nhưng vẫn trỏ về DB chưa shard. Bật dần theo % bằng feature flag; rollback trong vài giây.
    • Kiểm chứng view: load test bằng corpus query production (overhead tối đa <10%), cộng shadow reads so sánh kết quả có view và không có view.
    • Rồi mới tách vật lý 1 → N, tái dùng công cụ từ lần tách dọc.
  • DBProxy (Go), đứng giữa app và PgBouncer:
    • query engine gồm parser → AST, logical planner (lấy shard ID), physical planner (viết lại query cho từng shard);
    • load-shedding, request hedging, quản lý topology (cập nhật dưới 1 giây).
    • Query không có khoá shard thì phải scatter-gather tới mọi shard, tải không giảm gì so với khi chưa shard.
  • Shadow application readiness / shadow planning: chạy pha planning trên traffic thật với các phương án khoá shard giả định, ghi vào data warehouse để phân tích. Từ đó chọn tập SQL phủ ~90% query phổ biến: cho phép range scan và point query; JOIN chỉ trong cùng colo và trên khoá shard.
  • Logical replication toàn bộ dữ liệu sang mỗi shard, rồi chỉ cho đọc/ghi phần thuộc shard đó, để khỏi phải tự làm filtered replication.

Kết quả

  • Mất khoảng 9 tháng để shard bảng đầu tiên (9/2023). Failover có 10 giây ảnh hưởng một phần trên primary, replica không bị ảnh hưởng, không thấy giảm latency hay availability.
  • Việc còn lại: schema change xuyên shard, sinh ID duy nhất toàn cục cho khoá chính đã shard, transaction xuyên shard cho nghiệp vụ sống còn, unique index phân tán, ORM tương thích, reshard một nút bấm.
  • Theo Figma: khi có đủ đường băng sẽ đánh giá lại việc tự làm so với NewSQL, vì các sản phẩm này đã trưởng thành hơn.

Bài học cho SWE

  • Tách logic trước, tách vật lý sau, và rollback logic chỉ là đổi config.
  • Hỗ trợ một tập con SQL có chủ đích, dựa trên dữ liệu traffic thật, thay vì cố tương thích toàn bộ.
  • ID theo thời gian (tốt cho sắp xếp, xem Sinh ID phân tán) lại xấu cho chia shard theo khoảng. Cách xử lý là hash.
  • Chọn giải pháp rủi ro thấp mình kiểm soát được khi bị ép thời gian, và hẹn ngày xem lại quyết định.

Liên quan