From Batch Snapshots to Near-Real-Time Data

Bài tóm tắt Doximity 2026 3. Quy mô lớn Độ khó: Khó

Doximity · 2026 · 3. Quy mô lớn · Độ khó: Khó — bài gốc

Bối cảnh

  • Doximity đã có pipeline batch chép định kỳ snapshot của DB ứng dụng vào warehouse (Snowflake): tin cậy, nhất quán giao dịch, nhưng độ tươi tính bằng giờ. Họ thêm CDC để thay đổi có mặt trong vài phút, giữ nguyên batch vì những bảo đảm nhất quán và phục hồi của nó.
  • Theo bài, phần khó không phải nối DB → Kafka → Snowflake mà là bốn câu hỏi xung quanh.

Thay đổi chính

  • Hai đường với điểm mạnh khác nhau: batch dựng snapshot từ point-in-time restore của Aurora, mọi bảng cùng một thời điểm (nhất quán giao dịch, là mốc phục hồi và kiểm tra); CDC (Debezium đọc binlog MySQL → Kafka → sink Snowflake) tối ưu độ tươi. Một bảng CDC có thể tươi hơn, nhưng hai bảng CDC liên quan có thể không cùng thời điểm (outer join, watermark, model tăng dần bị ảnh hưởng). Consumer chọn: nhất quán theo thời điểm, hay latency thấp.
  • Khởi tạo từ snapshot đã tin tưởng thay vì để Debezium chụp ban đầu (chế độ no_data: chỉ lấy schema). Snapshot ban đầu của connector có thể khoá toàn cục/bảng thoáng qua và quét mọi dòng trong một giao dịch repeatable-read trên DB ứng dụng. Thay vào đó: chờ snapshot phủ được sự kiện cũ nhất còn giữ, clone snapshot làm mốc (zero-copy clone của Snowflake), rồi phát lại sự kiện còn giữ qua đường replication thường (lặp lại trạng thái cũ một lúc là chấp nhận được).
  • Dựng lại bảng chính xác: snapshot ghi lại vị trí GTID và thời điểm nguồn; khi phải dựng lại (reset offset, migrate/failover DB làm đổi lịch sử binlog/GTID, hết hạn giữ log, kiểm tra phát hiện lệch) thì clone snapshot, đọc vị trí của nó, chỉ phát lại sự kiện từ vị trí đó. Bất biến: mốc tin cậy → chứng minh chồng lấp với log → xử lý mọi thay đổi sau ranh giới.
  • Gom vào rồi chia theo metadata: sink subscribe theo regex mọi topic CDC của một namespace và đổ vào một bảng raw chung (many-to-one), giữ metadata topic/DB/bảng; một router chia lại ra bảng trung gian theo từng bảng nguồn, tự tạo cấu trúc khi gặp bảng mới. Onboard bảng mới không cần sửa cấu hình connector, nhưng bảng phải tuân luật của nền tảng về khoá, schema, kiểu dữ liệu.
  • Có thể query trước khi merge: consumer đọc view = bảng base đã nén + delta chưa merge (lấy thay đổi mới nhất theo khoá chính, phủ lên base, bỏ dòng có thao tác cuối là delete). Worker merge (MERGE của Snowflake) khi delta vượt ngưỡng tuổi hoặc dung lượng, hoặc ngay khi đổi schema. Tách việc dữ liệu hiển thị khỏi việc bảo trì; ngưỡng giới hạn mức khuếch đại đọc. Đổi lại, view gánh trách nhiệm đúng đắn (update, delete, thứ tự, schema phải ra cùng kết quả trước và sau merge).
  • Delete kéo theo mà log không ghi: với ON DELETE CASCADE của InnoDB, binlog dạng row chỉ ghi delete của dòng cha. Một tiến trình đọc khoá ngoại và hành vi delete từ information schema; khi worker gặp delete cha, nó tìm dòng con ở đích và sinh delete tổng hợp đi qua cùng đường xử lý. Chỉ hỗ trợ khoá thay thế một cột (theo quy ước ActiveRecord); thiếu metadata thì bỏ qua và ghi log, không đoán.
  • Kiểm tra bằng mốc độc lập: clone bảng CDC (ghi vị trí), clone snapshot, phát sự kiện sau vị trí snapshot vào bản clone, rồi so số dòng và khoá khi hai bên cùng thời điểm. Thêm kiểm tra tự chữa với bảng có updated_at: dòng có ở snapshot mà thiếu hoặc cũ hơn ở stream thì upsert, dòng cũ chỉ có ở stream thì xoá, kèm ghi nhận bất thường.
  • Đo: lag từ CreateTime của bản ghi Kafka tới lúc router ghi vào bảng trung gian, theo từng bảng. Trong 12 ngày, mẫu phân tầng bảng hoạt động: 95% sự kiện có thể query trong 8 phút, 99% trong 13 phút (không tính thời gian trước Kafka và chưa tính merge vật lý).

Vì sao

Bài học cho SWE

  • Dùng lại trạng thái đã tin tưởng (snapshot + vị trí log) làm cơ chế khởi tạo và phục hồi.
  • Giữ metadata nguồn khi gom nhiều luồng vào một chỗ, để còn chia lại an toàn.
  • Tách "dữ liệu hiển thị" khỏi "merge vật lý" bằng view base + delta có giới hạn.
  • Nói rõ bảo đảm nhất quán: bảng tươi hơn không có nghĩa là một nhóm bảng nhất quán.

Liên quan