Cut-over logic in vitess schema migrations
Bài tóm tắt Vitess (PlanetScale/CNCF) 2022 3. Quy mô lớn Độ khó: Nâng cao
Vitess (PlanetScale/CNCF) · 2022 · 3. Quy mô lớn · Độ khó: Nâng cao — bài gốc
Bài kỹ thuật của dự án mã nguồn mở Vitess, không phải câu chuyện của một công ty. Wiki giữ vì nó giải thích bước khó nhất của online schema change, thứ mà GoCardless, GitHub, Etsy đều phải đối mặt.
Bối cảnh
- Online DDL (pt-online-schema-change, fb-osc, gh-ost, Vitess): tạo bảng bóng với schema mới, chép dữ liệu, bắt kịp thay đổi, rồi cut-over: đổi tên bảng gốc đi và đưa bảng bóng vào chỗ.
- Cut-over là bước nguy hiểm nhất: hai bảng phải khớp hoàn toàn trong lúc đổi, nên luôn cần một dạng khoá, gây query bị chặn và kết nối tăng vọt.
- Vitess lan truyền thay đổi bất đồng bộ (đọc binlog rồi áp vào bảng bóng), nên lúc nào hai bảng cũng có thể lệch một chút.
- Các cách cũ:
- fb-osc: đổi tên hai bước, có khoảnh khắc bảng không tồn tại nên app gặp lỗi lạ; công cụ chết giữa hai lần đổi tên thì sao?
- gh-ost: khoá bảng bằng một cơ chế phức tạp, trông nguyên tử với app; nhưng dựa vào thứ tự ưu tiên khoá nội bộ của MySQL, và có người dùng chỉ ra trường hợp thứ tự đó không đúng.
Thay đổi chính: cut-over nhiều lớp của Vitess
Mục tiêu: với app trông như nguyên tử (tệ nhất là chờ vài giây, không có lỗi lạ), và chắc chắn không có lệnh ghi nào lọt vào bảng gốc trong lúc bắt kịp.
- Đệm ghi bằng ACL có hạn: mọi query đi qua proxy (VTGate → VTTablet). Đặt rule đệm 10 giây lên bảng: query mới đứng chờ. Huỷ rule thì query chạy tiếp trên bảng mới; rule hết hạn thì query lỗi. Bộ đệm có giới hạn, nên cut-over phải nhanh (thường 2–3 giây).
- Dừng thêm 100 ms cho các query đã qua kiểm tra ACL ngay trước đó kịp chạy xong. Theo bài, đây không phải bảo đảm, vì "
sleepkhông bao giờ là lời giải cho race condition". - "Đục lỗ":
RENAMEbảng gốc đi chỗ khác.RENAMEchờ query đang chạy xong; sau đó không thể còn lệnh ghi nào vào bảng gốc, kể cả query chạy thẳng vào MySQL. Ghi lại điểm thời gian (gtid_executed). - Hoàn tất: đọc nốt binlog tới điểm đó, áp vào bảng bóng. Hai bảng khớp thì đổi tên bảng bóng vào chỗ, gỡ ACL, query đang đệm chạy trên bảng mới.
- Lỗi ở bước nào cũng quay lui được: gỡ ACL, đổi tên bảng gốc về, thử lại sau. Nếu chính VTTablet chết giữa lúc "đục lỗ": trước khi đổi tên, hành động đã được ghi audit; VTTablet mới đọc audit, trả bảng gốc về chỗ, nhận lại migration và cut-over lại khi thích hợp (ACL nằm trong bộ nhớ nên tự mất).
Bài học cho SWE
- Có một proxy trên đường đi của mọi query cho phép đệm traffic trong lúc đổi, cùng ý với PgBouncer tạm dừng query của GoCardless (GoCardless 2017) (Migration không downtime).
- Kết hợp cơ chế mềm (đệm, chờ) với bảo đảm cứng (đổi tên bảng làm ghi trở nên bất khả). Đừng dựa vào
sleephay thứ tự khoá ngầm. - Thiết kế mọi bước để quay lui được, và ghi ý định trước khi làm (audit) để process khác khôi phục được nếu mình chết giữa chừng.
- Schema migration là một loại deploy cần giới hạn thời gian chờ, cùng tinh thần
lock_timeoutcủa GoCardless (Deploy và release an toàn).
Liên quan
Được nhắc tới trong
Công ty (1)