Doris的执行计划生成、分发与执行

这篇具有很好参考价值的文章主要介绍了Doris的执行计划生成、分发与执行。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一、概述

三、执行计划的生成

四、执行计划的分发

五、执行计划的执行

六、关于PipeLine

七、Stream Load 的执行计划

八、举个例子


一、概述

执行SQL的代码入口为StmtExecutor::execute()

Doris的执行计划生成、分发与执行,Doris,doris

Doris的执行计划生成、分发与执行,Doris,doris

三、执行计划的生成

在Doris的FE端,与大多数数据库系统一样,要从SQL或某种http请求,生成执行计划,从SQL生成,一开始是“抽象语法树”(Abstract Syntax Tree),这个抽象语法树不一定是规则的二叉树,而只是一些语法对象,通过类的成员变量联系起来,例如:

Doris的执行计划生成、分发与执行,Doris,doris

fe的语法定义文件为 ./fe-core/src/main/cup/sql_parser.cup ,这也是分析代码开始的地方,从这里可以看到一个命令被转换为怎样一个数据结构(AST)。

然后经过分析,重写步骤(有些数据库称为bind,语义分析等等),将AST改造成逻辑语法树,这个逻辑语法树,就是以关系运算符为主干的二叉树了,或者近似于这样一个二叉树了。

Doris的执行计划生成、分发与执行,Doris,doris

这个称为逻辑计划,生成逻辑计划以后,就可以进行各种优化了。在java代码中,逻辑计划的节点都是PlanNode的子类的对象,AST的节点都有个analyze(),analyze()被层层调用,生成逻辑计划树。

数据是分布式的分散到BE的,执行计划也是在BE节点上执行,而不是在FE节点上执行,FE只负责生成执行计划,并决定把执行计划发给谁。

接下来就是生成物理执行计划,并且把执行计划分布式化。

Doris的执行计划生成、分发与执行,Doris,doris

就是把PlanNode组成的执行计划树,分割成不同部分,这些不同部分称为fragment,代码中用PlanFragment类的对象表示,这些不同部分可以在不同的BE节点上执行,并且在BE节点上执行的同样一个PlanFragment,可以有多个并行执行的示例,虽然每个实例是一样的操作逻辑,但是读取的是同一个表的不同部分,这些部分称为ScanRange,例如一个PlanFragment的两个实例,它们在同一个BE节点上,读取同一个表dup1,它们的Scan操作符读取的也是同一个表,但是不同的实例的Scan操作符,读取的是不同的tablet,每个Scan操作符有自己的独一无二的ScanRange,ScanRange是一个tablet列表。

参考FE代码:Coordinator::computeScanRangeAssignment()

调试时,可以调用这个函数 tablets_id_to_string(_scan_ranges) 返回ScanRange里的tablet_id。
//every doris_scan_range is related with one tablet so that one olap scan node contains multiple tablet

Fragment之间通过网络通讯,新增了两个算子专门联系两个Fragment之间的运算符,就是DataSink和ExchangeNode,例如上图,之前直接联系的Hash Join Node和OlapScanNode,在分到不同的Fragment后通过DataSink和ExchangeNode沟通数据。

FE会决定执行计划划分为几个Fragment,并且决定这些Fragment分发到哪个BE上执行,也决定分发到BE上的Fragment,要创建几个实例,这些实例的scan操作符的ScanRange是什么,总之BE只负责无脑执行,所有执行细节都有FE在创建最终执行计划时设置好了。

过程调用的thrift和protobuf代码在doris/gensrc目录下,编译过程中,会根据其中的定义生成java和cpp文件。

fe使用生成的java文件,还需要将这些java文件复制到fe/fe-common/src/main/java/org/apache/doris/thrift目录下,然后编译,可见fe-be之间的通讯应该是走thrift RPC,be-be之间的通讯应该会走protobuf。

be使用生成的cpp文件,不必将这些cpp文件复制到be目录,而是直接编译。

四、执行计划的分发

执行计划在FE上生成完毕,由FE直接下发给需要执行的它的BE,而不会是先下发给一个所谓的coordinator BE,然后又它再分给其它BE,注意这一点,容易引起误会,select这样的计划,最终数据会汇总到一个BE上,再由这个BE传给FE,这个BE称为Root BE,它负责执行时数据的最终汇总,但是不负责执行计划的分发!

FE下发执行计划的入口函数是:

Coordinator::exec()

      |__> Coordinator::sendPipelineCtx()
