You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@doris.apache.org by yi...@apache.org on 2024/03/09 11:56:28 UTC
(doris) 03/07: [improvement](mtmv) Improve the performance for query rewritting by materialized view (#31886)
This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch branch-2.1
in repository https://gitbox.apache.org/repos/asf/doris.git
commit d5bf20c96ec31ae65b11c46efe38c18ed6317007
Author: seawinde <14...@users.noreply.github.com>
AuthorDate: Fri Mar 8 23:32:30 2024 +0800
[improvement](mtmv) Improve the performance for query rewritting by materialized view (#31886)
- Limit the number of times for the query rewritting to the group
- Remove the unnecessary log and explain detail info in query
---
.../query-async-materialized-view.md | 20 +--
.../query-async-materialized-view.md | 16 ++-
.../main/java/org/apache/doris/catalog/Column.java | 6 +-
.../org/apache/doris/nereids/NereidsPlanner.java | 2 +-
.../mv/AbstractMaterializedViewAggregateRule.java | 54 ++++----
.../mv/AbstractMaterializedViewJoinRule.java | 20 +--
.../mv/AbstractMaterializedViewRule.java | 146 ++++++++++++---------
.../mv/InitMaterializationContextHook.java | 2 +-
.../exploration/mv/MaterializationContext.java | 65 +++++----
.../java/org/apache/doris/qe/SessionVariable.java | 13 +-
.../agg_with_roll_up/aggregate_with_roll_up.groovy | 4 +-
.../mv/dimension/dimension_1.groovy | 8 +-
.../mv/dimension/dimension_2_4.groovy | 4 +-
13 files changed, 204 insertions(+), 156 deletions(-)
diff --git a/docs/en/docs/query-acceleration/async-materialized-view/query-async-materialized-view.md b/docs/en/docs/query-acceleration/async-materialized-view/query-async-materialized-view.md
index 0e29d3b415f..e6ff886bc5b 100644
--- a/docs/en/docs/query-acceleration/async-materialized-view/query-async-materialized-view.md
+++ b/docs/en/docs/query-acceleration/async-materialized-view/query-async-materialized-view.md
@@ -426,13 +426,14 @@ It will display a concise overview of the transparent rewriting process.
| Names: mv1
```
-**MaterializedViewRewriteFail**: Lists transparent rewrite failures and summarizes the reasons.
+**MaterializedViewRewriteSuccessAndChose**: Transparent rewrite succeeded, and the materialized view names list
+chosen by the CBO.
-**MaterializedViewRewriteSuccessButNotChose**: Transparent rewrite succeeded, but the final CBO did not choose the
+**MaterializedViewRewriteSuccessButNotChose**: Transparent rewrite succeeded, but the final CBO did not choose the
materialized view names list.
-**MaterializedViewRewriteSuccessAndChose**: Transparent rewrite succeeded, and the materialized view names list
-chosen by the CBO.
+**MaterializedViewRewriteFail**: Lists transparent rewrite failures and summarizes the reasons.
+
If you want to know the detailed information about materialized view candidates, rewriting, and the final selection process,
@@ -442,11 +443,12 @@ you can execute the following statement. It will provide a detailed breakdown of
## Relevant Environment Variables
-| Switch | Description |
-|---------------------------------------------------------------------------|----------------------------------------|
-| SET enable_nereids_planner = true; | Asynchronous materialized views are only supported under the new optimizer, so the new optimizer needs to be enabled. |
-| SET enable_materialized_view_rewrite = true; | Enable or disable query transparent rewriting, default is disabled |
-| SET materialized_view_rewrite_enable_contain_external_table = true; | Whether materialized views participating in transparent rewriting are allowed to contain external tables, default is not allowed |
+| Switch | Description |
+|---------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|
+| SET enable_nereids_planner = true; | Asynchronous materialized views are only supported under the new optimizer, so the new optimizer needs to be enabled. |
+| SET enable_materialized_view_rewrite = true; | Enable or disable query transparent rewriting, default is disabled |
+| SET materialized_view_rewrite_enable_contain_external_table = true; | Whether materialized views participating in transparent rewriting are allowed to contain external tables, default is not allowed |
+| SET materialized_view_rewrite_success_candidate_num = 3; | Transparently rewrites the successful result set, allowing the maximum number of CBO candidates to participate, the default is 3 |
## Limitations
diff --git a/docs/zh-CN/docs/query-acceleration/async-materialized-view/query-async-materialized-view.md b/docs/zh-CN/docs/query-acceleration/async-materialized-view/query-async-materialized-view.md
index ab6a7692c08..8e379153ffd 100644
--- a/docs/zh-CN/docs/query-acceleration/async-materialized-view/query-async-materialized-view.md
+++ b/docs/zh-CN/docs/query-acceleration/async-materialized-view/query-async-materialized-view.md
@@ -385,11 +385,12 @@ WHERE o_orderkey > 5 AND o_orderkey <= 10;
| MaterializedViewRewriteSuccessAndChose: |
| Names: mv1
```
-**MaterializedViewRewriteFail**:列举透明改写失败及原因摘要。
+**MaterializedViewRewriteSuccessAndChose**:透明改写成功,并且CBO选择的物化视图名称列表。
**MaterializedViewRewriteSuccessButNotChose**:透明改写成功,但是最终CBO没有选择的物化视图名称列表。
-**MaterializedViewRewriteSuccessAndChose**:透明改写成功,并且CBO选择的物化视图名称列表。
+**MaterializedViewRewriteFail**:列举透明改写失败及原因摘要。
+
如果想知道物化视图候选,改写和最终选择情况的过程详细信息,可以执行如下语句,会展示透明改写过程详细的信息。
@@ -397,11 +398,12 @@ WHERE o_orderkey > 5 AND o_orderkey <= 10;
## 相关环境变量
-| 开关 | 说明 |
-|------------------------------------------------------------------------|----------------------------------------|
-| SET enable_nereids_planner = true; | 异步物化视图只有在新优化器下才支持,所以需要开启新优化器 |
-| SET enable_materialized_view_rewrite = true; | 开启或者关闭查询透明改写,默认关闭 |
-| SET materialized_view_rewrite_enable_contain_external_table = true; | 参与透明改写的物化视图是否允许包含外表,默认不允许 |
+| 开关 | 说明 |
+|---------------------------------------------------------------------|-----------------------------------|
+| SET enable_nereids_planner = true; | 异步物化视图只有在新优化器下才支持,所以需要开启新优化器 |
+| SET enable_materialized_view_rewrite = true; | 开启或者关闭查询透明改写,默认关闭 |
+| SET materialized_view_rewrite_enable_contain_external_table = true; | 参与透明改写的物化视图是否允许包含外表,默认不允许 |
+| SET materialized_view_rewrite_success_candidate_num = 3; | 透明改写成功的结果集合,允许参与到CBO候选的最大数量,默认是3个 |
## 限制
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/Column.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/Column.java
index a51426745f9..920b21c0202 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/Column.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/Column.java
@@ -884,14 +884,14 @@ public class Column implements Writable, GsonPostProcessable {
&& Objects.equals(realDefaultValue, other.realDefaultValue)
&& clusterKeyId == other.clusterKeyId;
- if (!ok) {
- LOG.info("this column: name {} default value {} aggregationType {} isAggregationTypeImplicit {} "
+ if (!ok && LOG.isDebugEnabled()) {
+ LOG.debug("this column: name {} default value {} aggregationType {} isAggregationTypeImplicit {} "
+ "isKey {}, isAllowNull {}, datatype {}, strlen {}, precision {}, scale {}, visible {} "
+ "children {}, realDefaultValue {}, clusterKeyId {}",
name, getDefaultValue(), aggregationType, isAggregationTypeImplicit, isKey, isAllowNull,
getDataType(), getStrLen(), getPrecision(), getScale(), visible, children, realDefaultValue,
clusterKeyId);
- LOG.info("other column: name {} default value {} aggregationType {} isAggregationTypeImplicit {} "
+ LOG.debug("other column: name {} default value {} aggregationType {} isAggregationTypeImplicit {} "
+ "isKey {}, isAllowNull {}, datatype {}, strlen {}, precision {}, scale {}, visible {}, "
+ "children {}, realDefaultValue {}, clusterKeyId {}",
other.name, other.getDefaultValue(), other.aggregationType, other.isAggregationTypeImplicit,
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java
index 57048d345de..eedc77e9df7 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java
@@ -433,7 +433,7 @@ public class NereidsPlanner extends Planner {
+ "\n\n========== OPTIMIZED PLAN ==========\n"
+ optimizedPlan.treeString()
+ "\n\n========== MATERIALIZATIONS ==========\n"
- + MaterializationContext.toString(cascadesContext.getMaterializationContexts());
+ + MaterializationContext.toDetailString(cascadesContext.getMaterializationContexts());
break;
case ALL_PLAN:
plan = "========== PARSED PLAN "
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewAggregateRule.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewAggregateRule.java
index 24ecb41295b..995dbfb6b0c 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewAggregateRule.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewAggregateRule.java
@@ -103,16 +103,16 @@ public abstract class AbstractMaterializedViewAggregateRule extends AbstractMate
// get view and query aggregate and top plan correspondingly
Pair<Plan, LogicalAggregate<Plan>> viewTopPlanAndAggPair = splitToTopPlanAndAggregate(viewStructInfo);
if (viewTopPlanAndAggPair == null) {
- materializationContext.recordFailReason(queryStructInfo.getOriginalPlanId(),
- Pair.of("Split view to top plan and agg fail, view doesn't not contain aggregate",
- String.format("view plan = %s\n", viewStructInfo.getOriginalPlan().treeString())));
+ materializationContext.recordFailReason(queryStructInfo,
+ "Split view to top plan and agg fail, view doesn't not contain aggregate",
+ () -> String.format("view plan = %s\n", viewStructInfo.getOriginalPlan().treeString()));
return null;
}
Pair<Plan, LogicalAggregate<Plan>> queryTopPlanAndAggPair = splitToTopPlanAndAggregate(queryStructInfo);
if (queryTopPlanAndAggPair == null) {
- materializationContext.recordFailReason(queryStructInfo.getOriginalPlanId(),
- Pair.of("Split query to top plan and agg fail",
- String.format("query plan = %s\n", queryStructInfo.getOriginalPlan().treeString())));
+ materializationContext.recordFailReason(queryStructInfo,
+ "Split query to top plan and agg fail",
+ () -> String.format("query plan = %s\n", queryStructInfo.getOriginalPlan().treeString()));
return null;
}
// Firstly,if group by expression between query and view is equals, try to rewrite expression directly
@@ -131,13 +131,13 @@ public abstract class AbstractMaterializedViewAggregateRule extends AbstractMate
}
// if fails, record the reason and then try to roll up aggregate function
- materializationContext.recordFailReason(queryStructInfo.getOriginalPlanId(),
- Pair.of("Can not rewrite expression when no roll up",
- String.format("expressionToWrite = %s,\n mvExprToMvScanExprMapping = %s,\n"
- + "viewToQuerySlotMapping = %s",
- queryTopPlan.getOutput(),
- materializationContext.getMvExprToMvScanExprMapping(),
- viewToQuerySlotMapping)));
+ materializationContext.recordFailReason(queryStructInfo,
+ "Can not rewrite expression when no roll up",
+ () -> String.format("expressionToWrite = %s,\n mvExprToMvScanExprMapping = %s,\n"
+ + "viewToQuerySlotMapping = %s",
+ queryTopPlan.getOutput(),
+ materializationContext.getMvExprToMvScanExprMapping(),
+ viewToQuerySlotMapping));
}
// if view is scalar aggregate but query is not. Or if query is scalar aggregate but view is not
// Should not rewrite
@@ -145,12 +145,12 @@ public abstract class AbstractMaterializedViewAggregateRule extends AbstractMate
List<Expression> viewGroupByExpressions = viewTopPlanAndAggPair.value().getGroupByExpressions();
if ((queryGroupByExpressions.isEmpty() && !viewGroupByExpressions.isEmpty())
|| (!queryGroupByExpressions.isEmpty() && viewGroupByExpressions.isEmpty())) {
- materializationContext.recordFailReason(queryStructInfo.getOriginalPlanId(),
- Pair.of("only one the of query or view is scalar aggregate and "
- + "can not rewrite expression meanwhile",
- String.format("query aggregate = %s,\n view aggregate = %s,\n",
- queryTopPlanAndAggPair.value().treeString(),
- viewTopPlanAndAggPair.value().treeString())));
+ materializationContext.recordFailReason(queryStructInfo,
+ "only one the of query or view is scalar aggregate and "
+ + "can not rewrite expression meanwhile",
+ () -> String.format("query aggregate = %s,\n view aggregate = %s,\n",
+ queryTopPlanAndAggPair.value().treeString(),
+ viewTopPlanAndAggPair.value().treeString()));
return null;
}
// try to roll up.
@@ -178,10 +178,10 @@ public abstract class AbstractMaterializedViewAggregateRule extends AbstractMate
Expression rollupedExpression = queryFunctionShuttled.accept(AGGREGATE_EXPRESSION_REWRITER,
context);
if (!context.isValid()) {
- materializationContext.recordFailReason(queryStructInfo.getOriginalPlanId(),
- Pair.of("Query function roll up fail",
- String.format("queryFunctionShuttled = %s,\n mvExprToMvScanExprQueryBased = %s",
- queryFunctionShuttled, mvExprToMvScanExprQueryBased)));
+ materializationContext.recordFailReason(queryStructInfo,
+ "Query function roll up fail",
+ () -> String.format("queryFunctionShuttled = %s,\n mvExprToMvScanExprQueryBased = %s",
+ queryFunctionShuttled, mvExprToMvScanExprQueryBased));
return null;
}
finalOutputExpressions.add(new Alias(rollupedExpression));
@@ -196,10 +196,10 @@ public abstract class AbstractMaterializedViewAggregateRule extends AbstractMate
context);
if (!context.isValid()) {
// group expr can not rewrite by view
- materializationContext.recordFailReason(queryStructInfo.getOriginalPlanId(),
- Pair.of("View dimensions doesn't not cover the query dimensions",
- String.format("mvExprToMvScanExprQueryBased is %s,\n queryGroupShuttledExpr is %s",
- mvExprToMvScanExprQueryBased, queryGroupShuttledExpr)));
+ materializationContext.recordFailReason(queryStructInfo,
+ "View dimensions doesn't not cover the query dimensions",
+ () -> String.format("mvExprToMvScanExprQueryBased is %s,\n queryGroupShuttledExpr is %s",
+ mvExprToMvScanExprQueryBased, queryGroupShuttledExpr));
return null;
}
NamedExpression groupByExpression = rewrittenGroupByExpression instanceof NamedExpression
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewJoinRule.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewJoinRule.java
index dba0afb4925..df58345405a 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewJoinRule.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewJoinRule.java
@@ -17,7 +17,6 @@
package org.apache.doris.nereids.rules.exploration.mv;
-import org.apache.doris.common.Pair;
import org.apache.doris.nereids.rules.exploration.mv.StructInfo.PlanCheckContext;
import org.apache.doris.nereids.rules.exploration.mv.mapping.SlotMapping;
import org.apache.doris.nereids.trees.expressions.Alias;
@@ -52,21 +51,14 @@ public abstract class AbstractMaterializedViewJoinRule extends AbstractMateriali
);
// Can not rewrite, bail out
if (expressionsRewritten.isEmpty()) {
- materializationContext.recordFailReason(queryStructInfo.getOriginalPlanId(),
- Pair.of("Rewrite expressions by view in join fail",
- String.format("expressionToRewritten is %s,\n mvExprToMvScanExprMapping is %s,\n"
- + "targetToSourceMapping = %s",
- queryStructInfo.getExpressions(),
- materializationContext.getMvExprToMvScanExprMapping(),
- targetToSourceMapping)));
+ materializationContext.recordFailReason(queryStructInfo,
+ "Rewrite expressions by view in join fail",
+ () -> String.format("expressionToRewritten is %s,\n mvExprToMvScanExprMapping is %s,\n"
+ + "targetToSourceMapping = %s", queryStructInfo.getExpressions(),
+ materializationContext.getMvExprToMvScanExprMapping(),
+ targetToSourceMapping));
return null;
}
- // record the group id in materializationContext, and when rewrite again in
- // the same group, bail out quickly.
- if (queryStructInfo.getOriginalPlan().getGroupExpression().isPresent()) {
- materializationContext.addMatchedGroup(
- queryStructInfo.getOriginalPlan().getGroupExpression().get().getOwnerGroup().getGroupId());
- }
return new LogicalProject<>(
expressionsRewritten.stream()
.map(expression -> expression instanceof NamedExpression ? expression : new Alias(expression))
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewRule.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewRule.java
index e001359574e..32e90a67efa 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewRule.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewRule.java
@@ -22,13 +22,11 @@ import org.apache.doris.catalog.Partition;
import org.apache.doris.catalog.PartitionInfo;
import org.apache.doris.catalog.PartitionType;
import org.apache.doris.catalog.TableIf;
-import org.apache.doris.common.Pair;
import org.apache.doris.mtmv.BaseTableInfo;
import org.apache.doris.mtmv.MTMVPartitionInfo;
import org.apache.doris.mtmv.MTMVRewriteUtil;
import org.apache.doris.nereids.CascadesContext;
import org.apache.doris.nereids.jobs.executor.Rewriter;
-import org.apache.doris.nereids.memo.GroupExpression;
import org.apache.doris.nereids.rules.exploration.ExplorationRuleFactory;
import org.apache.doris.nereids.rules.exploration.mv.Predicates.SplitPredicate;
import org.apache.doris.nereids.rules.exploration.mv.mapping.ExpressionMapping;
@@ -45,7 +43,6 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.Nullable;
import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral;
import org.apache.doris.nereids.trees.expressions.literal.Literal;
import org.apache.doris.nereids.trees.plans.JoinType;
-import org.apache.doris.nereids.trees.plans.ObjectId;
import org.apache.doris.nereids.trees.plans.Plan;
import org.apache.doris.nereids.trees.plans.algebra.CatalogRelation;
import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
@@ -94,10 +91,13 @@ public abstract class AbstractMaterializedViewRule implements ExplorationRuleFac
// TODO Just support only one query struct info, support multi later.
StructInfo queryStructInfo = queryStructInfos.get(0);
try {
- rewrittenPlans.addAll(doRewrite(queryStructInfo, cascadesContext, context));
+ if (rewrittenPlans.size() < cascadesContext.getConnectContext()
+ .getSessionVariable().getMaterializedViewRewriteSuccessCandidateNum()) {
+ rewrittenPlans.addAll(doRewrite(queryStructInfo, cascadesContext, context));
+ }
} catch (Exception exception) {
- context.recordFailReason(queryStructInfo.getOriginalPlanId(),
- Pair.of("Materialized view rule exec fail", exception.toString()));
+ context.recordFailReason(queryStructInfo,
+ "Materialized view rule exec fail", exception::toString);
}
}
return rewrittenPlans;
@@ -117,10 +117,9 @@ public abstract class AbstractMaterializedViewRule implements ExplorationRuleFac
StructInfo queryStructInfo = queryStructInfos.get(0);
if (!checkPattern(queryStructInfo)) {
cascadesContext.getMaterializationContexts().forEach(ctx ->
- ctx.recordFailReason(queryStructInfo.getOriginalPlanId(),
- Pair.of("Query struct info is invalid",
- String.format("queryPlan is %s", queryPlan.treeString())))
- );
+ ctx.recordFailReason(queryStructInfo, "Query struct info is invalid",
+ () -> String.format("queryPlan is %s", queryPlan.treeString())
+ ));
return validQueryStructInfos;
}
validQueryStructInfos.add(queryStructInfo);
@@ -138,64 +137,70 @@ public abstract class AbstractMaterializedViewRule implements ExplorationRuleFac
materializationContext.getMvPlan(), cascadesContext);
if (viewStructInfos.size() > 1) {
// view struct info should only have one
- materializationContext.recordFailReason(queryStructInfo.getOriginalPlanId(),
- Pair.of("The num of view struct info is more then one",
- String.format("mv plan is %s", materializationContext.getMvPlan().treeString())));
+ materializationContext.recordFailReason(queryStructInfo,
+ "The num of view struct info is more then one",
+ () -> String.format("mv plan is %s", materializationContext.getMvPlan().treeString()));
return rewriteResults;
}
StructInfo viewStructInfo = viewStructInfos.get(0);
if (!checkPattern(viewStructInfo)) {
- materializationContext.recordFailReason(queryStructInfo.getOriginalPlanId(),
- Pair.of("View struct info is invalid",
- String.format(", view plan is %s", viewStructInfo.getOriginalPlan().treeString())));
+ materializationContext.recordFailReason(queryStructInfo,
+ "View struct info is invalid",
+ () -> String.format(", view plan is %s", viewStructInfo.getOriginalPlan().treeString()));
return rewriteResults;
}
MatchMode matchMode = decideMatchMode(queryStructInfo.getRelations(), viewStructInfo.getRelations());
if (MatchMode.COMPLETE != matchMode) {
- materializationContext.recordFailReason(queryStructInfo.getOriginalPlanId(),
- Pair.of("Match mode is invalid", String.format("matchMode is %s", matchMode)));
+ materializationContext.recordFailReason(queryStructInfo, "Match mode is invalid",
+ () -> String.format("matchMode is %s", matchMode));
return rewriteResults;
}
List<RelationMapping> queryToViewTableMappings = RelationMapping.generate(queryStructInfo.getRelations(),
viewStructInfo.getRelations());
// if any relation in query and view can not map, bail out.
if (queryToViewTableMappings == null) {
- materializationContext.recordFailReason(queryStructInfo.getOriginalPlanId(),
- Pair.of("Query to view table mapping is null", ""));
+ materializationContext.recordFailReason(queryStructInfo,
+ "Query to view table mapping is null", () -> "");
return rewriteResults;
}
for (RelationMapping queryToViewTableMapping : queryToViewTableMappings) {
SlotMapping queryToViewSlotMapping = SlotMapping.generate(queryToViewTableMapping);
if (queryToViewSlotMapping == null) {
- materializationContext.recordFailReason(queryStructInfo.getOriginalPlanId(),
- Pair.of("Query to view slot mapping is null", ""));
+ materializationContext.recordFailReason(queryStructInfo,
+ "Query to view slot mapping is null", () -> "");
continue;
}
SlotMapping viewToQuerySlotMapping = queryToViewSlotMapping.inverse();
+ // check the column used in query is in mv or not
+ if (!checkColumnUsedValid(queryStructInfo, viewStructInfo, queryToViewSlotMapping)) {
+ materializationContext.recordFailReason(queryStructInfo,
+ "The columns used by query are not in view",
+ () -> String.format("query struct info is %s, view struct info is %s",
+ queryStructInfo.getTopPlan().treeString(),
+ viewStructInfo.getTopPlan().treeString()));
+ continue;
+ }
LogicalCompatibilityContext compatibilityContext = LogicalCompatibilityContext.from(
queryToViewTableMapping, queryToViewSlotMapping, queryStructInfo, viewStructInfo);
ComparisonResult comparisonResult = StructInfo.isGraphLogicalEquals(queryStructInfo, viewStructInfo,
compatibilityContext);
if (comparisonResult.isInvalid()) {
- materializationContext.recordFailReason(queryStructInfo.getOriginalPlanId(),
- Pair.of("The graph logic between query and view is not consistent",
- comparisonResult.getErrorMessage()));
+ materializationContext.recordFailReason(queryStructInfo,
+ "The graph logic between query and view is not consistent",
+ comparisonResult::getErrorMessage);
continue;
}
SplitPredicate compensatePredicates = predicatesCompensate(queryStructInfo, viewStructInfo,
viewToQuerySlotMapping, comparisonResult, cascadesContext);
// Can not compensate, bail out
if (compensatePredicates.isInvalid()) {
- materializationContext.recordFailReason(queryStructInfo.getOriginalPlanId(),
- Pair.of("Predicate compensate fail",
- String.format("query predicates = %s,\n query equivalenceClass = %s, \n"
- + "view predicates = %s,\n query equivalenceClass = %s\n"
- + "comparisonResult = %s ",
- queryStructInfo.getPredicates(),
- queryStructInfo.getEquivalenceClass(),
- viewStructInfo.getPredicates(),
- viewStructInfo.getEquivalenceClass(),
- comparisonResult)));
+ materializationContext.recordFailReason(queryStructInfo,
+ "Predicate compensate fail",
+ () -> String.format("query predicates = %s,\n query equivalenceClass = %s, \n"
+ + "view predicates = %s,\n query equivalenceClass = %s\n"
+ + "comparisonResult = %s ", queryStructInfo.getPredicates(),
+ queryStructInfo.getEquivalenceClass(), viewStructInfo.getPredicates(),
+ viewStructInfo.getEquivalenceClass(), comparisonResult));
continue;
}
Plan rewrittenPlan;
@@ -209,13 +214,12 @@ public abstract class AbstractMaterializedViewRule implements ExplorationRuleFac
originalPlan, materializationContext.getMvExprToMvScanExprMapping(),
viewToQuerySlotMapping, true);
if (rewriteCompensatePredicates.isEmpty()) {
- materializationContext.recordFailReason(queryStructInfo.getOriginalPlanId(),
- Pair.of("Rewrite compensate predicate by view fail", String.format(
- "compensatePredicates = %s,\n mvExprToMvScanExprMapping = %s,\n"
+ materializationContext.recordFailReason(queryStructInfo,
+ "Rewrite compensate predicate by view fail",
+ () -> String.format("compensatePredicates = %s,\n mvExprToMvScanExprMapping = %s,\n"
+ "viewToQuerySlotMapping = %s",
- compensatePredicates,
- materializationContext.getMvExprToMvScanExprMapping(),
- viewToQuerySlotMapping)));
+ compensatePredicates, materializationContext.getMvExprToMvScanExprMapping(),
+ viewToQuerySlotMapping));
continue;
}
rewrittenPlan = new LogicalFilter<>(Sets.newHashSet(rewriteCompensatePredicates), mvScan);
@@ -226,37 +230,55 @@ public abstract class AbstractMaterializedViewRule implements ExplorationRuleFac
if (rewrittenPlan == null) {
continue;
}
- rewrittenPlan = rewriteByRules(cascadesContext, rewrittenPlan, originalPlan);
- if (!isOutputValid(originalPlan, rewrittenPlan)) {
- ObjectId planObjId = originalPlan.getGroupExpression().map(GroupExpression::getId)
- .orElseGet(() -> new ObjectId(-1));
- materializationContext.recordFailReason(planObjId, Pair.of(
+ final Plan finalRewrittenPlan = rewriteByRules(cascadesContext, rewrittenPlan, originalPlan);
+ if (!isOutputValid(originalPlan, finalRewrittenPlan)) {
+ materializationContext.recordFailReason(queryStructInfo,
"RewrittenPlan output logical properties is different with target group",
- String.format("planOutput logical properties = %s,\n"
- + "groupOutput logical properties = %s", rewrittenPlan.getLogicalProperties(),
- originalPlan.getLogicalProperties())));
+ () -> String.format("planOutput logical"
+ + " properties = %s,\n groupOutput logical properties = %s",
+ finalRewrittenPlan.getLogicalProperties(), originalPlan.getLogicalProperties()));
continue;
}
// check the partitions used by rewritten plan is valid or not
Set<Long> invalidPartitionsQueryUsed =
- calcInvalidPartitions(rewrittenPlan, materializationContext, cascadesContext);
+ calcInvalidPartitions(finalRewrittenPlan, materializationContext, cascadesContext);
if (!invalidPartitionsQueryUsed.isEmpty()) {
- materializationContext.recordFailReason(queryStructInfo.getOriginalPlanId(),
- Pair.of("Check partition query used validation fail",
- String.format("the partition used by query is invalid by materialized view,"
- + "invalid partition info query used is %s",
- materializationContext.getMTMV().getPartitions().stream()
- .filter(partition ->
- invalidPartitionsQueryUsed.contains(partition.getId()))
- .collect(Collectors.toSet()))));
+ materializationContext.recordFailReason(queryStructInfo,
+ "Check partition query used validation fail",
+ () -> String.format("the partition used by query is invalid by materialized view,"
+ + "invalid partition info query used is %s",
+ materializationContext.getMTMV().getPartitions().stream()
+ .filter(partition ->
+ invalidPartitionsQueryUsed.contains(partition.getId()))
+ .collect(Collectors.toSet())));
continue;
}
recordIfRewritten(originalPlan, materializationContext);
- rewriteResults.add(rewrittenPlan);
+ rewriteResults.add(finalRewrittenPlan);
}
return rewriteResults;
}
+ /**
+ * Check the column used by query is in materialized view output or not
+ */
+ protected boolean checkColumnUsedValid(StructInfo queryInfo, StructInfo mvInfo,
+ SlotMapping queryToViewSlotMapping) {
+ Set<ExprId> queryUsedSlotSetViewBased = ExpressionUtils.shuttleExpressionWithLineage(
+ queryInfo.getTopPlan().getOutput(), queryInfo.getTopPlan()).stream()
+ .flatMap(expr -> ExpressionUtils.replace(expr, queryToViewSlotMapping.toSlotReferenceMap())
+ .collectToSet(each -> each instanceof Slot).stream())
+ .map(each -> ((Slot) each).getExprId())
+ .collect(Collectors.toSet());
+
+ Set<ExprId> viewUsedSlotSet = ExpressionUtils.shuttleExpressionWithLineage(mvInfo.getTopPlan().getOutput(),
+ mvInfo.getTopPlan()).stream()
+ .flatMap(expr -> expr.collectToSet(each -> each instanceof Slot).stream())
+ .map(each -> ((Slot) each).getExprId())
+ .collect(Collectors.toSet());
+ return viewUsedSlotSet.containsAll(queryUsedSlotSetViewBased);
+ }
+
/**
* Rewrite by rules and try to make output is the same after optimize by rules
*/
@@ -324,7 +346,7 @@ public abstract class AbstractMaterializedViewRule implements ExplorationRuleFac
}
// get mv valid partitions
Set<Long> mvDataValidPartitionIdSet = MTMVRewriteUtil.getMTMVCanRewritePartitions(mtmv,
- cascadesContext.getConnectContext(), System.currentTimeMillis()).stream()
+ cascadesContext.getConnectContext(), System.currentTimeMillis()).stream()
.map(Partition::getId)
.collect(Collectors.toSet());
Set<Long> queryUsedPartitionIdSet = rewrittenPlan.collectToList(node -> node instanceof LogicalOlapScan
@@ -521,8 +543,8 @@ public abstract class AbstractMaterializedViewRule implements ExplorationRuleFac
.collect(Collectors.toSet());
// query pulledUp predicates should have null reject predicates and contains any require noNullable slot
return !queryPulledUpPredicates.containsAll(nullRejectPredicates)
- && requireNoNullableViewSlot.stream().noneMatch(
- set -> Sets.intersection(set, queryUsedNeedRejectNullSlotsViewBased).isEmpty());
+ && requireNoNullableViewSlot.stream().noneMatch(set ->
+ Sets.intersection(set, queryUsedNeedRejectNullSlotsViewBased).isEmpty());
}
/**
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/InitMaterializationContextHook.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/InitMaterializationContextHook.java
index ab8f4d69d45..8046639d1ba 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/InitMaterializationContextHook.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/InitMaterializationContextHook.java
@@ -95,7 +95,7 @@ public class InitMaterializationContextHook implements PlannerHook {
// todo should force keep consistency to mv sql plan output
Plan projectScan = new LogicalProject<Plan>(mvProjects, mvScan);
cascadesContext.addMaterializationContext(
- MaterializationContext.fromMaterializedView(materializedView, projectScan));
+ MaterializationContext.fromMaterializedView(materializedView, projectScan, cascadesContext));
});
}
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializationContext.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializationContext.java
index e64e1290954..b3de7443652 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializationContext.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializationContext.java
@@ -17,15 +17,18 @@
package org.apache.doris.nereids.rules.exploration.mv;
+import org.apache.doris.analysis.StatementBase;
import org.apache.doris.catalog.MTMV;
import org.apache.doris.catalog.Table;
import org.apache.doris.common.AnalysisException;
import org.apache.doris.common.Pair;
import org.apache.doris.mtmv.MTMVCache;
+import org.apache.doris.nereids.CascadesContext;
import org.apache.doris.nereids.memo.GroupId;
import org.apache.doris.nereids.rules.exploration.mv.mapping.ExpressionMapping;
import org.apache.doris.nereids.trees.plans.ObjectId;
import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.commands.ExplainCommand.ExplainLevel;
import org.apache.doris.nereids.util.ExpressionUtils;
import org.apache.doris.nereids.util.Utils;
@@ -38,6 +41,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
@@ -64,16 +68,20 @@ public class MaterializationContext {
// if rewrite by mv fail, record the reason, if success the failReason should be empty.
// The key is the query belonged group expression objectId, the value is the fail reason
private final Map<ObjectId, Pair<String, String>> failReason = new HashMap<>();
+ private boolean enableRecordFailureDetail = false;
/**
* MaterializationContext, this contains necessary info for query rewriting by mv
*/
- public MaterializationContext(MTMV mtmv, Plan mvScanPlan, List<Table> baseTables, List<Table> baseViews) {
+ public MaterializationContext(MTMV mtmv, Plan mvScanPlan, List<Table> baseTables, List<Table> baseViews,
+ CascadesContext cascadesContext) {
this.mtmv = mtmv;
this.mvScanPlan = mvScanPlan;
this.baseTables = baseTables;
this.baseViews = baseViews;
-
+ StatementBase parsedStatement = cascadesContext.getStatementContext().getParsedStatement();
+ this.enableRecordFailureDetail = parsedStatement != null && parsedStatement.isExplain()
+ && ExplainLevel.MEMO_PLAN == parsedStatement.getExplainOptions().getExplainLevel();
MTMVCache mtmvCache = null;
try {
mtmvCache = mtmv.getOrGenerateCache();
@@ -94,10 +102,6 @@ public class MaterializationContext {
this.mvPlan = mtmvCache.getLogicalPlan();
}
- public Set<GroupId> getMatchedGroups() {
- return matchedGroups;
- }
-
public boolean alreadyRewrite(GroupId groupId) {
return this.matchedGroups.contains(groupId);
}
@@ -138,6 +142,10 @@ public class MaterializationContext {
return failReason;
}
+ public boolean isEnableRecordFailureDetail() {
+ return enableRecordFailureDetail;
+ }
+
public void setSuccess(boolean success) {
this.success = success;
this.failReason.clear();
@@ -146,13 +154,18 @@ public class MaterializationContext {
/**
* recordFailReason
*/
- public void recordFailReason(ObjectId objectId, Pair<String, String> summaryAndReason) {
+ public void recordFailReason(StructInfo structInfo, String summary, Supplier<String> failureReasonSupplier) {
+ // record it's rewritten
+ if (structInfo.getTopPlan().getGroupExpression().isPresent()) {
+ this.addMatchedGroup(structInfo.getTopPlan().getGroupExpression().get().getOwnerGroup().getGroupId());
+ }
// once success, do not record the fail reason
if (this.success) {
return;
}
this.success = false;
- this.failReason.put(objectId, summaryAndReason);
+ this.failReason.put(structInfo.getOriginalPlanId(),
+ Pair.of(summary, this.isEnableRecordFailureDetail() ? failureReasonSupplier.get() : ""));
}
public boolean isSuccess() {
@@ -178,7 +191,7 @@ public class MaterializationContext {
/**
* toString, this contains summary and detail info.
*/
- public static String toString(List<MaterializationContext> materializationContexts) {
+ public static String toDetailString(List<MaterializationContext> materializationContexts) {
StringBuilder builder = new StringBuilder();
builder.append("materializationContexts:").append("\n");
for (MaterializationContext ctx : materializationContexts) {
@@ -200,6 +213,22 @@ public class MaterializationContext {
.collect(Collectors.toSet());
StringBuilder builder = new StringBuilder();
builder.append("\nMaterializedView");
+ // rewrite success and chosen
+ builder.append("\nMaterializedViewRewriteSuccessAndChose:\n");
+ if (!materializationChosenNameSet.isEmpty()) {
+ builder.append(" Names: ").append(String.join(", ", materializationChosenNameSet));
+ }
+ // rewrite success but not chosen
+ builder.append("\nMaterializedViewRewriteSuccessButNotChose:\n");
+ Set<String> rewriteSuccessButNotChoseNameSet = materializationContexts.stream()
+ .filter(materializationContext -> materializationContext.isSuccess()
+ && !materializationChosenNameSet.contains(materializationContext.getMTMV().getName()))
+ .map(materializationContext -> materializationContext.getMTMV().getName())
+ .collect(Collectors.toSet());
+ if (!rewriteSuccessButNotChoseNameSet.isEmpty()) {
+ builder.append(" Names: ").append(String.join(", ", rewriteSuccessButNotChoseNameSet));
+ }
+ // rewrite fail
builder.append("\nMaterializedViewRewriteFail:");
for (MaterializationContext ctx : materializationContexts) {
if (!ctx.isSuccess()) {
@@ -211,25 +240,15 @@ public class MaterializationContext {
.append(" FailSummary: ").append(String.join(", ", failReasonSet));
}
}
- builder.append("\nMaterializedViewRewriteSuccessButNotChose:\n");
- builder.append(" Names: ").append(materializationContexts.stream()
- .filter(materializationContext -> materializationContext.isSuccess()
- && !materializationChosenNameSet.contains(materializationContext.getMTMV().getName()))
- .map(materializationContext -> materializationContext.getMTMV().getName())
- .collect(Collectors.joining(", ")));
- builder.append("\nMaterializedViewRewriteSuccessAndChose:\n");
- builder.append(" Names: ").append(String.join(", ", materializationChosenNameSet));
return builder.toString();
}
/**
* MaterializationContext fromMaterializedView
*/
- public static MaterializationContext fromMaterializedView(MTMV materializedView, Plan mvScanPlan) {
- return new MaterializationContext(
- materializedView,
- mvScanPlan,
- ImmutableList.of(),
- ImmutableList.of());
+ public static MaterializationContext fromMaterializedView(MTMV materializedView, Plan mvScanPlan,
+ CascadesContext cascadesContext) {
+ return new MaterializationContext(materializedView, mvScanPlan, ImmutableList.of(), ImmutableList.of(),
+ cascadesContext);
}
}
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 f36e831a5c4..634904202ac 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
@@ -498,6 +498,9 @@ public class SessionVariable implements Serializable, Writable {
public static final String MATERIALIZED_VIEW_REWRITE_ENABLE_CONTAIN_EXTERNAL_TABLE
= "materialized_view_rewrite_enable_contain_external_table";
+ public static final String MATERIALIZED_VIEW_REWRITE_SUCCESS_CANDIDATE_NUM
+ = "materialized_view_rewrite_success_candidate_num";
+
public static final String CREATE_TABLE_PARTITION_MAX_NUM
= "create_table_partition_max_num";
@@ -1582,9 +1585,13 @@ public class SessionVariable implements Serializable, Writable {
@VariableMgr.VarAttr(name = MATERIALIZED_VIEW_REWRITE_ENABLE_CONTAIN_EXTERNAL_TABLE, needForward = true,
description = {"基于结构信息的透明改写,是否使用包含外表的物化视图",
- "whether to use a materialized view that contains the foreign table "
+ "Whether to use a materialized view that contains the foreign table "
+ "when using rewriting based on struct info"})
public boolean materializedViewRewriteEnableContainExternalTable = false;
+ @VariableMgr.VarAttr(name = MATERIALIZED_VIEW_REWRITE_SUCCESS_CANDIDATE_NUM, needForward = true,
+ description = {"异步物化视图透明改写成功的结果集合,允许参与到CBO候选的最大数量",
+ "The max candidate num which participate in CBO when using asynchronous materialized views"})
+ public int materializedViewRewriteSuccessCandidateNum = 3;
@VariableMgr.VarAttr(name = CREATE_TABLE_PARTITION_MAX_NUM, needForward = true,
description = {"建表时创建分区的最大数量",
@@ -3432,6 +3439,10 @@ public class SessionVariable implements Serializable, Writable {
return materializedViewRewriteEnableContainExternalTable;
}
+ public int getMaterializedViewRewriteSuccessCandidateNum() {
+ return materializedViewRewriteSuccessCandidateNum;
+ }
+
public int getCreateTablePartitionMaxNum() {
return createTablePartitionMaxNum;
}
diff --git a/regression-test/suites/nereids_rules_p0/mv/agg_with_roll_up/aggregate_with_roll_up.groovy b/regression-test/suites/nereids_rules_p0/mv/agg_with_roll_up/aggregate_with_roll_up.groovy
index 0b0c4aa107b..b314927636b 100644
--- a/regression-test/suites/nereids_rules_p0/mv/agg_with_roll_up/aggregate_with_roll_up.groovy
+++ b/regression-test/suites/nereids_rules_p0/mv/agg_with_roll_up/aggregate_with_roll_up.groovy
@@ -168,8 +168,8 @@ suite("aggregate_with_roll_up") {
explain {
sql("${query_sql}")
check {result ->
- def splitResult = result.split("MaterializedViewRewriteSuccessButNotChose")
- splitResult.length == 2 ? splitResult[1].contains(mv_name) : false
+ def splitResult = result.split("MaterializedViewRewriteFail")
+ splitResult.length == 2 ? splitResult[0].contains(mv_name) : false
}
}
}
diff --git a/regression-test/suites/nereids_rules_p0/mv/dimension/dimension_1.groovy b/regression-test/suites/nereids_rules_p0/mv/dimension/dimension_1.groovy
index 988bbdbd803..2b81dfd8aea 100644
--- a/regression-test/suites/nereids_rules_p0/mv/dimension/dimension_1.groovy
+++ b/regression-test/suites/nereids_rules_p0/mv/dimension/dimension_1.groovy
@@ -520,9 +520,9 @@ suite("partition_mv_rewrite_dimension_1") {
o_comment
"""
def agg_sql_explain_2 = sql """explain ${agg_sql_2};"""
- def mv_index_1 = agg_sql_explain_2.toString().indexOf("MaterializedViewRewriteSuccessButNotChose:")
+ def mv_index_1 = agg_sql_explain_2.toString().indexOf("MaterializedViewRewriteFail:")
assert(mv_index_1 != -1)
- assert(agg_sql_explain_2.toString().substring(mv_index_1).indexOf(agg_mv_name_2) != -1)
+ assert(agg_sql_explain_2.toString().substring(0, mv_index_1).indexOf(agg_mv_name_2) != -1)
sql """DROP MATERIALIZED VIEW IF EXISTS ${agg_mv_name_2};"""
// agg + with group by + with agg function
@@ -559,9 +559,9 @@ suite("partition_mv_rewrite_dimension_1") {
o_comment
"""
def agg_sql_explain_3 = sql """explain ${agg_sql_3};"""
- def mv_index_2 = agg_sql_explain_3.toString().indexOf("MaterializedViewRewriteSuccessButNotChose:")
+ def mv_index_2 = agg_sql_explain_3.toString().indexOf("MaterializedViewRewriteFail:")
assert(mv_index_2 != -1)
- assert(agg_sql_explain_3.toString().substring(mv_index_2).indexOf(agg_mv_name_3) != -1)
+ assert(agg_sql_explain_3.toString().substring(0, mv_index_2).indexOf(agg_mv_name_3) != -1)
sql """DROP MATERIALIZED VIEW IF EXISTS ${agg_mv_name_3};"""
diff --git a/regression-test/suites/nereids_rules_p0/mv/dimension/dimension_2_4.groovy b/regression-test/suites/nereids_rules_p0/mv/dimension/dimension_2_4.groovy
index 60a3aed84ac..180cf9b9095 100644
--- a/regression-test/suites/nereids_rules_p0/mv/dimension/dimension_2_4.groovy
+++ b/regression-test/suites/nereids_rules_p0/mv/dimension/dimension_2_4.groovy
@@ -564,9 +564,9 @@ suite("partition_mv_rewrite_dimension_2_4") {
o_comment """
def agg_sql_explain_1 = sql """explain ${sql_stmt_16};"""
- def mv_index_1 = agg_sql_explain_1.toString().indexOf("MaterializedViewRewriteSuccessButNotChose:")
+ def mv_index_1 = agg_sql_explain_1.toString().indexOf("MaterializedViewRewriteFail:")
assert(mv_index_1 != -1)
- assert(agg_sql_explain_1.toString().substring(mv_index_1).indexOf(mv_name_16) != -1)
+ assert(agg_sql_explain_1.toString().substring(0, mv_index_1).indexOf(mv_name_16) != -1)
compare_res(sql_stmt_16 + " order by 1,2,3")
sql """DROP MATERIALIZED VIEW IF EXISTS ${mv_name_16};"""
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org