本文介紹PolarDB-X如何優化和執行排序計算,達到減少數據傳輸量和提高執行效率的效果。
基本概念
排序操作(Sort)表示按照指定的ORDER BY列對輸入進行排序。本文介紹的均為不下推的排序類算子的實現。如果已被下推到LogicalView中,則由存儲層MySQL來選擇執行方式。
PolarDB-X中的排序算子主要包括MemSort、TopN,以及 MergeSort。
MemSort
PolarDB-X中通用的排序實現為MemSort算子,表示在內存中運行快速排序(Quick Sort)算法。如下示例使用了MemSort算子:
explain select t1.name from t1 join t2 on t1.id = t2.id order by t1.name,t2.name;
返回信息如下:
Project(name="name")
MemSort(sort="name ASC,name0 ASC")
Project(name="name", name0="name0")
BKAJoin(condition="id = id", type="inner")
Gather(concurrent=true)
LogicalView(tables="t1", shardCount=2, sql="SELECT `id`, `name` FROM `t1` AS `t1`")
Gather(concurrent=true)
LogicalView(tables="t2_[0-3]", shardCount=4, sql="SELECT `id`, `name` FROM `t2` AS `t2` WHERE (`id` IN ('?'))")
TopN
當SQL中ORDER BY和LIMIT一起出現時,Sort運算和Limit運算會合并成TopN算子。
TopN算子維護一個最大或最小堆,按照排序鍵的值,堆中始終保留最大或最小的N行數據。當處理完全部的輸入數據時,堆中留下的N個行(或小于N個)即為需要的結果。
explain select t1.name from t1 join t2 on t1.id = t2.id order by t1.name,t2.name limit 10;
返回信息如下:
Project(name="name")
TopN(sort="name ASC,name0 ASC", offset=0, fetch=?0)
Project(name="name", name0="name0")
BKAJoin(condition="id = id", type="inner")
Gather(concurrent=true)
LogicalView(tables="t1", shardCount=2, sql="SELECT `id`, `name` FROM `t1` AS `t1`")
Gather(concurrent=true)
LogicalView(tables="t2_[0-3]", shardCount=4, sql="SELECT `id`, `name` FROM `t2` AS `t2` WHERE (`id` IN ('?'))")
MergeSort
通常,只要語義允許,SQL中的排序操作會被下推到存儲層MySQL上執行,而PolarDB-X執行層只做最后的歸并運算,即MergeSort。嚴格來說,MergeSort不僅僅是排序,更是一種數據重分布算子(類似Gather)。下面的SQL表示對t1表進行排序,經過PolarDB-X查詢優化器的優化,Sort算子被下推至各個存儲層MySQL分片中執行,最終只在上層做歸并操作。
explain select name from t1 order by name;
返回信息如下:
MergeSort(sort="name ASC")
LogicalView(tables="t1", shardCount=2, sql="SELECT `name` FROM `t1` AS `t1` ORDER BY `name`")
相比MemSort,MergeSort算子可以減少PolarDB-X執行層的內存消耗,并充分利用存儲層MySQL層的計算能力。