底层调BackendServiceClient::execPlanFragmentPrepareAsync(),通过grpc把fragments信息发给BE。

Doris的执行计划生成、分发与执行,Doris,doris

在FE的代码里,PlanFragment里的planRoot成员变量,指向自己所包含的执行计划片段的最上层的一个节点,每个执行计划算子(PlanNode)都有一个fragment成员变量,指向自己所在的PlanFragment对象。

PlanFragment的children成员变量,将父子fragment联系起来。

Doris的执行计划生成、分发与执行,Doris,doris
在Coordinator::sendPipelineCtx() 中,beToPipelineExecCtxs存的是发给所有BE的fragment信息,其中每个PipelineExecCtxs是发给同一个BE的所有fragment信息(可能向一个BE发送多个不同的fragment,并且同一个fragment会有多个实例),一个PipelineExecCtxs包含多个PipelineExecContexts,一个PipelineExecContexts对应一个fragment,一个PipelineExecContexts,对象里又可能包含多个PipelineExecContext,每个表示一个fragment实例。

Doris的执行计划生成、分发与执行,Doris,doris

 FE中Coordinator这个类很重要,里面有个fragment list,就是要发给BE的要执行的fragment。

Doris的执行计划生成、分发与执行,Doris,doris

图片来自(Doris 源码分析 (五) gRpc 与 thrift 接口 - 简书 (jianshu.com))

FE是通过gRPC向BE分发Fragments的。

FE与BE之间的RPC调用,是有超时的,在FE端fe.conf通过下面两个参数可以设置超时时间:

backend_rpc_timeout_ms

remote_fragment_exec_timeout_ms

FE向BE分发fragment,并不是每个BE都分发相同的fragment,而其中发给Root BE的fragment与其它BE稍有不同,多了顶部的fragment,其它BE的数据汇总到这个Root BE,然后从这个Root BE统一发给FE,注意,不是每个BE分别向FE发数据。

BE之间的数据传输,底层也是用grpc。最底层调用

doris::PBackendService_Stub::transmit_block()

关于执行计划(fragments)的分发,是从FE直接向需要执行执行计划的BE发fragments,而不是发给coordinator BE由它转发给其它BE。

笼统的说,FE向BE分发执行计划并执行,大体分两种情况:

1、如果执行计划只有一个fragment,那么FE只向BE发一个RPC(BackendServiceClient::execPlanFragmentAsync()),把执行计划发给BE,BE端根据信息重建ExecNode组成的执行计划树,并且执行。注意,不管哪种情况,fragment信息通过rpc到达BE后,其中plan都有一个reconstruct的过程!

2、如果执行计划中有多个fragment,会分两步,第一步是FE调用BackendServiceClient::execPlanFragmentPrepareAsync()下发fragment,在BE端响应了这个RPC后,会根据fragment信息,重建ExecNode组成的执行计划树,但是不执行,当把所有fragment的执行计划树都重建好了,即prepare完毕。然后FE端再调用BackendServiceClient::execPlanFragmentStartAsync(),让BE上刚才准备好的执行计划开始执行。

上述逻辑FE端的代码在 Coordinator::sendPipelineCtx()

BackendServiceClient::execPlanFragmentPrepareAsync

BackendServiceClient::execPlanFragmentStartAsync

FE这两个函数已经比较底层了,里面就调用最底层的stub。

BE、FE交互的许多类型,定义在doris/gensrc/build/gen_cpp/下生成的文件里。

五、执行计划的执行

FE调用BackendServiceClient::execPlanFragmentPrepareAsync
导致BE调用PInternalServiceImpl::exec_plan_fragment_prepare
在这里面request参数包括所有fragment的信息。
(在我的单机FE+BE各一个环境,是FE一次远程调用,向BE下发所有fragment,
放在PExecPlanFragmentRequest->request里,这是个字符串,需要进行反序列化)
一步一步往下调用,在PInternalServiceImpl::_exec_plan_fragment里,
从FE传来的所有fragment信息,被反序列化到TPipelineFragmentParamsList,
里面每个param是一个fragment信息,每个fragment调用一次fragment_mgr()->exec_plan_fragment(),
进而调用PipelineFragmentContext::prepare(),以ExecNode的子类为节点构造执行计划树
PipelineFragmentContext::prepare->ExecNode::create_tree()。

FE调用BackendServiceClient::execPlanFragmentStartAsync
导致BE调用PInternalServiceImpl::exec_plan_fragment_start
进一步调用FragmentMgr::start_query_execution()(这个函数,整个query只调一次,不是每个fragment调一次)
设置query_id所指的执行计划为可执行状态

