La captura de datos modificados (CDC) es una técnica que se utiliza para realizar un seguimiento de los cambios en el nivel de fila en las operaciones de la base de datos (inserciones, actualizaciones, eliminaciones) y notificar a otros sistemas en el orden en que se producen los eventos. En escenarios de recuperación ante desastres, la CDC sincroniza principalmente los datos entre una base de datos principal y una de respaldo, lo que permite la sincronización de datos en tiempo real desde la base de datos principal a la secundaria.
source ----------> CDC ----------> sink
SeaTunnel CDC ofrece dos tipos de sincronización de datos:
Se hace hincapié en la fase de sincronización de instantáneas sin bloqueos porque muchas plataformas CDC existentes, como Debezium, pueden bloquear tablas durante la sincronización de datos históricos. La lectura de instantáneas es el proceso de sincronización de los datos históricos de una base de datos. El flujo básico de este proceso es el siguiente:
storage -------------> splitEnumerator ---------- split ----------> reader ^ | | | \----------------- report -----------/
Particionado dividido
splitEnumerator
(distribuidor dividido) divide los datos de la tabla en múltiples divisiones según campos específicos (como ID de tabla o claves únicas) y tamaño de paso definido.
Procesamiento paralelo
Cada división se asigna a un lector diferente para la lectura en paralelo. Un solo lector ocupará una conexión.
Comentarios sobre el evento
Después de completar la operación de lectura de una división, cada lector informa el progreso a splitEnumerator
. Los metadatos de la división se proporcionan de la siguiente manera:
String splitId # Routing ID TableId tableId # Table ID SeatunnelRowType splitKeyType # The type of field used for partitioning Object splitStart # Start point of the partition Object splitEnd # End point of the partition
Una vez que el lector recibe la información de la división, genera las instrucciones SQL correspondientes. Antes de comenzar, registra la posición correspondiente de la división actual en el registro de la base de datos. Después de completar la división actual, el lector informa el progreso al splitEnumerator
con los siguientes datos:
String splitId # Split ID Offset highWatermark # Log position corresponding to the split, for future validation
La fase de sincronización incremental comienza después de la fase de lectura de instantáneas. En esta etapa, todos los cambios que se producen en la base de datos de origen se capturan y se sincronizan con la base de datos de respaldo en tiempo real. Esta fase escucha el registro de la base de datos (por ejemplo, el binlog de MySQL). El seguimiento incremental suele ser de un solo subproceso para evitar extracciones duplicadas del binlog y reducir la carga de la base de datos. Por lo tanto, solo se utiliza un lector, que ocupa una única conexión.
data log -------------> splitEnumerator ---------- split ----------> reader ^ | | | \----------------- report -----------/
En la fase de sincronización incremental, todas las divisiones y tablas de la fase de instantáneas se combinan en una única división. Los metadatos de la división durante esta fase son los siguientes:
String splitId Offset startingOffset # The lowest log start position among all splits Offset endingOffset # Log end position, or "continuous" if ongoing, eg, in the incremental phase List<TableId> tableIds Map<TableId, Offset> tableWatermarks # Watermark for all splits List<CompletedSnapshotSplitInfo> completedSnapshotSplitInfos # Snapshot phase split details
Los campos CompletedSnapshotSplitInfo
son los siguientes:
String splitId TableId tableId SeatunnelRowType splitKeyType Object splitStart Object splitEnd Offset watermark # Corresponds to the highWatermark in the report
La división en la fase incremental contiene la marca de agua para todas las divisiones en la fase de instantánea. La marca de agua mínima se selecciona como punto de inicio para la sincronización incremental.
Ya sea en la fase de lectura de instantáneas o de lectura incremental, la base de datos también puede cambiar para la sincronización. ¿Cómo garantizamos exactamente una entrega?
En la fase de lectura de instantáneas, por ejemplo, se sincroniza una división mientras se producen cambios, como la inserción de una fila k3
, una actualización de k2
y una eliminación de k1
. Si no se utiliza ninguna identificación de tarea durante el proceso de lectura, las actualizaciones podrían perderse. SeaTunnel maneja esto de la siguiente manera:
split{start, end}
.
Si high = low
, los datos de la división no han cambiado durante la lectura. Si (high - low) > 0
, se han producido cambios durante el procesamiento. En tal caso, SeaTunnel hará lo siguiente:
low watermark
a high watermark
en orden, utilizando claves principales para reproducir operaciones en la tabla en memoria.
insert k3 update k2 delete k1 | | | vvv bin log --|---------------------------------------------------|-- log offset low watermark high watermark CDC reads: k1 k3 k4 | Replays v Real data: k2 k3' k4
Antes de comenzar la fase incremental, SeaTunnel primero valida todas las divisiones del paso anterior. Entre divisiones, los datos pueden actualizarse; por ejemplo, si se insertan nuevos registros entre la división 1 y la división 2, podrían perderse durante la fase de captura de pantalla. Para recuperar estos datos entre divisiones, SeaTunnel sigue este enfoque:
completedSnapshotSplitInfos
para ver si los datos se procesaron en alguna división. De lo contrario, se consideran datos entre divisiones y deben corregirse.
|------------filter split2-----------------| |----filter split1------| data log -|-----------------------|------------------|----------------------------------|- log offset min watermark split1 watermark split2 watermark max watermark
¿Qué sucede con la pausa y reanudación del CDC? SeaTunnel utiliza un algoritmo de captura de instantáneas distribuidas (Chandy-Lamport):
Supongamos que el sistema tiene dos procesos, p1
y p2
, donde p1
tiene tres variables X1 Y1 Z1
y p2
tiene tres variables X2 Y2 Z2
. Los estados iniciales son los siguientes:
p1 p2 X1:0 X2:4 Y1:0 Y2:2 Z1:0 Z2:3
En este punto, p1
inicia una instantánea global. p1
primero registra su estado de proceso y luego envía un marcador a p2
.
Antes de que el marcador llegue p2
, p2
envía el mensaje M
a p1
.
p1 p2 X1:0 -------marker-------> X2:4 Y1:0 <---------M---------- Y2:2 Z1:0 Z2:3
Al recibir el marcador, p2
registra su estado y p1
recibe el mensaje M
Como p1
ya realizó una instantánea local, solo necesita registrar el mensaje M
La instantánea final se ve así:
p1 M p2 X1:0 X2:4 Y1:0 Y2:2 Z1:0 Z2:3
En SeaTunnel CDC, los marcadores se envían a todos los lectores, enumeradores divididos, escritores y otros nodos, cada uno conserva su estado de memoria.