Impala源代码分析(3)-backend查询执行过程
这篇文章主要介绍impala-backend是怎么执行一个SQL Query的。 在Impala中SQL Query的入口函数是: void ImpalaServer::query(QueryHandle query_handle, const Query query) 生成一个QueryExecState伴随这个SQL执行的生命周期,代表正在执行的这个SQL; 调用E
这篇文章主要介绍impala-backend是怎么执行一个SQL Query的。
在Impala中SQL Query的入口函数是:
void ImpalaServer::query(QueryHandle& query_handle, const Query& query)
- 生成一个QueryExecState伴随这个SQL执行的生命周期,代表正在执行的这个SQL;
- 调用Execute函数启动执行流程;
- 启动一个Wait线程等待结果。
这个Execute()函数首先是通过JNI向impala-fe请求SQL解析和执行计划生成(已经在上一篇文章中讲了这个过程),得到该Query对应的TExecRequest对象,交由impala-backend执行。
从下面这个函数开始backend执行,同时开始fragment status report。
Status ImpalaServer::QueryExecState::Exec(TExecRequest* exec_request)
因为我们知道在impala里面,一个Query是分配到多个节点执行的,我们把其中负责分配和协调这个Query执行的组件叫Coordinator;参与这个Query执行的每个节点叫backend instance,每个backend instance上面会执行一个或者多个PlanFragment。那么每个Query就对应一个Coordinator对象和多个backend instance,同时Coordinator中的query_profile_ 变量是用来统计这个query的执行的整个profile的。
Coordinator
这里首先生成Coordinator用于协调这个Query的执行,然后调用
Status?Coordinator::Exec(
const TUniqueId& query_id, TQueryExecRequest* request,
const TQueryOptions& query_options)
启动异步的执行过程:说白了这个Coordinator就是老板,把活(PlanFragment)都给各个下属(backend instance)安排好了,发出去,然后自己下班走人了,才不会等着下属干完了才走呢。因为老板早就安排好自己的秘书(ImpalaServer::Wait())去盯着结果呢。
这个函数里面最重要的两个步骤:
- ComputeScanRangeAssignment(*request);
- ComputeFragmentExecParams(*request);
其中ComputeScanRangeAssignment(const TQueryExecRequest& exec_request)?用于填充std::vector
typedef boost::unordered_map
另外一个函数ComputeFragmentExecParams?(const TQueryExecRequest& exec_request)?用于填充std::vector
- Status Coordinator::ComputeFragmentHosts(const TQueryExecRequest& exec_request):为每个PlanFragment找到执行所在的backend instance。如果一个PlanFragment是UNPARTITIONED,那么就在这个Coordinator所在的host上运行;如果一个PlanFragment含有ScanNode,那么就调度这个PlanFragment到HDFS/HBase数据块所在的那些DataNodes上,也就是这些DataNodes就成为了执行这个Query的backend instance。
- 计算TQueryExecRequest.fragments中每个PlanFragment会在哪些hosts上得到执行,填充到fragment_exec_params_ 中。
- 依次给每个PlanFragment执行的每个host分配一个instance_id。
- 填充每个?FragmentExecParams?的destinations(即Data Sink的目的地PlanFragment)和per_exch_num_senders(这个ExchangeNode会接收来自多少个PlanFragment的数据)
回到Coordinator::Exec()函数中,下面就该把各个PlanFragment分配干活了。
- 如果有Coordinator PlanFragment,那么先new PlanFragmentExecutor()生成这个PlanFragment所对应的PlanFragmentExecutor。然后填充其对应的TExecPlanFragmentParams。
- 下面是个双层循环:外层遍历PlanFragment,内层遍历backend instance,生成与每个instance关联的BackendExecState(主要是生成TExecPlanFragmentParams用于Coordinator与多个backend instance交互时的参数),并加入backend_exec_states_列表,用于Coordinator对所有的backend instance执行状况的管理。然后向每个instance发起RPC请求开始执行,请求协议是ImpalaInternalService:: ExecPlanFragment(TExecPlanFragmentParams)
Status fragments_exec_status = ParallelExecutor::Exec(
bind
reinterpret_cast
num_hosts);
每个Coordinator,PlanFragmentExecutor和ExecNode都会有一个RuntimeProfile,所有的RuntimeProfile会构成树状结构来记录每个执行节点的执行过程中的信息。
在Coordinator有个成员变量boost::scoped_ptr
每个Coordinator还有个aggregate_profile_专门负责aggregate相关的profile。
PlanFragmentExecutor和ExecNode
无论是在Coordinator端还是在backend instance端执行的PlanFragment都是由一个PlanFragmentExecutor控制的。下面我们看看PlanFragment在backend instance是怎么执行的?
在RPC的server端调用了ImpalaServer::ExecPlanFragment()->ImpalaServer::StartPlanFragmentExecution()
生成FragmentExecState里面含有一个PlanFragmentExecutor。那么下面就是分析PlanFragmentExecutor怎么控制Query的执行的了。
- FragmentExecState::Prepare()调用PlanFragmentExecutor::Prepare()
- FragmentExecState::Exec()调用PlanFragmentExecutor::Open(),这个是PlanFragment执行的主循环,block直到该PlanFragment执行结束。
真正控制PlanFragment执行的是PlanFragmentExecutor,主要由Prepare()/Open()/GetNext()/Close()这几个函数组成。
1,? PlanFragmentExecutor::Prepare(TExecPlanFragmentParams):准备执行,主要流程如下:
- 设定这个query能够使用的内存mem_limit;
- DescriptorTbl::Create():初始化descriptor table;
- ExecNode::CreateTree():生成执行树的结构(父子关系)。执行树由ExecNode组成,每一个ExecNode也提供了Prepare(), Open(), GetNext()函数。后面执行ExecNode::Prepare/Open/GenNext /EvalConjuncts/Close函数都是按照这个树状结构递归下去的。初始化完成后,PlanFragmentExecutor ::plan_指向了执行树的根节点。在这棵树中,root节点被最后执行,叶子节点被最先执行;
- 设置该PlanFragment的Exchange Node会接收来自多少个sender的数据;
- 调用plan_->Prepare():从根节点开始递归初始化执行树,主要是初始化runtime_profile等统计信息和conjuncts的LLVM本地代码生成 (adding functions to the LlvmCodeGen object);
- 如果使用本地代码生成,调用runtime_state_->llvm_codegen()->OptimizedModule()进行优化;
- 把所有的ScanNode对应的Scan Range映射到file/offset/length;
- DataSink::CreateDataSink();
- set up profile counter;
- 生成RowBatch用于存储结果。
2,PlanFragmentExecutor::Open()
先是start the profile-reporting thread,然后调用OpenInternal()
(1)???? 调用plan_->Open()沿着生成的ExecNode执行树依次调用ExecNode:: Open()
下面以HdfsScanNode::Open()为例说明:
- 调用DiskIoMgr:: RegisterReader初始化与HDFS的连接hdfs_connection_;
- 把要读取的File 和Split加入HdfsScanNode的队列queued_ranges_中;
- 调用HdfsScanNode::DiskThread驱动HdfsScanNode::StartNewScannerThread()->HdfsScanNode::ScannerThread->HdfsScanner:: ProcessSplit()去读取数据(目前一个scanner thread只能读取一个scan range);
- 调用IssueQueuedRanges()把上面加入queued_ranges_中的预读取Range发送给DiskIoMgr。由于上一步中已经启动了disk thread,所以就可以读取数据了。
(2)???? 如果当前这个PlanFragmen有sink,那么需要把这个PlanFragment要发给其他PF的数据都发出去。在发出去之前肯定得获取要发的东西吧,调用PlanFragmentExecutor ::GetNextInternal()从上到下递归调用执行树的ExecNode::GetNext()获取执行结果。
上面说到对于ExecNode::Open()不同种类的ExecNode的逻辑是不一样的,对于GetNext()也是一样的,可以参考下HdfsScanNode::GetNext()或者HashJoinNode::GetNext()看看具体是怎么获取查询结果的。
3,? PlanFragmentExecutor::GextNext(RowBatch** batch)
显示触发执行树的ExecNode::GetNext()函数获取查询结果。当其标记PlanFragmentExecutor::done_==true时,则表明所有数据已经被处理完,该PlanFragmentExecutor可以退出了。
至此,impala-backend也分析完了。总的来说impala在执行过程中和MapReduce及Hive的不同可以概括为一拉一推。
- 在MapReduce中,Map的输出结果要等着Reduce去拉;而impala中各个PlanFragment执行结束之后DataSink是推送到其他PlanFragment的。这样能更加有效利用带宽,加快Job执行速度。
- 在Hive中,逻辑上下游节点是由上游节点推送给下游节点的;而impala中是下游节点通过递归调用GetNext()向上游节点拉取的。
原文地址:Impala源代码分析(3)-backend查询执行过程, 感谢原作者分享。

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

