日本熟妇hd丰满老熟妇,中文字幕一区二区三区在线不卡 ,亚洲成片在线观看,免费女同在线一区二区

列存索引中GroupJoin算子的實現

本文介紹了PolarDB IMCI中GroupJoin算子的一些限制條件和實現方式,以及其他數據的一些相關實現。閱讀本文前請先了解基礎的HASH JOIN與HASH GROUP BY算法。

背景

SELECT
  key1,
  SUM(sales) as total_sales
FROM
  fact_table LEFT JOIN dimension_table ON fact_table.key1 = dimension_table.key1
GROUP BY
  fact_table.key1
ORDER BY
  total_sales
LIMIT 100;

PolarDB IMCI中,類似以上查詢語句的執行計劃是先執行一遍HASH JOIN,再執行HASH GROUP BY key1。在這兩個操作中,都會使用key1創建哈希表(注意fact_table.key1 = dimension_table.key1),執行計劃說明如下:

  1. HASH JOIN:使用dimension_table.key1建哈希表,使用fact_table.key1查哈希表并輸出數據;

  2. HASH GROUP BY:使用fact_table.key1建哈希表,寫入哈希表的過程中做聚合運算。

從執行效率的角度來看,這兩個操作可以合并成一個,使用dimension_table.key1建哈希表以及做聚合運算,使用fact_table.key1查哈希表以及做聚合運算因此節省了使用fact_table.key1建哈希表的時間。這種將HASH JOIN與HASH GROUP BY兩個算子合并成一個的操作,就是GroupJoin。

從執行效率的角度來看,將這兩個操作合并成一個操作,不僅可以減少一次建哈希表的操作,還可以減小中間結果大小。因為JOIN是一個可能使“結果集膨脹”的運算,一張表的一行可能會匹配上另一張表的多行,最壞情況下便是笛卡兒積:N行的表與M行的表JOIN的結果最大可能是N×M的結果集。因此在HASH JOIN+HASH GROUP BY的執行方式中,一張N行的哈希表可能會輸出N×M×S行結果(S代表selectivity,0≤S≤1),然后在HASH GROUP BY的grouping操作中再被聚合成一張新的哈希表,這會造成資源浪費。即使是上面例子中“事實表”(大表,大小為M)與“維度表”(小表,大小為N)的LEFT OUTER JOIN,且key1都是unique key,也是從一張N行的哈希表, 經過HASH JOIN輸出M行結果,然后聚合成M行的哈希表。相對而言,GroupJoin只需要在N行的哈希表中完成join&aggr運算,不僅中間結果變少了,同時內存占用也變小了。

基于以上考慮,PolarDB MySQL版PolarDB IMCI中增加了GroupJoin算子。

算法設計

概述

IMCI里的GroupJoin實現,是HashJoin與HashGroupby兩個算子的融合:

  1. 先使用左表(小表)建立哈希表,涉及左表的aggr函數會在建哈希表的時候直接運算掉。這個過程與對左表聚合(i.e., HashGroupby left_table)的操作是相同的。

  2. 使用右表(大表)查哈希表,查詢命中則在hash table entry上運算涉及右表的aggr函數,否則丟棄或者直接輸出。

以上介紹了IMCI GroupJoin算法的基本思路,下文會對算法進行詳細的描述以及介紹簡化的方法。

限制條件

出于實現的復雜度考慮,相對于理論上最完備的GroupJoin實現,PolarDB MySQL版做了如下幾點限制:

  1. group by key要完全匹配某一邊,且只能是某一邊的join key,雖然某些情況下join key的一部分,也能唯一定義這個key(i.e., functional dependency);

  2. RIGHT JOIN、GROUP BY RIGHT的場景,要求right keys是unique keys。否則可能會轉成LEFT JOIN、GROUP BY LEFT的方式,或者不使用GroupJoin;

  3. 任意一個aggr函數只能單獨引用左表,或者單獨引用右表;如果原GROUP BY算子中的aggr函數同時引用了左右兩個表(e.g., SUN(t1.a+t2.a)),則不適用GroupJoin。