在BE的PInternalServiceImpl::_exec_plan_fragment()中,通过RPC 传来的参数TPipelineFragmentParams,代表一个fragment,其中的local_params,每个元素代表这个fragment的instance,每个元素的类型是TPipelineInstanceParams。它们的定义在 gensrc/thrift/PaloInternalService.thrift。

在BE端,谁分配到了最顶层的fragment,谁就是这次查询的Root Fragment。不同BE间,sink和exchange的通讯,是基于brpc(应该是百度内部优化过的rpc),BE代码中相关函数:transmit_block/transmit_data。

sink的底层是VDataStreamSender,内部的_channels数组,是Channel对象或PipChannel对象数组,一个Channel表示发给一个上层的exchange节点的通道,例如,OlapScanNode的VDataStreamSender有6个Channel对象,表示从tablet扫描到的记录,按照hash函数,分发给6个上层的Exchange节点。关于如何根据FE下发的信息创建VDataStreamSender,参考DataSink::create_data_sink()。

Channel底层是RPC调用,使用gRPC(百度版本的RPC?),接口定义在gen_cpp/internal_service.pb.cc/h里定义的PBackendService,在internal_service.proto里定义:

Channel::init()

Channel/PipChannel::add_rows() -- 积累行记录

Channel/PipChannel::send_(local_)block() -- 向另一个fragment发送行记录

在BE上fragment prepare(包括重建,准备好各种数据收发对象)完成后,就开始执行了,新的执行引擎模型称为pipeline,它与火山模型不同的是,不是通过遍历执行计划树来执行的,而是再把每个算子或fragment分成若干个operator,operator之间可以并行执行。PipelineTask是被pipeline系统调用的对象,可以理解为线程。整个pipeline引擎类似于一个线程池,一个BE只有一个pipeline引擎(TaskScheduler对象),和线程池不同的是,pipeline的线程在遇到阻塞时,会放弃任务,然后去执行其它不会阻塞的任务。pipeline引擎的阻塞任务队列有一个,就绪任务队列有好几个,有一个专门线程不断检查阻塞任务队列,将其中不再阻塞的任务(PipelineTask对象),加到其中一个就绪队列中,有好几个线程会从就绪队列里取PipelineTask执行。

在整个BE中,有一个(也只有一个)ExecEnv对象,通过ExecEnv::GetInstance()获得,里面包含了TaskScheduler对象(全局只有一个),这就是流水线的执行对象,TaskScheduler对象里包含了BlockedTaskScheduler对象。BE中还有一些其它模块的线程池,BE中有可能每个模块都有自己的线程池。

相关代码在:

ExecEnv::init_pipeline_task_scheduler()和TaskScheduler::start()

BE中Fragment、Pipeline、PipelineTask、Operator对象的关系:一个PipelineTask对应一个Pipeline对象,一个PipelineTask处理多个operator,operator chain在PipelineTask::_operators成员中。PipelineTask的operator chain是从Pipeline创建来的--Pipeline::build_operators()。

operator可以对应一个ExecNode,也可能一个ExecNode对应多个Operator(参考PipelineFragmentContext::_build_pipelines()中的逻辑就明白了,列入HashJoinNode和它的子节点会变成两个pipeline)。Operator的执行最终还是要调用相应ExecNode里的算法函数执行,一个Fragment会被分成多个Pipeline,一个Pipeline里有多个operator由一个PipelineTask执行。使用operator后,ExecNode的get_next不用了,用push和pull。

PipeLineTask对象和PipeLine对象是一一对应的,PipeLine定义了所有静态信息,如一个pipeline中有哪些operator(operator 链),sink operator,这些operator中会有指针指向对应的ExecNode,毕竟执行代码在ExecNode的方法里。

VMysqlResultWriter从BE将结果写给FE,FE通过BackendServiceClient::fetchDataAsync 接收BE的 VResultSink::send发来的数据。

BE中属于一个Fragment的ExecNode,创建出来的一些列的pipeline,归一个PipelineFragmentContext管理,pipeline由PipelineFragmentContext::_build_pipelines和_build_pipeline_tasks创建,最终由 PipelineFragmentContext::submit提交给pipeline执行引擎。

六、关于PipeLine

Doris的执行计划生成、分发与执行,Doris,doris

pipeline是doris里非常关键的执行引擎,查询、streamload等等操作最终都会以pipeline的方式执行,所以不可以不对它深入理解,最近看代码,记录一下对pipeline机制、代码的理解。

