通篇乾貨!縱觀 PolarDB-X 並行計算框架

<meta name="source" content="lake">

簡介: 本文將對整個分佈式並行執行框架做詳細的介紹,希望閱讀之後對我們的執行器有一個全面的認識。

作者:玄弟 七鋒

PolarDB-X 面向 HTAP 的混合執行器 一文詳細說明了PolarDB-X執行器設計的初衷,其初衷一直是致力於爲PolarDB-X注入並行計算的能力,兼顧TP和AP場景,逐漸爲其打造一款具備TB級數據處理能力的數據庫。爲了做到這一點,借鑑了各種數據庫和大數據庫產品,包括分析型數據庫,實時數倉等,吸取了各方面的優勢來打造出一個全新的並行執行引擎。這裏將對整個分佈式並行執行框架做詳細的介紹,希望閱讀之後對我們的執行器有一個全面的認識。

▶ 整體設計

PolarDB-X 是一個 Share Nothing 的數據庫,採樣了計算和存儲分離的架構。其中數據以分片的形式存儲於每個DN節點,計算節點叫CN。在計算過程中,DN和CN間、DN和DN、CN和CN間是通過千兆、萬兆網絡通信。每個CN節點上一般都會有調度組件、資源管理組件、RPC組件等。一個複雜的查詢,會被調度到多個CN節點上執行,考慮到數據一般都會根據均勻策略分到各個DN上,所以每個CN節點同時會訪問多個DN。

當用戶提交一條複雜的SQL語句,往往需要訪問多個DN節點,這個時候就會啓動並行調度策略,整個執行步驟可以簡單理解:

  1. 用戶所連接的這個 CN 將承擔查詢協調者(Query Coordinator)的角色;

  2. Query先發送給Query Coordinator,會首先經過優化器生成最新的Plan,然後會拆分到多個子計劃(Fragment), 每個Fragment可能會包含多個執行算子。如果有的Framgnt負責掃描DN的話,它裏頭必定包含Scan算子,從DN拉取數據;Fragment也可以包含Agg或者Join等其他算子;

  3. Query Coordinator裏頭的調度器(Task Scheduler)會按照定義的調度邏輯將各個Framgnts封裝成Task,調度到合適的CN上執行,這裏頭可能會涉及到一些資源上的計算;

  4. 各個CN收到Task後,會申請執行資源,構造執行的上下文,開始啓動Task,並定期會向Query Coordinator彙報狀態;

  5. 各個Task間會通過數據傳輸通道(DTL)交換數據,當所有的Task都執行完畢後,會將數據結果返回給Query Coordinator,由它負責將結果返回給用戶;

  6. 成功返回給用戶後,Query Coordinator和被調度到的各個CN節點的Task會做清理動作,釋放計算資源。

整個流程大致就這樣,有細心的同學會發現我們的框架在DN上有一層叫Split概念。我們的數據是按分片存儲在各個DN上的,Split指的是數據分片partition的地址。對於包含掃描算子Scan的 Task,會計算出需要訪問哪些 partition,這些 partition 分佈在哪些 DN 上,然後封裝成splits按比例劃分給這些掃描Task。但是實際運行過程中每個掃描Task並不是預分配好splits的,而是預分配部分splits給掃描Task,看哪一個Task掃描的更快就會從Query Coordinator繼續獲取餘下splits,這樣可以儘可能避免由於各個掃描Task資源不均衡導致的消費長尾現象。但是如果一個表只被分成了2個分片,是不是意味着掃描任務至多隻能是2,這可能起不到明顯的並行加速效果。所以我們也支持在分片上繼續按照分段做拆分,那麼這個時候的Split除了會記錄分片的地址,也會記錄在分片上分段的位移。按照分段做拆分後,即便數據的分片數量有限,執行過程我們依然可以啓動更多的掃描Task,並行去加速掃描。

▶ 執行計劃

執行引擎執行的是由優化器生成的分佈式執行計劃。執行計劃由算子組成。因爲PolarDB-X的數據按照分片存儲到各個的DN節點上去,執行計劃執行也會盡可能的滿足數據分佈的locality,能下推的計劃會被放到DN上執行,不能下推的計劃會會被切分成一個個子計劃(Fragment),會被放到各個CN節點上執行。所以這裏我們需要關心如何將一個從優化器出來的計劃,拆分成分佈式計劃,放到各個CN上執行?

