Flink 写入 Doris

6 篇文章 0 订阅
2 篇文章 0 订阅

环境

Doris :doris-2.0.0-alpha1-Unknown
Flink : 1.14.3
Flink-doris-connector :org.apache.doris : flink-doris-connector-1.14_2.12 : 1.1.1

写入方式

Flink DataStraem Api 方式写入 Uniq 模型表

实现

目前 Doris 官网提供两种写入方式:Flink DataStream Api 和 SQL 模式,这里主要使用 Flink DataStream Api 写入。

Flink Connector 写入原理

Flink Connector 写入 Doris 底层仍然采用 Doris Stream Load 方式实现,在原生的基础上结合 Flink 的 Checkpoint 机制和Doris Stream Load 的 two_phase_commit 机制来保证写入的原子性。

  • two_phase_commit

    在 Doris Stream Load 开启 two_phase_commit 时,Stream Load 导入分为两部分,
    数据写入完成即会返回信息给用户,主要信息为此次写入的状态信息,此时数据不可见,事务状态为PRECOMMITTED 状态,输出如下;

[14:59:25:847] [INFO] - org.apache.doris.flink.sink.writer.DorisStreamLoad.handlePreCommitResponse(DorisStreamLoad.java:206) - load Result {
    "TxnId": 4121190,
    "Label": "label-doris-1688540300385_2_1",
    "Comment": "",
    "TwoPhaseCommit": "true",
    "Status": "Success",
    "Message": "OK",
    "NumberTotalRows": 350,
    "NumberLoadedRows": 350,
    "NumberFilteredRows": 0,
    "NumberUnselectedRows": 0,
    "LoadBytes": 726192,
    "LoadTimeMs": 59146,
    "BeginTxnTimeMs": 0,
    "StreamLoadPutTimeMs": 5,
    "ReadDataTimeMs": 4,
    "WriteDataTimeMs": 59133,
    "CommitAndPublishTimeMs": 0
}

如果写入失败此处信息中会有错误连接地址,进入连接即可查看错误原因,地址连接类似如下:
http://xxxx/api/_load_error_log?file=__shard_4/error_log_insert_stmt_954ed81f15551cbc-892fd19aacef768b_954ed81f15551cbc_892fd19aacef768b
用户手动触发commit操作之后,数据才可见,输出如下。

[14:59:25:888] [INFO] - org.apache.doris.flink.sink.committer.DorisCommitter.commitTransaction(DorisCommitter.java:119) - load result {
    "status": "Success",
    "msg": "transaction [4121188] commit successfully."
}

写入格式

RowData 数据流

主要代码片段:构建Rowdata 数据流

dataStream.process(new ProcessFunction<Row, RowData>() {
            @Override
            public void processElement(Row value, Context ctx, Collector<RowData> out) throws Exception {
                // 根据 Row 生成 GenericRowData 数据
                final GenericRowData data = new GenericRowData(value.getKind(), value.getArity());
                // columnAndType 为自己定义的一个类,主要包含 Doris 表对应的字段和字段类型
                final String[] columnsName = columnAndType.getColumnsName();
                final DataType[] dataTypes = columnAndType.getDataTypes();
                for (int i = 0; i < columnsName.length; i++) {
                    final Object field = value.getFieldAs(columnsName[i]);
                    // 这里需要根据 字段类型转成指定的数据格式 比如 String -> StringData
                    // 否则在写入时会报错 类转换异常 String can not cast StringData
                    // 其他类型同理 这里应为笔者表中只存在两种类型需要转换所以没有补全类型
                    if (dataTypes[i].getLogicalType().getTypeRoot()
                    .equals(LogicalTypeRoot.DECIMAL)) {
                        data.setField(i, DecimalData.fromBigDecimal(
                                new BigDecimal(value.getFieldAs(columnsName[i]) == null ? "0" : value.getFieldAs(columnsName[i]).toString()), 22, 4
                        ));
                    } else if (dataTypes[i].getLogicalType().getTypeRoot()
                            .equals(LogicalTypeRoot.INTEGER)) {
                        data.setField(i,value.getFieldAs(columnsName[i]));
                    } else if (dataTypes[i].getLogicalType().getTypeRoot()
                            .equals(LogicalTypeRoot.VARCHAR)) {
                        data.setField(i,StringData.fromString(value.getFieldAs(columnsName[i]) == null ? "" : value.getFieldAs(columnsName[i]).toString()));
                    } else {
                        data.setField(i,StringData.fromString(value.getFieldAs(columnsName[i]).toString()));
                    }
                }

                out.collect(data);
            }
        }).name("TRANS").uid("TRANS")

