Better Crash-safe replication for MySQL

Bài tóm tắt Booking.com 2014 2. Tăng trưởng Độ khó: Nâng cao

Booking.com · 2014 · 2. Tăng trưởng · Độ khó: Nâng cao — bài gốc

Bối cảnh

  • Booking.com (2014) dùng replication MySQL để scale đọc, với hàng nghìn slave; số lượng đó nghĩa là sớm muộn sẽ có slave chết.
  • Slave có hai thread: IO thread tải event từ binlog của master và nối vào relay log; SQL thread đọc relay log và thực thi. Mỗi thread lưu vị trí của mình.
  • MySQL 5.5 trở về trước lưu vị trí trong file, không cập nhật nguyên tử cùng tiến độ. Crash thì event bị nối lại hai lần vào relay log, hoặc transaction bị chạy lại: lỗi "Duplicate entry for key PRIMARY" quen thuộc sau khi restart, tệ nhất là hỏng dữ liệu âm thầm.

MySQL 5.6 sửa được gì

  • relay-log-info-repository=TABLE: SQL thread lưu vị trí cùng transaction (với engine nguyên tử như InnoDB), nên không còn chạy lại.
  • IO thread thì không đồng bộ được tiến độ với trạng thái. Lời giải của 5.6 (relay-log-recovery=1): coi toàn bộ relay log là không tin được, đặt IO thread về vị trí của SQL thread rồi tải lại từ master.

Vấn đề của lời giải đó (theo Booking.com)

  1. Bật trong file cấu hình thì áp dụng mỗi lần khởi động, kể cả restart bình thường không có crash.
  2. Slave đang trễ mà restart thì tải lại binlog từ master, tốn mạng của master. Đây là mối lo lớn nhất của họ: card mạng của master vốn đã là giới hạn. Slave delayed replication giữ hàng chục GB relay log chưa thực thi, restart nó làm quá tải card mạng master trong vài phút; slave ở xa thì bão hoà đường WAN.
  3. Master không có mặt thì slave không tiến được dù còn nhiều relay log chưa chạy.
  4. Binlog cần đã bị purge trên master thì slave không nối lại được, nên DBA phải chắc mọi slave đã thực thi trước khi purge.
  5. Giữ lại relay log (relay-log-purge=0) thì bản lưu có thể trùng hoặc thiếu event sau crash.

Họ mở bốn bug/feature request cho MySQL và đề xuất lưu thêm "vị trí đã sync cuối cùng" để chỉ cắt relay log tới đó, không tải lại tất cả.

Cách làm tạm

  • Chạy Binlog Server ngay trên mỗi slave: Binlog Server tải binlog từ master, slave tải từ Binlog Server cục bộ. Restart hay crash thì slave tải lại từ máy mình, không đè lên master. Làm Binlog Server crash-safe thì đơn giản.
  • Giới hạn dung lượng gấp đôi bằng relay-log-space-limit (khoảng 2 lần max-relay-log-size), nhưng đổi tuỳ chọn này cần restart.

Bài học cho SWE

  • Scale đọc bằng rất nhiều replica biến lỗi hiếm (crash một slave) thành chuyện hằng ngày, nên đường phục hồi phải rẻ (Scale dọc trước khi scale ngang).
  • Lưu tiến độ trong cùng giao dịch với dữ liệu là cách duy nhất để không chạy lại hay bỏ sót; cùng ý với commit offset của consumer (Event-driven, message queue và Kafka).
  • Phục hồi của một node không được đổ tải lên node dùng chung (master). Thêm một tầng đệm cục bộ (Binlog Server) để cô lập.

Liên quan