算法

INNER JOIN/GROUP BY LEFT

此場景如下SQL所示:

l_table INNER JOIN r_table
ON l_table.key1 = r_table.key1
GROUP BY l_table.key1
說明

假設實際執行順序與SQL描述一樣,且Join過程中不會動態換邊。

  1. 使用左表建哈希表,并且創建哈希表的過程中直接運算涉及左表的aggr函數;涉及右表的aggr函數,對應設一個“repeat count”,這等同于一個hash table entry對應的payload的數量;

  2. 在join過程中,使用右表查哈希表,如果不匹配,則右表的行直接被丟棄;如果匹配,左表的aggr context的“repeat count”會增加1,右表的aggr函數直接進行運算;

  3. join完成后,只輸出曾經被匹配上的hash table entry的aggr結果,沒有被匹配上的hash table entry全部忽略;

  4. 輸出aggr結果時,要考慮“repeat count”,例如如果一個SUM(expr)的結果是200,“repeat count”是5,則最終結果是1000。

INNER JOIN/GROUP BY RIGHT

此場景如下SQL所示:

l_table INNER JOIN r_table
ON l_table.key1 = r_table.key1
GROUP BY r_table.key1

考慮到l_table.key1=r_table.key1,這種情況被歸到“INNER JOIN, GROUP BY LEFT”里。

LEFT OUTER JOIN/GROUP BY LEFT

此場景如下SQL所示:

l_table LEFT OUTER JOIN r_table
ON l_table.key1 = r_table.key1
GROUP BY l_table.key1
  1. 使用左表建哈希表,建哈希表的過程中運算左表的aggr函數;涉及右表的aggr函數,對應設一個“repeat count”;

  2. 在join過程中,使用右表查哈希表,如果不匹配,則右表的行直接被丟棄;如果匹配,左表的aggr context的“repeat count”會增加1,右表的aggr函數直接進行運算;

  3. 與INNER JOIN不同,此場景中join完成后,被匹配上的hash table entry的aggr結果直接輸出,沒有被匹配上的每個hash table entry單獨成為一個GROUP,對應的右表的aggr函數的輸入都是NULL。

LEFT OUTER JOIN/GROUP BY RIGHT

此場景如下SQL所示:

l_table LEFT OUTER JOIN r_table
ON l_table.key1 = r_table.key1
GROUP BY r_table.key1
  1. 使用左表建哈希表,建哈希表的過程中運算左表的aggr函數;涉及右表的aggr函數,對應設一個 “repeat count”;

  2. 在join過程中,使用右表查哈希表,如果不匹配,則右表的行直接被丟棄;如果匹配,左表的aggr context的“repeat count”會增加1,右表的aggr函數直接進行運算;

  3. 與其他場景不同,此場景中join完成后,被匹配上的hash table entry的aggr結果直接輸出,沒有被匹配上的所有hash table entry成為一個GROUP,對應的右表的aggr函數的輸入都是NULL。

RIGHT OUTER JOIN/GROUP BY LEFT

此場景如下SQL所示:

l_table RIGHT OUTER JOIN r_table
ON l_table.key1 = r_table.key1
GROUP BY l_table.key1
  1. 使用左表建哈希表,創建哈希表的過程中運算左表的aggr函數;涉及右表的aggr函數,對應設一個“repeat count”;

  2. 與其他場景不同,此場景在join過程中,使用右表查哈希表,如果匹配,左表的aggr context的“repeat count”會增加1,右表的aggr函數直接進行運算;如果不匹配,則右表的所有不匹配的行成為一個GROUP,對應的左表的aggr函數結果都是NULL;

  3. 與其他場景不同,此場景在join完成后,被匹配上的hash table entry的aggr結果直接輸出,沒有被匹配上的所有hash table entry全都忽略。