主要代码片段: 构建 RowData Doris Sink

DorisSink.Builder<RowData> builder = DorisSink.builder();
        DorisOptions.Builder dorisBuilder = DorisOptions.builder();
// Fe 地址
        dorisBuilder.setFenodes("xxxx:8030")
            // 表
                .setTableIdentifier("test_1229.test1")
            // 用户名
                .setUsername("root")
            // 密码
                .setPassword("");

        Properties properties = new Properties();
        properties.setProperty("format", "json");
        properties.setProperty("read_json_by_line", "true");
        DorisExecutionOptions.Builder  executionBuilder = DorisExecutionOptions.builder();
        executionBuilder.setLabelPrefix("label-doris-"+tableName+System.currentTimeMillis()) //streamload label prefix
                .setStreamLoadProp(properties)
            // 表是从 RowData 中识别 RowKind 已支持删除操作 
            .setDeletable(true); //streamload params

        builder.setDorisReadOptions(DorisReadOptions.builder().build())
                .setDorisExecutionOptions(executionBuilder.build())
                //serialize according to rowdata
                .setSerializer(RowDataSerializer.builder()
                               // Doris 列名
                        .setFieldNames(columnAndType.getColumnsName())
                        //json format
                        .setType("json")
                        .enableDelete(true)
                                // Doris 列类型
                        .setFieldType(columnAndType.getDataTypes()).build())
                .setDorisOptions(dorisBuilder.build());
        return builder
                .build()
                ;

DorisColumnAndType 类

public class DorisColumnAndType implements Serializable {
    private String[] columnsName;
    private DataType[] DataTypes;

    public String[] getColumnsName() {
        return columnsName;
    }

    public void setColumnsName(String[] columnsName) {
        this.columnsName = columnsName;
    }

    public DataType[] getDataTypes() {
        return DataTypes;
    }

    public void setDataTypes(DataType[] dataTypes) {
        DataTypes = dataTypes;
    }

    @Override
    public String toString() {
        return "DorisColumnAndType{" +
                "columnsName=" + Arrays.toString(columnsName) +
                ", DataTypes=" + Arrays.toString(DataTypes) +
                '}';
    }
}

SchemaChange 数据流

如果上游 Kafka 数据为 debezium-json 格式建议使用该方式写入较为方便。

主要代码片段:构建 Doris Sink

Properties props = new Properties();
props.setProperty("format", "json");
props.setProperty("read_json_by_line", "true");
DorisOptions dorisOptions = DorisOptions.builder()
    // FE 地址
        .setFenodes("xxxxx:8030")
    // Doris 库名+表明
        .setTableIdentifier("test_1229.test1")
    // Doris 用户名
        .setUsername("root")
    // Doris 密码
        .setPassword("").build();
DorisExecutionOptions.Builder  executionBuilder = DorisExecutionOptions.builder();
// 设置 LabelPrefix 即 Stream Load LabelPrefix
executionBuilder.setLabelPrefix("label-doris" + System.currentTimeMillis())
        .setStreamLoadProp(props)
    // 是否识别数据中的删除标识,即 识别 —D 的数据进行删除
    .setDeletable(true);
