版本信息:

  • Flink 1.17.1
  • Doris 1.2.3
  • Flink Doris Connector 1.4.0

写入方式

采用 String 数据流,依照社区网站的样例代码,在sink之前将数据转换为DataStream,分隔符采用"\t"。

运行异常

通过Stream Load返回结果json中的ErrorUrl可以看到如题的异常

Reason: actual column number in csv file is less than schema column number. actual number: 10, ..., schema column number: 11; src line: [...]

数据库表明明只有10个字段,提示schema column number却是11个。是自己眼花数错字段了吗?经过反复确认及同事确认,没有错,目标表就是10个字段,我写入的也是10个字段,是Flink Doris Connector 的bug吗?

分析过程

既然怀疑是bug,那就去扒代码。

实际数据写入逻辑封装在org.apache.doris.flink.sink.writer.DorisWriter,该类实现了org.apache.flink.api.connector.sink.SinkWriter接口。查看该类发现,写入Doris的过程实际是使用微批写入的。

    @Override
public void write(IN in, Context context) throws IOException {
checkLoadException();
byte[] serialize = serializer.serialize(in);
if(Objects.isNull(serialize)){
//ddl record
return;
}
if(!loading) {
//Start streamload only when there has data
dorisStreamLoad.startLoad(currentLabel);
loading = true;
}
dorisStreamLoad.writeRecord(serialize);
}
@Override
public List<DorisCommittable> prepareCommit(boolean flush) throws IOException {
if(!loading){
//There is no data during the entire checkpoint period
return Collections.emptyList();
}
// disable exception checker before stop load.
loading = false;
Preconditions.checkState(dorisStreamLoad != null);
RespContent respContent = dorisStreamLoad.stopLoad(currentLabel);
if (!DORIS_SUCCESS_STATUS.contains(respContent.getStatus())) {
String errMsg = String.format("stream load error: %s, see more in %s", respContent.getMessage(), respContent.getErrorURL());
throw new DorisRuntimeException(errMsg);
}
if (!executionOptions.enabled2PC()) {
return Collections.emptyList();
}
long txnId = respContent.getTxnId();
return ImmutableList.of(new DorisCommittable(dorisStreamLoad.getHostPort(), dorisStreamLoad.getDb(), txnId));
}

每一条记录都会触发write操作,从上述代码可以看到根据boolean变量loading的值,程序将会触发dorisStreamLoad.startLoad(currentLabel);,而loading的状态在preCommit方法中进行修改,而preCommit是在checkpoint时触发,所以数据提交动作是通过checkpoint触发的。查看startLoad源代码


/**
* start write data for new checkpoint.
* @param label
* @throws IOException
*/
public void startLoad(String label) throws IOException{
loadBatchFirstRecord = true;
HttpPutBuilder putBuilder = new HttpPutBuilder();
recordStream.startInput();
LOG.info("stream load started for {} on host {}", label, hostPort);
try {
InputStreamEntity entity = new InputStreamEntity(recordStream);
putBuilder.setUrl(loadUrlStr)
.baseAuth(user, passwd)
.addCommonHeader()
.addHiddenColumns(enableDelete)
.setLabel(label)
.setEntity(entity)
.addProperties(streamLoadProp);
if (enable2PC) {
putBuilder.enable2PC();
}
pendingLoadFuture = executorService.submit(() -> {
LOG.info("start execute load");
return httpClient.execute(putBuilder.build());
});
} catch (Exception e) {
String err = "failed to stream load data with label: " + label;
LOG.warn(err, e);
throw e;
}
}

DorisStreamLoad类负责将数据实际写入Doris,在上面的代码中我看到了一个陌生的词汇HiddenColumns,“隐藏列”,什么是隐藏列?.addHiddenColumns(enableDelete)的参数enableDelete 是一个boolean值,继续扒代码发现,默认值enableDelete = true;,addHiddenColumn(true)?是否意味着我的put操作数据中必须包含隐藏列?继续扒

    public HttpPutBuilder addHiddenColumns(boolean add) {
if(add){
header.put("hidden_columns", LoadConstants.DORIS_DELETE_SIGN);
}
return this;
}

在http请求header中添加了一个配置,似乎是指明了"hidden_columns"="DORIS_DELETE_SIGN",看着好像是一个列名称,使用IDEA的跟踪调用功能,查看下哪里用到了这个变量。



跟踪这些代码更确信,这是一个列名称。我的10列加上这一列就是11列啊,设置enableDelete = false,是否意味着我的put操作不再包含这一隐含列?

解决方案

修改构造DorisSink的代码添加.setDeletable(false);

        DorisExecutionOptions.Builder  executionBuilder = DorisExecutionOptions.builder();
executionBuilder.setLabelPrefix(labelPrefix) //streamload label prefix
.setDeletable(false);

重新运行代码,写入成功,问题解决。

总结

出现该异常是因为,Flink Doris Connector 在构造Sink时默认用户写入数据中包含了隐藏列__DORIS_DELETE_SIGN__

尽管问题解决了,但是还是有很多疑问,什么是隐藏列,__DORIS_DELETE_SIGN__这个隐藏列是什么意思,从前面的代码中可以看出其取值为0或1,导入数据时为什么默认需要传递该列,该列在最前面还是在最后面?不传递该列是否会有问题?