RIGHT OUTER JOIN/GROUP BY RIGHT

此場景如下SQL所示:

l_table RIGHT OUTER JOIN r_table
ON l_table.key1 = r_table.key1
GROUP BY r_table.key1

限制條件

要求r_table.key1必須是distinct的,否則這種join是不合法的;如果不能確定r_table.key1是distinct的,則需要在優化器里將這種join+groupby轉成LEFT OUTER JOIN、GROUP BY LEFT。

執行步驟

  1. 使用左表建哈希表,建哈希表的過程中運算左表的aggr函數;涉及右表的aggr函數,對應設一個“repeat count”;

  2. 與其他場景不同,此場景在join過程中,使用右表查哈希表,如果匹配,直接輸出左右表的aggr結果;如果不匹配,也輸出aggr結果,此時左表的aggr結果都是NULL;

  3. 與其他場景不同,此場景在join完成后,GroupJoin即完成,不需要處理任何hash table entry。

運行時落盤(spilling)處理

GroupJoin的落盤處理,類似于partition-style的HashJoin&HashGroupby的落盤處理,方法如下:

  1. GroupJoin的整體算法采用分區(partition)的方式;

  2. 使用左表構建哈希表時,內存中的partition,構建hash table的算法與算法一節描述一致;

  3. 使用左表構建哈希表時,不在內存中的partition,刷到磁盤中對應的臨時文件,后續新寫入這個partition 的數據也會直接刷到磁盤中對應的臨時文件;落盤的partition會建立一個bloomfilter,方便后續查找的時候快速過濾掉不可能匹配的右表數據;

  4. 完成左表的哈希表構建后,使用右表數據查哈希表:

    1. 在查哈希表的過程中,如果對應partition在內存中,則如算法中的處理方式一樣;如果對應的partition不在內存中,則先查bloomfilter,如果不命中bloomfilter則直接丟棄或者直接輸出,否則刷入該partition對應的臨時文件中。

    2. 內存中的partition完全處理完畢后,逐個處理磁盤中的partition,此時假設至少一個partition時能放入磁盤中的,不需要再切分一個partition;處理算法與算法中的處理方式一樣。

相關實現

2011年的一篇論文Accelerating Queries with Group-By and Join by Groupjoin(簡稱 paper_1)從理論角度闡述了GroupJoin在不同查詢計劃中的可行性,但是不涉及太多實現的細節。論文描述了GroupJoin運算時的約束,以及適用GroupJoin的場景,比如不同aggr函數如何處理等,描述得比較抽象,整體可讀性不是很好。

2021年有一篇論文A Practical Approach to Groupjoin and Nested Aggregates (簡稱 paper_2)描述了如何在(內存)數據庫中高效地實現GroupJoin算子。這篇論文的幾個重點是:

1. 解關聯子查詢的算法中使用GroupJoin

image.png

在解決“關聯項上方有 GROUP BY”這種關聯子查詢時,有一種方式是引入“MagicSet”操作(也就是 table distinct)并在上方增加一個JOIN+GROUP BY,從而完成子查詢的去關聯。這種模樣的執行計劃,恰好符合GroupJoin的適用場景。由于IMCI目前也使用了類似的解關聯算法,但是目前暫時不能生成此類shared children的執行計劃。

2. Eager aggregation

簡單來說,就是在hash build的過程中,把hash build那一邊的aggr函數直接計算掉,而不是為每個hash table entry保留完整的payload,最后再運算aggr函數。這與IMCI的實現思路是一樣的。

3. 使用memoizing的方式解決并發查哈希表做聚合運算時的沖突

