Flink CDC系列之:Kafka CSV 序列化器CsvSerializationSchema

这是一个 CSV 序列化器,负责将 Flink CDC 事件转换为 CSV 格式的数据。

类概述

java 复制代码
public class CsvSerializationSchema implements SerializationSchema<Event>

这个类实现了 Flink 的 SerializationSchema 接口,专门用于将 CDC 事件序列化为 CSV 格式的字节数组。

核心属性

java 复制代码
private static final long serialVersionUID = 1L;

/**
 * A map of {@link TableId} and its {@link SerializationSchema} to serialize Debezium JSON data.
 */
private final Map<TableId, TableSchemaInfo> csvSerializers;  // 表结构缓存

private final ZoneId zoneId;  // 时区配置

private InitializationContext context;  // Flink 初始化上下文

构造函数

java 复制代码
public CsvSerializationSchema(ZoneId zoneId) {
    this.zoneId = zoneId;
    csvSerializers = new HashMap<>();
}

参数说明:

  • zoneId:处理时区敏感数据(如时间戳)

核心方法详解

open() - 初始化方法

java 复制代码
@Override
public void open(InitializationContext context) {
    this.context = context;
}

作用:Flink 在启动序列化器时调用,用于初始化资源。

serialize() - 主序列化方法

java 复制代码
@Override
public byte[] serialize(Event event) {
    if (event instanceof SchemaChangeEvent) {
        handleSchemaChangeEvent((SchemaChangeEvent) event);  // 处理Schema变更
        return null;
    }
    DataChangeEvent dataChangeEvent = (DataChangeEvent) event;
    return serializeDataChangeEvent(dataChangeEvent);        // 处理数据变更
}

Schema 变更事件处理