执行计划从FE发送到BE后,从Thrift结构体转为 ExecNode 对象树,是在 ExecNode::create_tree 做的。之后可以通过遍历树执行(火山模型),也可以将这个执行计划转为若干pipeline,由pipeline引擎执行。

为了更好的理解pipeline代码和它如何工作的,避免理解时的困惑,有必要了解的一些信息:

1、pipeline引擎底层是基于ThreadPool(见threadpool.h),这个ThreadPool类是BE中广泛使用的,每个模块都有一个或多个属于自己的ThreadPool,管理若干用于自己模块的thread,使用线程池的关键,就是把你要执行的函数告诉线程池。

Doris的执行计划生成、分发与执行,Doris,doris

2、要了解BE中的代码,模块与模块之间,大多数情况下,并不是在一个线程中调用的,而是模块A产生数据放到一个缓存或队列里,模块B在另一个线程中读取这个数据。这样,查看函数调用栈来理解代码,就不理想了。应该更多的关注文档、中间数据结构(那个队列或缓存,数据流转的统一格式,哪个函数读写这个数据),并且不必追踪到太底层,一般跟踪到读取和写入缓存或队列的函数即可,再适当关心一下统一的数据格式。

一个fragment的所有ExecNode所对应的pipeline由一个PipelineFragmentContext管理,其中可能包含多个pipeline,一个pipeline又可能包含多个operator。

将ExecNode Tree转为pipeline的函数是 PipelineFragmentContext::_build_pipelines,ExecNode和pipeline、operator如何对应,都在这个函数里决定,它会被递归调用,遍历ExecNode Tree。ExecNode会对应一个或多个operator,一个pipeline对象会包含来自一个或多个ExecNode的operator。

例如 HashJoinNode会产生出HashJoinProbeOp和HashJoinBuildSinkOp两个operator,它对应的pipeline会包含HashJoinProbeOp,和它的子节点VNewOlapScanNode产生的ScanOperator,而HashJoinBuildSinkOp是另一个pipeline的_sink成员,这个pipeline的operator链中只包含一个ExechangeSourceOperator,但这个HashJoinBuildSinkOp会引用HashJoinNode并且调用它的sink,注意,ExecNode的sink方法其实是外界向本节点输入数据的调用函数,是被外界的operator或ExecNode调用的,不是本ExecNode向外输出的数据的方法,从生命中input_block的参数名可以看出。

pipeline的_sink成员指向的operator不一定是DataSinkOperator的子类,更有可能是StreamingOperator的子类。

一个pipeline中比较重要的是operator链,还有一个_sink成员,pipeline执行时(PipelineTask::execute),调用operator的get_block,从而调用operator链上所有operator的get_block,这些operator再调用它们对应的ExecNode的pull方法。调用get_block获得数据(block)后再调用sink将数据发给下游pipeline。

pipeline的operator有四种:

DataSinkOperator -- 对应SinkNode的子类,关于SinkNode对应的operator的创建在PipelineFragmentContext::_create_sink

StreamingOperator 

SourceOperator -- StreamingOperator 的子类

StatefulOperator -- StreamingOperator 的子类

它们的区别除了 DataSinkOperator 要指向 SinkNode 子类外,其它三个的区别在于get_block的逻辑不同,而各个运算符(ExecNode)的operator大多是继承自这四个operator。

每个operator都有个指向ExecNode的指针,也都有个对应的builder,虽然我觉得大多数builder没有实质性的代码,为每个ExecNode创建operator和builder有点不必要,也许直接用父类即可。

创建新ExecNode,并且让它能够被pipeline引擎执行,需要做什么?

1、需要为他创建operator builder和operator,一般继承自StreamingOperator 或 StatefulOperator。

2、然后具体的计算逻辑要实现 ExecNode::pull,ExecNode::_sink,ExecNode::alloc_resource,ExecNode::get_next_after_projects

Doris的执行计划生成、分发与执行,Doris,doris

七、Stream Load 的执行计划

stream load 是BE向FE请求执行计划,FE生成执行计划后,通过RPC发给BE,BE上重建执行计划,重建后可以直接执行,或者通过pipeline引擎执行,一下是直接执行的流程:

FE下发执行计划给BE后,就不管了,BE直接从http客户端取得数据,客户端会把数据分块传输给BE,每传一块BE的StreamLoadAction::on_chunk_data()就被调用一次,这个函数会把数据缓存给StreamLoadPipe,同时运行执行计划的执行线程也在执行,ScanNode 的 scanner 不断从 StreamLoadPipe 取数据发给下一个节点 VOlapTableSink, 它调用VNodeChannel根据分区和分桶,通过RPC把记录发给不同的BE在上面会执行存储引擎的写盘逻辑。如何分发,是否要排序,这部分逻辑在哪里?在VOlapTableSink的方法里吗)

Doris的执行计划生成、分发与执行,Doris,doris

八、举个例子

集群结构为一台FE节点,三台BE节点,两个duplicate类型表:

CREATE TABLE IF NOT EXISTS dup1
(
    `user_id` BIGINT NOT NULL AUTO_INCREMENT COMMENT "用户id",
    `date` DATE NOT NULL COMMENT "数据灌入日期时间",
    `city` VARCHAR(20) COMMENT "用户所在城市",
    `age` SMALLINT COMMENT "用户年龄",
    `sex` TINYINT COMMENT "用户性别",
    `cost` BIGINT DEFAULT "0" COMMENT "用户总消费"
)
DUPLICATE KEY (`user_id`)
DISTRIBUTED BY HASH(`user_id`) BUCKETS 16
PROPERTIES (
"replication_allocation" = "tag.location.default: 3"
);

CREATE TABLE IF NOT EXISTS dup2
(
    `user_id` BIGINT NOT NULL AUTO_INCREMENT COMMENT "用户id",
    `date` DATE NOT NULL COMMENT "数据灌入日期时间",
    `city` VARCHAR(20) COMMENT "用户所在城市",
    `age` SMALLINT COMMENT "用户年龄",
    `sex` TINYINT COMMENT "用户性别",
    `cost` BIGINT DEFAULT "0" COMMENT "用户总消费"
)
DUPLICATE KEY (`user_id`)
DISTRIBUTED BY HASH(`user_id`) BUCKETS 16
PROPERTIES (
"replication_allocation" = "tag.location.default: 3"
);

查询SQL:

select count(1) from dup1 join dup2 on dup1.age = dup2.cost;

我们从FE生成逻辑Fragments开始分析,在上面这个环境中,FE为这条查询生成的Fragments(执行计划)如下:

Doris的执行计划生成、分发与执行,Doris,doris

发到BE节点后,根据FE发来的信息,重建执行计划树,但是在BE中没有PlanFragment这种对象,只有FE中的PlanNode,ExchangeNode,SinkNode在BE中有对应的对象创建。

对于这个例子,BE中的执行计划是这样的:

1、有三个BE节点,Fragment0里的算子,会分配到其中一个BE中,且只有这一个BE会有Fragment0,其他的Fragment的数据最终都会汇总到这个BE的Fragment0中,再发给FE,这个BE称为Root BE。

2、Fragment1里的算子,在每个BE上都会创建2个实例,整个BE集群里一共有6个join节点。

3、Fragment2、Fragment3里的算子,在每个BE上都会创建2个实例,整个BE集群里,读取dup1的ScanNode有6个,读取dup2的ScanNode有6个。

4、Fragment2、Fragment3里的每个ScanNode,都读取表的不同部分,每个ScanNode有一个ScanRange对象,其中的tablet id表示这个ScanNode要读取哪些tablet,每个ScanNode的ScanRange中的tablet id都不重复。

5、表dup1和表dup2,按照shuffle join,做分布式join,即读取dup1、dup2的每个ScanNode都按照相同的hash函数,将读取的记录分发给3个BE里的6个join节点(注意:每个ScanNode可能向所有6个join发记录,而不是只向其中3个join发记录,这6个join每个都是hash函数的全局不同的桶),如果两个表中有可join的行,那么它们一定被分发到同一join节点,这样就把两个大表的join工作分而治之了,每个join节点只管对发给自己的两个表的行做join,结果再发给上一层节点,即AggregateNode。

6、整个集群有6个join,也有6个join上层的AggregateNode,这些AggregateNode做一部工作,再把数据发给Root BE的AggregateNode完成汇总发给FE。

下图是BE中的算子以及数据流,scanNode和exchangeNode的数据流用send to EXCH_X+bucket{...}表示,对于每个scanNode是根据hash函数,把记录分发到全部6个JOIN的exchange节点的,这个分发是跨BE的,scanNode到一个exch节点数据流,对应一个VDataStreamSender的Channel。

Doris的执行计划生成、分发与执行,Doris,doris

Doris的执行计划生成、分发与执行,Doris,doris

参考:

