Dựa trên thực tiễn gần đây trong môi trường sản xuất sử dụng SeaTunnel CDC (Change Data Capture) để đồng bộ các kịch bản như Oracle, MySQL và SQL Server, và kết hợp với phản hồi từ một loạt người dùng, tôi đã viết bài viết này để giúp bạn hiểu quá trình SeaTunnel thực hiện CDC. Ba giai đoạn của CDC Quá trình đọc dữ liệu CDC tổng thể có thể được chia thành ba giai đoạn chính: Snapshot (Tải đầy đủ) Backfill tăng Bước 1: Snapshot Ý nghĩa của giai đoạn Snapshot rất trực quan: chụp ảnh tức thời dữ liệu bảng cơ sở dữ liệu hiện tại và thực hiện quét bảng đầy đủ thông qua JDBC. Lấy MySQL làm ví dụ, vị trí binlog hiện tại được ghi lại trong snapshot: SHOW MASTER STATUS; File Position Binlog_Do_DB Binlog_Ignore_DB Executed_Gtid_Set binlog.000011 1001373553 Đánh giá 0011 1001373553 SeaTunnel ghi lại tệp và vị trí như là . low watermark Lưu ý: Điều này không chỉ được thực hiện một lần, bởi vì SeaTunnel đã thực hiện logic chia cắt của riêng mình để tăng tốc độ chụp ảnh tức thời. Lưu ý: Điều này không chỉ được thực hiện một lần, bởi vì SeaTunnel đã thực hiện logic chia cắt của riêng mình để tăng tốc độ chụp ảnh tức thời. MySQL Snapshot Splitting Mechanism (Cơ chế chia tách) Giả sử sự song song toàn cầu là 10: SeaTunnel trước tiên sẽ phân tích tất cả các bảng và phạm vi khóa chính / khóa duy nhất của họ và chọn một cột chia thích hợp. Nó được chia theo giá trị tối đa và tối thiểu của cột này, với mặc định snapshot.split.size = 8096. Các bảng lớn có thể được cắt thành hàng trăm splits, được phân bổ cho 10 kênh song song bởi trình liệt kê theo thứ tự của các yêu cầu subtask (đang hướng tới sự phân phối cân bằng tổng thể). Table-level sequential processing (schematic): // Processing sequence: // 1. Table1 -> Generate [Table1-Split0, Table1-Split1, Table1-Split2] // 2. Table2 -> Generate [Table2-Split0, Table2-Split1] // 3. Table3 -> Generate [Table3-Split0, Table3-Split1, Table3-Split2, Table3-Split3] Split-level parallel allocation: // Allocation to different subtasks: // Subtask 0: [Table1-Split0, Table2-Split1, Table3-Split2] // Subtask 1: [Table1-Split1, Table3-Split0, Table3-Split3] // Subtask 2: [Table1-Split2, Table2-Split0, Table3-Split1] Mỗi Split thực sự là một truy vấn với một điều kiện phạm vi, ví dụ: SELECT * FROM user_orders WHERE order_id >= 1 AND order_id < 10001; Mỗi Split ghi lại riêng dấu nước thấp / dấu nước cao của riêng mình. Crucial: Đừng làm cho quá nhỏ; có quá nhiều Splits không nhất thiết phải nhanh hơn, và lịch trình và bộ nhớ sẽ rất lớn. Practical Advice: split_size Bước 2: Backfill Stage Hãy tưởng tượng bạn đang thực hiện một ảnh chụp toàn bộ của một bảng đang được viết thường xuyên.Khi bạn đọc hàng 100, dữ liệu trong hàng 1 có thể đã được sửa đổi.Nếu bạn chỉ đọc ảnh chụp, dữ liệu bạn giữ khi bạn kết thúc đọc thực sự là "không nhất quán" (một phần là cũ, một phần là mới). Why is Backfill needed? The role of Backfill is to compensate for the "data changes that occurred during the snapshot" so that the data is eventually consistent. Hành vi của giai đoạn này chủ yếu phụ thuộc vào cấu hình của Các Parameter exactly_once 2.1 Chế độ đơn giản ( ) exactly_once = false Đây là chế độ mặc định; logic tương đối đơn giản và trực tiếp, và nó không yêu cầu bộ nhớ đệm: Direct Snapshot Emission: Đọc dữ liệu snapshot và gửi nó trực tiếp xuống dòng mà không cần nhập bộ nhớ cache. Direct Log Emission: Đọc Binlog cùng một lúc và gửi nó trực tiếp xuống dòng. Bất kỳ sự nhất quán: Mặc dù sẽ có trùng lặp ở giữa (A cũ được gửi trước, sau đó là B mới), miễn là dòng downstream hỗ trợ viết idempotent (như MySQL REPLACE INTO), kết quả cuối cùng là nhất quán. 2.2.2 Chế độ làm việc ( ) exactly_once = true Đây là phần ấn tượng nhất của SeaTunnel CDC, và đó là bí mật để đảm bảo rằng dữ liệu không bao giờ bị mất, không bao giờ lặp lại. Đối với Deduplication memory buffer (Buffer) Hãy tưởng tượng rằng giáo viên yêu cầu bạn đếm có bao nhiêu người trong lớp ngay bây giờ (giai đoạn snapshot). tuy nhiên, học sinh trong lớp rất khó tính; trong khi bạn đang đếm, mọi người đang chạy vào và ra (thay đổi dữ liệu). Simple Explanation: SeaTunnel làm như thế này: Take a Photo First (Snapshot): Đếm số người trong lớp đầu tiên và ghi lại nó trong một sổ tay nhỏ (buffer bộ nhớ); không nói chính (downstream) chưa. Watch the Surveillance (Backfill): Nhận lại video giám sát (Binlog log) cho khoảng thời gian bạn đang đếm. Chỉnh sửa hồ sơ (Merge): Nếu giám sát cho thấy ai đó vừa vào, nhưng bạn không đếm chúng -> thêm chúng. Nếu giám sát cho thấy ai đó vừa chạy ra ngoài, nhưng bạn đã đếm chúng trong -> vượt qua chúng. Nếu giám sát cho thấy ai đó thay đổi quần áo của họ -> thay đổi hồ sơ để quần áo mới. Submit Homework (Send): Sau khi chỉnh sửa, sổ tay nhỏ trong tay của bạn là một danh sách hoàn toàn chính xác; bây giờ giao nó cho người quản lý. nghĩa Summary for Beginners: exactly_once = true "hold it in and don't send it until it's clearly verified." Lợi ích: Dữ liệu nhận được dưới dòng là hoàn toàn sạch sẽ, không có trùng lặp hoặc rối loạn. Chi phí: Bởi vì nó phải được "giữ trong", nó cần tiêu thụ một số bộ nhớ để lưu trữ dữ liệu. 2.3 Hai câu hỏi và câu trả lời chính Tại sao không có sự kiện READ trong giai đoạn Backfill? Q1: Why is case READ: throw Exception Sự kiện READ được xác định bởi chính SeaTunnel, cụ thể để đại diện cho "dữ liệu chứng khoán đọc từ ảnh chụp nhanh". Giai đoạn Backfill đọc Binlog của cơ sở dữ liệu. Binlog chỉ ghi lại "thêm, xóa, và sửa đổi" (INSERT/UPDATE/DELETE) và không bao giờ ghi lại " ai đó đã truy vấn một mảnh dữ liệu." Do đó, nếu bạn đọc một sự kiện READ trong giai đoạn Backfill, điều đó có nghĩa là logic mã bị nhầm lẫn. Q2: If it's placed in memory, can the memory hold it? Will it OOM? Nó không đặt toàn bộ bảng vào bộ nhớ: SeaTunnel xử lý bằng cách chia. Chia nhỏ: Một chia mặc định chỉ có 8096 hàng dữ liệu. Vứt bỏ sau khi sử dụng: Sau khi xử lý một chia, gửi nó, xóa bộ nhớ, và xử lý tiếp theo. Công thức lưu trữ: Parallelism × Split size × Single row data size. 2.4 Chi tiết quan trọng: Định dạng watermark giữa nhiều splits Đây là một vấn đề rất bí ẩn nhưng vô cùng quan trọng.Nếu không được xử lý tốt, it will lead to data being either lost or repeated. Vấn đề chạy nhanh / chậm: Hãy tưởng tượng hai học sinh (Split A và Split B) đang sao chép bài tập (dữ liệu Backfill). Plain Language Explanation: Học sinh A (nhanh): Sao chép đến trang 100 và kết thúc lúc 10:00. Học sinh B ( chậm): Sao chép sang trang 200 và vừa hoàn thành lúc 10:05. Bây giờ, giáo viên (nhiệm vụ gia tăng) cần tiếp tục dạy một bài học mới (đọc Binlog) từ nơi họ kết thúc sao chép. Nếu bắt đầu từ trang 200: Sinh viên B được kết nối, nhưng nội dung Sinh viên A bị bỏ lỡ giữa các trang 100 và 200 (điều xảy ra giữa 10:00 và 10:05) hoàn toàn bị mất. Nếu bắt đầu từ trang 100: Học sinh A được kết nối, nhưng Học sinh B sẽ phàn nàn: "Giáo viên, tôi đã sao chép nội dung từ trang 100 đến 200!" Giải pháp của SeaTunnel: Bắt đầu từ đầu và che tai cho những gì bạn đã nghe: SeaTunnel áp dụng một Chiến lược : "Minimum Watermark Starting Point + Dynamic Filtering" Xác định bắt đầu (chăm sóc cho chậm): Giáo viên quyết định bắt đầu từ trang 100 (dấu nước tối thiểu trong tất cả các chia). Bộ lọc động (không nghe những gì đã được nghe): Trong khi giáo viên đang giảng dạy (đọc Binlog), họ giữ một danh sách: { A: 100, B: 200 }. Khi giáo viên đến trang 150: Nhìn vào danh sách; nó là cho A? 150 > 100, A đã không nghe thấy nó, ghi lại nó (gửi). Nhìn vào danh sách; nó là cho B? 150 < 200, B đã sao chép nó, bỏ qua nó trực tiếp (discard). Chế độ tốc độ đầy đủ (tất cả mọi người đã hoàn thành việc nghe): Khi giáo viên đến trang 201 và thấy tất cả mọi người đã nghe nó, họ không còn cần danh sách. Với : Giai đoạn gia tăng chặt chẽ lọc theo sự kết hợp của "bắt đầu tách + phạm vi chia + watermark cao." Summary in one sentence: exactly_once Không Giai đoạn gia tăng trở thành một "sự tiêu thụ liên tiếp từ một sự bù trừ bắt đầu nhất định." exactly_once 3. giai đoạn tăng Sau khi cài đặt (for ) hoặc giai đoạn Snapshot kết thúc, nó đi vào giai đoạn gia tăng thuần túy: exactly_once = true MySQL: dựa trên binlog. Oracle: dựa trên redo/logminer. SQL Server: Dựa trên nhật ký giao dịch / LSN. PostgreSQL: dựa trên WAL. Hành vi của SeaTunnel trong giai đoạn gia tăng rất gần với Debezium bản địa: Sử dụng theo thứ tự offset. Xây dựng các sự kiện như INSERT/UPDATE/DELETE cho mỗi thay đổi. Khi exactly_once = true, trạng thái offset và split được bao gồm trong điểm kiểm tra để đạt được ngữ nghĩa "exactly-once" sau khi phục hồi thất bại. 4 Kết luận Triết lý thiết kế cốt lõi của SeaTunnel CDC là tìm ra sự cân bằng hoàn hảo giữa và "Fast" (parallel snapshots) "Stable" (data consistency). Chúng ta hãy xem xét các điểm chính của toàn bộ quá trình: Cắt (Split) là nền tảng của tăng tốc song song: Cắt các bảng lớn thành các mảnh nhỏ để cho phép nhiều chủ đề làm việc cùng một lúc. Snapshot chịu trách nhiệm cho lưu trữ di chuyển: Sử dụng các mảnh để đọc dữ liệu lịch sử song song. Backfill chịu trách nhiệm cho việc may các khoảng trống: Đây là bước quan trọng nhất. nó bù đắp cho những thay đổi trong snapshot và loại bỏ trùng lặp bằng cách sử dụng các thuật toán sáp nhập bộ nhớ để đạt được Exactly-Once. Incremental chịu trách nhiệm cho đồng bộ hóa thời gian thực: Kết nối liền mạch với giai đoạn Backfill và liên tục tiêu thụ nhật ký cơ sở dữ liệu. Hiểu về bộ ba này vai trò phối hợp của bên trong nó là để thực sự làm chủ bản chất của SeaTunnel CDC. "Snapshot -> Backfill -> Incremental" "Watermarks"