爲了更好地理解這個過程,我們這裏以一條簡單SQL: select * from (select useid, count(*) as b from user_data group by userid) as T where T.b > 10 爲例,經過優化器生成這樣的相對最優計劃:

針對並行執行計劃,爲了更高效地執行儘量減少數據傳輸,可以把執行計劃按照計算過程是否需要數據重分佈(ReDistribution)分爲不同片段(fragment)分佈到相應節點執行,並且把一些操作下推來減少掃描輸出的數據,上面的計劃可能就變成這樣的執行計劃,由多個子片段構成。

不同片段之間通過 NetWork Write/Read 算子進行數據交換。更復雜的比如多表關聯(join)查詢,會有更多的片段和更復雜的數據交換模式。每個片段的併發度可以不同, 併發度是基於代價推導出來的。多機調度的基本單位是Stage,Stage記錄了上下游片段的位置信息,以便上下游之間建立網絡通道(DTL)。每個片段調度到計算CN節點後,會被封裝成邏輯執行Task,比如fragment-1併發度是2的話,那麼會將Task-1.0和Task-1.1 兩個Task分別調度到兩個CN節點。

Task仍然是CN節點計算的邏輯單元,PolarDB-X執行器不僅僅可以支持單機並行能力(Parallel Query),也可以做多機並行(MPP)。所以在CN節點還引入了二層調度邏輯。當然二層調度的好處不僅僅於此,後面我們還會提到。這裏會繼續在Task內部根據算子間數據交換的特性,繼續做切分,切分成不同Pipeline。

不同的Pipeline併發度也可以不同,每個Pipeline會根據處理的數據規模大小會計算出不同的併發度,生成具體的執行單元Driver,Driver間會根據二層調度確定上下游的本地通道(Local Channel)。

至此你應該可以瞭解從執行邏輯計劃轉化爲分佈式物理執行的整個過程。引入了一些新的名稱,這裏統一做下梳理:

  • Fragment:指的是邏輯執行計劃按照計算過程中數據是否需要重分佈,切割成的子計劃。

  • Stage:是由Fragment封裝而成的調度邏輯單位,Stage除了封裝Fragment外,還會記錄上下游Stage間的調度位置信息。

  • Task:Stage並不會在CN上直接運行,他們是通過併發度分解成一系列可調度到CN上的Task, Task依然是邏輯執行單元。

  • Pipeline:對CN上的Task根據二層併發度做進一步切分,切分成不同的Pipeline。

  • Driver:一個Pipeline包含多個Driver,Driver是具體的執行單元,是一系列可運行算子的集合。

一般來說針對一個複雜查詢,一個query包含多個Fragment,每個Fragment和Stage一一對應,每個Stage包含多個Tasks,每個Task會切分成不同的Pipeline,一個Pipeline包含了多個Driver。只有理解上面說的Fragment/Stage/Task/Pipeline/Driver這些概念,你才能更清楚瞭解我們接下來的設計。

▶ 調度策略

並行計算在運行之初,需要解決任務調度問題。調度的直白理解,就是將切分好的Task調度到各個CN節點去執行,充分利用各個CN的計算資源。這裏頭大家很容易有這些疑問:

1. 執行過程中各個CN節點的計算資源是不均衡了,那麼在多機調度中是如何將各個Task打散到不同CN節點去執行? 2. 和各個DN交互的Task是如何並行的拉數據的?比如某個邏輯表分成了16個物理表,分佈在4個DN節點上,有4個Driver去並行拉數據,每個Driver並不是均勻拉取4個物理表,而是根據自身的消費能力來確定拉取的物理表數量;多個Driver下發掃描任務會不會同時恰好落地一個DN節點上,導致某個DN成爲瓶頸? 3. 我們完全可以在一個CN節點,同時調度多個Task執行,已經可以做到單機並行,爲什麼還要二層調度?

一層調度(多節點間)

爲了解決(1) 和 (2) 的問題,我們在CN節點內部引入了調度模塊(Task Scheduler),主要負責Task在不同CN節點上的調度,這一層調度我們這裏稱之爲一層調度,在這層調度中,同屬於一個Stage的多個Task一定會被調度到不同CN節點上去,確保一個CN節點只能有相同tage的一個Task。調度過程中通過心跳不斷維護Task狀態機,也維護着集羣各個CN節點Load信息,整個調度是基於CN Load做調度的。多機調度流程如下所示:

[圖片上傳失敗...(image-969295-1616574088458)]

