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 52031c86b7c2fbe713740740a488f150676eabde
Author: seawinde <149132972+seawi...@users.noreply.github.com>
AuthorDate: Thu Apr 25 22:11:14 2024 +0800

    [improvement](mtmv)  Optimize the performance of nested materialized view 
rewriting (#34127)
    
    Optimize the performance of nested materialized view rewriting gracefully, 
future performance optimzie base on this.
---
 .../java/org/apache/doris/nereids/memo/Memo.java   | 17 ++++-
 .../apache/doris/nereids/memo/StructInfoMap.java   | 86 +++++++++++-----------
 .../mv/AbstractMaterializedViewRule.java           |  1 +
 .../exploration/mv/MaterializedViewUtils.java      | 13 +++-
 .../doris/nereids/memo/StructInfoMapTest.java      | 20 ++---
 5 files changed, 77 insertions(+), 60 deletions(-)

diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Memo.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Memo.java
index db12c02e79a..8793cb5be51 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Memo.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Memo.java
@@ -17,6 +17,7 @@
 
 package org.apache.doris.nereids.memo;
 
+import org.apache.doris.catalog.MTMV;
 import org.apache.doris.common.IdGenerator;
 import org.apache.doris.common.Pair;
 import org.apache.doris.nereids.cost.Cost;
@@ -33,6 +34,7 @@ import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.plans.GroupPlan;
 import org.apache.doris.nereids.trees.plans.LeafPlan;
 import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.algebra.CatalogRelation;
 import org.apache.doris.nereids.trees.plans.algebra.SetOperation;
 import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
 import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
@@ -55,6 +57,7 @@ import java.util.Map;
 import java.util.Optional;
 import java.util.PriorityQueue;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import javax.annotation.Nullable;
@@ -69,6 +72,8 @@ public class Memo {
             EventChannel.getDefaultChannel().addConsumers(new 
LogConsumer(GroupMergeEvent.class, EventChannel.LOG)));
     private static long stateId = 0;
     private final ConnectContext connectContext;
+    private final Set<Long> needRefreshTableIdSet = new HashSet<>();
+    private final AtomicLong refreshVersion = new AtomicLong(1);
     private final IdGenerator<GroupId> groupIdGenerator = 
GroupId.createGenerator();
     private final Map<GroupId, Group> groups = Maps.newLinkedHashMap();
     // we could not use Set, because Set does not have get method.
@@ -118,6 +123,10 @@ public class Memo {
         return groupExpressions.size();
     }
 
+    public long getRefreshVersion() {
+        return refreshVersion.get();
+    }
+
     private Plan skipProject(Plan plan, Group targetGroup) {
         // Some top project can't be eliminated
         if (plan instanceof LogicalProject && ((LogicalProject<?>) 
plan).canEliminate()) {
@@ -406,14 +415,15 @@ public class Memo {
                     plan.getLogicalProperties(), 
targetGroup.getLogicalProperties());
             throw new IllegalStateException("Insert a plan into targetGroup 
but differ in logicalproperties");
         }
+        // TODO Support sync materialized view in the future
+        if (plan instanceof CatalogRelation && ((CatalogRelation) 
plan).getTable() instanceof MTMV) {
+            refreshVersion.incrementAndGet();
+        }
         Optional<GroupExpression> groupExpr = plan.getGroupExpression();
         if (groupExpr.isPresent()) {
             
Preconditions.checkState(groupExpressions.containsKey(groupExpr.get()));
             return CopyInResult.of(false, groupExpr.get());
         }
-        if (targetGroup != null) {
-            targetGroup.getstructInfoMap().setRefreshed(false);
-        }
         List<Group> childrenGroups = Lists.newArrayList();
         for (int i = 0; i < plan.children().size(); i++) {
             // skip useless project.
@@ -562,7 +572,6 @@ public class Memo {
         if (source == root) {
             root = destination;
         }
-        destination.getstructInfoMap().setRefreshed(false);
         groups.remove(source.getGroupId());
     }
 
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/StructInfoMap.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/StructInfoMap.java
index 4119c6f2f89..efa2bef1792 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/StructInfoMap.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/StructInfoMap.java
@@ -42,31 +42,34 @@ import javax.annotation.Nullable;
 public class StructInfoMap {
     private final Map<BitSet, Pair<GroupExpression, List<BitSet>>> 
groupExpressionMap = new HashMap<>();
     private final Map<BitSet, StructInfo> infoMap = new HashMap<>();
-    private boolean refreshed;
+    private long refreshVersion = 0;
 
     /**
      * get struct info according to table map
      *
-     * @param mvTableMap the original table map
+     * @param tableMap the original table map
      * @param foldTableMap the fold table map
      * @param group the group that the mv matched
      * @return struct info or null if not found
      */
-    public @Nullable StructInfo getStructInfo(BitSet mvTableMap, BitSet 
foldTableMap, Group group, Plan originPlan) {
-        if (!infoMap.containsKey(mvTableMap)) {
-            if ((groupExpressionMap.containsKey(foldTableMap) || 
groupExpressionMap.isEmpty())
-                    && !groupExpressionMap.containsKey(mvTableMap)) {
-                refresh(group);
-            }
-            if (groupExpressionMap.containsKey(mvTableMap)) {
-                Pair<GroupExpression, List<BitSet>> groupExpressionBitSetPair 
= getGroupExpressionWithChildren(
-                        mvTableMap);
-                StructInfo structInfo = 
constructStructInfo(groupExpressionBitSetPair.first,
-                        groupExpressionBitSetPair.second, mvTableMap, 
originPlan);
-                infoMap.put(mvTableMap, structInfo);
-            }
+    public @Nullable StructInfo getStructInfo(Memo memo, BitSet tableMap, 
BitSet foldTableMap,
+            Group group, Plan originPlan) {
+        StructInfo structInfo = infoMap.get(tableMap);
+        if (structInfo != null) {
+            return structInfo;
+        }
+        if (groupExpressionMap.isEmpty() || 
!groupExpressionMap.containsKey(tableMap)) {
+            refresh(group, memo.getRefreshVersion(), foldTableMap);
+            
group.getstructInfoMap().setRefreshVersion(memo.getRefreshVersion());
         }
-        return infoMap.get(mvTableMap);
+        if (groupExpressionMap.containsKey(tableMap)) {
+            Pair<GroupExpression, List<BitSet>> groupExpressionBitSetPair = 
getGroupExpressionWithChildren(
+                    tableMap);
+            structInfo = constructStructInfo(groupExpressionBitSetPair.first,
+                    groupExpressionBitSetPair.second, tableMap, originPlan);
+            infoMap.put(tableMap, structInfo);
+        }
+        return structInfo;
     }
 
     public Set<BitSet> getTableMaps() {
@@ -81,12 +84,12 @@ public class StructInfoMap {
         return groupExpressionMap.get(tableMap);
     }
 
-    public boolean isRefreshed() {
-        return refreshed;
+    public long getRefreshVersion() {
+        return refreshVersion;
     }
 
-    public void setRefreshed(boolean refreshed) {
-        this.refreshed = refreshed;
+    public void setRefreshVersion(long refreshVersion) {
+        this.refreshVersion = refreshVersion;
     }
 
     private StructInfo constructStructInfo(GroupExpression groupExpression, 
List<BitSet> children,
@@ -114,27 +117,24 @@ public class StructInfoMap {
      *
      * @param group the root group
      *
-     * @return whether groupExpressionMap is updated
      */
-    public boolean refresh(Group group) {
-        Set<Group> refreshedGroup = new HashSet<>();
-        int originSize = groupExpressionMap.size();
+    public void refresh(Group group, long refreshVersion, BitSet targetBitSet) 
{
+        Set<Integer> refreshedGroup = new HashSet<>();
         for (GroupExpression groupExpression : group.getLogicalExpressions()) {
-            List<Set<BitSet>> childrenTableMap = new ArrayList<>();
-            boolean needRefresh = groupExpressionMap.isEmpty();
+            List<Set<BitSet>> childrenTableMap = new LinkedList<>();
             if (groupExpression.children().isEmpty()) {
                 BitSet leaf = constructLeaf(groupExpression);
-                groupExpressionMap.put(leaf, Pair.of(groupExpression, new 
ArrayList<>()));
+                groupExpressionMap.put(leaf, Pair.of(groupExpression, new 
LinkedList<>()));
                 continue;
             }
-
             for (Group child : groupExpression.children()) {
-                if (!refreshedGroup.contains(child) && 
!child.getstructInfoMap().isRefreshed()) {
-                    StructInfoMap childStructInfoMap = 
child.getstructInfoMap();
-                    needRefresh |= childStructInfoMap.refresh(child);
-                    childStructInfoMap.setRefreshed(true);
+                StructInfoMap childStructInfoMap = child.getstructInfoMap();
+                if (!refreshedGroup.contains(child.getGroupId().asInt())
+                        && refreshVersion != 
childStructInfoMap.getRefreshVersion()) {
+                    childStructInfoMap.refresh(child, refreshVersion, 
targetBitSet);
+                    childStructInfoMap.setRefreshVersion(refreshVersion);
                 }
-                refreshedGroup.add(child);
+                refreshedGroup.add(child.getGroupId().asInt());
                 childrenTableMap.add(child.getstructInfoMap().getTableMaps());
             }
             // if one same groupExpression have refreshed, continue
@@ -150,15 +150,14 @@ public class StructInfoMap {
             }
             // if cumulative child table map is different from current
             // or current group expression map is empty, should update the 
groupExpressionMap currently
-            Collection<Pair<BitSet, List<BitSet>>> bitSetWithChildren = 
cartesianProduct(childrenTableMap);
-            if (needRefresh) {
-                for (Pair<BitSet, List<BitSet>> bitSetWithChild : 
bitSetWithChildren) {
-                    groupExpressionMap.putIfAbsent(bitSetWithChild.first,
-                            Pair.of(groupExpression, bitSetWithChild.second));
-                }
+            Collection<Pair<BitSet, List<BitSet>>> bitSetWithChildren = 
cartesianProduct(childrenTableMap,
+                    new BitSet());
+            for (Pair<BitSet, List<BitSet>> bitSetWithChild : 
bitSetWithChildren) {
+                groupExpressionMap.putIfAbsent(bitSetWithChild.first,
+                        Pair.of(groupExpression, bitSetWithChild.second));
             }
+
         }
-        return originSize != groupExpressionMap.size();
     }
 
     private BitSet constructLeaf(GroupExpression groupExpression) {
@@ -172,7 +171,8 @@ public class StructInfoMap {
         return tableMap;
     }
 
-    private Collection<Pair<BitSet, List<BitSet>>> 
cartesianProduct(List<Set<BitSet>> childrenTableMap) {
+    private Collection<Pair<BitSet, List<BitSet>>> 
cartesianProduct(List<Set<BitSet>> childrenTableMap,
+            BitSet targetBitSet) {
         Set<List<BitSet>> cartesianLists = 
Sets.cartesianProduct(childrenTableMap);
         List<Pair<BitSet, List<BitSet>>> resultPairSet = new LinkedList<>();
         for (List<BitSet> bitSetList : cartesianLists) {
@@ -180,6 +180,10 @@ public class StructInfoMap {
             for (BitSet b : bitSetList) {
                 bitSet.or(b);
             }
+            // filter the useless bitset which targetBitSet not contains, 
avoid exponential expansion
+            if (!targetBitSet.isEmpty() && 
!StructInfo.containsAll(targetBitSet, bitSet)) {
+                continue;
+            }
             resultPairSet.add(Pair.of(bitSet, bitSetList));
         }
         return resultPairSet;
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 6bf47f00c35..3405942c3a8 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
@@ -142,6 +142,7 @@ public abstract class AbstractMaterializedViewRule 
implements ExplorationRuleFac
     protected List<StructInfo> getValidQueryStructInfos(Plan queryPlan, 
CascadesContext cascadesContext,
             BitSet materializedViewTableSet) {
         List<StructInfo> validStructInfos = new ArrayList<>();
+        // For every materialized view we should trigger refreshing struct 
info map
         List<StructInfo> uncheckedStructInfos = 
MaterializedViewUtils.extractStructInfo(queryPlan, cascadesContext,
                 materializedViewTableSet);
         uncheckedStructInfos.forEach(queryStructInfo -> {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewUtils.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewUtils.java
index 46d0adde06e..5f7dc419eaf 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewUtils.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewUtils.java
@@ -148,16 +148,23 @@ public class MaterializedViewUtils {
         if (plan.getGroupExpression().isPresent()) {
             Group ownerGroup = plan.getGroupExpression().get().getOwnerGroup();
             StructInfoMap structInfoMap = ownerGroup.getstructInfoMap();
-            structInfoMap.refresh(ownerGroup);
+            if (cascadesContext.getMemo().getRefreshVersion() != 
structInfoMap.getRefreshVersion()
+                    || structInfoMap.getTableMaps().isEmpty()) {
+                structInfoMap.refresh(ownerGroup, 
cascadesContext.getMemo().getRefreshVersion(),
+                        materializedViewTableSet);
+                
structInfoMap.setRefreshVersion(cascadesContext.getMemo().getRefreshVersion());
+            }
             Set<BitSet> queryTableSets = structInfoMap.getTableMaps();
             ImmutableList.Builder<StructInfo> structInfosBuilder = 
ImmutableList.builder();
             if (!queryTableSets.isEmpty()) {
                 for (BitSet queryTableSet : queryTableSets) {
+                    // TODO As only support MatchMode.COMPLETE, so only get 
equaled query table struct info
                     if (!materializedViewTableSet.isEmpty()
-                            && 
!StructInfo.containsAll(materializedViewTableSet, queryTableSet)) {
+                            && 
!materializedViewTableSet.equals(queryTableSet)) {
                         continue;
                     }
-                    StructInfo structInfo = 
structInfoMap.getStructInfo(queryTableSet, queryTableSet, ownerGroup, plan);
+                    StructInfo structInfo = 
structInfoMap.getStructInfo(cascadesContext.getMemo(),
+                            queryTableSet, queryTableSet, ownerGroup, plan);
                     if (structInfo != null) {
                         structInfosBuilder.add(structInfo);
                     }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/StructInfoMapTest.java 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/StructInfoMapTest.java
index 13bdf35252e..9192f86cf3b 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/StructInfoMapTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/StructInfoMapTest.java
@@ -50,7 +50,7 @@ class StructInfoMapTest extends SqlTestBase {
         Group root = c1.getMemo().getRoot();
         Set<BitSet> tableMaps = root.getstructInfoMap().getTableMaps();
         Assertions.assertTrue(tableMaps.isEmpty());
-        root.getstructInfoMap().refresh(root);
+        root.getstructInfoMap().refresh(root, 1, new BitSet());
         Assertions.assertEquals(1, tableMaps.size());
         new MockUp<MTMVRelationManager>() {
             @Mock
@@ -76,7 +76,7 @@ class StructInfoMapTest extends SqlTestBase {
                 .optimize()
                 .printlnBestPlanTree();
         root = c1.getMemo().getRoot();
-        root.getstructInfoMap().refresh(root);
+        root.getstructInfoMap().refresh(root, 1, new BitSet());
         tableMaps = root.getstructInfoMap().getTableMaps();
         Assertions.assertEquals(2, tableMaps.size());
         dropMvByNereids("drop materialized view mv1");
@@ -97,10 +97,8 @@ class StructInfoMapTest extends SqlTestBase {
         Group root = c1.getMemo().getRoot();
         Set<BitSet> tableMaps = root.getstructInfoMap().getTableMaps();
         Assertions.assertTrue(tableMaps.isEmpty());
-        boolean refreshed = root.getstructInfoMap().refresh(root);
-        Assertions.assertTrue(refreshed);
-        refreshed = root.getstructInfoMap().refresh(root);
-        Assertions.assertFalse(refreshed);
+        root.getstructInfoMap().refresh(root, 1, new BitSet());
+        root.getstructInfoMap().refresh(root, 1, new BitSet());
         Assertions.assertEquals(1, tableMaps.size());
         new MockUp<MTMVRelationManager>() {
             @Mock
@@ -126,10 +124,8 @@ class StructInfoMapTest extends SqlTestBase {
                 .optimize()
                 .printlnBestPlanTree();
         root = c1.getMemo().getRoot();
-        refreshed = root.getstructInfoMap().refresh(root);
-        Assertions.assertTrue(refreshed);
-        refreshed = root.getstructInfoMap().refresh(root);
-        Assertions.assertFalse(refreshed);
+        root.getstructInfoMap().refresh(root, 1, new BitSet());
+        root.getstructInfoMap().refresh(root, 1, new BitSet());
         tableMaps = root.getstructInfoMap().getTableMaps();
         Assertions.assertEquals(2, tableMaps.size());
         dropMvByNereids("drop materialized view mv1");
@@ -166,13 +162,13 @@ class StructInfoMapTest extends SqlTestBase {
                 .rewrite()
                 .optimize();
         Group root = c1.getMemo().getRoot();
-        root.getstructInfoMap().refresh(root);
+        root.getstructInfoMap().refresh(root, 1, new BitSet());
         StructInfoMap structInfoMap = root.getstructInfoMap();
         Assertions.assertEquals(2, structInfoMap.getTableMaps().size());
         BitSet mvMap = structInfoMap.getTableMaps().stream()
                 .filter(b -> b.cardinality() == 2)
                 .collect(Collectors.toList()).get(0);
-        StructInfo structInfo = structInfoMap.getStructInfo(mvMap, mvMap, 
root, null);
+        StructInfo structInfo = structInfoMap.getStructInfo(c1.getMemo(), 
mvMap, mvMap, root, null);
         System.out.println(structInfo.getOriginalPlan().treeString());
         BitSet bitSet = new BitSet();
         structInfo.getRelations().forEach(r -> bitSet.set((int) 
r.getTable().getId()));


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

Reply via email to