舉個極端的例子:hash probe的過程中,所有數據都命中hash table的同一個entry,因此要在此entry進行聚合運算(比如SUM(2 * col)),因此需要使用同一個“aggr context運行aggr函數;例如,對于 SUM() 聚合函數來說,就是不斷使用同一個sum_value進行加法操作。即使是原子變量的加法操作,遇到contention也是有性能問題的,更何況是通用的聚合函數運算。這篇論文的思路是為所有“沒有搶到entry所有權”的線程建立各自的local hash table進行運算,對于每個entry,利用CAS指令設置一個owner thread id,除了owner thread外,后續到來的thread都使用自己的local hash table進行運算,最后再將這些local hash table合并到global hash table里。

4. 并不是所有場景都適用于GroupJoin

有些場景更適用于Join+Groupby。舉個例子,假設hash build一邊的selectivity很低,hash probe完成后,這一邊的大多數行都不會被選中,那么就會遇到了一個兩難的境地了:

  • 如果在hash build的時候,同時把這一邊的聚合運算做掉(i.e.,eager aggregation),則不需要保留hash table的payload,節省了內存,但是因為join的selectivity很低,大多數行最后都不會被選中,因此這些提前運算的agg運算都浪費掉了;

  • 如果不提前做aggr運算,則需要多花一些內存;但是在join的selectivity很高(大多數行都被選中)的情況下,這些內存本可以通過提前進行的aggr運算節省掉。

所以,如果join的selectivity很低,那么更好的方式或許是:join完后,得到非常少的group,然后在HashGroupby中做局部性很強的聚合操作。因此,這篇論文提出了幾種場景下不同的實現。而為了知道一個查詢適用于哪種場景,需要優化器提供selectivity和cardinality的估算,因此論文后面配套提供了一些優化器里用到的估算方法。

從實現者的角度看來,上面所說的“兩難的境地”其實并不是太大的問題,這是因為:

  • PolarDB IMCI幾乎總是用小的一邊(小表)建哈希表;

  • 即使JOIN的selectivity很低,使用eager aggregation提前運算聚合函數的策略,雖然浪費了針對一個小表的運算時間,但是無論如何也節省了內存,并不是一無所獲;這種情況下,相對于HashJoin+HashGroupby,也算是時間換空間。

在IMCI的實現里面,除了上文說的RIGHT JOIN+GROUP BY RIGHT場景,PolarDB IMCI幾乎總是認為GroupJoin的執行效率是優于HashJoin+HashGroupby。

從作者以及論文里面的測試情況來看,上述的兩篇論文應該都來自慕尼黑大學的hyper數據庫團隊。除hyper外,目前還沒有見到其他數據庫實現GroupJoin算子,但是應該有“shared hash table”操作的其他實現方式,后續再進一步討論。

GroupJoin在TPCH中的應用

TPCH是一個常用的測試一個AP系統的分析查詢能力的benchmark。在TPCH的22條查詢中,有不少都是適用GroupJoin算子的。不過,除了TPCH Q13,其他的查詢語句都需要經過一定改造才能適用GroupJoin算子。

Q13

TPCH Q13,可以直接適用GroupJoin算子:

select
    c_count,
    count(*) as custdist
from
    (
        select
            c_custkey,
            count(o_orderkey) as c_count
        from
            customer
            left outer join orders on c_custkey = o_custkey
            and o_comment not like '%pending%deposits%'
        group by
            c_custkey
    ) c_orders
group by
    c_count
order by
    custdist desc,
    c_count desc;
  • 在IMCI中,如果不使用GroupJoin,則執行計劃如下:

    image.png
  • 如果使用GroupJoin,執行計劃如下:

    image.png

Q3

對TPCH的Q3而言,GroupJoin的優化需要經過一系列等價變換:

select
    l_orderkey,
    sum(l_extendedprice * (1 - l_discount)) as revenue,
    o_orderdate,
    o_shippriority
from
    customer,
    orders,
    lineitem
where
    c_mktsegment = 'BUILDING'
    and c_custkey = o_custkey
    and l_orderkey = o_orderkey
    and o_orderdate < date '1995-03-15'
    and l_shipdate > date '1995-03-15'