在Linux下更新curl版本,您可以按照以下步驟進行操作:檢查目前curl版本:首先,您需要確定目前系統中安裝的curl版本。開啟終端,並執行以下指令:curl--version該指令將顯示目前curl的版本資訊。確認可用的curl版本:在更新curl之前,您需要確定可用的最新版本。您可以造訪curl的官方網站(curl.haxx.se)或相關的軟體來源,尋找最新版本的curl。下載curl原始碼:使用curl或瀏覽器,下載您選擇的curl版本的原始碼檔案(通常為.tar.gz或.tar.bz2

12306訂票app下載最新版是一款大家非常滿意的出行購票軟體,想去哪裡就去那裡非常方便,軟體內提供的票源非常多,只需要通過實名認證就能在線購票,所有用戶的出行車票機票都可以輕鬆買到,享受不同的優惠折扣。還能提前開啟預約搶票,預約飯店、專車接送都是可以的,有了它想去哪裡就去那裡一鍵購票,出行更加簡單方便,讓大家的出行體驗更舒服,現在小編在線詳細為12306用戶帶來查看歷史購票記錄的方法。 1.打開鐵路12306,點擊右下角我的,點擊我的訂單 2.在訂單頁面點擊已支付。 3.在已支付頁

學信網如何查詢自己的學歷?在學信網中是可以查詢到自己的學歷,很多用戶都不知道如何在學信網中查詢到自己的學歷,接下來就是小編為用戶帶來的學信網查詢自己學歷方法圖文教程,感興趣的用戶快來一起看看吧!學信網使用教程學信網如何查詢自己的學歷一、學信網入口:https://www.chsi.com.cn/二、網站查詢:第一步:點選上方學信網位址,進入首頁點選【學歷查詢】;第二步:在最新的網頁中點選如下圖箭頭所示的【查詢】;第三步:之後在新頁面點選【的登陸學信檔案】;第四步:在登陸頁面輸入資料點選【登陸】;

MySQL與PL/SQL是兩種不同的資料庫管理系統,分別代表了關係型資料庫和過程化語言的特性。本文將比較MySQL和PL/SQL的異同點,並附帶具體的程式碼範例進行說明。 MySQL是一種流行的關聯式資料庫管理系統,採用結構化查詢語言(SQL)來管理和操作資料庫。而PL/SQL是Oracle資料庫特有的過程化語言,用於編寫預存程序、觸發器和函數等資料庫物件。相同

使用蘋果手機想要查詢啟動日期,最好的方法是透過手機中的序號來查詢,也可以透過存取蘋果的官網來進行查詢,透過連接電腦查詢,下載第三方軟體查詢。蘋果手機怎麼查詢啟動日期答:序號查詢,蘋果官網查詢,電腦查詢,第三方軟體查詢1、用戶最好的方式就是知道自己手機的序號,開啟設定通用關於本機就可以看到序號。 2.使用序號不僅可以知道自己手機的啟動日期,還可以查看手機版本,手機產地,手機出廠日期等。 3.用戶訪問蘋果的官網找到技術支持,找到頁面底部的服務和維修欄目,裡面查看iPhone的激活信息。 4.用戶

Linux內核是一個開源的作業系統內核,其原始碼儲存在一個專門的程式碼倉庫中。在本文中,我們將詳細解析Linux核心原始碼的存放路徑,並透過具體的程式碼範例來幫助讀者更好地理解。 1.Linux核心原始碼存放路徑Linux核心原始碼儲存在一個名為linux的Git倉庫中,該倉庫託管在[https://github.com/torvalds/linux](http

這是一篇深度探索Linux內核原始碼分佈的關於1500字的文章。因為篇幅有限,我們將重點介紹Linux核心原始碼的組織結構,並提供一些具體的程式碼範例,以幫助讀者更好地理解。 Linux核心是一個開源的作業系統內核,其原始碼託管在GitHub上。整個Linux核心原始碼分佈非常龐大,包含了數十萬行程式碼,涉及多個不同的子系統和模組。要深入了解Linux核心原始碼

標題:解析織夢CMS二級目錄打不開的原因及解決方案織夢CMS(DedeCMS)是一款功能強大的開源內容管理系統,被廣泛應用於各類網站建設中。然而,有時在搭建網站過程中可能會遇到二級目錄無法開啟的情況,這給網站的正常運作帶來了困擾。在本文中,我們將分析二級目錄打不開的可能原因,並提供具體的程式碼範例來解決這個問題。一、可能的原因分析:偽靜態規則配置問題:在使用
