全部產品
Search
文件中心

PolarDB:運算元介紹

更新時間:Jul 06, 2024

本文介紹了一些基本的運算元含義和實現。

運算元介紹

含義運算元
可下推至DN的運算元LogicalView,LogicalModifyView,PhyTableOperation, IndexScan
串連(Join)BKAJoin,NLJoin,HashJoin,SortMergeJoin,HashSemiJoin,SortMergeSemiJoin,MaterializedSemiJoin
排序MemSort,TopN, MergeSort
彙總(Group By)HashAgg,SortAgg
資料重分布或者彙總Exchange、Gather
過濾Filter
投影Project
求並集UnionAll、UnionDistinct
設定結果集輸出行數(Limit/Offset...Fetch)Limit
視窗函數OverWindow

可下推至DN執行的運算元

LogicalView

LogicalView表示從儲存層MySQL資料來源擷取資料的運算元,類似於其他資料庫中的TableScan或IndexScan,但支援更多的下推。LogicalView中包含下推的SQL語句和資料來源資訊,更像一個視圖。其中下推的SQL可能包含Project、Filter、彙總、排序、Join和子查詢等。以下樣本展示了LogicalView的輸出資訊及其含義:

explain select * From sbtest1 where id > 1000;

返回資訊如下:

Gather(concurrent=true)
   LogicalView(tables="[0000-0031].sbtest1_[000-127]", shardCount=128, sql="SELECT * FROM `sbtest1` WHERE (`id` > ?)")
LogicalView的資訊由三部分構成:
  • tables:儲存層MySQL對應的表名,以英文句號.分割,英文句號.之前是分庫對應的編號,之後是表名及其編號,如[000-127]表示表名編號從000到127的所有表。
  • shardCount:需訪問的分表總數,該樣本會訪問從000到127共計128張分表。
  • sql:下發至儲存層MySQL的SQL模版,PolarDB-X在執行時會將表名替換為物理表名,參數化的常量問號?替換成實際參數,詳情請參見執行計畫管理

LogicalModifyView