group by
    l_orderkey,
    o_orderdate,
    o_shippriority
order by
    revenue desc,
    o_orderdate
limit
    10;

Q3的一種可行的執行計劃如下(IMCI中的執行計劃): DERKEY,TEMPTABLE

image.png

由于此SQL的grouping keys是l_orderkey、o_orderdate、o_shippriority,與任何一個join keys都不相同,因此并不能直接適用GroupJoin。通過一些等價推導,可得出以下結論:

  1. 由于lineitem與orders表的join predicate是l_orderkey=o_orderkey,而且是INNER JOIN,因此可以判斷出,這個join的結果集里面,l_orderkey=o_orderkey;

  2. 由于l_orderkey=o_orderkey,因此 GROUP BY l_orderkey、o_orderdate、o_shippriority 等價于 GROUP BY o_orderkey、o_orderdate、o_shippriority;

  3. 由于o_orderkey是orders表的PRIMARY KEY,因此每一個o_orderkey都能直接確定唯一的o_orderdate和o_shippriority (i.e.,o_orderdate and o_shippriority functionally depend on o_orderkey);

  4. 由于o_orderkey能唯一確定o_orderdate和o_shippriority,因此GROUP BY o_orderkey、o_orderdate、o_shippriority等價于GROUP BY o_orderkey;

