You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by zy...@apache.org on 2023/01/09 07:53:12 UTC

[iotdb] branch master updated: [IOTDB-4982] Replace recursion algorithm in Traverser with iteration algorithm (#8765)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new cea3c5ce21 [IOTDB-4982] Replace recursion algorithm in Traverser with iteration algorithm (#8765)
cea3c5ce21 is described below

commit cea3c5ce21515cb9a7e830f45c7409b8c97ab7e1
Author: Chen YZ <43...@users.noreply.github.com>
AuthorDate: Mon Jan 9 15:53:06 2023 +0800

    [IOTDB-4982] Replace recursion algorithm in Traverser with iteration algorithm (#8765)
    
    [IOTDB-4982] Replace recursion algorithm in Traverser with iteration algorithm (#8765)
---
 .../persistence/schema/ClusterSchemaInfo.java      |  14 -
 .../commons/schema/tree/AbstractTreeVisitor.java   |  92 ++++-
 .../iotdb/commons/schema/tree/ITreeNode.java       |   4 +-
 .../schemaregion/rocksdb/RSchemaRegion.java        |   2 +-
 .../schemaregion/rocksdb/mnode/RMNode.java         |   5 +
 .../idtable/entry/InsertMeasurementMNode.java      |   5 +
 .../db/metadata/mnode/AboveDatabaseMNode.java      |  20 +-
 .../org/apache/iotdb/db/metadata/mnode/IMNode.java |   7 +-
 .../iotdb/db/metadata/mnode/InternalMNode.java     |   5 +
 .../iotdb/db/metadata/mnode/MeasurementMNode.java  |   5 +
 .../mnode/iterator/AbstractTraverserIterator.java  | 115 ++++++
 .../iterator/CachedTraverserIterator.java}         |  27 +-
 .../iterator/MemoryTraverserIterator.java}         |  22 +-
 .../iotdb/db/metadata/mtree/ConfigMTree.java       | 229 +++++------
 .../iotdb/db/metadata/mtree/IMTreeBelowSG.java     |  46 ++-
 .../db/metadata/mtree/MTreeBelowSGCachedImpl.java  | 315 +++++++-------
 .../db/metadata/mtree/MTreeBelowSGMemoryImpl.java  | 366 ++++++++++-------
 .../db/metadata/mtree/store/CachedMTreeStore.java  |  18 +
 .../iotdb/db/metadata/mtree/store/IMTreeStore.java |   6 +
 .../db/metadata/mtree/store/MemMTreeStore.java     |  19 +
 .../db/metadata/mtree/traverser/Traverser.java     | 452 ++-------------------
 .../traverser/TraverserWithLimitOffsetWrapper.java | 122 ++++++
 .../DatabaseTraverser.java}                        |  49 ++-
 .../EntityTraverser.java}                          |  60 ++-
 .../mtree/traverser/basic/MNodeTraverser.java      | 103 +++++
 .../MeasurementTraverser.java}                     |  41 +-
 .../traverser/collector/CollectorTraverser.java    |  96 -----
 ...eGroupCollector.java => DatabaseCollector.java} |  38 +-
 .../mtree/traverser/collector/EntityCollector.java |  48 +--
 .../traverser/collector/MNodeAboveSGCollector.java |  25 +-
 .../mtree/traverser/collector/MNodeCollector.java  |  58 +--
 .../traverser/collector/MeasurementCollector.java  |  58 +--
 .../metadata/mtree/traverser/counter/Counter.java  |   8 +-
 ...orageGroupCounter.java => DatabaseCounter.java} |  31 +-
 ...StorageGroupCounter.java => EntityCounter.java} |  29 +-
 .../mtree/traverser/updater/EntityUpdater.java     |  65 +++
 .../traverser/updater/MeasurementUpdater.java      |  66 +++
 .../metadata/mtree/traverser/updater/Updater.java  |  11 +-
 .../schemaregion/SchemaRegionMemoryImpl.java       | 107 ++---
 .../schemaregion/SchemaRegionSchemaFileImpl.java   |  43 +-
 .../visitor/SchemaTreeDeviceVisitor.java           |   2 +-
 .../visitor/SchemaTreeMeasurementVisitor.java      |   3 +-
 .../iotdb/db/metadata/mtree/ConfigMTreeTest.java   |  25 --
 .../schemaRegion/SchemaRegionBasicTest.java        |  13 +-
 .../schemaRegion/SchemaRegionTemplateTest.java     |  62 +++
 45 files changed, 1535 insertions(+), 1402 deletions(-)

diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/schema/ClusterSchemaInfo.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/schema/ClusterSchemaInfo.java
index 573e1b05a5..29c1ed7bb8 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/schema/ClusterSchemaInfo.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/schema/ClusterSchemaInfo.java
@@ -553,20 +553,6 @@ public class ClusterSchemaInfo implements SnapshotProcessor {
     return matchedPathsInNextLevel;
   }
 
-  public Pair<Set<String>, Set<PartialPath>> getChildNodeNameInNextLevel(PartialPath partialPath) {
-    Pair<Set<String>, Set<PartialPath>> matchedNamesInNextLevel =
-        new Pair<>(new HashSet<>(), new HashSet<>());
-    storageGroupReadWriteLock.readLock().lock();
-    try {
-      matchedNamesInNextLevel = mTree.getChildNodeNameInNextLevel(partialPath);
-    } catch (MetadataException e) {
-      LOGGER.error("Error get matched names in next level.", e);
-    } finally {
-      storageGroupReadWriteLock.readLock().unlock();
-    }
-    return matchedNamesInNextLevel;
-  }
-
   public TSStatus createSchemaTemplate(CreateSchemaTemplatePlan createSchemaTemplatePlan) {
     try {
       Template template = createSchemaTemplatePlan.getTemplate();
diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/schema/tree/AbstractTreeVisitor.java b/node-commons/src/main/java/org/apache/iotdb/commons/schema/tree/AbstractTreeVisitor.java
index b752728af7..0255db8180 100644
--- a/node-commons/src/main/java/org/apache/iotdb/commons/schema/tree/AbstractTreeVisitor.java
+++ b/node-commons/src/main/java/org/apache/iotdb/commons/schema/tree/AbstractTreeVisitor.java
@@ -66,7 +66,7 @@ public abstract class AbstractTreeVisitor<N extends ITreeNode, R>
     implements Iterator<R>, AutoCloseable {
 
   // command parameters
-  protected final N root;
+  protected N root;
 
   // finite automation constructed from given path pattern or pattern tree
   protected final IPatternFA patternFA;
@@ -136,14 +136,6 @@ public abstract class AbstractTreeVisitor<N extends ITreeNode, R>
     ancestorStack.add(new AncestorStackEntry(root, currentStateMatchInfo));
   }
 
-  public boolean isSuccess() {
-    return throwable != null;
-  }
-
-  public Throwable getThrowable() {
-    return throwable;
-  }
-
   public void reset() {
     close();
     visitorStack.clear();
@@ -274,7 +266,16 @@ public abstract class AbstractTreeVisitor<N extends ITreeNode, R>
     }
   }
 
-  protected final String[] generateFullPathNodes() {
+  /**
+   * Get full path of parent of current node. This method should be used in {@linkplain
+   * AbstractTreeVisitor#acceptInternalMatchedNode}, {@linkplain
+   * AbstractTreeVisitor#acceptFullMatchedNode},{@linkplain
+   * AbstractTreeVisitor#shouldVisitSubtreeOfInternalMatchedNode} or {@linkplain
+   * AbstractTreeVisitor#shouldVisitSubtreeOfFullMatchedNode}.
+   *
+   * @return full path from traverse start node to the parent of current node
+   */
+  protected PartialPath getParentPartialPath() {
     List<String> nodeNames = new ArrayList<>();
     Iterator<AncestorStackEntry> iterator = ancestorStack.iterator();
     for (int i = 0, size = shouldVisitSubtree ? ancestorStack.size() - 1 : ancestorStack.size();
@@ -284,10 +285,41 @@ public abstract class AbstractTreeVisitor<N extends ITreeNode, R>
         nodeNames.add(iterator.next().node.getName());
       }
     }
-    nodeNames.add(nextMatchedNode.getName());
+    return new PartialPath(nodeNames.toArray(new String[0]));
+  }
+
+  /**
+   * Get partial path from root to node.
+   *
+   * @param node node must be concluded in ancestorStack or nextMatchedNode
+   * @return partial path from traverse start node to the specified node
+   */
+  protected final PartialPath getPartialPathFromRootToNode(N node) {
+    return new PartialPath(getFullPathFromRootToNode(node));
+  }
+
+  /**
+   * Get full path from root to node.
+   *
+   * @param node node must be concluded in ancestorStack or nextMatchedNode
+   * @return full path from traverse start node to the specified node
+   */
+  protected final String[] getFullPathFromRootToNode(N node) {
+    List<String> nodeNames = new ArrayList<>();
+    for (AncestorStackEntry entry : ancestorStack) {
+      nodeNames.add(entry.node.getName());
+      if (entry.node == node) {
+        return nodeNames.toArray(new String[0]);
+      }
+    }
+    nodeNames.add(node.getName());
     return nodeNames.toArray(new String[0]);
   }
 
+  protected final N getAncestorNodeByLevel(int level) {
+    return ancestorStack.get(level).node;
+  }
+
   protected final N getParentOfNextMatchedNode() {
     if (shouldVisitSubtree) {
       return ancestorStack.get(ancestorStack.size() - 2).node;
@@ -296,10 +328,36 @@ public abstract class AbstractTreeVisitor<N extends ITreeNode, R>
     }
   }
 
+  /**
+   * Get level from root to NextMatchedNode. Level of root is 0. For example, root.sg.d1.s1,
+   * NextMatchedNode is s1, then return 3.
+   *
+   * @return level from root to NextMatchedNode
+   */
+  protected final int getLevelOfNextMatchedNode() {
+    if (shouldVisitSubtree) {
+      return ancestorStack.size() - 1;
+    } else {
+      return ancestorStack.size();
+    }
+  }
+
+  protected final int getSizeOfAncestor() {
+    return ancestorStack.size();
+  }
+
   protected void setFailure(Throwable e) {
     this.throwable = e;
   }
 
+  protected Throwable getFailure() {
+    return throwable;
+  }
+
+  public boolean isSuccess() {
+    return throwable == null;
+  }
+
   // Get a child with the given childName.
   protected abstract N getChild(N parent, String childName) throws Exception;
 
@@ -479,7 +537,9 @@ public abstract class AbstractTreeVisitor<N extends ITreeNode, R>
     @Override
     protected void close() {
       super.close();
-      releaseNodeIterator(childrenIterator);
+      if (childrenIterator != null) {
+        releaseNodeIterator(childrenIterator);
+      }
     }
   }
 
@@ -550,7 +610,9 @@ public abstract class AbstractTreeVisitor<N extends ITreeNode, R>
     @Override
     protected void close() {
       super.close();
-      releaseNodeIterator(iterator);
+      if (iterator != null) {
+        releaseNodeIterator(iterator);
+      }
     }
   }
 
@@ -736,7 +798,9 @@ public abstract class AbstractTreeVisitor<N extends ITreeNode, R>
     @Override
     protected void close() {
       super.close();
-      releaseNodeIterator(iterator);
+      if (iterator != null) {
+        releaseNodeIterator(iterator);
+      }
     }
   }
 
diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/schema/tree/ITreeNode.java b/node-commons/src/main/java/org/apache/iotdb/commons/schema/tree/ITreeNode.java
index 900e943fbd..71537bc303 100644
--- a/node-commons/src/main/java/org/apache/iotdb/commons/schema/tree/ITreeNode.java
+++ b/node-commons/src/main/java/org/apache/iotdb/commons/schema/tree/ITreeNode.java
@@ -19,7 +19,9 @@
 
 package org.apache.iotdb.commons.schema.tree;
 