LogicalModifyView表示對底層資料來源進行修改的運算元,其中也會記錄一個SQL語句,該SQL可能是INSERT、UPDATE或者DELETE。以下樣本展示了LogicalModifyView的輸出資訊及其含義:
  • 樣本1
    explain update sbtest1 set c='Hello, DRDS' where id > 1000;

    返回資訊如下:

    LogicalModifyView(tables="[0000-0031].sbtest1_[000-127]", shardCount=128, sql="UPDATE `sbtest1` SET `c` = ? WHERE (`id` > ?)"
  • 樣本2
    explain delete from sbtest1 where id > 1000;

    返回資訊如下:

    LogicalModifyView(tables="[0000-0031].sbtest1_[000-127]", shardCount=128, sql="DELETE FROM `sbtest1` WHERE (`id` > ?)")
LogicalModifyView查詢計劃的內容與LogicalView類似,包括下發的物理分表,分表數以及SQL模版。同樣,由於開啟了執行計畫緩衝,對SQL做了參數化處理,SQL模版中的常量會用?替換。

PhyTableOperation

PhyTableOperation表示對某個物理分表直接執行一個操作。

說明 通常情況下,該運算元僅用於INSERT語句。但當路由分發到一個分區時,該運算元也會出現在SELECT語句中。
explain insert into sbtest1 values(1, 1, '1', '1'),(2, 2, '2', '2');

返回資訊如下:

PhyTableOperation(tables="SYSBENCH_CORONADB_1526954857179TGMMSYSBENCH_CORONADB_VGOC_0000_RDS.[sbtest1_001]", sql="INSERT INTO ? (`id`, `k`, `c`, `pad`) VALUES(?, ?, ?, ?)", params="`sbtest1_001`,1,1,1,1")
PhyTableOperation(tables="SYSBENCH_CORONADB_1526954857179TGMMSYSBENCH_CORONADB_VGOC_0000_RDS.[sbtest1_002]", sql="INSERT INTO ? (`id`, `k`, `c`, `pad`) VALUES(?, ?, ?, ?)", params="`sbtest1_002`,2,2,2,2")
樣本中,INSERT插入兩行資料,每行資料對應一個PhyTableOperation運算元。PhyTableOperation運算元的內容包括三部分:
  • tables:物理表名,僅有唯一一個物理表名。
  • sql:SQL模版,該SQL模版中表名和常量均被參數化,用?替換,對應的參數在隨後的params中給出。
  • params:SQL模版對應的參數,包括表名和常量。

IndexScan

IndexScan和LogicalView一樣也表示從儲存層MySQL資料來源擷取資料的運算元,掃描的是索引表。以下樣本展示了IndexScan的輸出資訊及其含義:

explain select * from sequence_one_base where integer_test=1;

返回資訊如下:

+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| IndexScan(tables="DRDS_POLARX1_QATEST_APP_000000_GROUP.gsi_sequence_one_index_3a0A_01", sql="SELECT `pk`, `integer_test`, `varchar_test`, `char_test`, `blob_test`, `tinyint_test`, `tinyint_1bit_test`, `smallint_test`, `mediumint_test`, `bit_test`, `bigint_test`, `float_test`, `double_test`, `decimal_test`, `date_test`, `time_test`, `datetime_test`, `timestamp_test`, `year_test`, `mediumtext_test` FROM `gsi_dml_sequence_one_index_index1` AS `gsi_dml_sequence_one_index_index1` WHERE (`integer_test` = ?)") |
上述SQL正常應該掃描sequence_one_base表,由於integer_test不是分區鍵,因此需要掃描sequence_one_base所有的分區。但是由於sequence_one_base表在integer_test列上存在全域二級索引gsi_sequence_one_index,將integer_test=1作為謂詞條件對索引表做裁剪,實際上會產生IndexScan運算元,表示只會掃描gsi_sequence_one_index索引表的一個分區。

可在CN或DN上執行的運算元

UnionAll與UnionDistinct

顧名思義,Union All對應UNIONALL,Union Distinct對應UNIONDISTINCT。該運算元通常有2個或更多個輸入,表示將多個輸入的資料合併在一起。例如:

explain select * From sbtest1 where id > 1000 union distinct select * From sbtest1 where id < 200;

返回資訊如下:

UnionDistinct(concurrent=true)
  Gather(concurrent=true)
    LogicalView(tables="[0000-0031].sbtest1_[000-127]", shardCount=128, sql="SELECT * FROM `sbtest1` WHERE (`id` > ?)")
  Gather(concurrent=true)
    LogicalView(tables="[0000-0031].sbtest1_[000-127]", shardCount=128, sql="SELECT * FROM `sbtest1` WHERE (`id` < ?)")

Gather

Gather將多份資料合併成同份資料。上面的例子中,Gather將各個分表上查詢到的資料合併成一份。Gather通常出現在LogicalView上方,表示收集合并各個分表的資料。

Exchange

Exchange是一個邏輯運算元,本身不對計算過程中的資料做計算,只是將輸入的資料做重分布後,輸出給下遊運算元。一般重分布策略包括:
  • SINGLETON:將上遊多份資料進行合并輸出,這種重分布策略等價於Gather。
  • HASH_DISTRIBUTED:將上遊輸入的資料按照某些列做repartition,常見於包含Join和Agg的執行計畫中。
  • BROADCAST_DISTRIBUTED:將上遊相同一份資料分發成多份,廣播給下遊多個節點,主要應用於MPP執行計畫中。

MergeSort

MergeSort即歸併排序運算元,表示將有序的資料流進行歸併排序,合并成一個有序的資料流。樣本如下:

explain select * from sbtest1 where id > 1000 order by id limit 5,10; 

返回資訊如下:

MergeSort(sort="id ASC", offset=?1, fetch=?2)   
   LogicalView(tables="[0000-0031].sbtest1_[000-127]", shardCount=128, sql="SELECT * FROM `sbtest1` WHERE (`id` > ?) ORDER BY `id` LIMIT (? + ?)")
MergeSort運算元包含三部分內容:
  • sort:表示排序欄位以及排列順序,id ASC表示按照ID欄位遞增排序,DESC表示遞減排序。
  • offset:表示擷取結果集時的位移量,例子中被參數化了,實際值為5。
  • fetch:表示最多返回的資料行數。與offset類似,同樣是參數化的表示,實際對應的值為10。

Project

Project表示投影操作,即從輸入資料中選擇部分列輸出,或者對某些列進行轉換(通過函數或者運算式計算)後輸出,也可以包含常量。

explain select '你好, DRDS', 1 / 2, CURTIME(); 

返回資訊如下:

Project(你好, DRDS="_UTF-16'你好, DRDS'", 1 / 2="1 / 2", CURTIME()="CURTIME()")
Project的計劃中包括每列的列名及其對應的列、值、函數或者運算式。

Filter

Filter表示過濾操作,其中包含一些過濾條件。該運算元對輸入資料進行過濾,若滿足條件,則輸出,否則丟棄。如下是一個較複雜的例子,包含了以上介紹的大部分運算元。

explain select k, avg(id) avg_id from sbtest1 where id > 1000 group by k having avg_id > 1300;

返回資訊如下:

Filter(condition="avg_id > ?1")
  Project(k="k", avg_id="sum_pushed_sum / sum_pushed_count")
    SortAgg(group="k", sum_pushed_sum="SUM(pushed_sum)", sum_pushed_count="SUM(pushed_count)")
      MergeSort(sort="k ASC")
        LogicalView(tables="[0000-0031].sbtest1_[000-127]", shardCount=128, sql="SELECT `k`, SUM(`id`) AS `pushed_sum`, COUNT(`id`) AS `pushed_count` FROM `sbtest1` WHERE (`id` > ?) GROUP BY `k` ORDER BY `k`")
WHERE id>1000中的條件沒有對應的Filter運算元,是因為這個運算元最終被下推到了LogicalView中,可以在LogicalView的SQL中看到WHERE (id > ?)