由上面的推導,可以將Q3的group by clause等價變換成GROUP BY o_orderkey,如此可適用于GroupJoin了:KEY,TEMPTABLE3.SUM(LINETTEM.EXTENDEDPRTCE*1.00-LUNETEM._DLSCOL

image.png

這種“functional dependency”的推導,對優化器有一定要求。目前MySQL優化器中,實現了部分functional dependency的推導,但是依然無法推導出上面的GROUP BY o_orderkey變換。經過嘗試,發現SQL SERVER是可以推導出GROUP BY o_orderkey變換的,這方面有比較完備的理論,但是IMCI目前在這方面還沒有完全實現。在TPCH里面,Q3/Q4/Q10/Q13/Q18/Q20/Q21都有這種特征,如果能做這種等價推導,將可以縮短GROUP BY的grouping keys,提高聚合操作的速度。

Q10

TPCH的Q10也不能直接適用GroupJoin:

select
    c_custkey,
    c_name,
    sum(l_extendedprice * (1 - l_discount)) as revenue,
    c_acctbal,
    n_name,
    c_address,
    c_phone,
    c_comment
from
    customer,
    orders,
    lineitem,
    nation
where
    c_custkey = o_custkey
    and l_orderkey = o_orderkey
    and o_orderdate >= date '1993-10-01'
    and o_orderdate < date '1993-10-01' + interval '3' month
    and l_returnflag = 'R'
    and c_nationkey = n_nationkey
group by
    c_custkey,
    c_name,
    c_acctbal,
    c_phone,
    n_name,
    c_address,
    c_comment
order by
    revenue desc
limit
    20;

如果要使用GroupJoin,需要做以下兩個變換:

  1. 通過等價變換把grouping keys變成c_custkey(customer表的PRIMARY KEY),這個變換與上文的Q3類似;

  2. Join order要調整,使得customer表的JOIN在最外層。

其中1總是有益的,但是2中join order的調整,不一定是有益的。

Q17

TPCH的Q17包含一條關聯子查詢:

select
    sum(l_extendedprice) / 7.0 as avg_yearly
from
    lineitem,
    part
where
    p_partkey = l_partkey
    and p_brand = 'Brand#44'
    and p_container = 'WRAP PKG'
    and l_quantity < (
        select
            0.2 * avg(l_quantity)
        from
            lineitem
        where
            l_partkey = p_partkey
    );

其去關聯的方式有幾種,目前IMCI針對scalar aggr實現的兩種去關聯算法得到的執行計劃分別是:

image.pngRE

image.png

這些執行計劃都不適用GroupJoin算子。如果采用MagicSet算子的去關聯方式,在移除MagicSet算子之前,會得到一個適合GroupJoin的中間態:

image.png

也就是paper_2中所描述的過程: NERALNESTING:DEEORRELATIONOFDEPENDENTSUB-

image.png

因此可以適用GroupJoin。目前IMCI部分實現了采用MagicSet算子的去關聯方式,但是不會生成hared children的執行計劃,因此IMCI里面無法對TPCH Q17適用GroupJoin。

Q18

TPCH Q18也是可以適用GroupJoin的,不過依然要利用等價變換轉換執行計劃,才能得到適用GroupJoin的執行計劃。為了方便描述,不失一般性,此處把Q18里的IN子查詢以及最后的ORDER BY去掉:

select
    c_name,
    c_custkey,
    o_orderkey,
    o_orderdate,
    o_totalprice,
    sum(l_quantity)
from
    customer,
    orders,
    lineitem
where
    c_custkey = o_custkey
    and o_orderkey = l_orderkey
group by
    c_name,
    c_custkey,
    o_orderkey,
    o_orderdate,
    o_totalprice

對于這個查詢,做如下等價推導:

  1. 因為c_custkey是customer表的PRIMARY KEY,因此c_name可以由c_custkey唯一確定(functional dependency);同理o_orderkey是orders表的PRIMARY KEY,o_orderdate與o_totalprice都可以由o_orderkey唯一確定。因此,group by clause可以被等價轉換為GROUP BY c_custkey, o_orderkey;

  2. 由于customer表與orders表的join predicate是c_custkey=o_custkey,因此可以斷言,join的結果集中,c_custkey=o_custkey;

  3. 由于c_custkey=o_custkey,因此group by clause可以被等價轉換為GROUP BY o_custkey, o_orderkey;

  4. 由于o_orderkey唯一確定o_custkey (o_orderkey是orders表的主鍵),因此group by clause可以被等價改寫為GROUP BY o_orderkey。

經過以上等價推導,整個查詢可以被等價改成成類似如下一個SQL:

select
    ANY_VALUE(c_name),
    ANY_VALUE(c_custkey),
    o_orderkey,
    ANY_VALUE(o_orderdate),
    ANY_VALUE(o_totalprice),
    sum(l_quantity)
from
    customer,
    orders,
    lineitem
where
    c_custkey = o_custkey
    and o_orderkey = l_orderkey
group by
    o_orderkey
  • 不帶GroupJoin的執行計劃

    image.png
  • 帶GroupJoin的執行計劃

    image.png

上面的等價推導,因為能減少GROUP BY的grouping keys的長度,因此針對常規的執行計劃,也是有用的。

Q20

TPCH Q20的關聯子查詢的pattern與Q17是類似的:采用MagicSet算子的去關聯方式,在移除MagicSet算子之前,會得到一個適合GroupJoin的中間態。

select
...
and ps_availqty > (
    select
        0.5 * sum(l_quantity) < ! --- scalar aggr --->
    from
        lineitem
    where
        l_partkey = ps_partkey         < ! --- 關聯項 1 --->
        and l_suppkey = ps_suppkey     < ! --- 關聯項 2 --->
        and l_shipdate >= '1993-01-01'
        and l_shipdate < date_add('1993-01-01', interval '1' year)
)

其他

按論文paper_1和paper_2所述,Q5/Q9/Q16/Q21這4條SQL都適用GroupJoin算子,但是暫時還沒找到合適的轉換路徑;通過查詢hyper數據庫的執行計劃(https://hyper-db.de/interface.html#),它的優化器也沒有為這幾條SQL生成帶有GroupJoin的執行計劃。

相關實現

TPCH benchmark里面的許多query都是JOIN+GROUP BY的模式,因此TPCH里有不少的query都能通過GroupJoin優化掉。在論文paper_1里,作者列出Q3/Q5/Q9/Q10/Q13/Q16/Q17/Q20/Q21這些query在使用GroupJoin與不使用GroupJoin兩種情況下的性能:

image.png

此處使用的是TPCH 1 GB的數據量。可以看出GroupJoin對優化TPCH類型query有一定的作用(總體時延1932ms降到1295ms)。

在論文paper_2中,則是分別列出了Q3/Q13/Q17/Q18這些query在使用GroupJoin與不使用GroupJoin幾種情況下的性能(TPCH 10 GB數據量):

image.png

圖中的幾組線條含義如下:

  1. 圖中 “seperate”這一組線條代表“分別做JOIN和GROUP BY”,也就是不使用GroupJoin;

  2. 圖中“eager”代表上文所說的“eager aggregation”這一優化;

  3. 圖中“memoizing”代表上文所說的“如何處理并發查哈希表做aggr運算時的沖突”這一優化。 可以看到,在 Q3/Q13/Q17/Q18 這4條query中:

    1. "memoizing" 的方式幾乎總是與一般的HASH JOIN+HASH GROUP BY的方式有著類似的性能;

    2. eaegr aggregation的方式只在Q13這一條query中占有優勢,其余都不占優勢。

按照圖中的數據可得:不同的處理方法,在不同場景中差別很大。因此這個數據呼應了作者提出的 “GroupJoin 的執行方式,需要優化器提供更準確的統計信息,以選擇最優的執行方式”這一觀點,而不是無差別地選擇某一種GroupJoin算法,甚至無差別地選擇使用GroupJoin。

雖然如此,對于這個結論,PolarDB有不同的觀點:

  1. 文章中使用tuples per second這個指標來衡量算法的好壞,但是與PolarDB IMCI中得到的結論卻不太一樣。使用IMCI在并發度=32的情況下測試Q3/Q13/Q18這3條query的GroupJoin算子的throughput(單位tuples/s),結果如下:

    Query

    HashJoin+HashGroupby

    GroupJoin

    Q3

    130 MB

    152 MB

    Q13

    11 MB

    33 MB

    Q18

    315 MB

    1 GB

    說明

    Q17在IMCI里暫時無法使用GroupJoin。

    這個測試數據與上圖中的數據在量級上是相似的,但是每條query都稍有不同。也許是實現方式的不同,從PolarDB的測試數據中可以觀察到,除了上文說的right join+groupby right情況外,GroupJoin幾乎總是優于HashJoin+HashGroupby的。

  2. 對于上面3.a的結論,即“memoizing”的方式幾乎總是與一般的HASH JOIN+HASH GROUP BY的方式有著類似的性能,根據我們的觀察,TPCH的這幾條query只有非常少量的contention,因此memoizing的方式所用的local hash table等,在實際運行時基本不會用到,因此在這幾條query里面,這個算法得到的性能與HASH JOIN+HASH GROUP BY是類似的;論文里引用這幾條query的性能作為對比,其實不說明問題PolarDB是通過直接加鎖的方式來測試運行時的contention的。

總結

效果來講,因為GroupJoin在運行時能避免的重復的工作,因此在某些場景能得到比較大的性能提升。這個效果已經在實際應用中得到驗證。因此從結果的角度,GroupJoin是值得實現的。

從通用性來講,GroupJoin并不通用。GroupJoin只適用于equal join+group by且要求grouping keys與任意一邊join keys相同,而且對aggr函數、實現方式等有諸多限制;這是一種特化,而隨之而來的是比較大的實現和維護代價。從開發的角度來說,應該花更大力氣去優化“通用路徑”,利用通用路徑的性能提升來達到優化SQL查詢效率的目的,而不是通過為某個場景尋求定制性的解法。因此從這個角度來說,GroupJoin不是一個好方法。

因此在實現的時候,應該做一定的裁剪或簡化,不追求在一個特化實現里面實現最完備的功能,但是追求最常見場景的效用(性能)最大化。