Skip to content

Commit dbd2b0a

Browse files
authored
[improvement](mtmv) improve mv rewrite performance by reuse the shuttled expression (#37197) (#37935)
## Proposed changes chrry-pick 2.1 pr: #37197 commitId: 701c7db
1 parent 359e50f commit dbd2b0a

File tree

9 files changed

+88
-63
lines changed

9 files changed

+88
-63
lines changed

fe/fe-core/src/main/java/org/apache/doris/mtmv/MTMVCache.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ public Plan visitLogicalResultSink(LogicalResultSink<? extends Plan> logicalResu
108108
return childContext.getRewritePlan();
109109
}, mvPlan, originPlan);
110110
// Construct structInfo once for use later
111-
Optional<StructInfo> structInfoOptional = MaterializationContext.constructStructInfo(mvPlan,
111+
Optional<StructInfo> structInfoOptional = MaterializationContext.constructStructInfo(mvPlan, originPlan,
112112
planner.getCascadesContext(),
113113
new BitSet());
114114
return new MTMVCache(mvPlan, originPlan, planner.getCascadesContext().getMemo().getRoot().getStatistics(),

fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewRule.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,8 @@ protected List<StructInfo> getValidQueryStructInfos(Plan queryPlan, CascadesCont
145145
BitSet materializedViewTableSet) {
146146
List<StructInfo> validStructInfos = new ArrayList<>();
147147
// For every materialized view we should trigger refreshing struct info map
148-
List<StructInfo> uncheckedStructInfos = MaterializedViewUtils.extractStructInfo(queryPlan, cascadesContext,
149-
materializedViewTableSet);
148+
List<StructInfo> uncheckedStructInfos = MaterializedViewUtils.extractStructInfo(queryPlan, queryPlan,
149+
cascadesContext, materializedViewTableSet);
150150
uncheckedStructInfos.forEach(queryStructInfo -> {
151151
boolean valid = checkQueryPattern(queryStructInfo, cascadesContext) && queryStructInfo.isValid();
152152
if (!valid) {

fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AsyncMaterializationContext.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ public class AsyncMaterializationContext extends MaterializationContext {
5050

5151
private static final Logger LOG = LogManager.getLogger(AsyncMaterializationContext.class);
5252
private final MTMV mtmv;
53+
private List<String> materializationQualifier;
5354

5455
/**
5556
* MaterializationContext, this contains necessary info for query rewriting by mv
@@ -72,7 +73,10 @@ Plan doGenerateScanPlan(CascadesContext cascadesContext) {
7273

7374
@Override
7475
List<String> getMaterializationQualifier() {
75-
return this.mtmv.getFullQualifiers();
76+
if (this.materializationQualifier == null) {
77+
this.materializationQualifier = this.mtmv.getFullQualifiers();
78+
}
79+
return this.materializationQualifier;
7680
}
7781

7882
@Override

fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializationContext.java

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
import org.apache.doris.nereids.trees.plans.physical.PhysicalPlan;
3737
import org.apache.doris.nereids.trees.plans.physical.PhysicalRelation;
3838
import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanVisitor;
39-
import org.apache.doris.nereids.util.ExpressionUtils;
4039
import org.apache.doris.statistics.ColumnStatistic;
4140
import org.apache.doris.statistics.Statistics;
4241

@@ -119,32 +118,31 @@ public MaterializationContext(Plan plan, Plan originalPlan, Plan scanPlan,
119118
this.exprToScanExprMapping.put(originalPlanOutput.get(slotIndex), scanPlanOutput.get(slotIndex));
120119
}
121120
}
122-
this.planOutputShuttledExpressions = ExpressionUtils.shuttleExpressionWithLineage(originalPlanOutput,
123-
originalPlan, new BitSet());
124-
// materialization output expression shuttle, this will be used to expression rewrite
125-
this.shuttledExprToScanExprMapping = ExpressionMapping.generate(
126-
this.planOutputShuttledExpressions,
127-
scanPlanOutput);
128121
// Construct materialization struct info, catch exception which may cause planner roll back
129-
if (structInfo == null) {
130-
Optional<StructInfo> structInfoOptional = constructStructInfo(plan, cascadesContext, new BitSet());
131-
if (!structInfoOptional.isPresent()) {
132-
this.available = false;
133-
}
134-
this.structInfo = structInfoOptional.orElseGet(() -> null);
135-
} else {
136-
this.structInfo = structInfo;
122+
this.structInfo = structInfo == null
123+
? constructStructInfo(plan, originalPlan, cascadesContext, new BitSet()).orElseGet(() -> null)
124+
: structInfo;
125+
this.available = this.structInfo != null;
126+
if (available) {
127+
this.planOutputShuttledExpressions = this.structInfo.getPlanOutputShuttledExpressions();
128+
// materialization output expression shuttle, this will be used to expression rewrite
129+
this.shuttledExprToScanExprMapping = ExpressionMapping.generate(
130+
this.planOutputShuttledExpressions,
131+
scanPlanOutput);
137132
}
138133
}
139134

140135
/**
141136
* Construct materialized view Struct info
137+
* @param plan maybe remove unnecessary plan node, and the logical output maybe wrong
138+
* @param originalPlan original plan, the output is right
142139
*/
143-
public static Optional<StructInfo> constructStructInfo(Plan plan, CascadesContext cascadesContext,
144-
BitSet expectedTableBitSet) {
140+
public static Optional<StructInfo> constructStructInfo(Plan plan, Plan originalPlan,
141+
CascadesContext cascadesContext, BitSet expectedTableBitSet) {
145142
List<StructInfo> viewStructInfos;
146143
try {
147-
viewStructInfos = MaterializedViewUtils.extractStructInfo(plan, cascadesContext, expectedTableBitSet);
144+
viewStructInfos = MaterializedViewUtils.extractStructInfo(plan, originalPlan,
145+
cascadesContext, expectedTableBitSet);
148146
if (viewStructInfos.size() > 1) {
149147
// view struct info should only have one, log error and use the first struct info
150148
LOG.warn(String.format("view strut info is more than one, materialization plan is %s",

fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewUtils.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,10 @@ public static boolean containTableQueryOperator(Plan analyzedPlan) {
167167

168168
/**
169169
* Extract struct info from plan, support to get struct info from logical plan or plan in group.
170+
* @param plan maybe remove unnecessary plan node, and the logical output maybe wrong
171+
* @param originalPlan original plan, the output is right
170172
*/
171-
public static List<StructInfo> extractStructInfo(Plan plan, CascadesContext cascadesContext,
173+
public static List<StructInfo> extractStructInfo(Plan plan, Plan originalPlan, CascadesContext cascadesContext,
172174
BitSet materializedViewTableSet) {
173175
// If plan belong to some group, construct it with group struct info
174176
if (plan.getGroupExpression().isPresent()) {
@@ -188,7 +190,7 @@ public static List<StructInfo> extractStructInfo(Plan plan, CascadesContext casc
188190
continue;
189191
}
190192
StructInfo structInfo = structInfoMap.getStructInfo(cascadesContext,
191-
queryTableSet, ownerGroup, plan);
193+
queryTableSet, ownerGroup, originalPlan);
192194
if (structInfo != null) {
193195
structInfosBuilder.add(structInfo);
194196
}
@@ -197,7 +199,7 @@ public static List<StructInfo> extractStructInfo(Plan plan, CascadesContext casc
197199
}
198200
}
199201
// if plan doesn't belong to any group, construct it directly
200-
return ImmutableList.of(StructInfo.of(plan, cascadesContext));
202+
return ImmutableList.of(StructInfo.of(plan, originalPlan, cascadesContext));
201203
}
202204

203205
/**

fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/Predicates.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141
import java.util.Objects;
4242
import java.util.Optional;
4343
import java.util.Set;
44-
import java.util.stream.Collectors;
4544

4645
/**
4746
* This record the predicates which can be pulled up or some other type predicates.
@@ -70,11 +69,6 @@ public Predicates merge(Collection<Expression> predicates) {
7069
return new Predicates(mergedPredicates);
7170
}
7271

73-
public Expression composedExpression() {
74-
return ExpressionUtils.and(pulledUpPredicates.stream().map(Expression.class::cast)
75-
.collect(Collectors.toList()));
76-
}
77-
7872
/**
7973
* Split the expression to equal, range and residual predicate.
8074
*/

fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/StructInfo.java

Lines changed: 55 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,16 @@ public class StructInfo {
106106
// this is for LogicalCompatibilityContext later
107107
private final Map<RelationId, StructInfoNode> relationIdStructInfoNodeMap;
108108
// this recorde the predicates which can pull up, not shuttled
109-
private Predicates predicates;
109+
private final Predicates predicates;
110110
// split predicates is shuttled
111-
private final SplitPredicate splitPredicate;
112-
private final EquivalenceClass equivalenceClass;
111+
private SplitPredicate splitPredicate;
112+
private EquivalenceClass equivalenceClass;
113113
// Key is the expression shuttled and the value is the origin expression
114114
// this is for building LogicalCompatibilityContext later.
115115
private final Map<ExpressionPosition, Map<Expression, Expression>> shuttledExpressionsToExpressionsMap;
116116
// Record the exprId and the corresponding expr map, this is used by expression shuttled
117117
private final Map<ExprId, Expression> namedExprIdAndExprMapping;
118+
private final List<? extends Expression> planOutputShuttledExpressions;
118119

119120
/**
120121
* The construct method for StructInfo
@@ -125,30 +126,25 @@ private StructInfo(Plan originalPlan, ObjectId originalPlanId, HyperGraph hyperG
125126
@Nullable Predicates predicates,
126127
Map<ExpressionPosition, Map<Expression, Expression>> shuttledExpressionsToExpressionsMap,
127128
Map<ExprId, Expression> namedExprIdAndExprMapping,
128-
BitSet tableIdSet) {
129+
BitSet tableIdSet,
130+
SplitPredicate splitPredicate,
131+
EquivalenceClass equivalenceClass,
132+
List<? extends Expression> planOutputShuttledExpressions) {
129133
this.originalPlan = originalPlan;
130134
this.originalPlanId = originalPlanId;
131135
this.hyperGraph = hyperGraph;
132-
this.valid = valid
133-
&& hyperGraph.getNodes().stream().allMatch(n -> ((StructInfoNode) n).getExpressions() != null);
136+
this.valid = valid;
134137
this.topPlan = topPlan;
135138
this.bottomPlan = bottomPlan;
136139
this.relations = relations;
137140
this.tableBitSet = tableIdSet;
138141
this.relationIdStructInfoNodeMap = relationIdStructInfoNodeMap;
139142
this.predicates = predicates;
140-
if (predicates == null) {
141-
// collect predicate from top plan which not in hyper graph
142-
Set<Expression> topPlanPredicates = new LinkedHashSet<>();
143-
topPlan.accept(PREDICATE_COLLECTOR, topPlanPredicates);
144-
this.predicates = Predicates.of(topPlanPredicates);
145-
}
146-
Pair<SplitPredicate, EquivalenceClass> derivedPredicates =
147-
predicatesDerive(this.predicates, topPlan, tableBitSet);
148-
this.splitPredicate = derivedPredicates.key();
149-
this.equivalenceClass = derivedPredicates.value();
143+
this.splitPredicate = splitPredicate;
144+
this.equivalenceClass = equivalenceClass;
150145
this.shuttledExpressionsToExpressionsMap = shuttledExpressionsToExpressionsMap;
151146
this.namedExprIdAndExprMapping = namedExprIdAndExprMapping;
147+
this.planOutputShuttledExpressions = planOutputShuttledExpressions;
152148
}
153149

154150
/**
@@ -157,7 +153,8 @@ private StructInfo(Plan originalPlan, ObjectId originalPlanId, HyperGraph hyperG
157153
public StructInfo withPredicates(Predicates predicates) {
158154
return new StructInfo(this.originalPlan, this.originalPlanId, this.hyperGraph, this.valid, this.topPlan,
159155
this.bottomPlan, this.relations, this.relationIdStructInfoNodeMap, predicates,
160-
this.shuttledExpressionsToExpressionsMap, this.namedExprIdAndExprMapping, this.tableBitSet);
156+
this.shuttledExpressionsToExpressionsMap, this.namedExprIdAndExprMapping, this.tableBitSet,
157+
null, null, this.planOutputShuttledExpressions);
161158
}
162159

163160
/**
@@ -166,7 +163,8 @@ public StructInfo withPredicates(Predicates predicates) {
166163
public StructInfo withTableBitSet(BitSet tableBitSet) {
167164
return new StructInfo(this.originalPlan, this.originalPlanId, this.hyperGraph, this.valid, this.topPlan,
168165
this.bottomPlan, this.relations, this.relationIdStructInfoNodeMap, this.predicates,
169-
this.shuttledExpressionsToExpressionsMap, this.namedExprIdAndExprMapping, tableBitSet);
166+
this.shuttledExpressionsToExpressionsMap, this.namedExprIdAndExprMapping, tableBitSet,
167+
this.splitPredicate, this.equivalenceClass, this.planOutputShuttledExpressions);
170168
}
171169

172170
private static boolean collectStructInfoFromGraph(HyperGraph hyperGraph,
@@ -252,11 +250,10 @@ private static boolean collectStructInfoFromGraph(HyperGraph hyperGraph,
252250
}
253251

254252
// derive some useful predicate by predicates
255-
private Pair<SplitPredicate, EquivalenceClass> predicatesDerive(Predicates predicates, Plan originalPlan,
256-
BitSet tableBitSet) {
253+
private static Pair<SplitPredicate, EquivalenceClass> predicatesDerive(Predicates predicates, Plan originalPlan) {
257254
// construct equivalenceClass according to equals predicates
258255
List<Expression> shuttledExpression = ExpressionUtils.shuttleExpressionWithLineage(
259-
new ArrayList<>(predicates.getPulledUpPredicates()), originalPlan, tableBitSet).stream()
256+
new ArrayList<>(predicates.getPulledUpPredicates()), originalPlan, new BitSet()).stream()
260257
.map(Expression.class::cast)
261258
.collect(Collectors.toList());
262259
SplitPredicate splitPredicate = Predicates.splitPredicates(ExpressionUtils.and(shuttledExpression));
@@ -328,9 +325,19 @@ public static StructInfo of(Plan originalPlan, @Nullable Plan topPlan, @Nullable
328325
relationIdStructInfoNodeMap,
329326
tableBitSet,
330327
cascadesContext);
328+
valid = valid
329+
&& hyperGraph.getNodes().stream().allMatch(n -> ((StructInfoNode) n).getExpressions() != null);
330+
// collect predicate from top plan which not in hyper graph
331+
Set<Expression> topPlanPredicates = new LinkedHashSet<>();
332+
topPlan.accept(PREDICATE_COLLECTOR, topPlanPredicates);
333+
Predicates predicates = Predicates.of(topPlanPredicates);
334+
// this should use the output of originalPlan to make sure the output right order
335+
List<? extends Expression> planOutputShuttledExpressions =
336+
ExpressionUtils.shuttleExpressionWithLineage(originalPlan.getOutput(), originalPlan, new BitSet());
331337
return new StructInfo(originalPlan, originalPlanId, hyperGraph, valid, topPlan, bottomPlan,
332-
relationList, relationIdStructInfoNodeMap, null, shuttledHashConjunctsToConjunctsMap,
333-
namedExprIdAndExprMapping, tableBitSet);
338+
relationList, relationIdStructInfoNodeMap, predicates, shuttledHashConjunctsToConjunctsMap,
339+
namedExprIdAndExprMapping, tableBitSet, null, null,
340+
planOutputShuttledExpressions);
334341
}
335342

336343
/**
@@ -350,10 +357,6 @@ public Predicates getPredicates() {
350357
return predicates;
351358
}
352359

353-
public EquivalenceClass getEquivalenceClass() {
354-
return equivalenceClass;
355-
}
356-
357360
public Plan getOriginalPlan() {
358361
return originalPlan;
359362
}
@@ -362,8 +365,28 @@ public HyperGraph getHyperGraph() {
362365
return hyperGraph;
363366
}
364367

368+
/**
369+
* lazy init for performance
370+
*/
365371
public SplitPredicate getSplitPredicate() {
366-
return splitPredicate;
372+
if (this.splitPredicate == null && this.predicates != null) {
373+
Pair<SplitPredicate, EquivalenceClass> derivedPredicates = predicatesDerive(this.predicates, topPlan);
374+
this.splitPredicate = derivedPredicates.key();
375+
this.equivalenceClass = derivedPredicates.value();
376+
}
377+
return this.splitPredicate;
378+
}
379+
380+
/**
381+
* lazy init for performance
382+
*/
383+
public EquivalenceClass getEquivalenceClass() {
384+
if (this.equivalenceClass == null && this.predicates != null) {
385+
Pair<SplitPredicate, EquivalenceClass> derivedPredicates = predicatesDerive(this.predicates, topPlan);
386+
this.splitPredicate = derivedPredicates.key();
387+
this.equivalenceClass = derivedPredicates.value();
388+
}
389+
return this.equivalenceClass;
367390
}
368391

369392
public boolean isValid() {
@@ -416,6 +439,10 @@ public BitSet getTableBitSet() {
416439
return tableBitSet;
417440
}
418441

442+
public List<? extends Expression> getPlanOutputShuttledExpressions() {
443+
return planOutputShuttledExpressions;
444+
}
445+
419446
/**
420447
* Judge the source graph logical is whether the same as target
421448
* For inner join should judge only the join tables,

fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/HyperGraphAggTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,9 @@ void testIJWithAgg() {
8989
}
9090

9191
LogicalCompatibilityContext constructContext(Plan p1, Plan p2) {
92-
StructInfo st1 = MaterializedViewUtils.extractStructInfo(p1,
92+
StructInfo st1 = MaterializedViewUtils.extractStructInfo(p1, p1,
9393
null, new BitSet()).get(0);
94-
StructInfo st2 = MaterializedViewUtils.extractStructInfo(p2,
94+
StructInfo st2 = MaterializedViewUtils.extractStructInfo(p2, p2,
9595
null, new BitSet()).get(0);
9696
RelationMapping rm = RelationMapping.generate(st1.getRelations(), st2.getRelations()).get(0);
9797
SlotMapping sm = SlotMapping.generate(rm);

fe/fe-core/src/test/java/org/apache/doris/nereids/sqltest/SqlTestBase.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,9 @@ protected void runBeforeEach() throws Exception {
100100
}
101101

102102
protected LogicalCompatibilityContext constructContext(Plan p1, Plan p2, CascadesContext context) {
103-
StructInfo st1 = MaterializedViewUtils.extractStructInfo(p1,
103+
StructInfo st1 = MaterializedViewUtils.extractStructInfo(p1, p1,
104104
context, new BitSet()).get(0);
105-
StructInfo st2 = MaterializedViewUtils.extractStructInfo(p2,
105+
StructInfo st2 = MaterializedViewUtils.extractStructInfo(p2, p2,
106106
context, new BitSet()).get(0);
107107
RelationMapping rm = RelationMapping.generate(st1.getRelations(), st2.getRelations()).get(0);
108108
SlotMapping sm = SlotMapping.generate(rm);

0 commit comments

Comments
 (0)