Goodbye to sequential integers, hello UUIDv7

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

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

Bài ngoài sheet. Buildkite là dịch vụ CI; bài kể chuyện chọn khoá chính của chính họ trong lúc chuẩn bị shard database.

Bối cảnh

  • Vài năm trước 2023, Buildkite chuẩn hoá hai định danh cho mọi bảng: số nguyên tăng dần làm khoá chính (join, khoá ngoại nhanh) và UUID làm khoá phụ cho mọi thứ lộ ra ngoài (API, URL; không lộ số liệu nội bộ, khó đoán).
  • Lý do không dùng UUIDv4 làm khoá chính: giá trị ngẫu nhiên nên chèn vào chỗ ngẫu nhiên trong B-tree (index locality kém). Sản phẩm của họ hay đọc dữ liệu mới nhất; với ID tuần tự thì dữ liệu mới nằm dồn ở mép phải của index, với ID ngẫu nhiên thì rải khắp nơi, phải đi qua nhiều trang index hơn và cache trúng kém.

Thay đổi chính

  • 2022, thử UUID có thời gian nhưng vẫn mang nhãn v4: 48 bit đầu (vốn ngẫu nhiên) thành timestamp, giữ nguyên bit version. Khi thử đặt version là 7 thì hỏng hệ thống của vài khách hàng vốn kiểm tra UUID mà chưa biết v7, nên phải giữ nhãn v4. Cùng với vài thay đổi khác trong 6 tuần, tốc độ sinh WAL của DB primary giảm 50% và write IO giảm tương tự, giúp dựng read replica và làm các migration khác dễ hơn.
  • 2023, UUIDv7 làm khoá chính cho mọi bảng mới: 48 bit timestamp mili giây, 74 bit ngẫu nhiên, vẫn là UUID chuẩn nên dùng được cột UUID sẵn có của Postgres. Lý do quyết định: họ đang shard DB lớn nhất theo organization ID, và giữ số nguyên tăng dần duy nhất giữa nhiều DB cần phối hợp. Bỏ được cột số nguyên thứ hai, UUID dùng được cả ngoài (API) lẫn trong (khoá ngoại).
  • Phương án đã cân nhắc: ShardingID của Instagram, khoá chính phức hợp của Shopify, giữ UUID "v4 có thời gian" tự chế. Chọn v7 vì là chuẩn sắp tới, ít tự chế. Chưa cần nhét số shard vào ID; nếu cần sau này có thể dùng UUIDv8.

Vì sao

  • Theo Buildkite: ID có tiền tố thời gian cho hiệu năng chèn gần như số tuần tự mà không cần ai cấp phát. Cái giá là 128 bit (gấp đôi 64 bit của Snowflake hay ShardingID), nhưng theo họ là nhỏ so với cả dòng dữ liệu.

Bài học cho SWE

  • Khoá chính ngẫu nhiên phạt cả ghi (WAL, IO) lẫn đọc dữ liệu mới; ID có thời gian ở đầu giữ được locality.
  • Đổi định dạng ID là một thay đổi giao diện với bên ngoài: hệ thống của khách có thể kiểm tra version.
  • Quyết định ID nên đi cùng kế hoạch shard: số nguyên tăng dần là món nợ khi tách DB.

Liên quan