DorisSink.Builder<String> builder = DorisSink.builder();
builder.setDorisReadOptions(DorisReadOptions.builder().build())
        .setDorisExecutionOptions(executionBuilder.build())
        .setDorisOptions(dorisOptions)
        .setSerializer(
            JsonDebeziumSchemaSerializer.builder().setDorisOptions(dorisOptions).build()
        );
final DorisSink<String> dorisSink = builder.build();

Flink 任务 UI 大致如下,不同的处理 UI 图可能不一样:
image.png

写入异常分析

  1. Reason: column(DORIS_DELETE_SIGN) values is null while columns is not nullable. src line [2023070520230705H701010018100190512303096D NULL NULL];

DORIS_DELETE_SIGN 该字段为 Doris 隐藏列, DorisSink默认会根据 RowKind 来区分事件的类型,通常这种在 cdc 情况下可以直接获取到事件类型,对隐藏列__DORIS_DELETE_SIGN__进行赋值达到删除的目的,而Kafka则需要根据业务逻辑判断,显示的传入隐藏列的值。在上述的

executionBuilder.setLabelPrefix("label-doris" + System.currentTimeMillis())
        .setStreamLoadProp(props)
    // 是否识别数据中的删除标识,即 识别 —D 的数据进行删除
    .setDeletable(true);