Resource Manager(RM)是CN節點上個一個資源管理模塊,RM會藉助Task心跳機制實時維護集羣各個CN節點的負載,Task Scheduler組件會基於負載選擇合適的CN節點下發執行任務,比如CN-1 負載相對集羣其他CN節點來說高很多,那麼當前查詢的Task會分發給其他CN節點,避免調度到CN-1節點去。執行器在執行Task任務時,Task並不是創建好的時候就確定了其消費DN splits的映射關係。各個Task按批次動態拉取splits進行消費, 直白理解就是誰的消費能力越強誰就有可能消費更多的splits。同樣爲了解決同一個時刻多個任務同時消費同一個DN上的splits問題,我們在調度之初會將splits根據地址信息按照Zig-zag方式,把各個DN上的splits打散到整個splits queue上去,消費的時候可以儘可能分攤各個DN壓力,這樣計算過程中也會充分利用各個DN的資源。

有了一層調度後,我們也可以將同屬於一個Stage的多個Task調度到同一個CN,這樣其實也可以做到單機並行。如果這樣設計的話,我們容易忽略兩個問題:

  • 一層調度的邏輯比較複雜,需要多次交互,一個CN內部需要同時維護各個Task的狀態,代價會比較大,這在TP場景是無法容忍的;

  • 一層調度中,併發度越高,生成Task就越多,這些Task間需要建立更多的網絡傳輸通道。

二層調度(節點內部)

爲了解決上述一層調度的不足,爲此我們在參考Hyper的論文[1],引入了二層調度,既在CN節點內部單獨做單機並行調度,簡單來說我們會在Task內部藉助CN的本地調度組件(Local Scheduler),對Task做進一步的並行調度,讓Task在CN上執行,也可以做到並行運行。下圖中,Stage-1和Stage-2是上下游關係,各自併發度都是9,調度到3個CN節點執行。如果只有一層併發度的話,每個CN節點還會調度運行3個Task,那麼上下游之間總共會建立81個Channel,CN節點內部Task是相互獨立的,這樣缺點還是很明顯:

  1. 多個Channel,放大了網絡開銷,同一份buffer會被髮送多次,發送和接收對CPU和Memory都有代價;

  2. 數據發送的對象是Task,數據本身有傾斜,會導致同節點內Task之間的負載不均衡(hash skew),存在長尾問題。

而一層調度和二層調度相結合的話,Stage-1和Stage-2的一層併發度是3,這樣每個CN節點只會有1個Task,Task內部併發度3。由於shuffle的對象是Task,所以Stage-1和Stage-2間只會建立9個Channel,大大減少了網絡開銷,同時Task內部的3個Driver內數據是共享的,Task內部的所有的Driver可以共同消費接受到的數據,並行執行,避免長尾問題。針對於HashJoin,假設Ta爲大表,Tb爲小表,這兩個表做HashJoin,可以讓Ta和Tb同時shuffle到同一個節點做計算;也可以讓小表Tb廣播到Ta所在節點做計算,前者的網絡代價是Ta+Tb,而後者的代價是N*Tb(N代表廣播的份數)。所以如果只有一層調度的話,N可能比較大,執行過程中我們可能會選擇兩端做shuffle的執行計劃;而一層和二層相結合的調度策略,可以讓執行過程中選擇BroadcastHashJoin,這樣可以避免大表做shuffle,提高執行效率。

此外在二層調度策略中,task內部的多線程很容易做到數據共享,有利於更高效的算法。如下圖,同樣是HashJoin過程中,build端的Task內部多個線程(driver)協同計算:build端收到shuffle的數據後,多線程協同建立一個共享的hash表。這樣一個task只有一個build table,probe端收到shuffle數據後,也不用做ReDistribution了,直接讀取接受到數據,進行並行的probe。

▶ 並行執行

聊完調度,接下來應該是關心任務是如何在CN上運行,運行過程中遇到異常我們系統是如何處理的呢?

線程模型

說到執行,有經驗的同學可能會發現我們的調度並沒有解決調度死鎖問題,比如對於下面這樣一個執行計劃,兩表Join。一般會遇到兩種問題:

1. 如果先調度f3和f2的話,這個時候假設集羣沒有調度資源,則f1不能遲遲調度起來。而HashJoin的邏輯就是需要先構建buildTable,這裏f1剛好是build table部分。最終會導致執行死鎖:f1在等待f3和f2的計算資源釋放,而f2和f3又在等待f1構建完buildTable;

2. 如果f1先調度起來了,假設這個時候f2和f3沒有調度資源,這個時候f1從DN拉出來的數據,其實是無法發送給f3的,因爲f3還沒有被調度起來。

