This is an automated email from the ASF dual-hosted git repository.

englefly pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 056fffbc6b6 [opt](nereids) add cost penalty for pattern 
Aggregation-nestedLoopJoin (#51127)
056fffbc6b6 is described below

commit 056fffbc6b6cbdfb811ae28ca659c9c85921a2d5
Author: minghong <zhoumingh...@selectdb.com>
AuthorDate: Fri May 30 10:34:51 2025 +0800

    [opt](nereids) add cost penalty for pattern Aggregation-nestedLoopJoin 
(#51127)
    
    ### What problem does this PR solve?
    lets illustrate this pr by an example
    `select count() from bigTable join smallTable on big.B > small.A group
    by B, A`
    where A is distribute key, but B is not.
    
    before this pr, we choose join order : smallTable join bigTable.
    after this pr, we choose bigTable join smallTable.
    Join order "smallTable join bigTable" is friendly to aggregation but
    unfriendly to join. However, since the estimated number of rows for the
    join is very large, the benefits of aggregation outweigh the losses of
    the join in the estimation system. Nevertheless, incorrect join order
    may lead to catastrophic consequences in actual operation, so we do not
    choose this join order.
---
 .../org/apache/doris/nereids/cost/CostModelV1.java |  17 ++++-
 .../java/org/apache/doris/qe/SessionVariable.java  |   3 -
 regression-test/data/shape_check/others/nlj.out    | Bin 0 -> 481 bytes
 .../suites/shape_check/others/nlj.groovy           |  73 +++++++++++++++++++++
 4 files changed, 89 insertions(+), 4 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/cost/CostModelV1.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/cost/CostModelV1.java
index 09e4d7e6773..5a7023dc749 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/cost/CostModelV1.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/cost/CostModelV1.java
@@ -559,9 +559,24 @@ class CostModelV1 extends PlanVisitor<Cost, PlanContext> {
         Preconditions.checkState(context.arity() == 2);
         Statistics leftStatistics = context.getChildStatistics(0);
         Statistics rightStatistics = context.getChildStatistics(1);
+        /*
+         * nljPenalty:
+         * The row count estimation for nested loop join (NLJ) results often 
has significant errors.
+         * When the estimated row count is higher than the actual value, the 
cost benefits of subsequent
+         * operators (e.g., aggregation) may be overestimated. This can lead 
the optimizer to choose a
+         * plan where a small table joins a large table, severely impacting 
the overall SQL execution efficiency.
+         *
+         * For example, if the subsequent operator is an aggregation (AGG) and 
the GROUP BY key aligns with
+         * the distribution key of the small table, the optimizer might 
prioritize avoiding shuffling the NLJ
+         * results by choosing to join the small table to the large table, 
even if this is suboptimal.
+         */
+        double nljPenalty = 1.0;
+        if (leftStatistics.getRowCount() < 10 * rightStatistics.getRowCount()) 
{
+            nljPenalty = Math.min(leftStatistics.getRowCount(), 
rightStatistics.getRowCount());
+        }
         return CostV1.of(context.getSessionVariable(),
                 leftStatistics.getRowCount() * rightStatistics.getRowCount(),
-                rightStatistics.getRowCount(),
+                rightStatistics.getRowCount() * nljPenalty,
                 0);
     }
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java 
b/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
index 3858f7c636a..1846f325913 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
@@ -2663,12 +2663,9 @@ public class SessionVariable implements Serializable, 
Writable {
         this.enableLocalExchange = random.nextBoolean();
         this.enableSharedExchangeSinkBuffer = random.nextBoolean();
         this.useSerialExchange = random.nextBoolean();
-        // This will cause be dead loop, disable it first
-        // this.disableJoinReorder = random.nextBoolean();
         this.enableCommonExpPushDownForInvertedIndex = random.nextBoolean();
         this.disableStreamPreaggregations = random.nextBoolean();
         this.enableShareHashTableForBroadcastJoin = random.nextBoolean();
-        this.enableParallelResultSink = random.nextBoolean();
 
         // 4KB = 4 * 1024 bytes
         int minBytes = 4 * 1024;
diff --git a/regression-test/data/shape_check/others/nlj.out 
b/regression-test/data/shape_check/others/nlj.out
new file mode 100644
index 00000000000..7a4caa03911
Binary files /dev/null and b/regression-test/data/shape_check/others/nlj.out 
differ
diff --git a/regression-test/suites/shape_check/others/nlj.groovy 
b/regression-test/suites/shape_check/others/nlj.groovy
new file mode 100644
index 00000000000..19a7f25b3e1
--- /dev/null
+++ b/regression-test/suites/shape_check/others/nlj.groovy
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+suite("nlj") {
+    sql """
+    drop table if exists a;
+    create table a (
+        `id` int, 
+        a_date dateV2,
+        x int,
+        y int
+    )
+    DISTRIBUTED BY HASH(id) BUCKETS 8
+    PROPERTIES (
+    "replication_allocation" = "tag.location.default: 1"
+    );
+    alter table a modify column a_date set stats ('row_count'='1428418116', 
'ndv'='498', 'min_value'='2023-12-31', 'max_value'='2025-05-18', 
'avg_size'='4');
+
+
+    drop table if exists b;
+    create table b (
+        `id` int, 
+        b_date dateV2,
+    )
+    DISTRIBUTED BY HASH(b_date) BUCKETS 8
+    PROPERTIES (
+    "replication_allocation" = "tag.location.default: 1"
+    );
+
+
+    alter table b modify column b_date set stats ('row_count'='505', 
'ndv'='505', 'min_value'='2025-05-14', 'max_value'='2025-05-15', 
'avg_size'='4');
+
+    drop table if exists c;
+    create table c (
+        `id` int, 
+        c_date dateV2,
+    )
+    DISTRIBUTED BY HASH(id) BUCKETS 8
+    PROPERTIES (
+    "replication_allocation" = "tag.location.default: 1"
+    );
+
+    alter table c modify column c_date set stats ('row_count'='505', 
'ndv'='505', 'min_value'='2025-05-14', 'max_value'='2025-05-15', 
'avg_size'='4');
+    
+    set disable_nereids_rules='PRUNE_EMPTY_PARTITION';
+    set runtime_filter_mode=off;
+    """
+
+    // expected join order : a b
+    // order b-a is good for aggregation, but usually this order is extremely 
bad for join.
+    qt_shape """
+    explain shape plan
+    select b_date
+    from a join b on a_date>b_date
+    group by b_date, a_date, a.x
+    """
+}
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org
For additional commands, e-mail: commits-h...@doris.apache.org

Reply via email to