如果没有设置 setDeletable 为True,将导致该字段为空从而导致写入失败。

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
上层应用业务对实时数据的需求,主要包含两部分内容:1、 整体数据的实时分析。2、 AB实验效果的实时监控。这几部分数据需求,都需要进行的下钻分析支持,我们希望能够建立统一的实时OLAP数据仓库,并提供一套安全、可靠的、灵活的实时数据服务。目前每日新增的曝光日志达到几亿条记录,再细拆到AB实验更细维度时,数据量则多达上百亿记录,多维数据组合下的聚合查询要求秒级响应时间,这样的数据量也给团队带来了不小的挑战。OLAP层的技术选型,需要满足以下几点:1:数据延迟在分钟级,查询响应时间在秒级2:标准SQL交互引擎,降低使用成本3:支持join操作,方便维度增加属性信息4:流量数据可以近似去重,但订单行要精准去重5:高吞吐,每分钟数据量在千W级记录,每天数百亿条新增记录6:前端业务较多,查询并发度不能太低通过对比开源的几款实时OLAP引擎,可以发现Doris和ClickHouse能够满足上面的需求,但是ClickHouse的并发度太低是个潜在的风险,而且ClickHouse的数据导入没有事务支持,无法实现exactly once语义,对标准SQL的支持也是有限的。所以针对以上需求Doris完全能解决我们的问题,DorisDB是一个性能非常高的分布式、面向交互式查询的分布式数据库,非常的强大,随着互联网发展,数据量会越来越大,实时查询需求也会要求越来越高,DorisDB人才需求也会越来越大,越早掌握DorisDB,以后就会有更大的机遇。本课程基于真实热门的互联网电商业务场景为案例讲解,具体分析指标包含:AB版本分析,下砖分析,营销分析,订单分析,终端分析等,能承载海量数据的实时分析,数据分析涵盖全端(PC、移动、小程序)应用。整个课程,会带大家实践一个完整系统,大家可以根据自己的公司业务修改,既可以用到项目中去,价值是非常高的。本课程包含的技术:开发工具为:IDEA、WebStormFlink1.9.0DorisDBHadoop2.7.5Hbase2.2.6Kafka2.1.0Hive2.2.0HDFS、MapReduceFlume、ZookeeperBinlog、Canal、MySQLSpringBoot2.0.8.RELEASESpringCloud Finchley.SR2Vue.js、Nodejs、Highcharts、ElementUILinux Shell编程等课程亮点:1.与企业接轨、真实工业界产品2.DorisDB高性能分布式数据库3.大数据热门技术Flink4.支持ABtest版本实时监控分析5.支持下砖分析6.数据分析涵盖全端(PC、移动、小程序)应用7.主流微服务后端系统8.天级别与小时级别多时间方位分析9.数据库实时同步解决方案10.涵盖主流前端技术VUE+jQuery+Ajax+NodeJS+ElementUI11.集成SpringCloud实现统一整合方案12.互联网大数据企业热门技术栈13.支持海量数据的实时分析14.支持全端实时数据分析15.全程代码实操,提供全部代码和资料16.提供答疑和提供企业技术方案咨询企业一线架构师讲授,代码在老师的指导下企业可以复用,提供企业解决方案。  版权归作者所有,盗版将进行法律维权。 
课程总体架构请观看89讲。数据仓库是一个面向主题的、集成的、随时间变化的、但信息本身相对稳定的数据集合,用于对管理决策过程的支持。数据仓库的应用有:1.数据分析、数据挖掘、人工智能、机器学习、风险控制、无人驾驶。2.数据化运营、精准运营。3.广告精准、智能投放等等。数据仓库是伴随着企业信息化发展起来的,在企业信息化的过程中,随着信息化工具的升级和新工具的应用,数据量变的越来越大,数据格式越来越多,决策要求越来越苛刻,数据仓库技术也在不停的发展。数据仓库有两个环节:数据仓库的构建与数据仓库的应用。随着IT技术走向互联网、移动化,数据源变得越来越丰富,在原来业  务数据库的基础上出现了非结构化数据,比如网站log,IoT设备数据,APP埋点数据等,这些数据量比以往结构化的数据大了几个量级,对ETL过程、存储都提出了更高的要求。互联网的在线特性也将业务需求推向了实时化 ,随时根据当前客户行为而调整策略变得越来越常见,比如大促过程中库存管理,运营管理等(即既有中远期策略型,也有短期操作型)。同时公司业务互联网化之后导致同时服务的客户剧增,有些情况人工难以完全处理,这就需要机器 自动决策 。比如欺诈检测和用户审核。总结来看,对数据仓库的需求可以抽象成两方面: 实时产生结果、处理和保存大量异构数据。本课程基于真实热门的互联网电商业务场景为案例讲解,结合分层理论和实战对数仓设计进行详尽的讲解,基于Flink+DorisDB实现真正的实时数仓,数据来及分析,实时报表应用。具体数仓报表应用指标包括:实时大屏分析、流量分析、订单分析、商品分析、商家分析等,数据涵盖全端(PC、移动、小程序)应用,与互联网企业大数据技术同步,让大家能够学到大数据企业级实时数据仓库的实战经验。本课程包含的技术: 开发工具为:IDEA、WebStorm Flink 1.11.3Hadoop 2.7.5Hive 2.2.0ZookeeperKafka 2.1.0、Spring boot 2.0.8.RELEASESpring Cloud Finchley.SR2Flume 、Hbase 2.2.6DorisDB 0.13.9、RedisVUE+jQuery+Ajax+NodeJS+ElementUI+Echarts+Datav等课程亮点: 1.与企业接轨、真实工业界产品2.DorisDB高性能分布式数据库3.大数据热门技术Flink最新版4.真正的实时数仓以及分层设计5.海量数据大屏实时报表6.数据分析涵盖全端(PC、移动、小程序)应用7.主流微服务后端系统8.数据库实时同步解决方案9.涵盖主流前端技术VUE+jQuery+Ajax+NodeJS+ElementUI+Echarts+Datav10.集成SpringCloud实现统一整合方案11.互联网大数据企业热门技术栈12.支持海量数据的实时数仓报表分析13.支持全端实时实时数仓报表分析14.全程代码实操,提供全部代码和资料 15.提供答疑和提供企业技术方案咨询企业一线架构师讲授,代码在老师的指导下企业可以复用,提供企业解决方案。  版权归作者所有,盗版将进行法律维权。 

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值