解決問題1,業界有很多方式,比較常見是在調度之初構建調度依賴關係(Scheduler Depedency):f1->f3-f2。而解決問題2,往往是將f1把DN拉出來的數據先放到內存中,實在放不下就落盤處理。可見處理上述兩個問題,執行框架不僅僅需要在多機調度上做複雜的調度依賴關係,同時還需要考慮對落盤的支持。而其實我們在調度的時候,並沒有去考慮調度依賴這個事情,我們是一次性把f1/f2/f3全部調度起來了,這個是爲何呢?這就要說下我們執行中的邏輯線程模型概念。在大多數計算引擎中,一個查詢首先會通過資源調度節點,在各個CN上申請執行線程和內存,申請成功後,這些執行資源會被調度組件佔用,用來分配當前查詢的Task,不可以再被其他查詢所利用,這種是真實的執行資源,和調度資源相互綁定,當CN上可利用的執行資源不夠的時候,纔會出現調度死鎖問題。而在PolarDB-X中,我們並沒有在調度的時候申請真實的線程資源,調度只需要考慮各個CN的負載,不需要考慮各個CN到底還剩多少可利用的真實資源。我們的線程模型也並沒有和調度資源綁死,每個Driver其實不獨佔一個真實的線程,換句話說,真實的線程也並沒有和調度資源一一對應。雖然說Driver是執行的基本單元,但是在調度上來看,它又是邏輯的線程模型而已。那是不是意味着只要有調度任務,都可以被成功調度到CN上去,答案是肯定的。一次性調度所有的執行單元到CN上去執行,對內存和CPU也是一種開銷。比如f2被執行起來後,但是f1並沒有執行完畢,那麼f2也會不斷執行,其數據其實也會被緩存起來,但是也不能無限緩存數據呀?爲了解決這個問題,接下來就需要藉助我們的時間片執行了。

時間片執行

我們在每個CN節點內部會有一組執行線程池來運行這些Driver,每個Driver會排隊進入線程池參與計算,如果Driver被阻塞就會退出到Blocking隊列中,等待被喚醒。比如f2 driver 啓動後,從DN拉了數據放到有限空間buffer裏頭去,這個時候假設f1 driver都沒有結束,那麼f2 driver 對應的buffer就會滿,滿了後就會阻塞住,一旦阻塞我們的執行框架就會讓f2 driver從執行器退出來,加入到Blocking隊列中,簡單的說就是將計算資源騰讓出來,等待被喚醒。直到f1 driver都執行完畢後, f2 driver會被喚醒,執行框架就會將他移動到Pending隊列中,等待被調度到執行線程池中繼續運行。這裏頭還是會浪費點內存,但相對於CPU資源來說,內存資源還是比較充裕的。

時間片執行的核心就是需要判斷Driver何時會被Block的,總結起來被阻塞的原因一般分爲三種情況:

  • 根據算子依賴模型來確定,比如圖中f1 driver未執行完畢,那麼f2 driver其實也會被阻塞(這個是一個可配置的選項);

  • 計算資源不足(主要指內存),對應的driver會被掛起,等待資源釋放;

  • 等待DN響應,物理SQL下發給DN後,Driver會被掛起,等待物理SQL執行完畢。

除此之外我們在借鑑Linux 時間片調度機制,會在軟件層面上統計Driver的運行時長,超過閾值(500ms),也會被強制退出執行線程,加入到Pending隊列,等待下一輪的執行調度。這種軟件層面上的時間片調度模型,可以解決複雜查詢長時間佔用計算資源問題。其實實現起來也挺簡單的,就是每計算完一個批數據後,我們會對driver的運行時長進行統計,超過閾值,就退出線程池。下面貼出了Driver處理邏輯的部分僞代碼,Driver在執行採用的是經典的Producer-Consumer模型,每消費一個Chunk我們就會累計時間,當超過既定閾值,就會退出來。

任務狀態機

高併發系統,頻繁地等待或者任務切換是常見的系統瓶頸。異步處理是一種已經被證明行之有效地避免這些瓶頸,把高併發系統性能推到極致的方法。所以PolarDB-X執行器的整個後端,統一使用全異步的執行框架;同時MPP執行過程涉及到多機的協調,所以這就要求我們在系統內部維護這些異步狀態。異步狀態的維護特別重要,比如某個查詢下的Task執行失敗,需要立即通知到整個集羣中該查詢正在運行的Task任務,以便立即中止,以防出現Suspend Task,造成資源不釋放問題。