-public interface ITreeNode {
+import java.io.Serializable;
+
+public interface ITreeNode extends Serializable {
 
   String getName();
 }
diff --git a/schema-engine-rocksdb/src/main/java/org/apache/iotdb/db/metadata/schemaregion/rocksdb/RSchemaRegion.java b/schema-engine-rocksdb/src/main/java/org/apache/iotdb/db/metadata/schemaregion/rocksdb/RSchemaRegion.java
index 4e9f225fa6..04d55df60f 100644
--- a/schema-engine-rocksdb/src/main/java/org/apache/iotdb/db/metadata/schemaregion/rocksdb/RSchemaRegion.java
+++ b/schema-engine-rocksdb/src/main/java/org/apache/iotdb/db/metadata/schemaregion/rocksdb/RSchemaRegion.java
@@ -873,7 +873,7 @@ public class RSchemaRegion implements ISchemaRegion {
   }
 
   public List<PartialPath> getNodesListInGivenLevel(
-      PartialPath pathPattern, int nodeLevel, boolean isPrefixMatch) throws MetadataException {
+      PartialPath pathPattern, int nodeLevel, boolean isPrefixMatch) {
     if (pathPattern.getFullPath().contains(IoTDBConstant.ONE_LEVEL_PATH_WILDCARD)) {
       throw new UnsupportedOperationException(
           formatNotSupportInfo(Thread.currentThread().getStackTrace()[1].getMethodName()));
diff --git a/schema-engine-rocksdb/src/main/java/org/apache/iotdb/db/metadata/schemaregion/rocksdb/mnode/RMNode.java b/schema-engine-rocksdb/src/main/java/org/apache/iotdb/db/metadata/schemaregion/rocksdb/mnode/RMNode.java
index 749997ddd6..2dbad227c9 100644
--- a/schema-engine-rocksdb/src/main/java/org/apache/iotdb/db/metadata/schemaregion/rocksdb/mnode/RMNode.java
+++ b/schema-engine-rocksdb/src/main/java/org/apache/iotdb/db/metadata/schemaregion/rocksdb/mnode/RMNode.java
@@ -183,6 +183,11 @@ public abstract class RMNode implements IMNode {
     throw new UnsupportedOperationException();
   }
 
+  @Override
+  public boolean isAboveDatabase() {
+    return false;
+  }
+
   @Override
   public boolean isStorageGroup() {
     return false;
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/idtable/entry/InsertMeasurementMNode.java b/server/src/main/java/org/apache/iotdb/db/metadata/idtable/entry/InsertMeasurementMNode.java
index 245eef7841..1b376d4c51 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/idtable/entry/InsertMeasurementMNode.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/idtable/entry/InsertMeasurementMNode.java
@@ -205,6 +205,11 @@ public class InsertMeasurementMNode implements IMeasurementMNode {
     throw new UnsupportedOperationException("insert measurement mnode doesn't support this method");
   }
 
+  @Override
+  public boolean isAboveDatabase() {
+    return false;
+  }
+
   @Override
   public boolean isStorageGroup() {
     throw new UnsupportedOperationException("insert measurement mnode doesn't support this method");
diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/schema/tree/ITreeNode.java b/server/src/main/java/org/apache/iotdb/db/metadata/mnode/AboveDatabaseMNode.java
similarity index 66%
copy from node-commons/src/main/java/org/apache/iotdb/commons/schema/tree/ITreeNode.java
copy to server/src/main/java/org/apache/iotdb/db/metadata/mnode/AboveDatabaseMNode.java
index 900e943fbd..9b2251d40f 100644
--- a/node-commons/src/main/java/org/apache/iotdb/commons/schema/tree/ITreeNode.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mnode/AboveDatabaseMNode.java
@@ -16,10 +16,22 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+package org.apache.iotdb.db.metadata.mnode;
 
-package org.apache.iotdb.commons.schema.tree;
+/** Used to fill the link list of MNode above database in IMTreeBelowSG */
+public class AboveDatabaseMNode extends InternalMNode {
+  /**
+   * Constructor of MNode.
+   *
+   * @param parent
+   * @param name
+   */
+  public AboveDatabaseMNode(IMNode parent, String name) {
+    super(parent, name);
+  }
 
-public interface ITreeNode {
-
-  String getName();
+  @Override
+  public boolean isAboveDatabase() {
+    return true;
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mnode/IMNode.java b/server/src/main/java/org/apache/iotdb/db/metadata/mnode/IMNode.java
index 847d5d8bc7..b7b2e452e8 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/mnode/IMNode.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mnode/IMNode.java
@@ -19,14 +19,13 @@
 package org.apache.iotdb.db.metadata.mnode;
 
 import org.apache.iotdb.commons.path.PartialPath;
+import org.apache.iotdb.commons.schema.tree.ITreeNode;
 import org.apache.iotdb.db.metadata.mnode.container.IMNodeContainer;
 import org.apache.iotdb.db.metadata.mnode.visitor.MNodeVisitor;
 import org.apache.iotdb.db.metadata.mtree.store.disk.cache.CacheEntry;
 
-import java.io.Serializable;
-
 /** This interface defines a MNode's operation interfaces. */
-public interface IMNode extends Serializable {
+public interface IMNode extends ITreeNode {
 
   String getName();
 
@@ -83,6 +82,8 @@ public interface IMNode extends Serializable {
 
   void unsetSchemaTemplate();
 
+  boolean isAboveDatabase();
+
   boolean isStorageGroup();
 
   boolean isEntity();
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mnode/InternalMNode.java b/server/src/main/java/org/apache/iotdb/db/metadata/mnode/InternalMNode.java
index 11c501fe1f..47e95f0b9c 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/mnode/InternalMNode.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mnode/InternalMNode.java
@@ -232,6 +232,11 @@ public class InternalMNode extends MNode {
     this.schemaTemplateId = -1;
   }
 
+  @Override
+  public boolean isAboveDatabase() {
+    return false;
+  }
+
   @Override
   public MNodeType getMNodeType(Boolean isConfig) {
     return isConfig ? MNodeType.SG_INTERNAL : MNodeType.INTERNAL;
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mnode/MeasurementMNode.java b/server/src/main/java/org/apache/iotdb/db/metadata/mnode/MeasurementMNode.java
index 4ecf573fb8..2151a170c0 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/mnode/MeasurementMNode.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mnode/MeasurementMNode.java
@@ -213,6 +213,11 @@ public class MeasurementMNode extends MNode implements IMeasurementMNode {
   @Override
   public void unsetSchemaTemplate() {}
 
+  @Override
+  public boolean isAboveDatabase() {
+    return false;
+  }
+
   @Override
   public void setUseTemplate(boolean useTemplate) {}
 
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mnode/iterator/AbstractTraverserIterator.java b/server/src/main/java/org/apache/iotdb/db/metadata/mnode/iterator/AbstractTraverserIterator.java
new file mode 100644
index 0000000000..821c901de5
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mnode/iterator/AbstractTraverserIterator.java
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ */
+package org.apache.iotdb.db.metadata.mnode.iterator;
+
+import org.apache.iotdb.commons.exception.MetadataException;
+import org.apache.iotdb.db.metadata.mnode.IEntityMNode;
+import org.apache.iotdb.db.metadata.mnode.IMNode;
+import org.apache.iotdb.db.metadata.mtree.store.IMTreeStore;
+import org.apache.iotdb.db.metadata.template.Template;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import static org.apache.iotdb.db.metadata.MetadataConstant.NON_TEMPLATE;
+
+/**
+ * TraverserIterator for traversing device node. The iterator returns the filtered child nodes in
+ * the mtree and the child nodes in the template.
+ */
+public abstract class AbstractTraverserIterator implements IMNodeIterator {
+  private final IMNodeIterator directChildrenIterator;
+  private Iterator<IMNode> templateChildrenIterator;
+  protected IMNode nextMatchedNode;
+  protected boolean usingDirectChildrenIterator = true;
+  // if true, the pre deleted measurement or pre deactivated template won't be processed
+  private boolean skipPreDeletedSchema = false;
+
+  protected AbstractTraverserIterator(
+      IMTreeStore store, IEntityMNode parent, Map<Integer, Template> templateMap)
+      throws MetadataException {
+    this.directChildrenIterator = store.getChildrenIterator(parent);
+    if (templateMap != null && parent.isUseTemplate()) {
+      Template template = getActivatedSchemaTemplate(parent, templateMap);
+      if (template != null) {
+        templateChildrenIterator = template.getDirectNodes().iterator();
+      }
+    }
+  }
+
+  public void setSkipPreDeletedSchema(boolean skipPreDeletedSchema) {
+    this.skipPreDeletedSchema = skipPreDeletedSchema;
+  }
+
+  private Template getActivatedSchemaTemplate(
+      IEntityMNode node, Map<Integer, Template> templateMap) {
+    // new cluster, the used template is directly recorded as template id in device mnode
+    if (node.getSchemaTemplateId() != NON_TEMPLATE) {
+      if (skipPreDeletedSchema && node.getAsEntityMNode().isPreDeactivateTemplate()) {
+        // skip this pre deactivated template, the invoker will skip this
+        return null;
+      }
+      return templateMap.get(node.getSchemaTemplateId());
+    }
+    // if the node is usingTemplate, the upperTemplate won't be null or the upperTemplateId won't be
+    // NON_TEMPLATE.
+    throw new IllegalStateException(
+        String.format(
+            "There should be a template mounted on any ancestor of the node [%s] usingTemplate.",
+            node.getFullPath()));
+  }
+
+  @Override
+  public boolean hasNext() {
+    while (nextMatchedNode == null) {
+      if (directChildrenIterator.hasNext()) {
+        nextMatchedNode = directChildrenIterator.next();
+        usingDirectChildrenIterator = true;
+      } else if (templateChildrenIterator != null && templateChildrenIterator.hasNext()) {
+        nextMatchedNode = templateChildrenIterator.next();
+        usingDirectChildrenIterator = false;
+      } else {
+        return false;
+      }
+      if (skipPreDeletedSchema
+          && nextMatchedNode.isMeasurement()
+          && nextMatchedNode.getAsMeasurementMNode().isPreDeleted()) {
+        nextMatchedNode = null;
+      }
+    }
+    return true;
+  }
+
+  @Override
+  public IMNode next() {
+    if (!hasNext()) {
+      throw new NoSuchElementException();
+    }
+    IMNode result = nextMatchedNode;
+    nextMatchedNode = null;
+    return result;
+  }
+
+  @Override
+  public void close() {
+    directChildrenIterator.close();
+    templateChildrenIterator = null;
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/counter/CounterTraverser.java b/server/src/main/java/org/apache/iotdb/db/metadata/mnode/iterator/CachedTraverserIterator.java
similarity index 60%
copy from server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/counter/CounterTraverser.java
copy to server/src/main/java/org/apache/iotdb/db/metadata/mnode/iterator/CachedTraverserIterator.java
index b1312cfb58..501f748315 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/counter/CounterTraverser.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mnode/iterator/CachedTraverserIterator.java
@@ -16,25 +16,30 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.iotdb.db.metadata.mtree.traverser.counter;
+package org.apache.iotdb.db.metadata.mnode.iterator;
 
 import org.apache.iotdb.commons.exception.MetadataException;
-import org.apache.iotdb.commons.path.PartialPath;
-import org.apache.iotdb.db.metadata.mnode.IMNode;
+import org.apache.iotdb.db.metadata.mnode.IEntityMNode;
 import org.apache.iotdb.db.metadata.mtree.store.IMTreeStore;
-import org.apache.iotdb.db.metadata.mtree.traverser.Traverser;
+import org.apache.iotdb.db.metadata.template.Template;
 
-// This class define the count as traversal result.
-public abstract class CounterTraverser extends Traverser {
+import java.util.Map;
 
-  protected long count;
+public class CachedTraverserIterator extends AbstractTraverserIterator {
+  private final IMTreeStore store;
 
-  protected CounterTraverser(IMNode startNode, PartialPath path, IMTreeStore store)
+  public CachedTraverserIterator(
+      IMTreeStore store, IEntityMNode parent, Map<Integer, Template> templateMap)
       throws MetadataException {
-    super(startNode, path, store);
+    super(store, parent, templateMap);
+    this.store = store;
   }
 
-  public long getCount() {
-    return count;
+  @Override
+  public void close() {
+    if (nextMatchedNode != null && usingDirectChildrenIterator) {
+      store.unPin(nextMatchedNode);
+    }
+    super.close();
   }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/counter/CounterTraverser.java b/server/src/main/java/org/apache/iotdb/db/metadata/mnode/iterator/MemoryTraverserIterator.java
similarity index 64%
copy from server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/counter/CounterTraverser.java
copy to server/src/main/java/org/apache/iotdb/db/metadata/mnode/iterator/MemoryTraverserIterator.java
index b1312cfb58..9a8d9be7bc 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/counter/CounterTraverser.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mnode/iterator/MemoryTraverserIterator.java
@@ -16,25 +16,19 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.iotdb.db.metadata.mtree.traverser.counter;
+package org.apache.iotdb.db.metadata.mnode.iterator;
 
 import org.apache.iotdb.commons.exception.MetadataException;
-import org.apache.iotdb.commons.path.PartialPath;
-import org.apache.iotdb.db.metadata.mnode.IMNode;
+import org.apache.iotdb.db.metadata.mnode.IEntityMNode;
 import org.apache.iotdb.db.metadata.mtree.store.IMTreeStore;
-import org.apache.iotdb.db.metadata.mtree.traverser.Traverser;
+import org.apache.iotdb.db.metadata.template.Template;
 
-// This class define the count as traversal result.
-public abstract class CounterTraverser extends Traverser {
+import java.util.Map;
 
-  protected long count;
-
-  protected CounterTraverser(IMNode startNode, PartialPath path, IMTreeStore store)
+public class MemoryTraverserIterator extends AbstractTraverserIterator {
+  public MemoryTraverserIterator(
+      IMTreeStore store, IEntityMNode parent, Map<Integer, Template> templateMap)
       throws MetadataException {
-    super(startNode, path, store);
-  }
-
-  public long getCount() {
-    return count;
+    super(store, parent, templateMap);
   }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/ConfigMTree.java b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/ConfigMTree.java
index 98830a2ac6..fd43420803 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/ConfigMTree.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/ConfigMTree.java
@@ -34,11 +34,10 @@ import org.apache.iotdb.db.metadata.mnode.InternalMNode;
 import org.apache.iotdb.db.metadata.mnode.StorageGroupMNode;
 import org.apache.iotdb.db.metadata.mnode.iterator.IMNodeIterator;
 import org.apache.iotdb.db.metadata.mtree.store.MemMTreeStore;
-import org.apache.iotdb.db.metadata.mtree.traverser.collector.CollectorTraverser;
+import org.apache.iotdb.db.metadata.mtree.traverser.collector.DatabaseCollector;
 import org.apache.iotdb.db.metadata.mtree.traverser.collector.MNodeAboveSGCollector;
-import org.apache.iotdb.db.metadata.mtree.traverser.collector.StorageGroupCollector;
-import org.apache.iotdb.db.metadata.mtree.traverser.counter.CounterTraverser;
-import org.apache.iotdb.db.metadata.mtree.traverser.counter.StorageGroupCounter;
+import org.apache.iotdb.db.metadata.mtree.traverser.collector.MNodeCollector;
+import org.apache.iotdb.db.metadata.mtree.traverser.counter.DatabaseCounter;
 import org.apache.iotdb.db.metadata.utils.MetaFormatUtils;
 import org.apache.iotdb.tsfile.utils.Pair;
 import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils;
@@ -212,16 +211,16 @@ public class ConfigMTree {
       PartialPath pathPattern, boolean isPrefixMatch, boolean collectInternal)
       throws MetadataException {
     List<PartialPath> result = new LinkedList<>();
-    StorageGroupCollector<List<PartialPath>> collector =
-        new StorageGroupCollector<List<PartialPath>>(root, pathPattern, store) {
+    try (DatabaseCollector<List<PartialPath>> collector =
+        new DatabaseCollector<List<PartialPath>>(root, pathPattern, store, isPrefixMatch) {
           @Override
-          protected void collectStorageGroup(IStorageGroupMNode node) {
+          protected void collectDatabase(IStorageGroupMNode node) {
             result.add(node.getPartialPath());
           }
-        };
-    collector.setCollectInternal(collectInternal);
-    collector.setPrefixMatch(isPrefixMatch);
-    collector.traverse();
+        }) {
+      collector.setCollectInternal(collectInternal);
+      collector.traverse();
+    }
     return result;
   }
 
@@ -254,10 +253,9 @@ public class ConfigMTree {
    */
   public int getStorageGroupNum(PartialPath pathPattern, boolean isPrefixMatch)
       throws MetadataException {
-    CounterTraverser counter = new StorageGroupCounter(root, pathPattern, store);
-    counter.setPrefixMatch(isPrefixMatch);
-    counter.traverse();
-    return (int) counter.getCount();
+    try (DatabaseCounter counter = new DatabaseCounter(root, pathPattern, store, isPrefixMatch)) {
+      return (int) counter.count();
+    }
   }
 
   /**
@@ -394,19 +392,20 @@ public class ConfigMTree {
    */
   public Pair<List<PartialPath>, Set<PartialPath>> getNodesListInGivenLevel(
       PartialPath pathPattern, int nodeLevel, boolean isPrefixMatch) throws MetadataException {
-    MNodeAboveSGCollector<List<PartialPath>> collector =
-        new MNodeAboveSGCollector<List<PartialPath>>(root, pathPattern, store) {
+    List<PartialPath> result = new LinkedList<>();
+    try (MNodeAboveSGCollector<?> collector =
+        new MNodeAboveSGCollector<Void>(root, pathPattern, store, isPrefixMatch) {
           @Override
-          protected void transferToResult(IMNode node) {
-            resultSet.add(getCurrentPartialPath(node));
+          protected Void collectMNode(IMNode node) {
+            result.add(getPartialPathFromRootToNode(node));
+            return null;
           }
-        };
-    collector.setResultSet(new LinkedList<>());
-    collector.setTargetLevel(nodeLevel);
-    collector.setPrefixMatch(isPrefixMatch);
-    collector.traverse();
+        }) {
 
-    return new Pair<>(collector.getResult(), collector.getInvolvedStorageGroupMNodes());
+      collector.setTargetLevel(nodeLevel);
+      collector.traverse();
+      return new Pair<>(result, collector.getInvolvedStorageGroupMNodes());
+    }
   }
 
   /**
@@ -424,55 +423,21 @@ public class ConfigMTree {
    */
   public Pair<Set<TSchemaNode>, Set<PartialPath>> getChildNodePathInNextLevel(
       PartialPath pathPattern) throws MetadataException {
-    try {
-      MNodeAboveSGCollector<Set<TSchemaNode>> collector =
-          new MNodeAboveSGCollector<Set<TSchemaNode>>(
-              root, pathPattern.concatNode(ONE_LEVEL_PATH_WILDCARD), store) {
-            @Override
-            protected void transferToResult(IMNode node) {
-              resultSet.add(
-                  new TSchemaNode(
-                      getCurrentPartialPath(node).getFullPath(),
-                      node.getMNodeType(true).getNodeType()));
-            }
-          };
-      collector.setResultSet(new TreeSet<>());
-      collector.traverse();
-
-      return new Pair<>(collector.getResult(), collector.getInvolvedStorageGroupMNodes());
-    } catch (IllegalPathException e) {
-      throw new IllegalPathException(pathPattern.getFullPath());
-    }
-  }
-
-  /**
-   * Get child node path in the next level of the given path pattern. This method only count in
-   * nodes above database. Nodes below database, including database node will be counted by certain
-   * MTreeBelowSG.
-   *
-   * <p>give pathPattern and the child nodes is those matching pathPattern.*
-   *
-   * <p>e.g., MTree has [root.a.sg1.d1.s1, root.b.sg1.d1.s2, root.c.sg1.d2.s1] given path = root
-   * return [a, b]
-   *
-   * @param pathPattern The given path
-   * @return All child nodes' seriesPath(s) of given seriesPath.
-   */
-  public Pair<Set<String>, Set<PartialPath>> getChildNodeNameInNextLevel(PartialPath pathPattern)
-      throws MetadataException {
-    try {
-      MNodeAboveSGCollector<Set<String>> collector =
-          new MNodeAboveSGCollector<Set<String>>(
-              root, pathPattern.concatNode(ONE_LEVEL_PATH_WILDCARD), store) {
-            @Override
-            protected void transferToResult(IMNode node) {
-              resultSet.add(node.getName());
-            }
-          };
-      collector.setResultSet(new TreeSet<>());
+    Set<TSchemaNode> result = new TreeSet<>();
+    try (MNodeAboveSGCollector<?> collector =
+        new MNodeAboveSGCollector<Void>(
+            root, pathPattern.concatNode(ONE_LEVEL_PATH_WILDCARD), store, false) {
+          @Override
+          protected Void collectMNode(IMNode node) {
+            result.add(
+                new TSchemaNode(
+                    getPartialPathFromRootToNode(node).getFullPath(),
+                    node.getMNodeType(true).getNodeType()));
+            return null;
+          }
+        }) {
       collector.traverse();
-
-      return new Pair<>(collector.getResult(), collector.getInvolvedStorageGroupMNodes());
+      return new Pair<>(result, collector.getInvolvedStorageGroupMNodes());
     } catch (IllegalPathException e) {
       throw new IllegalPathException(pathPattern.getFullPath());
     }
@@ -535,39 +500,46 @@ public class ConfigMTree {
   public List<String> getPathsSetOnTemplate(int templateId, boolean filterPreUnset)
       throws MetadataException {
     List<String> resSet = new ArrayList<>();
-    CollectorTraverser<Set<String>> setTemplatePaths =
-        new CollectorTraverser<Set<String>>(root, new PartialPath(ALL_RESULT_NODES), store) {
+    try (MNodeCollector<?> collector =
+        new MNodeCollector<Void>(root, new PartialPath(ALL_RESULT_NODES), store, false) {
           @Override
-          protected boolean processInternalMatchedMNode(IMNode node, int idx, int level) {
-            // will never get here, implement for placeholder
+          protected boolean acceptFullMatchedNode(IMNode node) {
+            if (super.acceptFullMatchedNode(node)) {
+              // if node not set template, go on traversing
+              if (node.getSchemaTemplateId() != NON_TEMPLATE) {
+                if (filterPreUnset && node.isSchemaTemplatePreUnset()) {
+                  // filter the pre unset template
+                  return false;
+                }
+                // if set template, and equals to target or target for all, add to result
+                return templateId == ALL_TEMPLATE || templateId == node.getSchemaTemplateId();
+              }
+            }
             return false;
           }
 
           @Override
-          protected boolean processFullMatchedMNode(IMNode node, int idx, int level)
-              throws MetadataException {
-            // shall not traverse nodes inside template
-            if (!node.getPartialPath().equals(getCurrentPartialPath(node))) {
-              return true;
-            }
+          protected Void collectMNode(IMNode node) {
+            resSet.add(node.getFullPath());
+            return null;
+          }
 
-            // if node not set template, go on traversing
-            if (node.getSchemaTemplateId() != NON_TEMPLATE) {
-              if (filterPreUnset && node.isSchemaTemplatePreUnset()) {
-                // filter the pre unset template
-                return true;
-              }
-              // if set template, and equals to target or target for all, add to result
-              if (templateId == ALL_TEMPLATE || templateId == node.getSchemaTemplateId()) {
-                resSet.add(node.getFullPath());
-              }
-              // descendants of the node cannot set another template, exit from this branch
-              return true;
-            }
-            return false;
+          @Override
+          protected boolean shouldVisitSubtreeOfFullMatchedNode(IMNode node) {
+            // descendants of the node cannot set another template, exit from this branch
+            return (node.getSchemaTemplateId() == NON_TEMPLATE)
+                && super.shouldVisitSubtreeOfFullMatchedNode(node);
           }
-        };
-    setTemplatePaths.traverse();
+
+          @Override
+          protected boolean shouldVisitSubtreeOfInternalMatchedNode(IMNode node) {
+            // descendants of the node cannot set another template, exit from this branch
+            return (node.getSchemaTemplateId() == NON_TEMPLATE)
+                && super.shouldVisitSubtreeOfFullMatchedNode(node);
+          }
+        }) {
+      collector.traverse();
+    }
     return resSet;
   }
 
@@ -575,37 +547,44 @@ public class ConfigMTree {
   public Map<Integer, Set<PartialPath>> getTemplateSetInfo(PartialPath pathPattern)
       throws MetadataException {
     Map<Integer, Set<PartialPath>> result = new HashMap<>();
-    CollectorTraverser<List<Integer>> collector =
-        new CollectorTraverser<List<Integer>>(root, pathPattern, store) {
+    try (MNodeCollector<?> collector =
+        new MNodeCollector<Void>(root, pathPattern, store, false) {
           @Override
-          protected boolean processInternalMatchedMNode(IMNode node, int idx, int level)
-              throws MetadataException {
-            if (node.getSchemaTemplateId() != NON_TEMPLATE) {
-              // node set template
-              result
-                  .computeIfAbsent(node.getSchemaTemplateId(), k -> new HashSet<>())
-                  .add(getCurrentPartialPath(node));
-              // descendants of the node cannot set another template, exit from this branch
-              return true;
-            }
-            return false;
+          protected boolean acceptFullMatchedNode(IMNode node) {
+            return (node.getSchemaTemplateId() != NON_TEMPLATE)
+                || super.acceptFullMatchedNode(node);
           }
 
           @Override
-          protected boolean processFullMatchedMNode(IMNode node, int idx, int level)
-              throws MetadataException {
-            if (node.getSchemaTemplateId() != NON_TEMPLATE) {
-              // node set template
-              result
-                  .computeIfAbsent(node.getSchemaTemplateId(), k -> new HashSet<>())
-                  .add(getCurrentPartialPath(node));
-              // descendants of the node cannot set another template, exit from this branch
-              return true;
-            }
-            return false;
+          protected boolean acceptInternalMatchedNode(IMNode node) {
+            return (node.getSchemaTemplateId() != NON_TEMPLATE)
+                || super.acceptInternalMatchedNode(node);
           }
-        };
-    collector.traverse();
+
+          @Override
+          protected Void collectMNode(IMNode node) {
+            result
+                .computeIfAbsent(node.getSchemaTemplateId(), k -> new HashSet<>())
+                .add(getPartialPathFromRootToNode(node));
+            return null;
+          }
+
+          @Override
+          protected boolean shouldVisitSubtreeOfFullMatchedNode(IMNode node) {
+            // descendants of the node cannot set another template, exit from this branch
+            return (node.getSchemaTemplateId() == NON_TEMPLATE)
+                && super.shouldVisitSubtreeOfFullMatchedNode(node);
+          }
+
+          @Override
+          protected boolean shouldVisitSubtreeOfInternalMatchedNode(IMNode node) {
+            // descendants of the node cannot set another template, exit from this branch
+            return (node.getSchemaTemplateId() == NON_TEMPLATE)
+                && super.shouldVisitSubtreeOfFullMatchedNode(node);
+          }
+        }) {
+      collector.traverse();
+    }
     return result;
   }
 
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/IMTreeBelowSG.java b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/IMTreeBelowSG.java
index 29b10aff07..3525a4650e 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/IMTreeBelowSG.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/IMTreeBelowSG.java
@@ -97,6 +97,22 @@ public interface IMTreeBelowSG {
 
   boolean isEmptyInternalMNode(IMNode node) throws MetadataException;
 
+  /**
+   * Construct schema black list via setting matched timeseries to pre deleted.
+   *
+   * @param pathPattern path pattern
+   * @return PartialPath of timeseries that has been set to pre deleted
+   */
+  List<PartialPath> constructSchemaBlackList(PartialPath pathPattern) throws MetadataException;
+
+  /**
+   * Rollback schema black list via setting matched timeseries to not pre deleted.
+   *
+   * @param pathPattern path pattern
+   * @return PartialPath of timeseries that has been set to not pre deleted
+   */
+  List<PartialPath> rollbackSchemaBlackList(PartialPath pathPattern) throws MetadataException;
+
   /**
    * Get all pre-deleted timeseries matched by given pathPattern. For example, given path pattern
    * root.sg.*.s1 and pre-deleted timeseries root.sg.d1.s1, root.sg.d2.s1, then the result set is
@@ -152,16 +168,34 @@ public interface IMTreeBelowSG {
 
   List<IMeasurementMNode> getAllMeasurementMNode() throws MetadataException;
 
+  void activateTemplate(PartialPath activatePath, Template template) throws MetadataException;
+
   /**
-   * Get IMeasurementMNode by path pattern
+   * constructSchemaBlackListWithTemplate
    *
-   * @param pathPattern full path or path pattern with wildcard
-   * @return list of IMeasurementMNode
+   * @param templateSetInfo PathPattern and templateId to pre-deactivate
+   * @return Actual full path and templateId that has been pre-deactivated
    */
-  List<IMeasurementMNode> getMatchedMeasurementMNode(PartialPath pathPattern)
-      throws MetadataException;
+  Map<PartialPath, List<Integer>> constructSchemaBlackListWithTemplate(
+      Map<PartialPath, List<Integer>> templateSetInfo) throws MetadataException;
 
-  void activateTemplate(PartialPath activatePath, Template template) throws MetadataException;
+  /**
+   * rollbackSchemaBlackListWithTemplate
+   *
+   * @param templateSetInfo PathPattern and templateId to rollback pre-deactivate
+   * @return Actual full path and templateId that has been rolled back
+   */
+  Map<PartialPath, List<Integer>> rollbackSchemaBlackListWithTemplate(
+      Map<PartialPath, List<Integer>> templateSetInfo) throws MetadataException;
+
+  /**
+   * deactivateTemplateInBlackList
+   *
+   * @param templateSetInfo PathPattern and templateId to rollback deactivate
+   * @return Actual full path and templateId that has been deactivated
+   */
+  Map<PartialPath, List<Integer>> deactivateTemplateInBlackList(
+      Map<PartialPath, List<Integer>> templateSetInfo) throws MetadataException;
 
   long countPathsUsingTemplate(PartialPath pathPattern, int templateId) throws MetadataException;
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/MTreeBelowSGCachedImpl.java b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/MTreeBelowSGCachedImpl.java
index f7d4c74437..e35cec440d 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/MTreeBelowSGCachedImpl.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/MTreeBelowSGCachedImpl.java
@@ -33,6 +33,7 @@ import org.apache.iotdb.db.exception.metadata.PathNotExistException;
 import org.apache.iotdb.db.exception.metadata.template.TemplateImcompatibeException;
 import org.apache.iotdb.db.exception.metadata.template.TemplateIsInUseException;
 import org.apache.iotdb.db.metadata.MetadataConstant;
+import org.apache.iotdb.db.metadata.mnode.AboveDatabaseMNode;
 import org.apache.iotdb.db.metadata.mnode.IEntityMNode;
 import org.apache.iotdb.db.metadata.mnode.IMNode;
 import org.apache.iotdb.db.metadata.mnode.IMeasurementMNode;
@@ -41,10 +42,13 @@ import org.apache.iotdb.db.metadata.mnode.InternalMNode;
 import org.apache.iotdb.db.metadata.mnode.MeasurementMNode;
 import org.apache.iotdb.db.metadata.mnode.iterator.IMNodeIterator;
 import org.apache.iotdb.db.metadata.mtree.store.CachedMTreeStore;
+import org.apache.iotdb.db.metadata.mtree.traverser.TraverserWithLimitOffsetWrapper;
 import org.apache.iotdb.db.metadata.mtree.traverser.collector.EntityCollector;
 import org.apache.iotdb.db.metadata.mtree.traverser.collector.MNodeCollector;
 import org.apache.iotdb.db.metadata.mtree.traverser.collector.MeasurementCollector;
-import org.apache.iotdb.db.metadata.mtree.traverser.counter.CounterTraverser;
+import org.apache.iotdb.db.metadata.mtree.traverser.counter.EntityCounter;
+import org.apache.iotdb.db.metadata.mtree.traverser.updater.EntityUpdater;
+import org.apache.iotdb.db.metadata.mtree.traverser.updater.MeasurementUpdater;
 import org.apache.iotdb.db.metadata.plan.schemaregion.read.IShowDevicesPlan;
 import org.apache.iotdb.db.metadata.plan.schemaregion.read.IShowNodesPlan;
 import org.apache.iotdb.db.metadata.plan.schemaregion.read.IShowTimeSeriesPlan;
@@ -67,7 +71,6 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -103,6 +106,7 @@ public class MTreeBelowSGCachedImpl implements IMTreeBelowSG {
 
   private final CachedMTreeStore store;
   private volatile IStorageGroupMNode storageGroupMNode;
+  private volatile IMNode rootNode;
   private final Function<IMeasurementMNode, Map<String, String>> tagGetter;
   private final int levelOfSG;
 
@@ -115,23 +119,30 @@ public class MTreeBelowSGCachedImpl implements IMTreeBelowSG {
     this.tagGetter = tagGetter;
     store = new CachedMTreeStore(storageGroupPath, schemaRegionId);
     this.storageGroupMNode = store.getRoot().getAsStorageGroupMNode();
-
-    this.storageGroupMNode.setParent(generatePrefix(storageGroupPath));
+    this.rootNode = generatePrefix(storageGroupPath, this.storageGroupMNode);
     levelOfSG = storageGroupPath.getNodeLength() - 1;
   }
 
-  // generate the ancestor nodes of storageGroupNode
-  private IMNode generatePrefix(PartialPath storageGroupPath) {
+  /**
+   * Generate the ancestor nodes of storageGroupNode
+   *
+   * @return root node
+   */
+  private IMNode generatePrefix(
+      PartialPath storageGroupPath, IStorageGroupMNode storageGroupMNode) {
     String[] nodes = storageGroupPath.getNodes();
     // nodes[0] must be root
-    IMNode cur = new InternalMNode(null, nodes[0]);
+    IMNode root = new AboveDatabaseMNode(null, nodes[0]);
+    IMNode cur = root;
     IMNode child;
     for (int i = 1; i < nodes.length - 1; i++) {
-      child = new InternalMNode(cur, nodes[i]);
+      child = new AboveDatabaseMNode(cur, nodes[i]);
       cur.addChild(nodes[i], child);
       cur = child;
     }
-    return cur;
+    storageGroupMNode.setParent(cur);
+    cur.addChild(storageGroupMNode);
+    return root;
   }
 
   /** Only used for load snapshot */
@@ -143,24 +154,22 @@ public class MTreeBelowSGCachedImpl implements IMTreeBelowSG {
       throws MetadataException {
     this.store = store;
     this.storageGroupMNode = store.getRoot().getAsStorageGroupMNode();
-    this.storageGroupMNode.setParent(generatePrefix(storageGroupPath));
+    this.rootNode = generatePrefix(storageGroupPath, this.storageGroupMNode);
     levelOfSG = storageGroupMNode.getPartialPath().getNodeLength() - 1;
     this.tagGetter = tagGetter;
 
     // recover measurement
-    MeasurementCollector<?> collector =
+    try (MeasurementCollector<?> collector =
         new MeasurementCollector<Void>(
-            this.storageGroupMNode,
-            new PartialPath(storageGroupMNode.getFullPath()),
-            this.store,
-            false) {
+            this.rootNode, new PartialPath(storageGroupMNode.getFullPath()), this.store, true) {
           @Override
-          protected void collectMeasurement(IMeasurementMNode node) {
+          protected Void collectMeasurement(IMeasurementMNode node) {
             measurementProcess.accept(node);
+            return null;
           }
-        };
-    collector.setPrefixMatch(true);
-    collector.traverse();
+        }) {
+      collector.traverse();
+    }
   }
 
   @Override
@@ -169,6 +178,11 @@ public class MTreeBelowSGCachedImpl implements IMTreeBelowSG {
     storageGroupMNode = null;
   }
 
+  protected void replaceStorageGroupMNode(IStorageGroupMNode newMNode) {
+    this.storageGroupMNode.getParent().replaceChild(this.storageGroupMNode.getName(), newMNode);
+    this.storageGroupMNode = newMNode;
+  }
+
   @Override
   public boolean createSnapshot(File snapshotDir) {
     return store.createSnapshot(snapshotDir);
@@ -265,7 +279,7 @@ public class MTreeBelowSGCachedImpl implements IMTreeBelowSG {
           } else {
             entityMNode = store.setToEntity(device);
             if (entityMNode.isStorageGroup()) {
-              this.storageGroupMNode = entityMNode.getAsStorageGroupMNode();
+              replaceStorageGroupMNode(entityMNode.getAsStorageGroupMNode());
             }
             device = entityMNode;
           }
@@ -349,7 +363,7 @@ public class MTreeBelowSGCachedImpl implements IMTreeBelowSG {
             entityMNode = store.setToEntity(device);
             entityMNode.setAligned(true);
             if (entityMNode.isStorageGroup()) {
-              this.storageGroupMNode = entityMNode.getAsStorageGroupMNode();
+              replaceStorageGroupMNode(entityMNode.getAsStorageGroupMNode());
             }
             device = entityMNode;
           }
@@ -386,7 +400,7 @@ public class MTreeBelowSGCachedImpl implements IMTreeBelowSG {
   @Override
   public Map<Integer, MetadataException> checkMeasurementExistence(
       PartialPath devicePath, List<String> measurementList, List<String> aliasList) {
-    IMNode device = null;
+    IMNode device;
     try {
       device = getNodeByPath(devicePath);
     } catch (MetadataException e) {
@@ -546,7 +560,7 @@ public class MTreeBelowSGCachedImpl implements IMTreeBelowSG {
         synchronized (this) {
           curNode = store.setToInternal(entityMNode);
           if (curNode.isStorageGroup()) {
-            this.storageGroupMNode = curNode.getAsStorageGroupMNode();
+            replaceStorageGroupMNode(curNode.getAsStorageGroupMNode());
           }
         }
       }
@@ -578,22 +592,58 @@ public class MTreeBelowSGCachedImpl implements IMTreeBelowSG {
     }
   }
 
+  @Override
+  public List<PartialPath> constructSchemaBlackList(PartialPath pathPattern)
+      throws MetadataException {
+    List<PartialPath> result = new ArrayList<>();
+    try (MeasurementUpdater updater =
+        new MeasurementUpdater(rootNode, pathPattern, store, false) {
+          @Override
+          protected void updateMeasurement(IMeasurementMNode node) throws MetadataException {
+            node.setPreDeleted(true);
+            updateMNode(node);
+            result.add(getPartialPathFromRootToNode(node));
+          }
+        }) {
+      updater.update();
+    }
+    return result;
+  }
+
+  @Override
+  public List<PartialPath> rollbackSchemaBlackList(PartialPath pathPattern)
+      throws MetadataException {
+    List<PartialPath> result = new ArrayList<>();
+    try (MeasurementUpdater updater =
+        new MeasurementUpdater(rootNode, pathPattern, store, false) {
+          @Override
+          protected void updateMeasurement(IMeasurementMNode node) throws MetadataException {
+            node.setPreDeleted(false);
+            updateMNode(node);
+            result.add(getPartialPathFromRootToNode(node));
+          }
+        }) {
+      updater.update();
+    }
+    return result;
+  }
+
   @Override
   public List<PartialPath> getPreDeletedTimeseries(PartialPath pathPattern)
       throws MetadataException {
     List<PartialPath> result = new LinkedList<>();
-    MeasurementCollector<List<PartialPath>> collector =
-        new MeasurementCollector<List<PartialPath>>(storageGroupMNode, pathPattern, store) {
+    try (MeasurementCollector<Void> collector =
+        new MeasurementCollector<Void>(rootNode, pathPattern, store, false) {
           @Override
-          protected void collectMeasurement(IMeasurementMNode node) throws MetadataException {
+          protected Void collectMeasurement(IMeasurementMNode node) {
             if (node.isPreDeleted()) {
-              result.add(getCurrentPartialPath(node));
+              result.add(getPartialPathFromRootToNode(node));
             }
+            return null;
           }
-        };
-    collector.setResultSet(result);
-    collector.setShouldTraverseTemplate(false);
-    collector.traverse();
+        }) {
+      collector.traverse();
+    }
     return result;
   }
 
@@ -601,16 +651,19 @@ public class MTreeBelowSGCachedImpl implements IMTreeBelowSG {
   public Set<PartialPath> getDevicesOfPreDeletedTimeseries(PartialPath pathPattern)
       throws MetadataException {
     Set<PartialPath> result = new HashSet<>();
-    MeasurementCollector<List<PartialPath>> collector =
-        new MeasurementCollector<List<PartialPath>>(storageGroupMNode, pathPattern, store) {
+    try (MeasurementCollector<Void> collector =
+        new MeasurementCollector<Void>(rootNode, pathPattern, store, false) {
           @Override
-          protected void collectMeasurement(IMeasurementMNode node) throws MetadataException {
+          protected Void collectMeasurement(IMeasurementMNode node) {
             if (node.isPreDeleted()) {
-              result.add(getCurrentPartialPath(node).getDevicePath());
+              result.add(getPartialPathFromRootToNode(node).getDevicePath());
             }
+            return null;
           }
-        };
-    collector.traverse();
+        }) {
+      collector.traverse();
+    }
+
     return result;
   }
 
@@ -657,20 +710,24 @@ public class MTreeBelowSGCachedImpl implements IMTreeBelowSG {
   @Override
   public List<ShowDevicesResult> getDevices(IShowDevicesPlan plan) throws MetadataException {
     List<ShowDevicesResult> res = new ArrayList<>();
-    EntityCollector<List<ShowDevicesResult>> collector =
-        new EntityCollector<List<ShowDevicesResult>>(
-            storageGroupMNode, plan.getPath(), store, plan.getLimit(), plan.getOffset()) {
+    try (EntityCollector<ShowDevicesResult> collector =
+        new EntityCollector<ShowDevicesResult>(
+            rootNode, plan.getPath(), store, plan.isPrefixMatch()) {
           @Override
-          protected void collectEntity(IEntityMNode node) {
-            PartialPath device = getCurrentPartialPath(node);
-            res.add(new ShowDevicesResult(device.getFullPath(), node.isAligned()));
+          protected ShowDevicesResult collectEntity(IEntityMNode node) {
+            PartialPath device = getPartialPathFromRootToNode(node);
+            return new ShowDevicesResult(device.getFullPath(), node.isAligned());
           }
-        };
-    collector.setPrefixMatch(plan.isPrefixMatch());
-    if (plan.usingSchemaTemplate()) {
-      collector.setSchemaTemplateFilter(plan.getSchemaTemplateId());
+        }) {
+      if (plan.usingSchemaTemplate()) {
+        collector.setSchemaTemplateFilter(plan.getSchemaTemplateId());
+      }
+      TraverserWithLimitOffsetWrapper<ShowDevicesResult> traverser =
+          new TraverserWithLimitOffsetWrapper<>(collector, plan.getLimit(), plan.getOffset());
+      while (traverser.hasNext()) {
+        res.add(traverser.next());
+      }
     }
-    collector.traverse();
 
     return res;
   }
@@ -682,32 +739,31 @@ public class MTreeBelowSGCachedImpl implements IMTreeBelowSG {
       IShowTimeSeriesPlan plan,
       Function<Long, Pair<Map<String, String>, Map<String, String>>> tagAndAttributeProvider)
       throws MetadataException {
-    int limit = plan.getLimit();
-    int offset = plan.getOffset();
-
-    MeasurementCollector<List<ShowTimeSeriesResult>> collector =
-        new MeasurementCollector<List<ShowTimeSeriesResult>>(
-            storageGroupMNode, plan.getPath(), store, limit, offset) {
+    List<ShowTimeSeriesResult> result = new LinkedList<>();
+    try (MeasurementCollector<ShowTimeSeriesResult> collector =
+        new MeasurementCollector<ShowTimeSeriesResult>(
+            rootNode, plan.getPath(), store, plan.isPrefixMatch()) {
           @Override
-          protected void collectMeasurement(IMeasurementMNode node) {
+          protected ShowTimeSeriesResult collectMeasurement(IMeasurementMNode node) {
             Pair<Map<String, String>, Map<String, String>> tagAndAttribute =
                 tagAndAttributeProvider.apply(node.getOffset());
-            resultSet.add(
-                new ShowTimeSeriesResult(
-                    getCurrentPartialPath(node).getFullPath(),
-                    node.getAlias(),
-                    (MeasurementSchema) node.getSchema(),
-                    tagAndAttribute.left,
-                    tagAndAttribute.right,
-                    getCurrentNodeParent().getAsEntityMNode().isAligned()));
+            return new ShowTimeSeriesResult(
+                getPartialPathFromRootToNode(node).getFullPath(),
+                node.getAlias(),
+                (MeasurementSchema) node.getSchema(),
+                tagAndAttribute.left,
+                tagAndAttribute.right,
+                getParentOfNextMatchedNode().getAsEntityMNode().isAligned());
           }
-        };
-    collector.setPrefixMatch(plan.isPrefixMatch());
-    collector.setTemplateMap(plan.getRelatedTemplate());
-    collector.setResultSet(new LinkedList<>());
-    collector.traverse();
-
-    return collector.getResult();
+        }) {
+      collector.setTemplateMap(plan.getRelatedTemplate());
+      TraverserWithLimitOffsetWrapper<ShowTimeSeriesResult> traverser =
+          new TraverserWithLimitOffsetWrapper<>(collector, plan.getLimit(), plan.getOffset());
+      while (traverser.hasNext()) {
+        result.add(traverser.next());
+      }
+    }
+    return result;
   }
 
   // endregion
@@ -763,12 +819,12 @@ public class MTreeBelowSGCachedImpl implements IMTreeBelowSG {
       PartialPath pathPattern, Map<Integer, Template> templateMap, boolean withTags)
       throws MetadataException {
     List<MeasurementPath> result = new LinkedList<>();
-    MeasurementCollector<List<PartialPath>> collector =
-        new MeasurementCollector<List<PartialPath>>(storageGroupMNode, pathPattern, store) {
+    try (MeasurementCollector<Void> collector =
+        new MeasurementCollector<Void>(rootNode, pathPattern, store, false) {
           @Override
-          protected void collectMeasurement(IMeasurementMNode node) {
+          protected Void collectMeasurement(IMeasurementMNode node) {
             if (node.isPreDeleted()) {
-              return;
+              return null;
             }
             MeasurementPath path = getCurrentMeasurementPathInTraverse(node);
             if (nodes[nodes.length - 1].equals(node.getAlias())) {
@@ -779,10 +835,12 @@ public class MTreeBelowSGCachedImpl implements IMTreeBelowSG {
               path.setTagMap(tagGetter.apply(node));
             }
             result.add(path);
+            return null;
           }
-        };
-    collector.setTemplateMap(templateMap);
-    collector.traverse();
+        }) {
+      collector.setTemplateMap(templateMap);
+      collector.traverse();
+    }
     return result;
   }
 
@@ -824,24 +882,6 @@ public class MTreeBelowSGCachedImpl implements IMTreeBelowSG {
       }
     }
   }
-
-  @Override
-  public List<IMeasurementMNode> getMatchedMeasurementMNode(PartialPath pathPattern)
-      throws MetadataException {
-    List<IMeasurementMNode> result = new ArrayList<>();
-    MeasurementCollector<List<IMeasurementMNode>> collector =
-        new MeasurementCollector<List<IMeasurementMNode>>(storageGroupMNode, pathPattern, store) {
-          @Override
-          protected void collectMeasurement(IMeasurementMNode node) throws MetadataException {
-            pinMNode(node);
-            result.add(node);
-          }
-        };
-    collector.setShouldTraverseTemplate(false);
-    collector.traverse();
-    return result;
-  }
-
   // endregion
 
   // region Interfaces and Implementation for Template check and query
@@ -879,7 +919,7 @@ public class MTreeBelowSGCachedImpl implements IMTreeBelowSG {
         } else {
           entityMNode = store.setToEntity(cur);
           if (entityMNode.isStorageGroup()) {
-            this.storageGroupMNode = entityMNode.getAsStorageGroupMNode();
+            replaceStorageGroupMNode(entityMNode.getAsStorageGroupMNode());
           }
         }
       }
@@ -896,14 +936,15 @@ public class MTreeBelowSGCachedImpl implements IMTreeBelowSG {
     }
   }
 
+  @Override
   public Map<PartialPath, List<Integer>> constructSchemaBlackListWithTemplate(
       Map<PartialPath, List<Integer>> templateSetInfo) throws MetadataException {
     Map<PartialPath, List<Integer>> resultTemplateSetInfo = new HashMap<>();
     for (Map.Entry<PartialPath, List<Integer>> entry : templateSetInfo.entrySet()) {
-      EntityCollector<List<IEntityMNode>> collector =
-          new EntityCollector<List<IEntityMNode>>(storageGroupMNode, entry.getKey(), store) {
+      try (EntityUpdater updater =
+          new EntityUpdater(rootNode, entry.getKey(), store, false) {
             @Override
-            protected void collectEntity(IEntityMNode node) throws MetadataException {
+            protected void updateEntity(IEntityMNode node) throws MetadataException {
               if (entry.getValue().contains(node.getSchemaTemplateId())) {
                 resultTemplateSetInfo.put(
                     node.getPartialPath(), Collections.singletonList(node.getSchemaTemplateId()));
@@ -911,20 +952,22 @@ public class MTreeBelowSGCachedImpl implements IMTreeBelowSG {
                 store.updateMNode(node);
               }
             }
-          };
-      collector.traverse();
+          }) {
+        updater.update();
+      }
     }
     return resultTemplateSetInfo;
   }
 
+  @Override
   public Map<PartialPath, List<Integer>> rollbackSchemaBlackListWithTemplate(
       Map<PartialPath, List<Integer>> templateSetInfo) throws MetadataException {
     Map<PartialPath, List<Integer>> resultTemplateSetInfo = new HashMap<>();
     for (Map.Entry<PartialPath, List<Integer>> entry : templateSetInfo.entrySet()) {
-      EntityCollector<List<IEntityMNode>> collector =
-          new EntityCollector<List<IEntityMNode>>(storageGroupMNode, entry.getKey(), store) {
+      try (EntityUpdater updater =
+          new EntityUpdater(rootNode, entry.getKey(), store, false) {
             @Override
-            protected void collectEntity(IEntityMNode node) throws MetadataException {
+            protected void updateEntity(IEntityMNode node) throws MetadataException {
               if (entry.getValue().contains(node.getSchemaTemplateId())
                   && node.isPreDeactivateTemplate()) {
                 resultTemplateSetInfo.put(
@@ -933,20 +976,22 @@ public class MTreeBelowSGCachedImpl implements IMTreeBelowSG {
                 store.updateMNode(node);
               }
             }
-          };
-      collector.traverse();
+          }) {
+        updater.update();
+      }
     }
     return resultTemplateSetInfo;
   }
 
+  @Override
   public Map<PartialPath, List<Integer>> deactivateTemplateInBlackList(
       Map<PartialPath, List<Integer>> templateSetInfo) throws MetadataException {
     Map<PartialPath, List<Integer>> resultTemplateSetInfo = new HashMap<>();
     for (Map.Entry<PartialPath, List<Integer>> entry : templateSetInfo.entrySet()) {
-      EntityCollector<List<IEntityMNode>> collector =
-          new EntityCollector<List<IEntityMNode>>(storageGroupMNode, entry.getKey(), store) {
+      try (EntityUpdater collector =
+          new EntityUpdater(rootNode, entry.getKey(), store, false) {
             @Override
-            protected void collectEntity(IEntityMNode node) throws MetadataException {
+            protected void updateEntity(IEntityMNode node) throws MetadataException {
               if (entry.getValue().contains(node.getSchemaTemplateId())
                   && node.isPreDeactivateTemplate()) {
                 resultTemplateSetInfo.put(
@@ -956,8 +1001,9 @@ public class MTreeBelowSGCachedImpl implements IMTreeBelowSG {
                 deleteEmptyInternalMNodeAndReturnEmptyStorageGroup(node);
               }
             }
-          };
-      collector.traverse();
+          }) {
+        collector.traverse();
+      }
     }
     return resultTemplateSetInfo;
   }
@@ -965,23 +1011,10 @@ public class MTreeBelowSGCachedImpl implements IMTreeBelowSG {
   @Override
   public long countPathsUsingTemplate(PartialPath pathPattern, int templateId)
       throws MetadataException {
-    CounterTraverser counterTraverser =
-        new CounterTraverser(storageGroupMNode, pathPattern, store) {
-          @Override
-          protected boolean processInternalMatchedMNode(IMNode node, int idx, int level) {
-            return false;
-          }
-
-          @Override
-          protected boolean processFullMatchedMNode(IMNode node, int idx, int level) {
-            if (node.isEntity() && node.getAsEntityMNode().getSchemaTemplateId() == templateId) {
-              count++;
-            }
-            return false;
-          }
-        };
-    counterTraverser.traverse();
-    return counterTraverser.getCount();
+    try (EntityCounter counter = new EntityCounter(rootNode, pathPattern, store, false)) {
+      counter.setSchemaTemplateFilter(templateId);
+      return counter.count();
+    }
   }
 
   // endregion
@@ -1014,34 +1047,30 @@ public class MTreeBelowSGCachedImpl implements IMTreeBelowSG {
   // region Interfaces for schema reader
   public ISchemaReader<INodeSchemaInfo> getNodeReader(IShowNodesPlan showNodesPlan)
       throws MetadataException {
-    MNodeCollector<Set<INodeSchemaInfo>> collector =
-        new MNodeCollector<Set<INodeSchemaInfo>>(
-            storageGroupMNode, showNodesPlan.getPath(), store) {
+    MNodeCollector<INodeSchemaInfo> collector =
+        new MNodeCollector<INodeSchemaInfo>(
+            rootNode, showNodesPlan.getPath(), store, showNodesPlan.isPrefixMatch()) {
           @Override
-          protected void transferToResult(IMNode node) {
-            resultSet.add(
-                new ShowNodesResult(
-                    getCurrentPartialPath(node).getFullPath(), node.getMNodeType(false)));
+          protected INodeSchemaInfo collectMNode(IMNode node) {
+            return new ShowNodesResult(
+                getPartialPathFromRootToNode(node).getFullPath(), node.getMNodeType(false));
           }
         };
-    collector.setResultSet(new HashSet<>());
     collector.setTargetLevel(showNodesPlan.getLevel());
-    collector.setPrefixMatch(showNodesPlan.isPrefixMatch());
-    collector.traverse();
-
-    Iterator<INodeSchemaInfo> iterator = collector.getResult().iterator();
     return new ISchemaReader<INodeSchemaInfo>() {
       @Override
-      public void close() throws Exception {}
+      public void close() {
+        collector.close();
+      }
 
       @Override
       public boolean hasNext() {
-        return iterator.hasNext();
+        return collector.hasNext();
       }
 
       @Override
       public INodeSchemaInfo next() {
-        return iterator.next();
+        return collector.next();
       }
     };
   }
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/MTreeBelowSGMemoryImpl.java b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/MTreeBelowSGMemoryImpl.java
index 2920f6baa1..aff1496d27 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/MTreeBelowSGMemoryImpl.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/MTreeBelowSGMemoryImpl.java
@@ -33,6 +33,7 @@ import org.apache.iotdb.db.exception.metadata.PathNotExistException;
 import org.apache.iotdb.db.exception.metadata.template.TemplateImcompatibeException;
 import org.apache.iotdb.db.exception.metadata.template.TemplateIsInUseException;
 import org.apache.iotdb.db.metadata.MetadataConstant;
+import org.apache.iotdb.db.metadata.mnode.AboveDatabaseMNode;
 import org.apache.iotdb.db.metadata.mnode.IEntityMNode;
 import org.apache.iotdb.db.metadata.mnode.IMNode;
 import org.apache.iotdb.db.metadata.mnode.IMeasurementMNode;
@@ -41,10 +42,13 @@ import org.apache.iotdb.db.metadata.mnode.InternalMNode;
 import org.apache.iotdb.db.metadata.mnode.MeasurementMNode;
 import org.apache.iotdb.db.metadata.mnode.iterator.IMNodeIterator;
 import org.apache.iotdb.db.metadata.mtree.store.MemMTreeStore;
+import org.apache.iotdb.db.metadata.mtree.traverser.TraverserWithLimitOffsetWrapper;
 import org.apache.iotdb.db.metadata.mtree.traverser.collector.EntityCollector;
 import org.apache.iotdb.db.metadata.mtree.traverser.collector.MNodeCollector;
 import org.apache.iotdb.db.metadata.mtree.traverser.collector.MeasurementCollector;
-import org.apache.iotdb.db.metadata.mtree.traverser.counter.CounterTraverser;
+import org.apache.iotdb.db.metadata.mtree.traverser.counter.EntityCounter;
+import org.apache.iotdb.db.metadata.mtree.traverser.updater.EntityUpdater;
+import org.apache.iotdb.db.metadata.mtree.traverser.updater.MeasurementUpdater;
 import org.apache.iotdb.db.metadata.plan.schemaregion.read.IShowDevicesPlan;
 import org.apache.iotdb.db.metadata.plan.schemaregion.read.IShowNodesPlan;
 import org.apache.iotdb.db.metadata.plan.schemaregion.read.IShowTimeSeriesPlan;
@@ -67,7 +71,6 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -104,6 +107,7 @@ public class MTreeBelowSGMemoryImpl implements IMTreeBelowSG {
   // this implementation is based on memory, thus only MTree write operation must invoke MTreeStore
   private MemMTreeStore store;
   private volatile IStorageGroupMNode storageGroupMNode;
+  private volatile IMNode rootNode;
   private final Function<IMeasurementMNode, Map<String, String>> tagGetter;
   private int levelOfSG;
 
@@ -114,7 +118,7 @@ public class MTreeBelowSGMemoryImpl implements IMTreeBelowSG {
       int schemaRegionId) {
     store = new MemMTreeStore(storageGroupPath, true);
     this.storageGroupMNode = store.getRoot().getAsStorageGroupMNode();
-    this.storageGroupMNode.setParent(generatePrefix(storageGroupPath));
+    this.rootNode = generatePrefix(storageGroupPath, this.storageGroupMNode);
     levelOfSG = storageGroupPath.getNodeLength() - 1;
     this.tagGetter = tagGetter;
   }
@@ -126,23 +130,31 @@ public class MTreeBelowSGMemoryImpl implements IMTreeBelowSG {
       int schemaRegionId) {
     this.store = store;
     this.storageGroupMNode = store.getRoot().getAsStorageGroupMNode();
-    this.storageGroupMNode.setParent(generatePrefix(storageGroupPath));
+    this.rootNode = generatePrefix(storageGroupPath, this.storageGroupMNode);
     levelOfSG = storageGroupPath.getNodeLength() - 1;
     this.tagGetter = tagGetter;
   }
 
-  // generate the ancestor nodes of storageGroupNode
-  private IMNode generatePrefix(PartialPath storageGroupPath) {
+  /**
+   * Generate the ancestor nodes of storageGroupNode
+   *
+   * @return root node
+   */
+  private IMNode generatePrefix(
+      PartialPath storageGroupPath, IStorageGroupMNode storageGroupMNode) {
     String[] nodes = storageGroupPath.getNodes();
     // nodes[0] must be root
-    IMNode cur = new InternalMNode(null, nodes[0]);
+    IMNode root = new AboveDatabaseMNode(null, nodes[0]);
+    IMNode cur = root;
     IMNode child;
     for (int i = 1; i < nodes.length - 1; i++) {
-      child = new InternalMNode(cur, nodes[i]);
+      child = new AboveDatabaseMNode(cur, nodes[i]);
       cur.addChild(nodes[i], child);
       cur = child;
     }
-    return cur;
+    storageGroupMNode.setParent(cur);
+    cur.addChild(storageGroupMNode);
+    return root;
   }
 
   @Override
@@ -151,6 +163,11 @@ public class MTreeBelowSGMemoryImpl implements IMTreeBelowSG {
     storageGroupMNode = null;
   }
 
+  protected void replaceStorageGroupMNode(IStorageGroupMNode newMNode) {
+    this.storageGroupMNode.getParent().replaceChild(this.storageGroupMNode.getName(), newMNode);
+    this.storageGroupMNode = newMNode;
+  }
+
   @Override
   public synchronized boolean createSnapshot(File snapshotDir) {
     return store.createSnapshot(snapshotDir);
@@ -241,7 +258,7 @@ public class MTreeBelowSGMemoryImpl implements IMTreeBelowSG {
       } else {
         entityMNode = store.setToEntity(device);
         if (entityMNode.isStorageGroup()) {
-          this.storageGroupMNode = entityMNode.getAsStorageGroupMNode();
+          replaceStorageGroupMNode(entityMNode.getAsStorageGroupMNode());
         }
       }
 
@@ -323,7 +340,7 @@ public class MTreeBelowSGMemoryImpl implements IMTreeBelowSG {
         entityMNode = store.setToEntity(device);
         entityMNode.setAligned(true);
         if (entityMNode.isStorageGroup()) {
-          this.storageGroupMNode = entityMNode.getAsStorageGroupMNode();
+          replaceStorageGroupMNode(entityMNode.getAsStorageGroupMNode());
         }
       }
 
@@ -391,7 +408,7 @@ public class MTreeBelowSGMemoryImpl implements IMTreeBelowSG {
   @Override
   public Map<Integer, MetadataException> checkMeasurementExistence(
       PartialPath devicePath, List<String> measurementList, List<String> aliasList) {
-    IMNode device = null;
+    IMNode device;
     try {
       device = getNodeByPath(devicePath);
     } catch (PathNotExistException e) {
@@ -475,7 +492,7 @@ public class MTreeBelowSGMemoryImpl implements IMTreeBelowSG {
         synchronized (this) {
           curNode = store.setToInternal(entityMNode);
           if (curNode.isStorageGroup()) {
-            this.storageGroupMNode = curNode.getAsStorageGroupMNode();
+            replaceStorageGroupMNode(curNode.getAsStorageGroupMNode());
           }
         }
       }
@@ -501,22 +518,56 @@ public class MTreeBelowSGMemoryImpl implements IMTreeBelowSG {
         && node.getChildren().isEmpty();
   }
 
+  @Override
+  public List<PartialPath> constructSchemaBlackList(PartialPath pathPattern)
+      throws MetadataException {
+    List<PartialPath> result = new ArrayList<>();
+    try (MeasurementUpdater updater =
+        new MeasurementUpdater(rootNode, pathPattern, store, false) {
+          @Override
+          protected void updateMeasurement(IMeasurementMNode node) {
+            node.setPreDeleted(true);
+            result.add(getPartialPathFromRootToNode(node));
+          }
+        }) {
+      updater.update();
+    }
+    return result;
+  }
+
+  @Override
+  public List<PartialPath> rollbackSchemaBlackList(PartialPath pathPattern)
+      throws MetadataException {
+    List<PartialPath> result = new ArrayList<>();
+    try (MeasurementUpdater updater =
+        new MeasurementUpdater(rootNode, pathPattern, store, false) {
+          @Override
+          protected void updateMeasurement(IMeasurementMNode node) {
+            node.setPreDeleted(false);
+            result.add(getPartialPathFromRootToNode(node));
+          }
+        }) {
+      updater.update();
+    }
+    return result;
+  }
+
   @Override
   public List<PartialPath> getPreDeletedTimeseries(PartialPath pathPattern)
       throws MetadataException {
     List<PartialPath> result = new LinkedList<>();
-    MeasurementCollector<List<PartialPath>> collector =
-        new MeasurementCollector<List<PartialPath>>(storageGroupMNode, pathPattern, store) {
+    try (MeasurementCollector<Void> collector =
+        new MeasurementCollector<Void>(rootNode, pathPattern, store, false) {
           @Override
-          protected void collectMeasurement(IMeasurementMNode node) throws MetadataException {
+          protected Void collectMeasurement(IMeasurementMNode node) {
             if (node.isPreDeleted()) {
-              result.add(getCurrentPartialPath(node));
+              result.add(getPartialPathFromRootToNode(node));
             }
+            return null;
           }
-        };
-    collector.setResultSet(result);
-    collector.setShouldTraverseTemplate(false);
-    collector.traverse();
+        }) {
+      collector.traverse();
+    }
     return result;
   }
 
@@ -524,16 +575,18 @@ public class MTreeBelowSGMemoryImpl implements IMTreeBelowSG {
   public Set<PartialPath> getDevicesOfPreDeletedTimeseries(PartialPath pathPattern)
       throws MetadataException {
     Set<PartialPath> result = new HashSet<>();
-    MeasurementCollector<List<PartialPath>> collector =
-        new MeasurementCollector<List<PartialPath>>(storageGroupMNode, pathPattern, store) {
+    try (MeasurementCollector<Void> collector =
+        new MeasurementCollector<Void>(rootNode, pathPattern, store, false) {
           @Override
-          protected void collectMeasurement(IMeasurementMNode node) throws MetadataException {
+          protected Void collectMeasurement(IMeasurementMNode node) {
             if (node.isPreDeleted()) {
-              result.add(getCurrentPartialPath(node).getDevicePath());
+              result.add(getPartialPathFromRootToNode(node).getDevicePath());
             }
+            return null;
           }
-        };
-    collector.traverse();
+        }) {
+      collector.traverse();
+    }
     return result;
   }
 
@@ -575,21 +628,24 @@ public class MTreeBelowSGMemoryImpl implements IMTreeBelowSG {
   @Override
   public List<ShowDevicesResult> getDevices(IShowDevicesPlan plan) throws MetadataException {
     List<ShowDevicesResult> res = new ArrayList<>();
-    EntityCollector<List<ShowDevicesResult>> collector =
-        new EntityCollector<List<ShowDevicesResult>>(
-            storageGroupMNode, plan.getPath(), store, plan.getLimit(), plan.getOffset()) {
+    try (EntityCollector<ShowDevicesResult> collector =
+        new EntityCollector<ShowDevicesResult>(
+            rootNode, plan.getPath(), store, plan.isPrefixMatch()) {
           @Override
-          protected void collectEntity(IEntityMNode node) {
-            PartialPath device = getCurrentPartialPath(node);
-            res.add(new ShowDevicesResult(device.getFullPath(), node.isAligned()));
+          protected ShowDevicesResult collectEntity(IEntityMNode node) {
+            PartialPath device = getPartialPathFromRootToNode(node);
+            return new ShowDevicesResult(device.getFullPath(), node.isAligned());
           }
-        };
-    collector.setPrefixMatch(plan.isPrefixMatch());
-    if (plan.usingSchemaTemplate()) {
-      collector.setSchemaTemplateFilter(plan.getSchemaTemplateId());
+        }) {
+      if (plan.usingSchemaTemplate()) {
+        collector.setSchemaTemplateFilter(plan.getSchemaTemplateId());
+      }
+      TraverserWithLimitOffsetWrapper<ShowDevicesResult> traverser =
+          new TraverserWithLimitOffsetWrapper<>(collector, plan.getLimit(), plan.getOffset());
+      while (traverser.hasNext()) {
+        res.add(traverser.next());
+      }
     }
-    collector.traverse();
-
     return res;
   }
   // endregion
@@ -601,10 +657,10 @@ public class MTreeBelowSGMemoryImpl implements IMTreeBelowSG {
       PartialPath pathPattern, Map<Integer, Template> templateMap, boolean withTags)
       throws MetadataException {
     List<MeasurementPath> result = new LinkedList<>();
-    MeasurementCollector<List<PartialPath>> collector =
-        new MeasurementCollector<List<PartialPath>>(storageGroupMNode, pathPattern, store) {
+    try (MeasurementCollector<Void> collector =
+        new MeasurementCollector<Void>(rootNode, pathPattern, store, false) {
           @Override
-          protected void collectMeasurement(IMeasurementMNode node) {
+          protected Void collectMeasurement(IMeasurementMNode node) {
             MeasurementPath path = getCurrentMeasurementPathInTraverse(node);
             if (nodes[nodes.length - 1].equals(node.getAlias())) {
               // only when user query with alias, the alias in path will be set
@@ -614,11 +670,13 @@ public class MTreeBelowSGMemoryImpl implements IMTreeBelowSG {
               path.setTagMap(tagGetter.apply(node));
             }
             result.add(path);
+            return null;
           }
-        };
-    collector.setTemplateMap(templateMap);
-    collector.setSkipPreDeletedSchema(true);
-    collector.traverse();
+        }) {
+      collector.setTemplateMap(templateMap);
+      collector.setSkipPreDeletedSchema(true);
+      collector.traverse();
+    }
     return result;
   }
 
@@ -626,32 +684,31 @@ public class MTreeBelowSGMemoryImpl implements IMTreeBelowSG {
       IShowTimeSeriesPlan plan,
       Function<Long, Pair<Map<String, String>, Map<String, String>>> tagAndAttributeProvider)
       throws MetadataException {
-    int limit = plan.getLimit();
-    int offset = plan.getOffset();
-
-    MeasurementCollector<List<ShowTimeSeriesResult>> collector =
-        new MeasurementCollector<List<ShowTimeSeriesResult>>(
-            storageGroupMNode, plan.getPath(), store, limit, offset) {
+    List<ShowTimeSeriesResult> result = new LinkedList<>();
+    try (MeasurementCollector<ShowTimeSeriesResult> collector =
+        new MeasurementCollector<ShowTimeSeriesResult>(
+            rootNode, plan.getPath(), store, plan.isPrefixMatch()) {
           @Override
-          protected void collectMeasurement(IMeasurementMNode node) {
+          protected ShowTimeSeriesResult collectMeasurement(IMeasurementMNode node) {
             Pair<Map<String, String>, Map<String, String>> tagAndAttribute =
                 tagAndAttributeProvider.apply(node.getOffset());
-            resultSet.add(
-                new ShowTimeSeriesResult(
-                    getCurrentPartialPath(node).getFullPath(),
-                    node.getAlias(),
-                    (MeasurementSchema) node.getSchema(),
-                    tagAndAttribute.left,
-                    tagAndAttribute.right,
-                    getCurrentNodeParent().getAsEntityMNode().isAligned()));
+            return new ShowTimeSeriesResult(
+                getPartialPathFromRootToNode(node).getFullPath(),
+                node.getAlias(),
+                (MeasurementSchema) node.getSchema(),
+                tagAndAttribute.left,
+                tagAndAttribute.right,
+                getParentOfNextMatchedNode().getAsEntityMNode().isAligned());
           }
-        };
-    collector.setPrefixMatch(plan.isPrefixMatch());
-    collector.setTemplateMap(plan.getRelatedTemplate());
-    collector.setResultSet(new LinkedList<>());
-    collector.traverse();
-
-    return collector.getResult();
+        }) {
+      collector.setTemplateMap(plan.getRelatedTemplate());
+      TraverserWithLimitOffsetWrapper<ShowTimeSeriesResult> traverser =
+          new TraverserWithLimitOffsetWrapper<>(collector, plan.getLimit(), plan.getOffset());
+      while (traverser.hasNext()) {
+        result.add(traverser.next());
+      }
+    }
+    return result;
   }
 
   // endregion
@@ -697,7 +754,7 @@ public class MTreeBelowSGMemoryImpl implements IMTreeBelowSG {
   }
 
   @Override
-  public List<IMeasurementMNode> getAllMeasurementMNode() throws MetadataException {
+  public List<IMeasurementMNode> getAllMeasurementMNode() {
     IMNode cur = storageGroupMNode;
     // collect all the LeafMNode in this database
     List<IMeasurementMNode> leafMNodes = new LinkedList<>();
@@ -719,22 +776,6 @@ public class MTreeBelowSGMemoryImpl implements IMTreeBelowSG {
     return leafMNodes;
   }
 
-  @Override
-  public List<IMeasurementMNode> getMatchedMeasurementMNode(PartialPath pathPattern)
-      throws MetadataException {
-    List<IMeasurementMNode> result = new ArrayList<>();
-    MeasurementCollector<List<IMeasurementMNode>> collector =
-        new MeasurementCollector<List<IMeasurementMNode>>(storageGroupMNode, pathPattern, store) {
-          @Override
-          protected void collectMeasurement(IMeasurementMNode node) throws MetadataException {
-            result.add(node);
-          }
-        };
-    collector.setShouldTraverseTemplate(false);
-    collector.traverse();
-    return result;
-  }
-
   // endregion
 
   // region Interfaces and Implementation for Template check and query
@@ -767,7 +808,7 @@ public class MTreeBelowSGMemoryImpl implements IMTreeBelowSG {
       } else {
         entityMNode = store.setToEntity(cur);
         if (entityMNode.isStorageGroup()) {
-          this.storageGroupMNode = entityMNode.getAsStorageGroupMNode();
+          replaceStorageGroupMNode(entityMNode.getAsStorageGroupMNode());
         }
       }
     }
@@ -779,6 +820,76 @@ public class MTreeBelowSGMemoryImpl implements IMTreeBelowSG {
     entityMNode.setSchemaTemplateId(template.getId());
   }
 
+  @Override
+  public Map<PartialPath, List<Integer>> constructSchemaBlackListWithTemplate(
+      Map<PartialPath, List<Integer>> templateSetInfo) throws MetadataException {
+    Map<PartialPath, List<Integer>> resultTemplateSetInfo = new HashMap<>();
+    for (Map.Entry<PartialPath, List<Integer>> entry : templateSetInfo.entrySet()) {
+      try (EntityUpdater updater =
+          new EntityUpdater(rootNode, entry.getKey(), store, false) {
+            @Override
+            protected void updateEntity(IEntityMNode node) throws MetadataException {
+              if (entry.getValue().contains(node.getSchemaTemplateId())) {
+                resultTemplateSetInfo.put(
+                    node.getPartialPath(), Collections.singletonList(node.getSchemaTemplateId()));
+                node.preDeactivateTemplate();
+                store.updateMNode(node);
+              }
+            }
+          }) {
+        updater.update();
+      }
+    }
+    return resultTemplateSetInfo;
+  }
+
+  @Override
+  public Map<PartialPath, List<Integer>> rollbackSchemaBlackListWithTemplate(
+      Map<PartialPath, List<Integer>> templateSetInfo) throws MetadataException {
+    Map<PartialPath, List<Integer>> resultTemplateSetInfo = new HashMap<>();
+    for (Map.Entry<PartialPath, List<Integer>> entry : templateSetInfo.entrySet()) {
+      try (EntityUpdater updater =
+          new EntityUpdater(rootNode, entry.getKey(), store, false) {
+            @Override
+            protected void updateEntity(IEntityMNode node) {
+              if (entry.getValue().contains(node.getSchemaTemplateId())
+                  && node.isPreDeactivateTemplate()) {
+                resultTemplateSetInfo.put(
+                    node.getPartialPath(), Collections.singletonList(node.getSchemaTemplateId()));
+                node.rollbackPreDeactivateTemplate();
+              }
+            }
+          }) {
+        updater.update();
+      }
+    }
+    return resultTemplateSetInfo;
+  }
+
+  @Override
+  public Map<PartialPath, List<Integer>> deactivateTemplateInBlackList(
+      Map<PartialPath, List<Integer>> templateSetInfo) throws MetadataException {
+    Map<PartialPath, List<Integer>> resultTemplateSetInfo = new HashMap<>();
+    for (Map.Entry<PartialPath, List<Integer>> entry : templateSetInfo.entrySet()) {
+      try (EntityUpdater collector =
+          new EntityUpdater(rootNode, entry.getKey(), store, false) {
+            @Override
+            protected void updateEntity(IEntityMNode node) {
+              if (entry.getValue().contains(node.getSchemaTemplateId())
+                  && node.isPreDeactivateTemplate()) {
+                resultTemplateSetInfo.put(
+                    node.getPartialPath(), Collections.singletonList(node.getSchemaTemplateId()));
+                node.deactivateTemplate();
+                deleteEmptyInternalMNodeAndReturnEmptyStorageGroup(node);
+              }
+            }
+          }) {
+        collector.traverse();
+      }
+    }
+    return resultTemplateSetInfo;
+  }
+
   public void activateTemplateWithoutCheck(
       PartialPath activatePath, int templateId, boolean isAligned) {
     String[] nodes = activatePath.getNodes();
@@ -793,7 +904,7 @@ public class MTreeBelowSGMemoryImpl implements IMTreeBelowSG {
     } else {
       entityMNode = store.setToEntity(cur);
       if (entityMNode.isStorageGroup()) {
-        this.storageGroupMNode = entityMNode.getAsStorageGroupMNode();
+        replaceStorageGroupMNode(entityMNode.getAsStorageGroupMNode());
       }
     }
 
@@ -804,61 +915,13 @@ public class MTreeBelowSGMemoryImpl implements IMTreeBelowSG {
     entityMNode.setSchemaTemplateId(templateId);
   }
 
-  public List<IEntityMNode> getDeviceMNodeUsingTargetTemplate(
-      PartialPath pathPattern, List<Integer> templateIdList) throws MetadataException {
-    List<IEntityMNode> result = new ArrayList<>();
-    EntityCollector<List<IEntityMNode>> collector =
-        new EntityCollector<List<IEntityMNode>>(storageGroupMNode, pathPattern, store) {
-          @Override
-          protected void collectEntity(IEntityMNode node) {
-            if (templateIdList.contains(node.getSchemaTemplateId())) {
-              result.add(node);
-            }
-          }
-        };
-    collector.traverse();
-    return result;
-  }
-
-  public List<IEntityMNode> getPreDeactivatedDeviceMNode(
-      PartialPath pathPattern, List<Integer> templateIdList) throws MetadataException {
-    List<IEntityMNode> result = new ArrayList<>();
-    EntityCollector<List<IEntityMNode>> collector =
-        new EntityCollector<List<IEntityMNode>>(storageGroupMNode, pathPattern, store) {
-          @Override
-          protected void collectEntity(IEntityMNode node) {
-            if (templateIdList.contains(node.getSchemaTemplateId())
-                && node.isPreDeactivateTemplate()) {
-              result.add(node);
-            }
-          }
-        };
-    collector.traverse();
-    return result;
-  }
-
   @Override
   public long countPathsUsingTemplate(PartialPath pathPattern, int templateId)
       throws MetadataException {
-    CounterTraverser counterTraverser =
-        new CounterTraverser(storageGroupMNode, pathPattern, store) {
-          @Override
-          protected boolean processInternalMatchedMNode(IMNode node, int idx, int level)
-              throws MetadataException {
-            return false;
-          }
-
-          @Override
-          protected boolean processFullMatchedMNode(IMNode node, int idx, int level)
-              throws MetadataException {
-            if (node.isEntity() && node.getAsEntityMNode().getSchemaTemplateId() == templateId) {
-              count++;
-            }
-            return false;
-          }
-        };
-    counterTraverser.traverse();
-    return counterTraverser.getCount();
+    try (EntityCounter counter = new EntityCounter(rootNode, pathPattern, store, false)) {
+      counter.setSchemaTemplateFilter(templateId);
+      return counter.count();
+    }
   }
 
   // endregion
@@ -866,34 +929,31 @@ public class MTreeBelowSGMemoryImpl implements IMTreeBelowSG {
   // region Interfaces for schema reader
   public ISchemaReader<INodeSchemaInfo> getNodeReader(IShowNodesPlan showNodesPlan)
       throws MetadataException {
-    MNodeCollector<Set<INodeSchemaInfo>> collector =
-        new MNodeCollector<Set<INodeSchemaInfo>>(
-            storageGroupMNode, showNodesPlan.getPath(), store) {
+    MNodeCollector<INodeSchemaInfo> collector =
+        new MNodeCollector<INodeSchemaInfo>(
+            rootNode, showNodesPlan.getPath(), store, showNodesPlan.isPrefixMatch()) {
           @Override
-          protected void transferToResult(IMNode node) {
-            resultSet.add(
-                new ShowNodesResult(
-                    getCurrentPartialPath(node).getFullPath(), node.getMNodeType(false)));
+          protected INodeSchemaInfo collectMNode(IMNode node) {
+            return new ShowNodesResult(
+                getPartialPathFromRootToNode(node).getFullPath(), node.getMNodeType(false));
           }
         };
-    collector.setResultSet(new HashSet<>());
     collector.setTargetLevel(showNodesPlan.getLevel());
-    collector.setPrefixMatch(showNodesPlan.isPrefixMatch());
-    collector.traverse();
 
-    Iterator<INodeSchemaInfo> iterator = collector.getResult().iterator();
     return new ISchemaReader<INodeSchemaInfo>() {
       @Override
-      public void close() throws Exception {}
+      public void close() {
+        collector.close();
+      }
 
       @Override
       public boolean hasNext() {
-        return iterator.hasNext();
+        return collector.hasNext();
       }
 
       @Override
       public INodeSchemaInfo next() {
-        return iterator.next();
+        return collector.next();
       }
     };
   }
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/store/CachedMTreeStore.java b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/store/CachedMTreeStore.java
index fe84289890..fa6d1dda71 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/store/CachedMTreeStore.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/store/CachedMTreeStore.java
@@ -26,6 +26,8 @@ import org.apache.iotdb.db.metadata.mnode.IMNode;
 import org.apache.iotdb.db.metadata.mnode.IMeasurementMNode;
 import org.apache.iotdb.db.metadata.mnode.MNodeUtils;
 import org.apache.iotdb.db.metadata.mnode.estimator.IMNodeSizeEstimator;
+import org.apache.iotdb.db.metadata.mnode.iterator.AbstractTraverserIterator;
+import org.apache.iotdb.db.metadata.mnode.iterator.CachedTraverserIterator;
 import org.apache.iotdb.db.metadata.mnode.iterator.IMNodeIterator;
 import org.apache.iotdb.db.metadata.mtree.store.disk.ICachedMNodeContainer;
 import org.apache.iotdb.db.metadata.mtree.store.disk.MTreeFlushTaskManager;
@@ -36,6 +38,7 @@ import org.apache.iotdb.db.metadata.mtree.store.disk.memcontrol.IMemManager;
 import org.apache.iotdb.db.metadata.mtree.store.disk.memcontrol.MemManagerHolder;
 import org.apache.iotdb.db.metadata.mtree.store.disk.schemafile.ISchemaFile;
 import org.apache.iotdb.db.metadata.mtree.store.disk.schemafile.SchemaFile;
+import org.apache.iotdb.db.metadata.template.Template;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -44,6 +47,7 @@ import java.io.File;
 import java.io.IOException;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReadWriteLock;
@@ -193,6 +197,20 @@ public class CachedMTreeStore implements IMTreeStore {
     }
   }
 
+  @Override
+  public IMNodeIterator getTraverserIterator(
+      IMNode parent, Map<Integer, Template> templateMap, boolean skipPreDeletedSchema)
+      throws MetadataException {
+    if (parent.isEntity()) {
+      AbstractTraverserIterator iterator =
+          new CachedTraverserIterator(this, parent.getAsEntityMNode(), templateMap);
+      iterator.setSkipPreDeletedSchema(skipPreDeletedSchema);
+      return iterator;
+    } else {
+      return getChildrenIterator(parent);
+    }
+  }
+
   // must pin parent first
   @Override
   public IMNode addChild(IMNode parent, String childName, IMNode child) {
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/store/IMTreeStore.java b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/store/IMTreeStore.java
index 7e72dd8531..feac00898a 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/store/IMTreeStore.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/store/IMTreeStore.java
@@ -23,8 +23,10 @@ import org.apache.iotdb.db.metadata.mnode.IEntityMNode;
 import org.apache.iotdb.db.metadata.mnode.IMNode;
 import org.apache.iotdb.db.metadata.mnode.IMeasurementMNode;
 import org.apache.iotdb.db.metadata.mnode.iterator.IMNodeIterator;
+import org.apache.iotdb.db.metadata.template.Template;
 
 import java.io.File;
+import java.util.Map;
 
 /**
  * This interface defines the basic access methods of an MTreeStore.
@@ -55,6 +57,10 @@ public interface IMTreeStore {
 
   IMNodeIterator getChildrenIterator(IMNode parent) throws MetadataException;
 
+  IMNodeIterator getTraverserIterator(
+      IMNode parent, Map<Integer, Template> templateMap, boolean skipPreDeletedSchema)
+      throws MetadataException;
+
   IMNode addChild(IMNode parent, String childName, IMNode child);
 
   void deleteChild(IMNode parent, String childName) throws MetadataException;
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/store/MemMTreeStore.java b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/store/MemMTreeStore.java
index 19f5502fbd..c5ea4cf348 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/store/MemMTreeStore.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/store/MemMTreeStore.java
@@ -20,6 +20,7 @@ package org.apache.iotdb.db.metadata.mtree.store;
 
 import org.apache.iotdb.commons.conf.CommonDescriptor;
 import org.apache.iotdb.commons.conf.IoTDBConstant;
+import org.apache.iotdb.commons.exception.MetadataException;
 import org.apache.iotdb.commons.path.PartialPath;
 import org.apache.iotdb.db.metadata.mnode.IEntityMNode;
 import org.apache.iotdb.db.metadata.mnode.IMNode;
@@ -29,13 +30,17 @@ import org.apache.iotdb.db.metadata.mnode.MNodeUtils;
 import org.apache.iotdb.db.metadata.mnode.StorageGroupMNode;
 import org.apache.iotdb.db.metadata.mnode.estimator.BasicMNodSizeEstimator;
 import org.apache.iotdb.db.metadata.mnode.estimator.IMNodeSizeEstimator;
+import org.apache.iotdb.db.metadata.mnode.iterator.AbstractTraverserIterator;
 import org.apache.iotdb.db.metadata.mnode.iterator.IMNodeIterator;
 import org.apache.iotdb.db.metadata.mnode.iterator.MNodeIterator;
+import org.apache.iotdb.db.metadata.mnode.iterator.MemoryTraverserIterator;
 import org.apache.iotdb.db.metadata.mtree.snapshot.MemMTreeSnapshotUtil;
 import org.apache.iotdb.db.metadata.rescon.MemoryStatistics;
+import org.apache.iotdb.db.metadata.template.Template;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.Map;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.Consumer;
 
@@ -84,6 +89,20 @@ public class MemMTreeStore implements IMTreeStore {
     return new MNodeIterator(parent.getChildren().values().iterator());
   }
 
+  @Override
+  public IMNodeIterator getTraverserIterator(
+      IMNode parent, Map<Integer, Template> templateMap, boolean skipPreDeletedSchema)
+      throws MetadataException {
+    if (parent.isEntity()) {
+      AbstractTraverserIterator iterator =
+          new MemoryTraverserIterator(this, parent.getAsEntityMNode(), templateMap);
+      iterator.setSkipPreDeletedSchema(skipPreDeletedSchema);
+      return iterator;
+    } else {
+      return getChildrenIterator(parent);
+    }
+  }
+
   @Override
   public IMNode addChild(IMNode parent, String childName, IMNode child) {
     IMNode result = parent.addChild(childName, child);
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/Traverser.java b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/Traverser.java
index 53a534c78b..670a64954c 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/Traverser.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/Traverser.java
@@ -21,22 +21,16 @@ package org.apache.iotdb.db.metadata.mtree.traverser;
 import org.apache.iotdb.commons.exception.IllegalPathException;
 import org.apache.iotdb.commons.exception.MetadataException;
 import org.apache.iotdb.commons.path.PartialPath;
+import org.apache.iotdb.commons.schema.tree.AbstractTreeVisitor;
 import org.apache.iotdb.db.metadata.mnode.IMNode;
 import org.apache.iotdb.db.metadata.mnode.iterator.IMNodeIterator;
+import org.apache.iotdb.db.metadata.mnode.iterator.MNodeIterator;
 import org.apache.iotdb.db.metadata.mtree.store.IMTreeStore;
 import org.apache.iotdb.db.metadata.template.Template;
 
-import java.util.ArrayDeque;
-import java.util.Arrays;
-import java.util.Deque;
 import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
 import java.util.Map;
-import java.util.regex.Pattern;
 
-import static org.apache.iotdb.commons.conf.IoTDBConstant.MULTI_LEVEL_PATH_WILDCARD;
-import static org.apache.iotdb.commons.conf.IoTDBConstant.ONE_LEVEL_PATH_WILDCARD;
 import static org.apache.iotdb.commons.conf.IoTDBConstant.PATH_ROOT;
 import static org.apache.iotdb.db.metadata.MetadataConstant.NON_TEMPLATE;
 
@@ -50,23 +44,14 @@ import static org.apache.iotdb.db.metadata.MetadataConstant.NON_TEMPLATE;
  *   <li>collector: to collect customized results of the matched node or measurement
  * </ol>
  */
-public abstract class Traverser {
+public abstract class Traverser<R> extends AbstractTreeVisitor<IMNode, R> {
 
   protected IMTreeStore store;
 
   protected IMNode startNode;
   protected String[] nodes;
-  protected int startIndex;
-  protected int startLevel;
-  protected boolean isPrefixStart = false;
 
-  // to construct full path or find mounted node on MTree when traverse into template
-  protected Deque<IMNode> traverseContext;
-
-  protected boolean isInTemplate = false;
-
-  // if true, measurement in template should be processed
-  protected boolean shouldTraverseTemplate = false;
+  // measurement in template should be processed only if templateMap is not null
   protected Map<Integer, Template> templateMap;
 
   // if true, the pre deleted measurement or pre deactivated template won't be processed
@@ -75,6 +60,8 @@ public abstract class Traverser {
   // default false means fullPath pattern match
   protected boolean isPrefixMatch = false;
 
+  protected Traverser() {}
+
   /**
    * To traverse subtree under root.sg, e.g., init Traverser(root, "root.sg.**")
    *
@@ -82,8 +69,9 @@ public abstract class Traverser {
    * @param path use wildcard to specify which part to traverse
    * @throws MetadataException
    */
-  protected Traverser(IMNode startNode, PartialPath path, IMTreeStore store)
+  protected Traverser(IMNode startNode, PartialPath path, IMTreeStore store, boolean isPrefixMatch)
       throws MetadataException {
+    super(startNode, path, isPrefixMatch);
     String[] nodes = path.getNodes();
     if (nodes.length == 0 || !nodes[0].equals(PATH_ROOT)) {
       throw new IllegalPathException(
@@ -92,49 +80,6 @@ public abstract class Traverser {
     this.startNode = startNode;
     this.nodes = nodes;
     this.store = store;
-    this.traverseContext = new ArrayDeque<>();
-    initStartIndexAndLevel(path);
-  }
-
-  /**
-   * The traverser may start traversing from a storageGroupMNode, which is an InternalMNode of the
-   * whole MTree.
-   */
-  private void initStartIndexAndLevel(PartialPath path) throws MetadataException {
-    IMNode parent = startNode.getParent();
-    Deque<IMNode> ancestors = new ArrayDeque<>();
-    ancestors.push(startNode);
-
-    startLevel = 0;
-    while (parent != null) {
-      startLevel++;
-      traverseContext.addLast(parent);
-
-      ancestors.push(parent);
-      parent = parent.getParent();
-    }
-
-    IMNode cur;
-    // given root.a.sg, accept path starting with prefix like root.a.sg, root.*.*, root.**,
-    // root.a.**, which means the prefix matches the startNode's fullPath
-    for (startIndex = 0; startIndex <= startLevel && startIndex < nodes.length; startIndex++) {
-      cur = ancestors.pop();
-      if (nodes[startIndex].equals(MULTI_LEVEL_PATH_WILDCARD)) {
-        return;
-      } else if (!nodes[startIndex].equals(cur.getName())
-          && !nodes[startIndex].contains(ONE_LEVEL_PATH_WILDCARD)) {
-        throw new IllegalPathException(
-            path.getFullPath(), path.getFullPath() + " doesn't start with " + cur.getFullPath());
-      }
-    }
-
-    if (startIndex <= startLevel) {
-      if (!nodes[startIndex - 1].equals(MULTI_LEVEL_PATH_WILDCARD)) {
-        isPrefixStart = true;
-      }
-    } else {
-      startIndex--;
-    }
   }
 
   /**
@@ -142,380 +87,61 @@ public abstract class Traverser {
    * overriding or implement concerned methods.
    */
   public void traverse() throws MetadataException {
-    if (isPrefixStart && !isPrefixMatch) {
-      return;
-    }
-    traverse(startNode, startIndex, startLevel);
-  }
-
-  /**
-   * The recursive method for MTree traversal. If the node matches nodes[idx], then do some
-   * operation and traverse the children with nodes[idx+1].
-   *
-   * @param node current node that match the targetName in given path
-   * @param idx the index of targetName in given path
-   * @param level the level of current node in MTree
-   * @throws MetadataException some result process may throw MetadataException
-   */
-  protected void traverse(IMNode node, int idx, int level) throws MetadataException {
-
-    if (processMatchedMNode(node, idx, level)) {
-      return;
+    while (hasNext()) {
+      next();
     }
-
-    if (idx >= nodes.length - 1) {
-      if (nodes[nodes.length - 1].equals(MULTI_LEVEL_PATH_WILDCARD) || isPrefixMatch) {
-        processMultiLevelWildcard(node, idx, level);
-      }
-      return;
-    }
-
-    if (node.isMeasurement()) {
-      return;
-    }
-
-    String targetName = nodes[idx + 1];
-    if (MULTI_LEVEL_PATH_WILDCARD.equals(targetName)) {
-      processMultiLevelWildcard(node, idx, level);
-    } else if (targetName.contains(ONE_LEVEL_PATH_WILDCARD)) {
-      processOneLevelWildcard(node, idx, level);
-    } else {
-      processNameMatch(node, idx, level);
+    if (!isSuccess()) {
+      Throwable e = getFailure();
+      throw new MetadataException(e.getMessage(), e);
     }
   }
 
-  /**
-   * process curNode that matches the targetName during traversal. there are two cases: 1. internal
-   * match: root.sg internal match root.sg.**(pattern) 2. full match: root.sg.d full match
-   * root.sg.**(pattern) Both of them are default abstract and should be implemented according
-   * concrete tasks.
-   *
-   * @return whether this branch of recursive traversal should stop; if true, stop
-   */
-  private boolean processMatchedMNode(IMNode node, int idx, int level) throws MetadataException {
-    if (idx < nodes.length - 1) {
-      return processInternalMatchedMNode(node, idx, level);
+  @Override
+  protected IMNode getChild(IMNode parent, String childName) throws MetadataException {
+    IMNode child = null;
+    if (parent.isAboveDatabase()) {
+      child = parent.getChild(childName);
     } else {
-      return processFullMatchedMNode(node, idx, level);
-    }
-  }
-
-  /**
-   * internal match: root.sg internal match root.sg.**(pattern)
-   *
-   * @return whether this branch of recursive traversal should stop; if true, stop
-   */
-  protected abstract boolean processInternalMatchedMNode(IMNode node, int idx, int level)
-      throws MetadataException;
-
-  /**
-   * full match: root.sg.d full match root.sg.**(pattern)
-   *
-   * @return whether this branch of recursive traversal should stop; if true, stop
-   */
-  protected abstract boolean processFullMatchedMNode(IMNode node, int idx, int level)
-      throws MetadataException;
-
-  protected void processMultiLevelWildcard(IMNode node, int idx, int level)
-      throws MetadataException {
-    if (isInTemplate) {
-      traverseContext.push(node);
-      for (IMNode child : node.getChildren().values()) {
-        traverse(child, idx + 1, level + 1);
-      }
-      traverseContext.pop();
-      return;
-    }
-
-    traverseContext.push(node);
-    IMNode child;
-    IMNodeIterator iterator = store.getChildrenIterator(node);
-    try {
-      while (iterator.hasNext()) {
-        child = iterator.next();
-        try {
-          traverse(child, idx + 1, level + 1);
-        } finally {
-          store.unPin(child);
+      if (parent.getSchemaTemplateId() != NON_TEMPLATE) {
+        if (!skipPreDeletedSchema || !parent.getAsEntityMNode().isPreDeactivateTemplate()) {
+          child = templateMap.get(parent.getSchemaTemplateId()).getDirectNode(childName);
         }
       }
-    } finally {
-      iterator.close();
-    }
-
-    traverseContext.pop();
-
-    if (!shouldTraverseTemplate) {
-      return;
-    }
-
-    if (!node.isUseTemplate()) {
-      return;
     }
-
-    Template schemaTemplate = getActivatedSchemaTemplate(node);
-    if (schemaTemplate == null) {
-      // template == null means the template used by this node is not related to this query in new
-      // cluster.
-      return;
+    if (child == null) {
+      child = store.getChild(parent, childName);
     }
-    isInTemplate = true;
-    traverseContext.push(node);
-    for (IMNode childInTemplate : schemaTemplate.getDirectNodes()) {
-      traverse(childInTemplate, idx + 1, level + 1);
-    }
-    traverseContext.pop();
-    isInTemplate = false;
+    return child;
   }
 
-  protected void processOneLevelWildcard(IMNode node, int idx, int level) throws MetadataException {
-    boolean multiLevelWildcard = nodes[idx].equals(MULTI_LEVEL_PATH_WILDCARD);
-    String targetNameRegex = nodes[idx + 1].replace("*", ".*");
-
-    if (isInTemplate) {
-      traverseContext.push(node);
-      for (IMNode child : node.getChildren().values()) {
-        if (!Pattern.matches(targetNameRegex, child.getName())) {
-          continue;
-        }
-        traverse(child, idx + 1, level + 1);
-      }
-      traverseContext.pop();
-
-      if (multiLevelWildcard) {
-        traverseContext.push(node);
-        for (IMNode child : node.getChildren().values()) {
-          traverse(child, idx, level + 1);
-        }
-        traverseContext.pop();
-      }
-      return;
-    }
-
-    traverseContext.push(node);
-    IMNode child;
-    IMNodeIterator iterator = store.getChildrenIterator(node);
-    try {
-      while (iterator.hasNext()) {
-        child = iterator.next();
-        try {
-          if (child.isMeasurement()) {
-            String alias = child.getAsMeasurementMNode().getAlias();
-            if (!Pattern.matches(targetNameRegex, child.getName())
-                && !(alias != null && Pattern.matches(targetNameRegex, alias))) {
-              continue;
-            }
-          } else {
-            if (!Pattern.matches(targetNameRegex, child.getName())) {
-              continue;
-            }
-          }
-          traverse(child, idx + 1, level + 1);
-        } finally {
-          store.unPin(child);
-        }
-      }
-    } finally {
-      iterator.close();
-    }
-
-    traverseContext.pop();
-
-    if (multiLevelWildcard) {
-      traverseContext.push(node);
-      iterator = store.getChildrenIterator(node);
-      try {
-        while (iterator.hasNext()) {
-          child = iterator.next();
-          try {
-            traverse(child, idx, level + 1);
-          } finally {
-            store.unPin(child);
-          }
-        }
-      } finally {
-        iterator.close();
-      }
-      traverseContext.pop();
-    }
-
-    if (!shouldTraverseTemplate) {
-      return;
-    }
-
-    if (!node.isUseTemplate()) {
-      return;
-    }
-
-    Template schemaTemplate = getActivatedSchemaTemplate(node);
-    if (schemaTemplate == null) {
-      // template == null means the template used by this node is not related to this query in new
-      // cluster.
-      return;
+  @Override
+  protected void releaseNode(IMNode node) {
+    if (!node.isAboveDatabase() && !node.isStorageGroup()) {
+      // In any case we can call store#inpin directly because the unpin method will not do anything
+      // if it is an IMNode in template or in memory mode.
+      store.unPin(node);
     }
-    isInTemplate = true;
-    traverseContext.push(node);
-    for (IMNode childInTemplate : schemaTemplate.getDirectNodes()) {
-      if (!Pattern.matches(targetNameRegex, childInTemplate.getName())) {
-        continue;
-      }
-      traverse(childInTemplate, idx + 1, level + 1);
-    }
-    traverseContext.pop();
-
-    if (multiLevelWildcard) {
-      traverseContext.push(node);
-      for (IMNode childInTemplate : schemaTemplate.getDirectNodes()) {
-        traverse(childInTemplate, idx, level + 1);
-      }
-      traverseContext.pop();
-    }
-    isInTemplate = false;
   }
 
-  @SuppressWarnings("Duplicates")
-  protected void processNameMatch(IMNode node, int idx, int level) throws MetadataException {
-    boolean multiLevelWildcard = nodes[idx].equals(MULTI_LEVEL_PATH_WILDCARD);
-    String targetName = nodes[idx + 1];
-
-    if (isInTemplate) {
-      IMNode targetNode = node.getChild(targetName);
-      if (targetNode != null) {
-        traverseContext.push(node);
-        traverse(targetNode, idx + 1, level + 1);
-        traverseContext.pop();
-      }
-
-      if (multiLevelWildcard) {
-        traverseContext.push(node);
-        for (IMNode child : node.getChildren().values()) {
-          traverse(child, idx, level + 1);
-        }
-        traverseContext.pop();
-      }
-      return;
-    }
-
-    IMNode next = store.getChild(node, targetName);
-    if (next != null) {
-      try {
-        traverseContext.push(node);
-        traverse(next, idx + 1, level + 1);
-        traverseContext.pop();
-      } finally {
-        store.unPin(next);
-      }
-    }
-
-    if (multiLevelWildcard) {
-      traverseContext.push(node);
-      IMNode child;
-      IMNodeIterator iterator = store.getChildrenIterator(node);
-      try {
-        while (iterator.hasNext()) {
-          child = iterator.next();
-          try {
-            traverse(child, idx, level + 1);
-          } finally {
-            store.unPin(child);
-          }
-        }
-      } finally {
-        iterator.close();
-      }
-      traverseContext.pop();
-    }
-
-    if (!shouldTraverseTemplate) {
-      return;
-    }
-
-    if (!node.isUseTemplate()) {
-      return;
-    }
-
-    Template schemaTemplate = getActivatedSchemaTemplate(node);
-    if (schemaTemplate == null) {
-      // template == null means the template used by this node is not related to this query in new
-      // cluster.
-      return;
-    }
-    isInTemplate = true;
-    IMNode targetNode = schemaTemplate.getDirectNode(targetName);
-    if (targetNode != null) {
-      traverseContext.push(node);
-      traverse(targetNode, idx + 1, level + 1);
-      traverseContext.pop();
-    }
-
-    if (multiLevelWildcard) {
-      traverseContext.push(node);
-      for (IMNode child : schemaTemplate.getDirectNodes()) {
-        traverse(child, idx, level + 1);
-      }
-      traverseContext.pop();
+  @Override
+  protected Iterator<IMNode> getChildrenIterator(IMNode parent) throws MetadataException {
+    if (parent.isAboveDatabase()) {
+      return new MNodeIterator(parent.getChildren().values().iterator());
+    } else {
+      return store.getTraverserIterator(parent, templateMap, skipPreDeletedSchema);
     }
-    isInTemplate = false;
   }
 
-  protected Template getActivatedSchemaTemplate(IMNode node) {
-    // new cluster, the used template is directly recorded as template id in device mnode
-    if (node.getSchemaTemplateId() != NON_TEMPLATE) {
-      if (skipPreDeletedSchema && node.getAsEntityMNode().isPreDeactivateTemplate()) {
-        // skip this pre deactivated template, the invoker will skip this
-        return null;
-      }
-      return templateMap.get(node.getSchemaTemplateId());
-    }
-    // if the node is usingTemplate, the upperTemplate won't be null or the upperTemplateId won't be
-    // NON_TEMPLATE.
-    throw new IllegalStateException(
-        String.format(
-            "There should be a template mounted on any ancestor of the node [%s] usingTemplate.",
-            node.getFullPath()));
+  @Override
+  protected void releaseNodeIterator(Iterator<IMNode> nodeIterator) {
+    ((IMNodeIterator) nodeIterator).close();
   }
 
   public void setTemplateMap(Map<Integer, Template> templateMap) {
     this.templateMap = templateMap;
   }
 
-  public void setPrefixMatch(boolean isPrefixMatch) {
-    this.isPrefixMatch = isPrefixMatch;
-  }
-
-  public void setShouldTraverseTemplate(boolean shouldTraverseTemplate) {
-    this.shouldTraverseTemplate = shouldTraverseTemplate;
-  }
-
   public void setSkipPreDeletedSchema(boolean skipPreDeletedSchema) {
     this.skipPreDeletedSchema = skipPreDeletedSchema;
   }
-
-  /**
-   * @param currentNode the node need to get the full path of
-   * @return full path from traverse start node to the current node
-   */
-  protected PartialPath getCurrentPartialPath(IMNode currentNode) {
-    return new PartialPath(getCurrentPathNodes(currentNode));
-  }
-
-  protected String[] getCurrentPathNodes(IMNode currentNode) {
-    Iterator<IMNode> nodes = traverseContext.descendingIterator();
-    List<String> nodeNames = new LinkedList<>();
-    if (nodes.hasNext()) {
-      nodeNames.addAll(Arrays.asList(nodes.next().getPartialPath().getNodes()));
-    }
-
-    while (nodes.hasNext()) {
-      nodeNames.add(nodes.next().getName());
-    }
-
-    nodeNames.add(currentNode.getName());
-
-    return nodeNames.toArray(new String[0]);
-  }
-
-  protected IMNode getCurrentNodeParent() {
-    return traverseContext.peek();
-  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/TraverserWithLimitOffsetWrapper.java b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/TraverserWithLimitOffsetWrapper.java
new file mode 100644
index 0000000000..f43468756e
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/TraverserWithLimitOffsetWrapper.java
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+package org.apache.iotdb.db.metadata.mtree.traverser;
+
+import org.apache.iotdb.commons.exception.MetadataException;
+import org.apache.iotdb.db.metadata.mnode.IMNode;
+
+import java.util.NoSuchElementException;
+
+public class TraverserWithLimitOffsetWrapper<R> extends Traverser<R> {
+  private final Traverser<R> traverser;
+  private final int limit;
+  private final int offset;
+  private final boolean hasLimit;
+
+  private int count = 0;
+  int curOffset = 0;
+
+  public TraverserWithLimitOffsetWrapper(Traverser<R> traverser, int limit, int offset) {
+    this.traverser = traverser;
+    this.limit = limit;
+    this.offset = offset;
+    hasLimit = limit > 0 || offset > 0;
+
+    if (hasLimit) {
+      while (curOffset < offset && traverser.hasNext()) {
+        traverser.next();
+        curOffset++;
+      }
+    }
+  }
+
+  @Override
+  public boolean hasNext() {
+    if (hasLimit) {
+      return count < limit && traverser.hasNext();
+    } else {
+      return traverser.hasNext();
+    }
+  }
+
+  @Override
+  public R next() {
+    if (!hasNext()) {
+      throw new NoSuchElementException();
+    }
+    R result = traverser.next();
+    if (hasLimit) {
+      count++;
+    }
+    return result;
+  }
+
+  @Override
+  public void traverse() throws MetadataException {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  protected boolean shouldVisitSubtreeOfInternalMatchedNode(IMNode node) {
+    return false;
+  }
+
+  @Override
+  protected boolean shouldVisitSubtreeOfFullMatchedNode(IMNode node) {
+    return false;
+  }
+
+  @Override
+  protected boolean acceptInternalMatchedNode(IMNode node) {
+    return false;
+  }
+
+  @Override
+  protected boolean acceptFullMatchedNode(IMNode node) {
+    return false;
+  }
+
+  @Override
+  protected R generateResult(IMNode nextMatchedNode) {
+    return null;
+  }
+
+  @Override
+  public void close() {
+    traverser.close();
+  }
+
+  @Override
+  public void reset() {
+    traverser.reset();
+    count = 0;
+    curOffset = 0;
+    if (hasLimit) {
+      while (curOffset < offset && traverser.hasNext()) {
+        traverser.next();
+        curOffset++;
+      }
+    }
+  }
+
+  public int getNextOffset() {
+    return curOffset + count;
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/StorageGroupCollector.java b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/basic/DatabaseTraverser.java
similarity index 53%
copy from server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/StorageGroupCollector.java
copy to server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/basic/DatabaseTraverser.java
index 2017efbb5f..9a177b4a76 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/StorageGroupCollector.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/basic/DatabaseTraverser.java
@@ -16,45 +16,52 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.iotdb.db.metadata.mtree.traverser.collector;
+package org.apache.iotdb.db.metadata.mtree.traverser.basic;
 
 import org.apache.iotdb.commons.exception.MetadataException;
 import org.apache.iotdb.commons.path.PartialPath;
 import org.apache.iotdb.db.metadata.mnode.IMNode;
-import org.apache.iotdb.db.metadata.mnode.IStorageGroupMNode;
 import org.apache.iotdb.db.metadata.mtree.store.IMTreeStore;
+import org.apache.iotdb.db.metadata.mtree.traverser.Traverser;
 
-// This class implements database path collection function.
-public abstract class StorageGroupCollector<T> extends CollectorTraverser<T> {
+public abstract class DatabaseTraverser<R> extends Traverser<R> {
 
   protected boolean collectInternal = false;
 
-  protected StorageGroupCollector(IMNode startNode, PartialPath path, IMTreeStore store)
+  /**
+   * To traverse subtree under root.sg, e.g., init Traverser(root, "root.sg.**")
+   *
+   * @param startNode denote which tree to traverse by passing its root
+   * @param path use wildcard to specify which part to traverse
+   * @param store
+   * @param isPrefixMatch
+   * @throws MetadataException
+   */
+  public DatabaseTraverser(
+      IMNode startNode, PartialPath path, IMTreeStore store, boolean isPrefixMatch)
       throws MetadataException {
-    super(startNode, path, store);
+    super(startNode, path, store, isPrefixMatch);
   }
 
   @Override
-  protected boolean processInternalMatchedMNode(IMNode node, int idx, int level) {
-    if (node.isStorageGroup()) {
-      if (collectInternal) {
-        collectStorageGroup(node.getAsStorageGroupMNode());
-      }
-      return true;
-    }
-    return false;
+  protected boolean acceptFullMatchedNode(IMNode node) {
+    return node.isStorageGroup();
   }
 
   @Override
-  protected boolean processFullMatchedMNode(IMNode node, int idx, int level) {
-    if (node.isStorageGroup()) {
-      collectStorageGroup(node.getAsStorageGroupMNode());
-      return true;
-    }
-    return false;
+  protected boolean acceptInternalMatchedNode(IMNode node) {
+    return collectInternal && node.isStorageGroup();
   }
 
-  protected abstract void collectStorageGroup(IStorageGroupMNode node);
+  @Override
+  protected boolean shouldVisitSubtreeOfFullMatchedNode(IMNode node) {
+    return !node.isStorageGroup();
+  }
+
+  @Override
+  protected boolean shouldVisitSubtreeOfInternalMatchedNode(IMNode node) {
+    return !node.isStorageGroup();
+  }
 
   public void setCollectInternal(boolean collectInternal) {
     this.collectInternal = collectInternal;
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/EntityCollector.java b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/basic/EntityTraverser.java
similarity index 53%
copy from server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/EntityCollector.java
copy to server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/basic/EntityTraverser.java
index 3f324f037f..54fb6c6d93 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/EntityCollector.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/basic/EntityTraverser.java
@@ -16,61 +16,59 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.iotdb.db.metadata.mtree.traverser.collector;
+package org.apache.iotdb.db.metadata.mtree.traverser.basic;
 
 import org.apache.iotdb.commons.exception.MetadataException;
 import org.apache.iotdb.commons.path.PartialPath;
-import org.apache.iotdb.db.metadata.mnode.IEntityMNode;
 import org.apache.iotdb.db.metadata.mnode.IMNode;
 import org.apache.iotdb.db.metadata.mtree.store.IMTreeStore;
+import org.apache.iotdb.db.metadata.mtree.traverser.Traverser;
 
-// This class defines EntityMNode as target node and defines the Entity process framework.
-public abstract class EntityCollector<T> extends CollectorTraverser<T> {
+public abstract class EntityTraverser<R> extends Traverser<R> {
 
   private boolean usingTemplate = false;
   private int schemaTemplateId = -1;
 
-  protected EntityCollector(IMNode startNode, PartialPath path, IMTreeStore store)
+  /**
+   * To traverse subtree under root.sg, e.g., init Traverser(root, "root.sg.**")
+   *
+   * @param startNode denote which tree to traverse by passing its root
+   * @param path use wildcard to specify which part to traverse
+   * @param store
+   * @param isPrefixMatch
+   * @throws MetadataException
+   */
+  public EntityTraverser(
+      IMNode startNode, PartialPath path, IMTreeStore store, boolean isPrefixMatch)
       throws MetadataException {
-    super(startNode, path, store);
+    super(startNode, path, store, isPrefixMatch);
   }
 
-  protected EntityCollector(
-      IMNode startNode, PartialPath path, IMTreeStore store, int limit, int offset)
-      throws MetadataException {
-    super(startNode, path, store, limit, offset);
+  @Override
+  protected boolean acceptFullMatchedNode(IMNode node) {
+    if (node.isEntity()) {
+      return !usingTemplate || schemaTemplateId == node.getSchemaTemplateId();
+    }
+    return false;
   }
 
   @Override
-  protected boolean processInternalMatchedMNode(IMNode node, int idx, int level) {
+  protected boolean acceptInternalMatchedNode(IMNode node) {
     return false;
   }
 
   @Override
-  protected boolean processFullMatchedMNode(IMNode node, int idx, int level)
-      throws MetadataException {
-    if (node.isEntity()) {
-      if (usingTemplate && schemaTemplateId != node.getSchemaTemplateId()) {
-        return false;
-      }
-      if (hasLimit) {
-        curOffset += 1;
-        if (curOffset < offset) {
-          return true;
-        }
-      }
-      collectEntity(node.getAsEntityMNode());
-      if (hasLimit) {
-        count += 1;
-      }
-    }
-    return false;
+  protected boolean shouldVisitSubtreeOfFullMatchedNode(IMNode node) {
+    return !node.isMeasurement();
+  }
+
+  @Override
+  protected boolean shouldVisitSubtreeOfInternalMatchedNode(IMNode node) {
+    return !node.isMeasurement();
   }
 
   public void setSchemaTemplateFilter(int schemaTemplateId) {
     this.usingTemplate = true;
     this.schemaTemplateId = schemaTemplateId;
   }
-
-  protected abstract void collectEntity(IEntityMNode node) throws MetadataException;
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/basic/MNodeTraverser.java b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/basic/MNodeTraverser.java
new file mode 100644
index 0000000000..3c19bbb50f
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/basic/MNodeTraverser.java
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+package org.apache.iotdb.db.metadata.mtree.traverser.basic;
+
+import org.apache.iotdb.commons.exception.MetadataException;
+import org.apache.iotdb.commons.path.PartialPath;
+import org.apache.iotdb.db.metadata.mnode.IMNode;
+import org.apache.iotdb.db.metadata.mtree.store.IMTreeStore;
+import org.apache.iotdb.db.metadata.mtree.traverser.Traverser;
+
+/**
+ * This class defines any node in MTree as potential target node. On finding a path matching the
+ * given pattern, if a level is specified and the path is longer than the specified level,
+ * MNodeTraverser finds the node of the specified level on the path and process it. The same node
+ * will not be processed more than once. If a level is not given, the current node is processed.
+ */
+public abstract class MNodeTraverser<R> extends Traverser<R> {
+
+  // Level query option started from 0. For example, level of root.sg.d1.s1 is 3.
+  protected int targetLevel = -1;
+  protected IMNode lastVisitNode = null;
+
+  /**
+   * To traverse subtree under root.sg, e.g., init Traverser(root, "root.sg.**")
+   *
+   * @param startNode denote which tree to traverse by passing its root
+   * @param path use wildcard to specify which part to traverse
+   * @param store
+   * @param isPrefixMatch
+   * @throws MetadataException
+   */
+  public MNodeTraverser(
+      IMNode startNode, PartialPath path, IMTreeStore store, boolean isPrefixMatch)
+      throws MetadataException {
+    super(startNode, path, store, isPrefixMatch);
+  }
+
+  @Override
+  protected boolean acceptFullMatchedNode(IMNode node) {
+    if (targetLevel >= 0) {
+      if (getSizeOfAncestor() > targetLevel) {
+        return getAncestorNodeByLevel(targetLevel) != lastVisitNode;
+      } else if (getSizeOfAncestor() == targetLevel) {
+        return node != lastVisitNode;
+      } else {
+        return false;
+      }
+    } else {
+      return true;
+    }
+  }
+
+  @Override
+  protected boolean acceptInternalMatchedNode(IMNode node) {
+    return false;
+  }
+
+  @Override
+  protected boolean shouldVisitSubtreeOfFullMatchedNode(IMNode node) {
+    return !node.isMeasurement();
+  }
+
+  @Override
+  protected boolean shouldVisitSubtreeOfInternalMatchedNode(IMNode node) {
+    return !node.isMeasurement();
+  }
+
+  public void setTargetLevel(int targetLevel) {
+    this.targetLevel = targetLevel;
+  }
+
+  @Override
+  protected final R generateResult(IMNode nextMatchedNode) {
+    if (targetLevel >= 0) {
+      if (getLevelOfNextMatchedNode() == targetLevel) {
+        lastVisitNode = nextMatchedNode;
+      } else {
+        lastVisitNode = getAncestorNodeByLevel(targetLevel);
+      }
+      return transferToResult(lastVisitNode);
+    } else {
+      return transferToResult(nextMatchedNode);
+    }
+  }
+
+  protected abstract R transferToResult(IMNode node);
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/counter/CounterTraverser.java b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/basic/MeasurementTraverser.java
similarity index 52%
rename from server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/counter/CounterTraverser.java
rename to server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/basic/MeasurementTraverser.java
index b1312cfb58..1cc7dc6b1a 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/counter/CounterTraverser.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/basic/MeasurementTraverser.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.iotdb.db.metadata.mtree.traverser.counter;
+package org.apache.iotdb.db.metadata.mtree.traverser.basic;
 
 import org.apache.iotdb.commons.exception.MetadataException;
 import org.apache.iotdb.commons.path.PartialPath;
@@ -24,17 +24,40 @@ import org.apache.iotdb.db.metadata.mnode.IMNode;
 import org.apache.iotdb.db.metadata.mtree.store.IMTreeStore;
 import org.apache.iotdb.db.metadata.mtree.traverser.Traverser;
 
-// This class define the count as traversal result.
-public abstract class CounterTraverser extends Traverser {
+public abstract class MeasurementTraverser<R> extends Traverser<R> {
 
-  protected long count;
-
-  protected CounterTraverser(IMNode startNode, PartialPath path, IMTreeStore store)
+  /**
+   * To traverse subtree under root.sg, e.g., init Traverser(root, "root.sg.**")
+   *
+   * @param startNode denote which tree to traverse by passing its root
+   * @param path use wildcard to specify which part to traverse
+   * @param store
+   * @param isPrefixMatch
+   * @throws MetadataException
+   */
+  public MeasurementTraverser(
+      IMNode startNode, PartialPath path, IMTreeStore store, boolean isPrefixMatch)
       throws MetadataException {
-    super(startNode, path, store);
+    super(startNode, path, store, isPrefixMatch);
+  }
+
+  @Override
+  protected boolean acceptFullMatchedNode(IMNode node) {
+    return node.isMeasurement();
+  }
+
+  @Override
+  protected boolean acceptInternalMatchedNode(IMNode node) {
+    return false;
+  }
+
+  @Override
+  protected boolean shouldVisitSubtreeOfFullMatchedNode(IMNode node) {
+    return !node.isMeasurement();
   }
 
-  public long getCount() {
-    return count;
+  @Override
+  protected boolean shouldVisitSubtreeOfInternalMatchedNode(IMNode node) {
+    return !node.isMeasurement();
   }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/CollectorTraverser.java b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/CollectorTraverser.java
deleted file mode 100644
index fe9d70db98..0000000000
--- a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/CollectorTraverser.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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.
- */
-package org.apache.iotdb.db.metadata.mtree.traverser.collector;
-
-import org.apache.iotdb.commons.exception.MetadataException;
-import org.apache.iotdb.commons.path.PartialPath;
-import org.apache.iotdb.db.metadata.mnode.IMNode;
-import org.apache.iotdb.db.metadata.mtree.store.IMTreeStore;
-import org.apache.iotdb.db.metadata.mtree.traverser.Traverser;
-
-// This class defines the generic resultSet as traversal result and add more restrictions on MTree
-// traversal.
-public abstract class CollectorTraverser<T> extends Traverser {
-
-  // used for implement slimit and offset function in DDL
-  protected int limit;
-  protected int offset;
-
-  protected boolean hasLimit = false;
-  protected int count = 0;
-  protected int curOffset = -1;
-
-  protected T resultSet;
-
-  public CollectorTraverser(IMNode startNode, PartialPath path, IMTreeStore store)
-      throws MetadataException {
-    super(startNode, path, store);
-  }
-
-  public CollectorTraverser(
-      IMNode startNode, PartialPath path, IMTreeStore store, int limit, int offset)
-      throws MetadataException {
-    super(startNode, path, store);
-    this.limit = limit;
-    this.offset = offset;
-    if (limit > 0 || offset > 0) {
-      hasLimit = true;
-    }
-  }
-
-  /** extends traversal with limit restriction */
-  @Override
-  protected void traverse(IMNode node, int idx, int level) throws MetadataException {
-    if (hasLimit && count == limit) {
-      return;
-    }
-    super.traverse(node, idx, level);
-  }
-
-  /**
-   * After invoke traverse(), this method could be invoked to get result
-   *
-   * @return the traversal result
-   */
-  public T getResult() {
-    return resultSet;
-  }
-
-  public void setResultSet(T resultSet) {
-    this.resultSet = resultSet;
-  }
-
-  public int getCurOffset() {
-    return curOffset;
-  }
-
-  public void setLimit(int limit) {
-    this.limit = limit;
-    if (limit > 0) {
-      hasLimit = true;
-    }
-  }
-
-  public void setOffset(int offset) {
-    this.offset = offset;
-    if (offset > 0) {
-      hasLimit = true;
-    }
-  }
-}
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/StorageGroupCollector.java b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/DatabaseCollector.java
similarity index 58%
rename from server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/StorageGroupCollector.java
rename to server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/DatabaseCollector.java
index 2017efbb5f..ea3762f1dc 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/StorageGroupCollector.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/DatabaseCollector.java
@@ -23,40 +23,22 @@ import org.apache.iotdb.commons.path.PartialPath;
 import org.apache.iotdb.db.metadata.mnode.IMNode;
 import org.apache.iotdb.db.metadata.mnode.IStorageGroupMNode;
 import org.apache.iotdb.db.metadata.mtree.store.IMTreeStore;
+import org.apache.iotdb.db.metadata.mtree.traverser.basic.DatabaseTraverser;
 
 // This class implements database path collection function.
-public abstract class StorageGroupCollector<T> extends CollectorTraverser<T> {
-
-  protected boolean collectInternal = false;
-
-  protected StorageGroupCollector(IMNode startNode, PartialPath path, IMTreeStore store)
+public abstract class DatabaseCollector<R> extends DatabaseTraverser<R> {
+  protected DatabaseCollector(
+      IMNode startNode, PartialPath path, IMTreeStore store, boolean isPrefixMatch)
       throws MetadataException {
-    super(startNode, path, store);
+    super(startNode, path, store, isPrefixMatch);
   }
 
   @Override
-  protected boolean processInternalMatchedMNode(IMNode node, int idx, int level) {
-    if (node.isStorageGroup()) {
-      if (collectInternal) {
-        collectStorageGroup(node.getAsStorageGroupMNode());
-      }
-      return true;
-    }
-    return false;
+  protected R generateResult(IMNode nextMatchedNode) {
+    collectDatabase(nextMatchedNode.getAsStorageGroupMNode());
+    return null;
   }
 
-  @Override
-  protected boolean processFullMatchedMNode(IMNode node, int idx, int level) {
-    if (node.isStorageGroup()) {
-      collectStorageGroup(node.getAsStorageGroupMNode());
-      return true;
-    }
-    return false;
-  }
-
-  protected abstract void collectStorageGroup(IStorageGroupMNode node);
-
-  public void setCollectInternal(boolean collectInternal) {
-    this.collectInternal = collectInternal;
-  }
+  // TODO: make collectDatabase return R
+  protected abstract void collectDatabase(IStorageGroupMNode node);
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/EntityCollector.java b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/EntityCollector.java
index 3f324f037f..1756daf271 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/EntityCollector.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/EntityCollector.java
@@ -23,54 +23,22 @@ import org.apache.iotdb.commons.path.PartialPath;
 import org.apache.iotdb.db.metadata.mnode.IEntityMNode;
 import org.apache.iotdb.db.metadata.mnode.IMNode;
 import org.apache.iotdb.db.metadata.mtree.store.IMTreeStore;
+import org.apache.iotdb.db.metadata.mtree.traverser.basic.EntityTraverser;
 
 // This class defines EntityMNode as target node and defines the Entity process framework.
-public abstract class EntityCollector<T> extends CollectorTraverser<T> {
-
-  private boolean usingTemplate = false;
-  private int schemaTemplateId = -1;
-
-  protected EntityCollector(IMNode startNode, PartialPath path, IMTreeStore store)
-      throws MetadataException {
-    super(startNode, path, store);
-  }
+// TODO: set R is IDeviceSchemaInfo
+public abstract class EntityCollector<R> extends EntityTraverser<R> {
 
   protected EntityCollector(
-      IMNode startNode, PartialPath path, IMTreeStore store, int limit, int offset)
+      IMNode startNode, PartialPath path, IMTreeStore store, boolean isPrefixMatch)
       throws MetadataException {
-    super(startNode, path, store, limit, offset);
-  }
-
-  @Override
-  protected boolean processInternalMatchedMNode(IMNode node, int idx, int level) {
-    return false;
+    super(startNode, path, store, isPrefixMatch);
   }
 
   @Override
-  protected boolean processFullMatchedMNode(IMNode node, int idx, int level)
-      throws MetadataException {
-    if (node.isEntity()) {
-      if (usingTemplate && schemaTemplateId != node.getSchemaTemplateId()) {
-        return false;
-      }
-      if (hasLimit) {
-        curOffset += 1;
-        if (curOffset < offset) {
-          return true;
-        }
-      }
-      collectEntity(node.getAsEntityMNode());
-      if (hasLimit) {
-        count += 1;
-      }
-    }
-    return false;
-  }
-
-  public void setSchemaTemplateFilter(int schemaTemplateId) {
-    this.usingTemplate = true;
-    this.schemaTemplateId = schemaTemplateId;
+  protected R generateResult(IMNode nextMatchedNode) {
+    return collectEntity(nextMatchedNode.getAsEntityMNode());
   }
 
-  protected abstract void collectEntity(IEntityMNode node) throws MetadataException;
+  protected abstract R collectEntity(IEntityMNode node);
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/MNodeAboveSGCollector.java b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/MNodeAboveSGCollector.java
index f97fe38d74..06ae88d87b 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/MNodeAboveSGCollector.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/MNodeAboveSGCollector.java
@@ -30,29 +30,30 @@ public abstract class MNodeAboveSGCollector<T> extends MNodeCollector<T> {
 
   protected Set<PartialPath> involvedStorageGroupMNodes = new HashSet<>();
 
-  protected MNodeAboveSGCollector(IMNode startNode, PartialPath path, IMTreeStore store)
+  protected MNodeAboveSGCollector(
+      IMNode startNode, PartialPath path, IMTreeStore store, boolean isPrefixMatch)
       throws MetadataException {
-    super(startNode, path, store);
+    super(startNode, path, store, isPrefixMatch);
   }
 
   @Override
-  protected boolean processInternalMatchedMNode(IMNode node, int idx, int level) {
-    boolean shouldSkipSubtree = super.processInternalMatchedMNode(node, idx, level);
+  protected boolean shouldVisitSubtreeOfFullMatchedNode(IMNode node) {
     if (node.isStorageGroup()) {
-      involvedStorageGroupMNodes.add(node.getPartialPath());
-      return true;
+      involvedStorageGroupMNodes.add(getParentPartialPath().concatNode(node.getName()));
+      return false;
+    } else {
+      return super.shouldVisitSubtreeOfFullMatchedNode(node);
     }
-    return shouldSkipSubtree;
   }
 
   @Override
-  protected boolean processFullMatchedMNode(IMNode node, int idx, int level) {
-    boolean shouldSkipSubtree = super.processFullMatchedMNode(node, idx, level);
+  protected boolean shouldVisitSubtreeOfInternalMatchedNode(IMNode node) {
     if (node.isStorageGroup()) {
-      involvedStorageGroupMNodes.add(node.getPartialPath());
-      return true;
+      involvedStorageGroupMNodes.add(getParentPartialPath().concatNode(node.getName()));
+      return false;
+    } else {
+      return super.shouldVisitSubtreeOfInternalMatchedNode(node);
     }
-    return shouldSkipSubtree;
   }
 
   public Set<PartialPath> getInvolvedStorageGroupMNodes() {
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/MNodeCollector.java b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/MNodeCollector.java
index 53c57eea7c..5104f778b7 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/MNodeCollector.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/MNodeCollector.java
@@ -22,11 +22,7 @@ import org.apache.iotdb.commons.exception.MetadataException;
 import org.apache.iotdb.commons.path.PartialPath;
 import org.apache.iotdb.db.metadata.mnode.IMNode;
 import org.apache.iotdb.db.metadata.mtree.store.IMTreeStore;
-
-import java.util.ArrayDeque;
-import java.util.Deque;
-import java.util.HashSet;
-import java.util.Set;
+import org.apache.iotdb.db.metadata.mtree.traverser.basic.MNodeTraverser;
 
 /**
  * This class defines any node in MTree as potential target node. On finding a path matching the
@@ -34,54 +30,18 @@ import java.util.Set;
  * MNodeLevelCounter finds the node of the specified level on the path and process it. The same node
  * will not be processed more than once. If a level is not given, the current node is processed.
  */
-public abstract class MNodeCollector<T> extends CollectorTraverser<T> {
-
-  // level query option
-  protected int targetLevel = -1;
+// TODO: set R to IMNodeInfo
+public abstract class MNodeCollector<R> extends MNodeTraverser<R> {
 
-  private Set<IMNode> processedNodes = new HashSet<>();
-
-  protected MNodeCollector(IMNode startNode, PartialPath path, IMTreeStore store)
+  protected MNodeCollector(
+      IMNode startNode, PartialPath path, IMTreeStore store, boolean isPrefixMatch)
       throws MetadataException {
-    super(startNode, path, store);
+    super(startNode, path, store, isPrefixMatch);
   }
 
-  @Override
-  protected boolean processInternalMatchedMNode(IMNode node, int idx, int level) {
-    return false;
+  protected final R transferToResult(IMNode node) {
+    return collectMNode(node);
   }
 
-  @Override
-  protected boolean processFullMatchedMNode(IMNode node, int idx, int level) {
-    if (targetLevel >= 0) {
-      // move the cursor the given level when matched
-      if (level < targetLevel) {
-        return false;
-      }
-      Deque<IMNode> stack = new ArrayDeque<>();
-      while (level > targetLevel) {
-        node = traverseContext.pop();
-        stack.push(node);
-        level--;
-      }
-      // record processed node so they will not be processed twice
-      if (!processedNodes.contains(node)) {
-        processedNodes.add(node);
-        transferToResult(node);
-      }
-      while (!stack.isEmpty()) {
-        traverseContext.push(stack.pop());
-      }
-      return true;
-    } else {
-      transferToResult(node);
-    }
-    return false;
-  }
-
-  protected abstract void transferToResult(IMNode node);
-
-  public void setTargetLevel(int targetLevel) {
-    this.targetLevel = targetLevel;
-  }
+  protected abstract R collectMNode(IMNode node);
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/MeasurementCollector.java b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/MeasurementCollector.java
index c8453cc5ac..ff488befbb 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/MeasurementCollector.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/collector/MeasurementCollector.java
@@ -24,54 +24,21 @@ import org.apache.iotdb.commons.path.PartialPath;
 import org.apache.iotdb.db.metadata.mnode.IMNode;
 import org.apache.iotdb.db.metadata.mnode.IMeasurementMNode;
 import org.apache.iotdb.db.metadata.mtree.store.IMTreeStore;
+import org.apache.iotdb.db.metadata.mtree.traverser.basic.MeasurementTraverser;
 
 // This class defines MeasurementMNode as target node and defines the measurement process framework.
-public abstract class MeasurementCollector<T> extends CollectorTraverser<T> {
-
-  protected MeasurementCollector(IMNode startNode, PartialPath path, IMTreeStore store)
-      throws MetadataException {
-    super(startNode, path, store);
-    shouldTraverseTemplate = true;
-  }
-
-  protected MeasurementCollector(
-      IMNode startNode, PartialPath path, IMTreeStore store, boolean shouldTraverseTemplate)
-      throws MetadataException {
-    super(startNode, path, store);
-    this.shouldTraverseTemplate = shouldTraverseTemplate;
-  }
+// TODO: set R is ITimeseriesInfo
+public abstract class MeasurementCollector<R> extends MeasurementTraverser<R> {
 
   protected MeasurementCollector(
-      IMNode startNode, PartialPath path, IMTreeStore store, int limit, int offset)
+      IMNode startNode, PartialPath path, IMTreeStore store, boolean isPrefixMatch)
       throws MetadataException {
-    super(startNode, path, store, limit, offset);
-    shouldTraverseTemplate = true;
+    super(startNode, path, store, isPrefixMatch);
   }
 
   @Override
-  protected boolean processInternalMatchedMNode(IMNode node, int idx, int level)
-      throws MetadataException {
-    return false;
-  }
-
-  @Override
-  protected boolean processFullMatchedMNode(IMNode node, int idx, int level)
-      throws MetadataException {
-    if (!node.isMeasurement()
-        || (skipPreDeletedSchema && node.getAsMeasurementMNode().isPreDeleted())) {
-      return false;
-    }
-    if (hasLimit) {
-      curOffset += 1;
-      if (curOffset < offset) {
-        return true;
-      }
-    }
-    collectMeasurement(node.getAsMeasurementMNode());
-    if (hasLimit) {
-      count += 1;
-    }
-    return true;
+  protected R generateResult(IMNode nextMatchedNode) {
+    return collectMeasurement(nextMatchedNode.getAsMeasurementMNode());
   }
 
   /**
@@ -79,7 +46,7 @@ public abstract class MeasurementCollector<T> extends CollectorTraverser<T> {
    *
    * @param node MeasurementMNode holding the measurement schema
    */
-  protected abstract void collectMeasurement(IMeasurementMNode node) throws MetadataException;
+  protected abstract R collectMeasurement(IMeasurementMNode node);
 
   /**
    * When traverse goes into a template, IMNode.getPartialPath may not work as nodes in template has
@@ -87,15 +54,10 @@ public abstract class MeasurementCollector<T> extends CollectorTraverser<T> {
    * stack traverseContext.
    */
   protected MeasurementPath getCurrentMeasurementPathInTraverse(IMeasurementMNode currentNode) {
-    IMNode par = traverseContext.peek();
+    IMNode par = getParentOfNextMatchedNode();
     MeasurementPath retPath =
-        new MeasurementPath(
-            new PartialPath(getCurrentPathNodes(currentNode)), currentNode.getSchema());
+        new MeasurementPath(getPartialPathFromRootToNode(currentNode), currentNode.getSchema());
     retPath.setUnderAlignedEntity(par.getAsEntityMNode().isAligned());
     return retPath;
   }
-
-  protected boolean isUnderAlignedEntity() {
-    return traverseContext.peek().getAsEntityMNode().isAligned();
-  }
 }
diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/schema/tree/ITreeNode.java b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/counter/Counter.java
similarity index 80%
copy from node-commons/src/main/java/org/apache/iotdb/commons/schema/tree/ITreeNode.java
copy to server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/counter/Counter.java
index 900e943fbd..e439dc9e1d 100644
--- a/node-commons/src/main/java/org/apache/iotdb/commons/schema/tree/ITreeNode.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/counter/Counter.java
@@ -16,10 +16,10 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+package org.apache.iotdb.db.metadata.mtree.traverser.counter;
 
-package org.apache.iotdb.commons.schema.tree;
+import org.apache.iotdb.commons.exception.MetadataException;
 
-public interface ITreeNode {
-
-  String getName();
+public interface Counter {
+  long count() throws MetadataException;
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/counter/StorageGroupCounter.java b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/counter/DatabaseCounter.java
similarity index 62%
copy from server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/counter/StorageGroupCounter.java
copy to server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/counter/DatabaseCounter.java
index 98d6413e47..0e7b589317 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/counter/StorageGroupCounter.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/counter/DatabaseCounter.java
@@ -22,27 +22,34 @@ import org.apache.iotdb.commons.exception.MetadataException;
 import org.apache.iotdb.commons.path.PartialPath;
 import org.apache.iotdb.db.metadata.mnode.IMNode;
 import org.apache.iotdb.db.metadata.mtree.store.IMTreeStore;
+import org.apache.iotdb.db.metadata.mtree.traverser.basic.DatabaseTraverser;
 
-// This class implements database count function.
-public class StorageGroupCounter extends CounterTraverser {
+// This class implement database counter.
+public class DatabaseCounter extends DatabaseTraverser<Void> implements Counter {
 
-  public StorageGroupCounter(IMNode startNode, PartialPath path, IMTreeStore store)
+  private int count;
+
+  public DatabaseCounter(
+      IMNode startNode, PartialPath path, IMTreeStore store, boolean isPrefixMatch)
       throws MetadataException {
-    super(startNode, path, store);
+    super(startNode, path, store, isPrefixMatch);
   }
 
   @Override
-  protected boolean processInternalMatchedMNode(IMNode node, int idx, int level) {
-    return node.isStorageGroup();
+  protected Void generateResult(IMNode nextMatchedNode) {
+    count++;
+    return null;
   }
 
   @Override
-  protected boolean processFullMatchedMNode(IMNode node, int idx, int level) {
-    if (node.isStorageGroup()) {
-      count++;
-      return true;
-    } else {
-      return false;
+  public long count() throws MetadataException {
+    while (hasNext()) {
+      next();
+    }
+    if (!isSuccess()) {
+      Throwable e = getFailure();
+      throw new MetadataException(e.getMessage(), e);
     }
+    return count;
   }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/counter/StorageGroupCounter.java b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/counter/EntityCounter.java
similarity index 63%
rename from server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/counter/StorageGroupCounter.java
rename to server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/counter/EntityCounter.java
index 98d6413e47..3feaa46b58 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/counter/StorageGroupCounter.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/counter/EntityCounter.java
@@ -22,27 +22,32 @@ import org.apache.iotdb.commons.exception.MetadataException;
 import org.apache.iotdb.commons.path.PartialPath;
 import org.apache.iotdb.db.metadata.mnode.IMNode;
 import org.apache.iotdb.db.metadata.mtree.store.IMTreeStore;
+import org.apache.iotdb.db.metadata.mtree.traverser.basic.EntityTraverser;
 
-// This class implements database count function.
-public class StorageGroupCounter extends CounterTraverser {
+// This class implement entity counter.
+public class EntityCounter extends EntityTraverser<Void> implements Counter {
+  private int count;
 
-  public StorageGroupCounter(IMNode startNode, PartialPath path, IMTreeStore store)
+  public EntityCounter(IMNode startNode, PartialPath path, IMTreeStore store, boolean isPrefixMatch)
       throws MetadataException {
-    super(startNode, path, store);
+    super(startNode, path, store, isPrefixMatch);
   }
 
   @Override
-  protected boolean processInternalMatchedMNode(IMNode node, int idx, int level) {
-    return node.isStorageGroup();
+  protected Void generateResult(IMNode nextMatchedNode) {
+    count++;
+    return null;
   }
 
   @Override
-  protected boolean processFullMatchedMNode(IMNode node, int idx, int level) {
-    if (node.isStorageGroup()) {
-      count++;
-      return true;
-    } else {
-      return false;
+  public long count() throws MetadataException {
+    while (hasNext()) {
+      next();
     }
+    if (!isSuccess()) {
+      Throwable e = getFailure();
+      throw new MetadataException(e.getMessage(), e);
+    }
+    return count;
   }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/updater/EntityUpdater.java b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/updater/EntityUpdater.java
new file mode 100644
index 0000000000..64a822d4ee
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/updater/EntityUpdater.java
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+package org.apache.iotdb.db.metadata.mtree.traverser.updater;
+
+import org.apache.iotdb.commons.exception.MetadataException;
+import org.apache.iotdb.commons.path.PartialPath;
+import org.apache.iotdb.db.metadata.mnode.IEntityMNode;
+import org.apache.iotdb.db.metadata.mnode.IMNode;
+import org.apache.iotdb.db.metadata.mtree.store.IMTreeStore;
+import org.apache.iotdb.db.metadata.mtree.traverser.basic.EntityTraverser;
+
+public abstract class EntityUpdater extends EntityTraverser<Void> implements Updater {
+  /**
+   * To traverse subtree under root.sg, e.g., init Traverser(root, "root.sg.**")
+   *
+   * @param startNode denote which tree to traverse by passing its root
+   * @param path use wildcard to specify which part to traverse
+   * @param store
+   * @param isPrefixMatch
+   * @throws MetadataException
+   */
+  public EntityUpdater(IMNode startNode, PartialPath path, IMTreeStore store, boolean isPrefixMatch)
+      throws MetadataException {
+    super(startNode, path, store, isPrefixMatch);
+  }
+
+  @Override
+  protected Void generateResult(IMNode nextMatchedNode) {
+    try {
+      updateEntity(nextMatchedNode.getAsEntityMNode());
+    } catch (MetadataException e) {
+      setFailure(e);
+    }
+    return null;
+  }
+
+  @Override
+  public void update() throws MetadataException {
+    while (super.hasNext()) {
+      super.next();
+    }
+    if (!isSuccess()) {
+      Throwable e = getFailure();
+      throw new MetadataException(e.getMessage(), e);
+    }
+  }
+
+  protected abstract void updateEntity(IEntityMNode node) throws MetadataException;
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/updater/MeasurementUpdater.java b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/updater/MeasurementUpdater.java
new file mode 100644
index 0000000000..2dd84d202b
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/updater/MeasurementUpdater.java
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+package org.apache.iotdb.db.metadata.mtree.traverser.updater;
+
+import org.apache.iotdb.commons.exception.MetadataException;
+import org.apache.iotdb.commons.path.PartialPath;
+import org.apache.iotdb.db.metadata.mnode.IMNode;
+import org.apache.iotdb.db.metadata.mnode.IMeasurementMNode;
+import org.apache.iotdb.db.metadata.mtree.store.IMTreeStore;
+import org.apache.iotdb.db.metadata.mtree.traverser.basic.MeasurementTraverser;
+
+public abstract class MeasurementUpdater extends MeasurementTraverser<Void> implements Updater {
+  /**
+   * To traverse subtree under root.sg, e.g., init Traverser(root, "root.sg.**")
+   *
+   * @param startNode denote which tree to traverse by passing its root
+   * @param path use wildcard to specify which part to traverse
+   * @param store
+   * @param isPrefixMatch
+   * @throws MetadataException
+   */
+  public MeasurementUpdater(
+      IMNode startNode, PartialPath path, IMTreeStore store, boolean isPrefixMatch)
+      throws MetadataException {
+    super(startNode, path, store, isPrefixMatch);
+  }
+
+  @Override
+  protected Void generateResult(IMNode nextMatchedNode) {
+    try {
+      updateMeasurement(nextMatchedNode.getAsMeasurementMNode());
+    } catch (MetadataException e) {
+      setFailure(e);
+    }
+    return null;
+  }
+
+  @Override
+  public void update() throws MetadataException {
+    while (super.hasNext()) {
+      super.next();
+    }
+    if (!isSuccess()) {
+      Throwable e = getFailure();
+      throw new MetadataException(e.getMessage(), e);
+    }
+  }
+
+  protected abstract void updateMeasurement(IMeasurementMNode node) throws MetadataException;
+}
diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/schema/tree/ITreeNode.java b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/updater/Updater.java
similarity index 61%
copy from node-commons/src/main/java/org/apache/iotdb/commons/schema/tree/ITreeNode.java
copy to server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/updater/Updater.java
index 900e943fbd..968c36bc18 100644
--- a/node-commons/src/main/java/org/apache/iotdb/commons/schema/tree/ITreeNode.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/traverser/updater/Updater.java
@@ -17,9 +17,14 @@
  * under the License.
  */
 
-package org.apache.iotdb.commons.schema.tree;
+package org.apache.iotdb.db.metadata.mtree.traverser.updater;
 
-public interface ITreeNode {
+import org.apache.iotdb.commons.exception.MetadataException;
 
-  String getName();
+// TODO: In schema file mode, since the updated node won't be cache evicted until it been flushed to
+// disk and currently the flush won't happen during traversing which takes the read lock, we need to
+// consider the concurrency of flush and traverse for better memory control in future work.
+public interface Updater {
+  // TODO: rename to traverse()
+  void update() throws MetadataException;
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/schemaregion/SchemaRegionMemoryImpl.java b/server/src/main/java/org/apache/iotdb/db/metadata/schemaregion/SchemaRegionMemoryImpl.java
index f6524c900a..2531bf0678 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/schemaregion/SchemaRegionMemoryImpl.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/schemaregion/SchemaRegionMemoryImpl.java
@@ -39,7 +39,6 @@ import org.apache.iotdb.db.metadata.logfile.FakeCRC32Deserializer;
 import org.apache.iotdb.db.metadata.logfile.FakeCRC32Serializer;
 import org.apache.iotdb.db.metadata.logfile.SchemaLogReader;
 import org.apache.iotdb.db.metadata.logfile.SchemaLogWriter;
-import org.apache.iotdb.db.metadata.mnode.IEntityMNode;
 import org.apache.iotdb.db.metadata.mnode.IMNode;
 import org.apache.iotdb.db.metadata.mnode.IMeasurementMNode;
 import org.apache.iotdb.db.metadata.mtree.MTreeBelowSGMemoryImpl;
@@ -85,7 +84,6 @@ import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
@@ -288,7 +286,7 @@ public class SchemaRegionMemoryImpl implements ISchemaRegion {
     long time = System.currentTimeMillis();
     // init the metadata from the operation log
     if (logFile.exists()) {
-      int idx = 0;
+      int idx;
       try (SchemaLogReader<ISchemaRegionPlan> mLogReader =
           new SchemaLogReader<>(
               schemaRegionDirPath,
@@ -408,7 +406,7 @@ public class SchemaRegionMemoryImpl implements ISchemaRegion {
     long startTime = System.currentTimeMillis();
 
     long mtreeSnapshotStartTime = System.currentTimeMillis();
-    isSuccess = isSuccess && mtree.createSnapshot(snapshotDir);
+    isSuccess = mtree.createSnapshot(snapshotDir);
     logger.info(
         "MTree snapshot creation of schemaRegion {} costs {}ms.",
         schemaRegionId,
@@ -701,16 +699,14 @@ public class SchemaRegionMemoryImpl implements ISchemaRegion {
   public long constructSchemaBlackList(PathPatternTree patternTree) throws MetadataException {
     long preDeletedNum = 0;
     for (PartialPath pathPattern : patternTree.getAllPathPatterns()) {
-      for (IMeasurementMNode measurementMNode : mtree.getMatchedMeasurementMNode(pathPattern)) {
-        // Given pathPatterns may match one timeseries multi times, which may results in the
-        // preDeletedNum larger than the actual num of timeseries. It doesn't matter since the main
-        // purpose is to check whether there's timeseries to be deleted.
-        preDeletedNum++;
-        measurementMNode.setPreDeleted(true);
+      // Given pathPatterns may match one timeseries multi times, which may results in the
+      // preDeletedNum larger than the actual num of timeseries. It doesn't matter since the main
+      // purpose is to check whether there's timeseries to be deleted.
+      List<PartialPath> paths = mtree.constructSchemaBlackList(pathPattern);
+      preDeletedNum += paths.size();
+      for (PartialPath path : paths) {
         try {
-          writeToMLog(
-              SchemaRegionWritePlanFactory.getPreDeleteTimeSeriesPlan(
-                  measurementMNode.getPartialPath()));
+          writeToMLog(SchemaRegionWritePlanFactory.getPreDeleteTimeSeriesPlan(path));
         } catch (IOException e) {
           throw new MetadataException(e);
         }
@@ -727,12 +723,10 @@ public class SchemaRegionMemoryImpl implements ISchemaRegion {
   @Override
   public void rollbackSchemaBlackList(PathPatternTree patternTree) throws MetadataException {
     for (PartialPath pathPattern : patternTree.getAllPathPatterns()) {
-      for (IMeasurementMNode measurementMNode : mtree.getMatchedMeasurementMNode(pathPattern)) {
-        measurementMNode.setPreDeleted(false);
+      List<PartialPath> paths = mtree.rollbackSchemaBlackList(pathPattern);
+      for (PartialPath path : paths) {
         try {
-          writeToMLog(
-              SchemaRegionWritePlanFactory.getRollbackPreDeleteTimeSeriesPlan(
-                  measurementMNode.getPartialPath()));
+          writeToMLog(SchemaRegionWritePlanFactory.getRollbackPreDeleteTimeSeriesPlan(path));
         } catch (IOException e) {
           throw new MetadataException(e);
         }
@@ -1180,72 +1174,39 @@ public class SchemaRegionMemoryImpl implements ISchemaRegion {
   @Override
   public long constructSchemaBlackListWithTemplate(IPreDeactivateTemplatePlan plan)
       throws MetadataException {
-    long preDeactivateNum = 0;
-    Map<PartialPath, List<Integer>> templateSetInfo = plan.getTemplateSetInfo();
-    for (Map.Entry<PartialPath, List<Integer>> entry : templateSetInfo.entrySet()) {
-      for (IEntityMNode entityMNode :
-          mtree.getDeviceMNodeUsingTargetTemplate(entry.getKey(), entry.getValue())) {
-        Map<PartialPath, List<Integer>> subTemplateSetInfo = new HashMap<>();
-        subTemplateSetInfo.put(
-            entityMNode.getPartialPath(),
-            Collections.singletonList(entityMNode.getSchemaTemplateId()));
-        entityMNode.preDeactivateTemplate();
-        preDeactivateNum++;
-        try {
-          writeToMLog(
-              SchemaRegionWritePlanFactory.getPreDeactivateTemplatePlan(subTemplateSetInfo));
-        } catch (IOException e) {
-          throw new MetadataException(e);
-        }
-      }
+    Map<PartialPath, List<Integer>> resultTemplateSetInfo =
+        mtree.constructSchemaBlackListWithTemplate(plan.getTemplateSetInfo());
+    try {
+      writeToMLog(SchemaRegionWritePlanFactory.getPreDeactivateTemplatePlan(resultTemplateSetInfo));
+    } catch (IOException e) {
+      throw new MetadataException(e);
     }
-    return preDeactivateNum;
+    return resultTemplateSetInfo.size();
   }
 
   @Override
   public void rollbackSchemaBlackListWithTemplate(IRollbackPreDeactivateTemplatePlan plan)
       throws MetadataException {
-    Map<PartialPath, List<Integer>> templateSetInfo = plan.getTemplateSetInfo();
-    for (Map.Entry<PartialPath, List<Integer>> entry : templateSetInfo.entrySet()) {
-      for (IEntityMNode entityMNode :
-          mtree.getPreDeactivatedDeviceMNode(entry.getKey(), entry.getValue())) {
-        if (!entityMNode.isPreDeactivateTemplate()) {
-          continue;
-        }
-        Map<PartialPath, List<Integer>> subTemplateSetInfo = new HashMap<>();
-        subTemplateSetInfo.put(
-            entityMNode.getPartialPath(),
-            Collections.singletonList(entityMNode.getSchemaTemplateId()));
-        entityMNode.rollbackPreDeactivateTemplate();
-        try {
-          writeToMLog(
-              SchemaRegionWritePlanFactory.getRollbackPreDeactivateTemplatePlan(
-                  subTemplateSetInfo));
-        } catch (IOException e) {
-          throw new MetadataException(e);
-        }
-      }
+    Map<PartialPath, List<Integer>> resultTemplateSetInfo =
+        mtree.rollbackSchemaBlackListWithTemplate(plan.getTemplateSetInfo());
+    try {
+      writeToMLog(
+          SchemaRegionWritePlanFactory.getRollbackPreDeactivateTemplatePlan(resultTemplateSetInfo));
+    } catch (IOException e) {
+      throw new MetadataException(e);
     }
   }
 
   @Override
   public void deactivateTemplateInBlackList(IDeactivateTemplatePlan plan) throws MetadataException {
-    Map<PartialPath, List<Integer>> templateSetInfo = plan.getTemplateSetInfo();
-    for (Map.Entry<PartialPath, List<Integer>> entry : templateSetInfo.entrySet()) {
-      for (IEntityMNode entityMNode :
-          mtree.getPreDeactivatedDeviceMNode(entry.getKey(), entry.getValue())) {
-        Map<PartialPath, List<Integer>> subTemplateSetInfo = new HashMap<>();
-        subTemplateSetInfo.put(
-            entityMNode.getPartialPath(),
-            Collections.singletonList(entityMNode.getSchemaTemplateId()));
-        entityMNode.deactivateTemplate();
-        mtree.deleteEmptyInternalMNodeAndReturnEmptyStorageGroup(entityMNode);
-        try {
-          writeToMLog(SchemaRegionWritePlanFactory.getDeactivateTemplatePlan(subTemplateSetInfo));
-        } catch (IOException e) {
-          throw new MetadataException(e);
-        }
-      }
+    // TODO: We can consider implement this as a consumer passed to MTree which takes responsibility
+    // of operating tree structure and concurrency control in future work.
+    Map<PartialPath, List<Integer>> resultTemplateSetInfo =
+        mtree.deactivateTemplateInBlackList(plan.getTemplateSetInfo());
+    try {
+      writeToMLog(SchemaRegionWritePlanFactory.getDeactivateTemplatePlan(resultTemplateSetInfo));
+    } catch (IOException e) {
+      throw new MetadataException(e);
     }
   }
 
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/schemaregion/SchemaRegionSchemaFileImpl.java b/server/src/main/java/org/apache/iotdb/db/metadata/schemaregion/SchemaRegionSchemaFileImpl.java
index 9f431395c8..11364d6e1b 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/schemaregion/SchemaRegionSchemaFileImpl.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/schemaregion/SchemaRegionSchemaFileImpl.java
@@ -757,27 +757,16 @@ public class SchemaRegionSchemaFileImpl implements ISchemaRegion {
   public long constructSchemaBlackList(PathPatternTree patternTree) throws MetadataException {
     long preDeletedNum = 0;
     for (PartialPath pathPattern : patternTree.getAllPathPatterns()) {
-      List<IMeasurementMNode> measurementMNodeList = mtree.getMatchedMeasurementMNode(pathPattern);
-      try {
-        for (IMeasurementMNode measurementMNode : measurementMNodeList) {
-          // Given pathPatterns may match one timeseries multi times, which may results in the
-          // preDeletedNum larger than the actual num of timeseries. It doesn't matter since the
-          // main
-          // purpose is to check whether there's timeseries to be deleted.
-          try {
-            preDeletedNum++;
-            measurementMNode.setPreDeleted(true);
-            mtree.updateMNode(measurementMNode);
-            writeToMLog(
-                SchemaRegionWritePlanFactory.getPreDeleteTimeSeriesPlan(
-                    measurementMNode.getPartialPath()));
-          } catch (IOException e) {
-            throw new MetadataException(e);
-          }
-        }
-      } finally {
-        for (IMeasurementMNode measurementMNode : measurementMNodeList) {
-          mtree.unPinMNode(measurementMNode);
+      // Given pathPatterns may match one timeseries multi times, which may results in the
+      // preDeletedNum larger than the actual num of timeseries. It doesn't matter since the main
+      // purpose is to check whether there's timeseries to be deleted.
+      List<PartialPath> paths = mtree.constructSchemaBlackList(pathPattern);
+      preDeletedNum += paths.size();
+      for (PartialPath path : paths) {
+        try {
+          writeToMLog(SchemaRegionWritePlanFactory.getPreDeleteTimeSeriesPlan(path));
+        } catch (IOException e) {
+          throw new MetadataException(e);
         }
       }
     }
@@ -787,17 +776,13 @@ public class SchemaRegionSchemaFileImpl implements ISchemaRegion {
   @Override
   public void rollbackSchemaBlackList(PathPatternTree patternTree) throws MetadataException {
     for (PartialPath pathPattern : patternTree.getAllPathPatterns()) {
-      for (IMeasurementMNode measurementMNode : mtree.getMatchedMeasurementMNode(pathPattern)) {
+      List<PartialPath> paths = mtree.rollbackSchemaBlackList(pathPattern);
+      ;
+      for (PartialPath path : paths) {
         try {
-          measurementMNode.setPreDeleted(false);
-          mtree.updateMNode(measurementMNode);
-          writeToMLog(
-              SchemaRegionWritePlanFactory.getRollbackPreDeleteTimeSeriesPlan(
-                  measurementMNode.getPartialPath()));
+          writeToMLog(SchemaRegionWritePlanFactory.getRollbackPreDeleteTimeSeriesPlan(path));
         } catch (IOException e) {
           throw new MetadataException(e);
-        } finally {
-          mtree.unPinMNode(measurementMNode);
         }
       }
     }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/common/schematree/visitor/SchemaTreeDeviceVisitor.java b/server/src/main/java/org/apache/iotdb/db/mpp/common/schematree/visitor/SchemaTreeDeviceVisitor.java
index 59072f43c0..4e0ab4fb56 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/common/schematree/visitor/SchemaTreeDeviceVisitor.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/common/schematree/visitor/SchemaTreeDeviceVisitor.java
@@ -47,7 +47,7 @@ public class SchemaTreeDeviceVisitor extends SchemaTreeVisitor<DeviceSchemaInfo>
 
   @Override
   protected DeviceSchemaInfo generateResult(SchemaNode nextMatchedNode) {
-    PartialPath path = new PartialPath(generateFullPathNodes());
+    PartialPath path = getPartialPathFromRootToNode(nextMatchedNode);
     List<MeasurementSchemaInfo> measurementSchemaInfoList = new ArrayList<>();
     Iterator<SchemaNode> iterator = getChildrenIterator(nextMatchedNode);
     SchemaNode node;
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/common/schematree/visitor/SchemaTreeMeasurementVisitor.java b/server/src/main/java/org/apache/iotdb/db/mpp/common/schematree/visitor/SchemaTreeMeasurementVisitor.java
index 9dd71548b3..7221b1641c 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/common/schematree/visitor/SchemaTreeMeasurementVisitor.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/common/schematree/visitor/SchemaTreeMeasurementVisitor.java
@@ -111,7 +111,8 @@ public class SchemaTreeMeasurementVisitor extends SchemaTreeVisitor<MeasurementP
   protected MeasurementPath generateResult(SchemaNode nextMatchedNode) {
     MeasurementPath result =
         new MeasurementPath(
-            generateFullPathNodes(), nextMatchedNode.getAsMeasurementNode().getSchema());
+            getFullPathFromRootToNode(nextMatchedNode),
+            nextMatchedNode.getAsMeasurementNode().getSchema());
     result.setTagMap(nextMatchedNode.getAsMeasurementNode().getTagMap());
     result.setUnderAlignedEntity(getParentOfNextMatchedNode().getAsEntityNode().isAligned());
     String alias = nextMatchedNode.getAsMeasurementNode().getAlias();
diff --git a/server/src/test/java/org/apache/iotdb/db/metadata/mtree/ConfigMTreeTest.java b/server/src/test/java/org/apache/iotdb/db/metadata/mtree/ConfigMTreeTest.java
index 12d74448b9..77fb3819b6 100644
--- a/server/src/test/java/org/apache/iotdb/db/metadata/mtree/ConfigMTreeTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/metadata/mtree/ConfigMTreeTest.java
@@ -35,9 +35,6 @@ import org.junit.Test;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
@@ -97,28 +94,6 @@ public class ConfigMTreeTest {
     assertTrue(root.isStorageGroupAlreadySet(new PartialPath("root.laptop.d1")));
   }
 
-  @Test
-  public void testGetAllChildNodeNamesByPath() {
-    try {
-      root.setStorageGroup(new PartialPath("root.a.d0"));
-      root.setStorageGroup(new PartialPath("root.a.d5"));
-
-      // getChildNodeByPath
-      Set<String> result1 = root.getChildNodeNameInNextLevel(new PartialPath("root.a.d0")).left;
-      Set<String> result2 = root.getChildNodeNameInNextLevel(new PartialPath("root.a")).left;
-      Set<String> result3 = root.getChildNodeNameInNextLevel(new PartialPath("root")).left;
-      assertEquals(new HashSet<>(), result1);
-      assertEquals(new HashSet<>(Arrays.asList("d0", "d5")), result2);
-      assertEquals(new HashSet<>(Collections.singletonList("a")), result3);
-
-      // if child node is nll   will return  null HashSet
-      Set<String> result4 = root.getChildNodeNameInNextLevel(new PartialPath("root.a.d5")).left;
-      assertEquals(result4, new HashSet<>(Collections.emptyList()));
-    } catch (MetadataException e1) {
-      e1.printStackTrace();
-    }
-  }
-
   @Test
   public void testSetStorageGroup() throws IllegalPathException {
     try {
diff --git a/server/src/test/java/org/apache/iotdb/db/metadata/schemaRegion/SchemaRegionBasicTest.java b/server/src/test/java/org/apache/iotdb/db/metadata/schemaRegion/SchemaRegionBasicTest.java
index 191b3b8b5d..5a5aa000cc 100644
--- a/server/src/test/java/org/apache/iotdb/db/metadata/schemaRegion/SchemaRegionBasicTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/metadata/schemaRegion/SchemaRegionBasicTest.java
@@ -514,13 +514,13 @@ public class SchemaRegionBasicTest extends AbstractSchemaRegionTest {
             "root.laptop.d2.s2"));
 
     Assert.assertEquals(
-        new LinkedList<>(Arrays.asList(new PartialPath("root"))),
+        new LinkedList<>(Collections.singletonList(new PartialPath("root"))),
         getNodesListInGivenLevel(schemaRegion, new PartialPath("root.**"), 0, false));
     Assert.assertEquals(
-        new LinkedList<>(Arrays.asList(new PartialPath("root.laptop"))),
+        new LinkedList<>(Collections.singletonList(new PartialPath("root.laptop"))),
         getNodesListInGivenLevel(schemaRegion, new PartialPath("root.**"), 1, false));
     Assert.assertEquals(
-        new LinkedList<>(Arrays.asList(new PartialPath("root.laptop"))),
+        new LinkedList<>(Collections.singletonList(new PartialPath("root.laptop"))),
         getNodesListInGivenLevel(schemaRegion, new PartialPath("root.laptop"), 1, false));
     Assert.assertEquals(
         new HashSet<>(
@@ -548,7 +548,7 @@ public class SchemaRegionBasicTest extends AbstractSchemaRegionTest {
             getNodesListInGivenLevel(schemaRegion, new PartialPath("root.laptop.*.s1"), 3, false)));
     // Empty return
     Assert.assertEquals(
-        new HashSet<>(Arrays.asList()),
+        new HashSet<>(Collections.emptyList()),
         new HashSet<>(
             getNodesListInGivenLevel(
                 schemaRegion, new PartialPath("root.laptop.notExists"), 1, false)));
@@ -569,7 +569,7 @@ public class SchemaRegionBasicTest extends AbstractSchemaRegionTest {
             "root.laptop.d2.s2"));
 
     Assert.assertEquals(
-        new HashSet<>(Arrays.asList()),
+        new HashSet<>(Collections.emptyList()),
         getChildNodePathInNextLevel(schemaRegion, new PartialPath("root.laptop.d0")));
 
     Assert.assertEquals(
@@ -597,7 +597,8 @@ public class SchemaRegionBasicTest extends AbstractSchemaRegionTest {
 
     Assert.assertEquals(
         new HashSet<>(
-            Arrays.asList(new ShowNodesResult("root.laptop.d1.s2.t1", MNodeType.MEASUREMENT))),
+            Collections.singletonList(
+                new ShowNodesResult("root.laptop.d1.s2.t1", MNodeType.MEASUREMENT))),
         getChildNodePathInNextLevel(schemaRegion, new PartialPath("root.**.s2")));
   }
 
diff --git a/server/src/test/java/org/apache/iotdb/db/metadata/schemaRegion/SchemaRegionTemplateTest.java b/server/src/test/java/org/apache/iotdb/db/metadata/schemaRegion/SchemaRegionTemplateTest.java
index ba88c50b05..f66c083b89 100644
--- a/server/src/test/java/org/apache/iotdb/db/metadata/schemaRegion/SchemaRegionTemplateTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/metadata/schemaRegion/SchemaRegionTemplateTest.java
@@ -19,9 +19,12 @@
 
 package org.apache.iotdb.db.metadata.schemaRegion;
 
+import org.apache.iotdb.commons.path.MeasurementPath;
 import org.apache.iotdb.commons.path.PartialPath;
 import org.apache.iotdb.commons.path.PathPatternTree;
+import org.apache.iotdb.db.metadata.plan.schemaregion.impl.read.SchemaRegionReadPlanFactory;
 import org.apache.iotdb.db.metadata.plan.schemaregion.impl.write.SchemaRegionWritePlanFactory;
+import org.apache.iotdb.db.metadata.plan.schemaregion.result.ShowTimeSeriesResult;
 import org.apache.iotdb.db.metadata.schemaregion.ISchemaRegion;
 import org.apache.iotdb.db.metadata.template.Template;
 import org.apache.iotdb.tsfile.file.metadata.enums.CompressionType;
@@ -33,6 +36,7 @@ import org.junit.Test;
 
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -179,4 +183,62 @@ public class SchemaRegionTemplateTest extends AbstractSchemaRegionTest {
     allPatternTree.constructTree();
     Assert.assertEquals(1, schemaRegion.countPathsUsingTemplate(templateId, allPatternTree));
   }
+
+  @Test
+  public void testFetchSchemaWithTemplate() throws Exception {
+    ISchemaRegion schemaRegion = getSchemaRegion("root.sg", 0);
+    SchemaRegionTestUtil.createSimpleTimeseriesByList(
+        schemaRegion, Arrays.asList("root.sg.wf01.wt01.status", "root.sg.wf01.wt01.temperature"));
+    int templateId = 1;
+    Template template =
+        new Template(
+            "t1",
+            Arrays.asList(Collections.singletonList("s1"), Collections.singletonList("s2")),
+            Arrays.asList(
+                Collections.singletonList(TSDataType.DOUBLE),
+                Collections.singletonList(TSDataType.INT32)),
+            Arrays.asList(
+                Collections.singletonList(TSEncoding.RLE),
+                Collections.singletonList(TSEncoding.RLE)),
+            Arrays.asList(
+                Collections.singletonList(CompressionType.SNAPPY),
+                Collections.singletonList(CompressionType.SNAPPY)));
+    template.setId(templateId);
+    schemaRegion.activateSchemaTemplate(
+        SchemaRegionWritePlanFactory.getActivateTemplateInClusterPlan(
+            new PartialPath("root.sg.wf01.wt01"), 3, templateId),
+        template);
+    schemaRegion.activateSchemaTemplate(
+        SchemaRegionWritePlanFactory.getActivateTemplateInClusterPlan(
+            new PartialPath("root.sg.wf02"), 2, templateId),
+        template);
+    Map<Integer, Template> templateMap = Collections.singletonMap(templateId, template);
+    List<String> expectedTimeseries =
+        Arrays.asList(
+            "root.sg.wf01.wt01.s1",
+            "root.sg.wf01.wt01.s2",
+            "root.sg.wf01.wt01.status",
+            "root.sg.wf01.wt01.temperature",
+            "root.sg.wf02.s1",
+            "root.sg.wf02.s2");
+
+    // check fetch schema
+    List<MeasurementPath> schemas =
+        schemaRegion.fetchSchema(new PartialPath("root.**"), templateMap, true);
+    Assert.assertEquals(expectedTimeseries.size(), schemas.size());
+    schemas.sort(Comparator.comparing(PartialPath::getFullPath));
+    for (int i = 0; i < schemas.size(); i++) {
+      Assert.assertEquals(expectedTimeseries.get(i), schemas.get(i).getFullPath());
+    }
+
+    // check show timeseries
+    List<ShowTimeSeriesResult> result =
+        schemaRegion.showTimeseries(
+            SchemaRegionReadPlanFactory.getShowTimeSeriesPlan(
+                new PartialPath("root.**"), templateMap));
+    result.sort(ShowTimeSeriesResult::compareTo);
+    for (int i = 0; i < result.size(); i++) {
+      Assert.assertEquals(expectedTimeseries.get(i), result.get(i).getFullPath());
+    }
+  }
 }