Flink cdc同步mysql到starrocks(日期时间格式/时区处理)

环境

flink 1.15.3(此时最新版本为1.16.1)
mysql 5.7+
starrocks 2.5.2

mysql同步表结构

mysql中的timestamp字段是可以正常同步的,但是多了8小时,设置了mysql链接属性也没效果

CREATE TABLE `temp_flink` (
  `id` int(11) NOT NULL,
  `name` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL,
  `remark` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL,
  `create_date` datetime DEFAULT NULL,
  `create_time` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

参考下方的链接有两种方式;

这里使用单独的转换器代码如下


import io.debezium.spi.converter.CustomConverter;
import io.debezium.spi.converter.RelationalColumn;
import org.apache.kafka.connect.data.SchemaBuilder;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Properties;

/**
 * mysql日期字段时区/格式处理
 * @author JGMa
 */
public class MySqlDateTimeConverter implements CustomConverter<SchemaBuilder, RelationalColumn> {

    private DateTimeFormatter dateFormatter = DateTimeFormatter.ISO_DATE;

    private DateTimeFormatter timeFormatter = DateTimeFormatter.ISO_TIME;

    private DateTimeFormatter datetimeFormatter = DateTimeFormatter.ISO_DATE_TIME;

    private DateTimeFormatter timestampFormatter = DateTimeFormatter.ISO_DATE_TIME;

    private ZoneId timestampZoneId = ZoneId.systemDefault();

    @Override
    public void configure(Properties props) {

    }

    @Override
    public void converterFor(RelationalColumn column, ConverterRegistration<SchemaBuilder> registration) {

        String sqlType = column.typeName().toUpperCase();

        SchemaBuilder schemaBuilder = null;

        Converter converter = null;

        if ("DATE".equals(sqlType)) {

            schemaBuilder = SchemaBuilder.string().optional().name("com.darcytech.debezium.date.string");

            converter = this::convertDate;

        }

        if ("TIME".equals(sqlType)) {

            schemaBuilder = SchemaBuilder.string().optional().name("com.darcytech.debezium.time.string");

            converter = this::convertTime;

        }

        if ("DATETIME".equals(sqlType)) {

            schemaBuilder = SchemaBuilder.string().optional().name("com.darcytech.debezium.datetime.string");

            converter = this::convertDateTime;


        }

        if ("TIMESTAMP".equals(sqlType)) {

            schemaBuilder = SchemaBuilder.string().optional().name("com.darcytech.debezium.timestamp.string");

            converter = this::convertTimestamp;

        }

        if (schemaBuilder != null) {

            registration.register(schemaBuilder, converter);

        }

    }


    private String convertDate(Object input) {

        if (input == null) {
            return null;
        }

        if (input instanceof LocalDate) {

            return dateFormatter.format((LocalDate) input);

        }

        if (input instanceof Integer) {

            LocalDate date = LocalDate.ofEpochDay((Integer) input);

            return dateFormatter.format(date);

        }

        return String.valueOf(input);

    }


    private String convertTime(Object input) {

        if (input == null) {
            return null;
        }

        if (input instanceof Duration) {

            Duration duration = (Duration) input;

            long seconds = duration.getSeconds();

            int nano = duration.getNano();

            LocalTime time = LocalTime.ofSecondOfDay(seconds).withNano(nano);

            return timeFormatter.format(time);

        }

        return String.valueOf(input);

    }


    private String convertDateTime(Object input) {

        if (input == null) {
            return null;
        }

        if (input instanceof LocalDateTime) {

            return datetimeFormatter.format((LocalDateTime) input).replaceAll("T", " ");

        }

        return String.valueOf(input);

    }


    private String convertTimestamp(Object input) {

        if (input == null) {
            return null;
        }

        if (input instanceof ZonedDateTime) {

            // mysql的timestamp会转成UTC存储,这里的zonedDatetime都是UTC时间

            ZonedDateTime zonedDateTime = (ZonedDateTime) input;

            LocalDateTime localDateTime = zonedDateTime.withZoneSameInstant(timestampZoneId).toLocalDateTime();

            return timestampFormatter.format(localDateTime).replaceAll("T", " ");

        }
        return String.valueOf(input);
    }
}

使用

{

    public static void main(String[] args) {
        String tableName = "temp_flink";
        String srcHost = "192.168.10.14";
        String srcDatabase = "xcode";
        String srcUsername = "root";
        String srcPassword = "123456";
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setRuntimeMode(RuntimeExecutionMode.STREAMING);

        Properties mysqlProperties = new Properties();
//        mysqlProperties.setProperty("characterEncoding","UTF-8");
//        mysqlProperties.setProperty("connectionTimeZone","Asia/Shanghai");
        //自定义时间转换配置
        mysqlProperties.setProperty("converters", "dateConverters");
        mysqlProperties.setProperty("dateConverters.type", "com.txlc.flink.core.MySqlDateTimeConverter");

        MySqlSource<String> mySqlSource = MySqlSource.<String>builder()
                .hostname(srcHost)
//                .jdbcProperties(mysqlProperties)
                .port(3306)
                .databaseList(srcDatabase)
                .tableList(srcDatabase + "." + tableName)
                .username(srcUsername)
                .password(srcPassword)
//                .serverTimeZone("Asia/Shanghai")
// 主要是这里
                .debeziumProperties(mysqlProperties)
                .deserializer(new JsonStringDebeziumDeserializationSchema())
                .build();

        DataStreamSource<String> streamSource = env.fromSource(mySqlSource, WatermarkStrategy.forMonotonousTimestamps(), "[temp_flink-source]")
                .setParallelism(1);

        streamSource.addSink(StarRocksSink.sink(
                // the sink options
                StarRocksSinkOptions.builder()
                        .withProperty("jdbc-url", "jdbc:mysql://192.168.10.245:9030?characterEncoding=utf-8")
                        .withProperty("load-url", "192.168.10.245:8030")
                        .withProperty("database-name", "xcode")
                        .withProperty("username", "root")
                        .withProperty("password", "123456")
                        .withProperty("table-name", tableName)
                        // 自 2.4 版本,支持更新主键模型中的部分列。您可以通过以下两个属性指定需要更新的列。
                        // .withProperty("sink.properties.partial_update", "true")
                        // .withProperty("sink.properties.columns", "k1,k2,k3")
                        .withProperty("sink.properties.format", "json")
                        .withProperty("sink.properties.strip_outer_array", "true")
                        // 设置并行度,多并行度情况下需要考虑如何保证数据有序性
                        .withProperty("sink.parallelism", "1")
                        .build())
        ).name(">>>StarRocks temp_flink Sink<<<");

        try {
            env.execute("temp_flink stream sync");
        } catch (Exception e) {
            e.printStackTrace();
            log.error("[sync error] info : {}", e);
        }


    }

}

参考资料
https://blog.csdn.net/cloudbigdata/article/details/122935333
https://blog.csdn.net/WuBoooo/article/details/127387144

### 使用 Flink CDC 实现 MySQLMySQL 的数据同步 为了实现从一个 MySQL 数据库到另一个 MySQL 数据库的数据同步,可以采用类似于其他目标系统的方案,即利用 Flink CDC 来捕捉源端 MySQL 中的变化并传输这些变化至目的端 MySQL。具体过程如下: #### 创建 Flink 应用程序 构建一个新的 Flink 应用项目,在此过程中会涉及到使用 Flink Table API 或者 Flink SQL 去定义源表以及目标表。对于本案例而言,两个表都将对应于不同的 MySQL 实例。 ```sql CREATE TABLE source_table ( id BIGINT, name STRING, age INT, PRIMARY KEY(id) NOT ENFORCED ) WITH ( 'connector' = 'mysql-cdc', 'hostname' = 'source-mysql-hostname', 'port' = '3306', 'username' = 'your-username', 'password' = 'your-password', 'database-name' = 'your-database', 'table-name' = 'your-table' ); ``` 上述代码展示了如何设置来自源 MySQL 表的连接器[^1]。 接着设定目的地 MySQL 表: ```sql CREATE TABLE sink_table ( id BIGINT, name STRING, age INT, PRIMARY KEY(id) NOT ENFORCED ) WITH ( 'connector' = 'jdbc', 'url' = 'jdbc:mysql://target-mysql-hostname:3306/your-target-db?useSSL=false&serverTimezone=UTC', 'table-name' = 'your-target-table', 'driver' = 'com.mysql.cj.jdbc.Driver', 'username' = 'your-username', 'password' = 'your-password' ); ``` 这里指定了通往目标 MySQL 数据库的 JDBC 连接参数,并特别注意到了时区配置以避免潜在的时间戳解析错误[^3]。 #### 配置 MySQL 服务器 确保两台 MySQL 服务都已经启用了 binlog 功能以便支持变更数据捕获(CDC),这对于源端尤其重要因为这是变更记录产生的地方。此外,还需确认两端都安装有必要的驱动和支持包用于与 Flink 和对方数据库交互。 #### 执行数据流作业 一旦所有的准备工作完成之后就可以启动实际的数据流转移工作了。这一步骤通常涉及编写一段简单的查询语句来指定要复制哪些字段或整个表格的内容。 ```sql INSERT INTO sink_table SELECT * FROM source_table; ``` 这条命令指示 Flink 将 `source_table` 中的所有更新推送到 `sink_table` 中去。 #### 测试和验证 如同任何重要的迁移活动一样,建议先在一个测试环境中进行全面的功能性和性能上的检验再推广到生产环境当中。可以通过向源数据库添加一些新条目或者修改现有条目来看看它们能否被及时准确无误地反映在目标位置上。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值