所以在執行器內部,我們從三個維度(Task Stage Query)去維護狀態, 這三種State是相互依賴耦合的,比如Query 被Cancel,會立即通知其所有的Stage,各個Stage監聽到狀態變化,會及時通知給其所有的Task,只有等待Task都被Cancel後,Stage 最後的狀態才變更爲Cancel,最終Query的狀態才被標記爲Cancel。在這個過程中我們會引入對狀態機異步監聽機制,一旦狀態發送變更就會異步回調相關處理邏輯。通過維護這些狀態,我們也可以及時通過查詢或者監控診斷到任務是否異常,異常發生在哪個環節,也便於我們後期排查問題。

▶ 資源隔離

如果併發請求過多的時候,資源緊張會讓請求線程參與排隊。但是正在運行的線程,需要耗費比較多的計算資源(CPU和Memory)的時候,會嚴重影響到其他正常正在運行的Driver。這對我們這種面向HTAP場景的執行器是決定不被允許的。所以在資源隔離這一塊,我們會針對不同WorkLoad做計算資源隔離,但這種隔離是搶佔式的。

CPU

在CPU層面上我們是基於CGroup做資源隔離的,根據WorkLoad不同我們把CPU資源分爲AP Group和TP Group兩組,其中對TP Group的CPU資源不限制;而對AP Group是基於CGroup做硬隔離,其CPU使用水位的最小閾值(cpu.min.cfs_quota)和最大閾值(cpu.max.cfs_quota)來做限制。執行線程分爲三組: TP Core Pool 、AP Core Pool、SlowQuery AP Core Pool,其中後兩者會被劃分到AP Croup一組,做嚴格的CPU限制。Driver會根據WorkLoad劃分到不同的Pool執行。看似很美的實現,這裏頭依然存在兩個問題:

1. 基於COST識別的WorkLoad不準怎麼辦?

2. AP查詢比較耗資源,在同一個Group下的多個慢查詢相互影響怎麼辦?

出現問題(1)主要的場景是我們把AP類型的查詢識別成了TP,結果會導致AP影響到TP,這是不可以接受的。所以我們在執行過程中會監視TP Driver的執行時長,超過一定閾值後仍沒有結束的查詢,會主動退出時間片,然後將其它調度到AP Core Pool執行。而爲了解決問題(2),我們會將AP Core Pool中長時間運行都未結束的Driver,進一步做優雅降級,調度到SlowQuery AP Core Pool執行。其中SlowQuery AP Core Pool會設置執行權重,儘可能降低其執行Driver的頻率。

MEMORY

在內存層面上,會將CN節點堆內內存區域大致可以分爲四大塊:

  • TP Memory:用於存放TP計算過程中的臨時數據

  • AP Memory:用於存放AP計算過程中的臨時數據

  • Other:存放數據結構、臨時對象和元數據等

  • System Reserverd:系統保留內存

TP和AP Memory分別會有最大閾值和最小閾值限制,兩者內存使用過程中可以相互搶佔,但是基本原則是:TP Memory可以搶佔AP Memory,直到查詢結束才釋放;而AP Memory可以搶佔內存TP,但是一旦TP需要內存的時候,AP Memory需要立即釋放內存,釋放方式可以是自殺或者落盤。

▶ 數據傳輸層(DTL)

並行計算是充分利用各個CN資源參與計算,那麼DN與DN之間必然會存在數據交互。各個DN上的上下游的Task數據需要傳輸,比如上游的Task數量N,下游的Task數量是M,那麼他們之間的數據傳輸通道需要用到M*N個通道(Channel),同樣的我們將這些通道(Channel)的概念抽象成數據傳輸層。這個傳輸層的設計往往也會面臨兩個問題:

1. 通道分爲發送端和接受端,當發送端源源不斷髮送數據,而接受端無法處理的話就會造成內存雪崩;

2. 數據在傳輸過程中丟失。

在業界實現數據傳輸主要有兩種傳輸方式:Push和Pull。Push就是發送端往接受端推送數據,這裏頭爲了避免接收端處理不過來,需要引入流控邏輯,一般的做法都是在接收端預留了槽位,當槽位被數據佔滿時會通知發送端暫停發送數據,當有接收端數據被消費空閒槽位出現時通知發送端繼續發送,這裏頭會涉及到發送端和接收端的多次交互,流控機制相對比較複雜。Pull就是發送端將數據先發送到buffer裏頭去,接收端按需從發送端的的buffer拉數據,而當發送端發送的數據到buffer,接收端假設長時間不來拉數據,最後發送端buffer滿了,也會觸發上游反壓,爲了避免頻繁反壓,往往發送端的buffer不應該設置太小。綜合起來我們選擇了pull方式來做。採樣pull方式也會遇到兩個問題:

1. 每個receiver一般會和上游多個sender建立連接,那麼每次都是通過廣播的方式從上游所有的sender拉數據嗎?

2. 一次從sender端到底請求多少的數據呢,即averageBytesPerRequest?

我們先回答問題(2),我們這裏會記錄上一次請求的數據量lastAverageBytesPerRequest、當前建連通道個數n以及上一次總共返回的數據量responseBytes,來計算出當前averageBytesPerRequest,具體的公式下面也給出了。至於問題(1),有了當前的averageBytesPerRequest後,結合目前receiver上buffer剩餘空間,可以估算出這一次需要向上遊幾個sender發送請求。

[圖片上傳失敗...(image-9b19e1-1616574088452)]

在異步通信過程中爲了保證傳輸可靠性,我們採用了類似tcp ack的方式,當receiver端帶着token去上游拉數據的時候,則表示當前token之前的數據均已經被receiver端消費完畢,sender可以釋放這些數據,然後將下一批數據以及nextToken返回給receiver端。

▶ 效果展示

前後說了很多幹貨,下面咱們來點簡單實際的東西。這裏以TPCH Q13爲例來演示下執行器在不同場景下的加速效果,爲了方便截圖在Q13後面都加了limit。該測試環環境下,CN和DN規格都是2*16C64G。

單機單線程下運行,耗時3min31s

使用Parallel Query加速,既單機多線程執行,耗時23.3s

使用MPP加速,既同時利用兩個CN節點的資源計算,耗時11.4s

▶ 總結

不管是簡單查詢,還是 Parallel Query和MPP場景下的複雜查詢,共用的都是一套執行框架。不同場景下對執行器的要求,更多的是併發度設置和調度策略的差異。相對於業界其他產品來說,PolarDB-X執行器主要特點:

  1. 在資源模式上使用的是輕量化的資源管理,不像大數據計算引擎,需要額外引入的資源管理的節點,做嚴格的資源預分配,主要考慮到我們的場景是針對於小集羣的在線計算;

  2. 在調度模型上執行器支持DAG調度,相對於MPP調度可以做到更加靈活的併發控制模型,各個Stage間、Pipeline間的併發可以不一樣;

  3. 區別與其他產品,AP加速引用的是外掛並行計算引擎,PolarDB-X並行執行器是內置的,不同查詢間共用一套執行模型,確保TP和AP享有一致的SQL兼容性。

PolarDB-X並行計算在線上已經平穩運行了近兩年,這兩年來我們不僅僅在執行框架上做了很多穩定性工作,在算子層的優化我們也沉澱了不少的技術。但這些還不夠,目前比較熱的是自適應執行,結合Pipeline模式的自適應執行挑戰比較大,我們近期也在研究,歡迎感興趣的朋友來拍拍磚,一起進步!

▶ Reference

[1] V. Leis, et al., Morsel-Driven Parallelism: A NUMA-Aware Query Evaluation Framework for the Many-Core Age, in SIGMOD, 2014.
[2] Presto: SQL on Everything.
[3] A Deep Dive into Query Execution Engine of Spark SQL.
[4] Impala: A Modern, Open-Source SQL Engine for Hadoop
[5] FusionInsight LibrA: Huawei's Enterprise Cloud Data Analytics Platform. Proc. VLDB Endow. 11(12): 1822-1834 (2018)

【相關閱讀】

HTAP 數據庫“必修課”:PolarDB-X Online Schema Change

PolarDB-X 向量化引擎的類型綁定與代碼生成

每次都需要解釋大量指令?使用 PolarDB-X 向量化引擎

PolarDB-X 面向 HTAP 的混合執行器

PolarDB-X 面向 HTAP 的 CBO 優化器

如寶馬3系和5系:PolarDB-X 與 DRDS 並駕齊驅

PolarDB-X 存儲架構之“基於Paxos的最佳生產實踐”

PolarDB-X 私有協議:提升集羣的性能和穩定性

技術解讀 | PolarDB-X 分佈式事務的實現

技術解讀 | PolarDB-X 強一致分佈式事務

PolarDB-X 一致性共識協議 (X-Paxos)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章