Doris写入数据异常提示actual column number in csv file is less than schema column number的更多相关文章

  1. redis数据库写入数据时提示redis.exceptions.ResponseError错误

    今天运行Django项目在redis数据库写入数据时提示如下错误: ERROR log 228 Internal Server Error: /image_code/cf9ccd75-d274-45c ...

  2. asp.net mvc 4 json大数据异常 提示JSON字符长度超出限制的异常

    今天客户突然过来找我说在后台添加了一篇超长的文章后,所有后台的文章都显示不出来了.后台的前端显示是用easyui的,返回的数据全是用json.根据客户的描述进行了同样的操作后,在firebug下发现a ...

  3. NFC(7)向NFC硬件写入数据的两个示例(nfc硬件启动android应用,nfc硬件打开uri)

    向NFC标签写入数据基本步骤 1,获取Tag对象 Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); 2,判断NFC标签的数据类型(通 ...

  4. mina写入数据的过程

    mina架构图  写数据.读数据触发点: 写数据: 1.写操作很简单,是调用session的write方法,进行写数据的,写数据的最终结果保存在一个缓存队列里面,等待发送,并把当前session放入f ...

  5. 深入理解 EF Core:EF Core 写入数据时发生了什么?

    阅读本文大概需要 14 分钟. 原文:https://bit.ly/2C67m1C 作者:Jon P Smith 翻译:王亮 声明:我翻译技术文章不是逐句翻译的,而是根据我自己的理解来表述的.其中可能 ...

  6. Python中,添加写入数据到已经存在的Excel的xls文件,即打开excel文件,写入新数据

    背景 Python中,想要打开已经存在的excel的xls文件,然后在最后新的一行的数据. 折腾过程 1.找到了参考资料: writing to existing workbook using xlw ...

  7. android 向SD卡写入数据

    原文:android 向SD卡写入数据 1.代码: /** * 向sdcard中写入文件 * @param filename 文件名 * @param content 文件内容 */ public v ...

  8. java多线程向数据库写入数据

    任务: 从sqlserver中将一个表A(约16W条数据)导到mysql中对应的一个表B中. 思路:分段获取A表中的数据后,用多个线程同时向B表中写入. 关键代码 //将数据库中的数据条数分段 pub ...

  9. HBase BulkLoad批量写入数据实战

    1.概述 在进行数据传输中,批量加载数据到HBase集群有多种方式,比如通过HBase API进行批量写入数据.使用Sqoop工具批量导数到HBase集群.使用MapReduce批量导入等.这些方式, ...

  10. Kafka权威指南 读书笔记之(三)Kafka 生产者一一向 Kafka 写入数据

    不管是把 Kafka 作为消息队列.消息总线还是数据存储平台来使用 ,总是需要有一个可以往 Kafka 写入数据的生产者和一个从 Kafka 读取数据的消费者,或者一个兼具两种角色的应用程序. 开发者 ...

随机推荐

  1. java生成机器码

    java根据系统参数生成每个计算机的唯一标识. 1. 获取CPU序列号 /** * 获取CPU序列号 * @return * @throws IOException */ public static ...

  2. html/css 添加图片

    通过img.src添加图片 添加一个img元素,设置content,会发现在IE.safari等浏览器内显示为空白. 一般我们使用img,是通过src来设置的,可以通过react的import图片添加 ...

  3. [Pytorch框架] 1.3、张量

    文章目录 PyTorch是什么? Tensors(张量) NumPy 转换 CUDA 张量 PyTorch是什么? 基于Python的科学计算包,服务于以下两种场景: 作为NumPy的替代品,可以使用 ...

  4. Python Requets库学习总结

    快速开始 发送请求 >>> import requests >>> r = requests.get('https://api.github.com/events' ...

  5. FreeSWITCH对接vosk实现实时语音识别

    环境:CentOS 7.6_x64 FreeSWITCH版本 :1.10.9 Python版本:3.9.2 一.背景描述 vosk是一个开源语音识别工具,可识别中文,之前介绍过python使用vosk ...

  6. 用go设计开发一个自己的轻量级登录库/框架吧

    用go设计开发一个自己的轻量级登录库/框架吧 几乎每个项目都会有登录,退出等用户功能,而登录又不单仅仅是登录,我们要考虑很多东西. token该怎么生成?生成什么样的? 是在Cookie存token还 ...

  7. vue中点击其他任意位置关闭弹框

    mounted() { //点击任意位置关闭区域弹窗 document.addEventListener('click', (e) => { //获取弹窗对象 const userCon = d ...

  8. 2021-12-03:石子游戏 IV。Alice 和 Bob 两个人轮流玩一个游戏,Alice 先手。 一开始,有 n 个石子堆在一起。每个人轮流操作,正在操作的玩家可以从石子堆里拿走 任意 非零 平

    2021-12-03:石子游戏 IV.Alice 和 Bob 两个人轮流玩一个游戏,Alice 先手. 一开始,有 n 个石子堆在一起.每个人轮流操作,正在操作的玩家可以从石子堆里拿走 任意 非零 平 ...

  9. 记一次排查:接口返回值写入excel后,从单元格copy出来的数据会带有多重引号的问题

    在项目里刚好有3个服务,同一个网关内层的3个服务,两个php的,一个golang的,为了提高负载以及进行分流,部分客户的接口调用会被网关自动分配到go服务. 恰好为了测试,我写了一个全量用户的生产.测 ...

  10. Must use destructuring props assignmenteslint

    eslint 检测提示Must use destructuring props assignmenteslint 使用对象结构就可以解决了