java 复制代码
private void handleSchemaChangeEvent(SchemaChangeEvent schemaChangeEvent) {
    Schema schema;
    if (event instanceof CreateTableEvent) {
        // 新建表:获取表结构
        CreateTableEvent createTableEvent = (CreateTableEvent) event;
        schema = createTableEvent.getSchema();
    } else {
        // 更新表结构:应用Schema变更
        schema = SchemaUtils.applySchemaChangeEvent(
                csvSerializers.get(schemaChangeEvent.tableId()).getSchema(),
                schemaChangeEvent);
    }
    
    // 构建CSV序列化器
    CsvRowDataSerializationSchema csvSerializer = buildSerializationForPrimaryKey(schema);
    try {
        csvSerializer.open(context);  // 初始化序列化器
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
    
    // 缓存表结构信息
    csvSerializers.put(
            schemaChangeEvent.tableId(),
            new TableSchemaInfo(
                    schemaChangeEvent.tableId(), schema, csvSerializer, zoneId));
}

数据变更事件处理

java 复制代码
private byte[] serializeDataChangeEvent(DataChangeEvent dataChangeEvent) {
    // 确定要序列化的数据
    RecordData recordData =
            dataChangeEvent.op().equals(OperationType.DELETE)
                    ? dataChangeEvent.before()  // 删除操作使用before数据
                    : dataChangeEvent.after();  // 其他操作使用after数据
    
    // 获取表结构信息
    TableSchemaInfo tableSchemaInfo = csvSerializers.get(dataChangeEvent.tableId());
    
    // 转换为RowData并序列化为CSV
    return tableSchemaInfo
            .getSerializationSchema()
            .serialize(tableSchemaInfo.getRowDataFromRecordData(recordData, true));
}

buildSerializationForPrimaryKey() - 构建CSV序列化器

java 复制代码
private CsvRowDataSerializationSchema buildSerializationForPrimaryKey(Schema schema) {
    // 构建包含表名和主键的字段数组
    DataField[] fields = new DataField[schema.primaryKeys().size() + 1];
    fields[0] = DataTypes.FIELD("TableId", DataTypes.STRING());  // 表标识字段
    
    // 添加所有主键字段
    for (int i = 0; i < schema.primaryKeys().size(); i++) {
        Column column = schema.getColumn(schema.primaryKeys().get(i)).get();
        fields[i + 1] = DataTypes.FIELD(column.getName(), column.getType());
    }
    
    // 构建数据类型
    DataType dataType = DataTypes.ROW(fields).notNull();
    LogicalType rowType = DataTypeUtils.toFlinkDataType(dataType).getLogicalType();
    
    // 创建CSV序列化器
    return new CsvRowDataSerializationSchema.Builder((RowType) rowType).build();
}

生成的 CSV 格式

数据结构

CSV 输出包含以下字段:

  • TableId:表标识(第一个字段)
  • 主键字段:所有主键列的值

示例输出

源表结构

java 复制代码
CREATE TABLE users (
    user_id BIGINT PRIMARY KEY,
    username VARCHAR(50),
    email VARCHAR(100),
    created_at TIMESTAMP
);

CREATE TABLE orders (
    order_id BIGINT PRIMARY KEY, 
    user_id BIGINT,
    amount DECIMAL(10,2),
    order_date DATE
);

CSV 输出示例

java 复制代码
# users 表数据
"users","1001"
"users","1002"

# orders 表数据  
"orders","5001"
"orders","5002"

完整数据 CSV 序列化

java 复制代码
// 可以修改为序列化所有字段
private CsvRowDataSerializationSchema buildSerializationForAllColumns(Schema schema) {
    DataField[] fields = new DataField[schema.getColumns().size() + 1];
    fields[0] = DataTypes.FIELD("TableId", DataTypes.STRING());
    for (int i = 0; i < schema.getColumns().size(); i++) {
        Column column = schema.getColumns().get(i);
        fields[i + 1] = DataTypes.FIELD(column.getName(), column.getType());
    }
    // ... 其余代码类似
}

这段代码定义了一个私有方法 buildSerializationForAllColumns,用于构建一个 CSV 行数据序列化模式。以下是逐行详细解释:

java 复制代码
private CsvRowDataSerializationSchema buildSerializationForAllColumns(Schema schema) {

声明一个私有的方法,返回类型为 CsvRowDataSerializationSchema,接受一个 Schema 对象作为参数。

java 复制代码
DataField[] fields = new DataField[schema.getColumns().size() + 1];

创建一个 DataField 数组,其长度比传入的 schema 中的列数多 1。这是因为除了原表的所有列外,还额外添加了一列(例如标识符或元数据)。

java 复制代码
fields[0] = DataTypes.FIELD("TableId", DataTypes.STRING());

将新数组的第一个元素设置为名为 "TableId" 的字段,数据类型为字符串。这通常用作表的唯一标识或其他元信息。

java 复制代码
for (int i = 0; i < schema.getColumns().size(); i++) {
    Column column = schema.getColumns().get(i);
    fields[i + 1] = DataTypes.FIELD(column.getName(), column.getType());
}

通过循环遍历 schema 中的所有列,从索引 1 开始填充 fields 数组。每个元素的名称和类型取自原始列的对应属性。

总结:这个 CsvSerializationSchema 是一个轻量级的序列化器,专门用于将 CDC 事件转换为包含表名和主键的 CSV 格式。它适用于需要轻量级变更通知、审计跟踪或主键级别数据同步的场景,通过只序列化关键信息来减少数据传输

相关推荐
Cat God 00717 小时前
基于 Docker 部署 Kafka(KRaft + SASL/PLAIN 认证)
docker·容器·kafka
KD21 小时前
设计模式——责任链模式实战,优雅处理Kafka消息
后端·设计模式·kafka
原神启动11 天前
Kafka详解
分布式·kafka
一只懒鱼a2 天前
搭建kafka集群(安装包 + docker方式)
运维·容器·kafka
青春不流名2 天前
如何在Kafka中使用SSL/TLS证书认证
分布式·kafka·ssl
青春不流名2 天前
Kafka 的认证机制
kafka
hanyi_qwe2 天前
ZooKeeper+Kafka
分布式·zookeeper·kafka
BullSmall2 天前
Kafka 安全加固实践指南(可直接落地)
分布式·安全·kafka
Chasing__Dreams2 天前
kafka--基础知识点--6.4--LSO
数据库·分布式·kafka
Query*2 天前
分布式消息队列kafka【五】—— kafka海量日志收集实战
分布式·kafka