Doris全面解析】Doris SQL 原理解析 (qq.com)

Doris原理分享(2) - 知乎 (zhihu.com)

Pipeline 执行引擎 - Apache Doris

Apache Doris 源码阅读与解析系列直播——第四讲 一条SQL的执行过程_哔哩哔哩_bilibili

Doris 源码分析 (五) gRpc 与 thrift 接口 - 简书 (jianshu.com)

Doris 源码分析 (三) 基础语法 - 简书 (jianshu.com)文章来源地址https://www.toymoban.com/news/detail-655818.html

到了这里,关于Doris的执行计划生成、分发与执行的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • doris安装部署-通过docker部署doris集群

    配置一个FE+三个BE的集群,使用版本1.1.5,并且指定固定IP和网络。 下载FE和BE包 准备FE和BE环境 配置FE 配置BE 在FE中添加BE 开始使用doris 从官方下载已经编译好的包: doris下载 1.1 doris官方下载 配置FE和BE的目录结构; 把第一步的压缩包解压后放在对应的FE和BE; 运行命令: 各

    2024年02月03日
    浏览(46)
  • Apache Doris (四十八): Doris表结构变更-替换表

     🏡 个人主页:IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客  🚩 私聊博主:加入大数据技术讨论群聊,获取更多大数据资料。  🔔 博主个人B栈地址:豹哥教你大数据的个人空间-豹哥教你大数据个人主页-哔哩哔哩视频 目录

    2024年02月07日
    浏览(43)
  • 大数据Doris(十四):Doris表中的数据基本概念

    文章目录 Doris表中的数据基本概念 一、​​​​​​​Row Column

    2024年02月06日
    浏览(48)
  • Apache Doris 系列: 基础篇-Flink SQL写入Doris

    本文介绍 Flink SQL如何流式写入 Apache Doris,分为一下几个部分: Flink Doris connector Doris FE 节点配置 Flink SQL 写 Doris Flink Doris connector 本质是通过Stream Load来时实现数据的查询和写入功能。 支持二阶段提交,可实现Exatly Once的写入。 1)需在 apache-doris/fe/fe.conf 配置文件添加如下配置

    2023年04月08日
    浏览(48)
  • Doris:MySQL数据同步到Doris的N种方式

    目录 1.CSV文件方式 1.1 导出mysql数据 1.2 导入数据 2.JDBC 编码方式 3.JDBC Catalog 方式 3.1 上传mysql驱动包 3.2 创建mysql catalog 3.3. 插入数据 4.Binlog Load 方式         当mysql与doris服务之间无法通过网络互联时,可以通过将mysql数据导出成csv文件,然后再在doris服务器导入csv文件的方

    2024年02月04日
    浏览(49)
  • 【Doris实战】Apache-doris-2.0.2部署帮助手册

    校验时间:2023年10月11日 版权声明:本文为CSDN博主「顧棟」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_43820556/article/details/133754689 安装包 apache-doris-2.0.2-bin-x64.tar.gz jdk1.8.0_131.tgz mysql-5.7.43-linux-glibc2.12-x86_64.tar.g

    2024年02月07日
    浏览(53)
  • Apache Doris (二十三) :Doris 数据导入(一)Insert Into

    目录 1. 语法及参数 2. 案例 ​​​​3. 注意事项 3.1. 关于插入数据量

    2024年02月13日
    浏览(57)
  • Doris注意事项,Doris部署在阿里云,写不进去数据

    Doris官网 https://doris.apache.org/ 本地idea访问FE,FE会返回BE的地址,但是在服务器上通过ip addr查看,发现只有局域网IP,所以FE返回了局域网的IP,导致idea连接不上BE 重写BackendV2类,返回公网IP即可。 在项目下新建包名 然后放入 BackendV2类 然后重写修改toBackendString()方法,将公网

    2024年02月14日
    浏览(37)
  • Apache Doris (十六) :Doris分区和分桶2-List分区

    目录 1. List分区 1.1 创建List分区方式 1.2 增删分区 ​​​​​​​1

    2024年02月12日
    浏览(40)
  • Apache Doris (六十四): Flink Doris Connector - (1)-源码编译

     🏡 个人主页:IT贫道-CSDN博客   🚩 私聊博主:私聊博主加WX好友,获取更多资料哦~  🔔 博主个人B栈地址:豹哥教你学编程的个人空间-豹哥教你学编程个人主页-哔哩哔哩视频 目录 1. Flink与Doris版本兼容

    2024年01月18日
    浏览(56)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包