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 格式。它适用于需要轻量级变更通知、审计跟踪或主键级别数据同步的场景,通过只序列化关键信息来减少数据传输