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 2022/07/09 01:49:30 UTC

[iotdb] branch master updated: [IOTDB-3560]Support basic create and query template (#6608)

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 63de9af1eb [IOTDB-3560]Support basic create and query template (#6608)
63de9af1eb is described below

commit 63de9af1eb69d5b80f5b803dd48cd55b69e5c21f
Author: yunchan86 <yu...@163.com>
AuthorDate: Sat Jul 9 09:49:24 2022 +0800

    [IOTDB-3560]Support basic create and query template (#6608)
    
    [IOTDB-3560]Support basic create and query template #6608
---
 .../consensus/request/ConfigPhysicalPlan.java      |  12 ++
 .../consensus/request/ConfigPhysicalPlanType.java  |   7 +-
 .../request/read/GetNodesInSchemaTemplatePlan.java |  77 ++++++++
 .../request/read/GetSchemaTemplatePlan.java        |  54 +++++
 .../request/write/CreateSchemaTemplatePlan.java    |  74 +++++++
 .../consensus/response/TemplateInfoResp.java       |  48 +++++
 .../confignode/manager/ClusterSchemaManager.java   |  76 +++++++
 .../iotdb/confignode/manager/ConfigManager.java    |  36 ++++
 .../apache/iotdb/confignode/manager/IManager.java  |  26 +++
 .../confignode/persistence/ClusterSchemaInfo.java  |  63 ++++++
 .../persistence/executor/ConfigPlanExecutor.java   |   9 +
 .../persistence/schema/TemplateTable.java          | 219 +++++++++++++++++++++
 .../thrift/ConfigNodeRPCServiceProcessor.java      |  18 ++
 .../request/ConfigPhysicalPlanSerDeTest.java       |  55 ++++++
 .../confignode/persistence/TemplateTableTest.java  | 135 +++++++++++++
 .../apache/iotdb/db/client/ConfigNodeClient.java   |  51 +++++
 .../metadata/template/ClusterTemplateManager.java  | 161 +++++++++++++++
 .../db/metadata/template/ITemplateManager.java     |  49 +++++
 .../iotdb/db/metadata/template/Template.java       |  37 +++-
 .../iotdb/db/mpp/common/header/HeaderConstant.java |  23 +++
 .../apache/iotdb/db/mpp/plan/analyze/Analyzer.java |  40 ++++
 .../iotdb/db/mpp/plan/constant/StatementType.java  |   2 +
 .../plan/execution/config/ConfigTaskVisitor.java   |  21 ++
 .../execution/config/CreateSchemaTemplateTask.java |  40 ++++
 .../config/ShowNodesInSchemaTemplateTask.java      |  79 ++++++++
 .../execution/config/ShowSchemaTemplateTask.java   |  67 +++++++
 .../config/executor/ClusterConfigTaskExecutor.java |  67 +++++++
 .../config/executor/IConfigTaskExecutor.java       |  12 ++
 .../executor/StandaloneConfigTaskExecutor.java     |  39 ++++
 .../iotdb/db/mpp/plan/parser/ASTVisitor.java       | 158 +++++++++++++++
 .../db/mpp/plan/statement/StatementVisitor.java    |  23 ++-
 .../template/CreateSchemaTemplateStatement.java    | 181 +++++++++++++++++
 .../ShowNodesInSchemaTemplateStatement.java        |  55 ++++++
 .../template/ShowSchemaTemplateStatement.java      |  44 +++++
 .../java/org/apache/iotdb/rpc/TSStatusCode.java    |   2 +
 .../src/main/thrift/confignode.thrift              |  25 +++
 36 files changed, 2079 insertions(+), 6 deletions(-)

diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlan.java b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlan.java
index 55b4289994..af8050f6f1 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlan.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlan.java
@@ -23,10 +23,12 @@ import org.apache.iotdb.confignode.consensus.request.read.CountStorageGroupPlan;
 import org.apache.iotdb.confignode.consensus.request.read.GetDataNodeInfoPlan;
 import org.apache.iotdb.confignode.consensus.request.read.GetDataPartitionPlan;
 import org.apache.iotdb.confignode.consensus.request.read.GetNodePathsPartitionPlan;
+import org.apache.iotdb.confignode.consensus.request.read.GetNodesInSchemaTemplatePlan;
 import org.apache.iotdb.confignode.consensus.request.read.GetOrCreateDataPartitionPlan;
 import org.apache.iotdb.confignode.consensus.request.read.GetOrCreateSchemaPartitionPlan;
 import org.apache.iotdb.confignode.consensus.request.read.GetRegionInfoListPlan;
 import org.apache.iotdb.confignode.consensus.request.read.GetSchemaPartitionPlan;
+import org.apache.iotdb.confignode.consensus.request.read.GetSchemaTemplatePlan;
 import org.apache.iotdb.confignode.consensus.request.read.GetStorageGroupPlan;
 import org.apache.iotdb.confignode.consensus.request.write.ActivateDataNodePlan;
 import org.apache.iotdb.confignode.consensus.request.write.AdjustMaxRegionGroupCountPlan;
@@ -35,6 +37,7 @@ import org.apache.iotdb.confignode.consensus.request.write.CreateDataPartitionPl
 import org.apache.iotdb.confignode.consensus.request.write.CreateFunctionPlan;
 import org.apache.iotdb.confignode.consensus.request.write.CreateRegionGroupsPlan;
 import org.apache.iotdb.confignode.consensus.request.write.CreateSchemaPartitionPlan;
+import org.apache.iotdb.confignode.consensus.request.write.CreateSchemaTemplatePlan;
 import org.apache.iotdb.confignode.consensus.request.write.DeleteProcedurePlan;
 import org.apache.iotdb.confignode.consensus.request.write.DeleteRegionsPlan;
 import org.apache.iotdb.confignode.consensus.request.write.DeleteStorageGroupPlan;
@@ -199,6 +202,15 @@ public abstract class ConfigPhysicalPlan implements IConsensusRequest {
         case DropFunction:
           req = new DropFunctionPlan();
           break;
+        case CreateSchemaTemplate:
+          req = new CreateSchemaTemplatePlan();
+          break;
+        case ShowSchemaTemplate:
+          req = new GetSchemaTemplatePlan();
+          break;
+        case ShowNodesInSchemaTemplate:
+          req = new GetNodesInSchemaTemplatePlan();
+          break;
         case GetNodePathsPartition:
           req = new GetNodePathsPartitionPlan();
           break;
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanType.java b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanType.java
index 6262e45dba..31bdf30554 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanType.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanType.java
@@ -64,7 +64,10 @@ public enum ConfigPhysicalPlanType {
   RemoveConfigNode,
   CreateFunction,
   DropFunction,
-  GetNodePathsPartition,
   GetRegionInfoList,
-  GetDataNodesInfoList;
+  GetDataNodesInfoList,
+  GetNodePathsPartition,
+  CreateSchemaTemplate,
+  ShowSchemaTemplate,
+  ShowNodesInSchemaTemplate;
 }
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/read/GetNodesInSchemaTemplatePlan.java b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/read/GetNodesInSchemaTemplatePlan.java
new file mode 100644
index 0000000000..058e33436a
--- /dev/null
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/read/GetNodesInSchemaTemplatePlan.java
@@ -0,0 +1,77 @@
+/*
+ * 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.confignode.consensus.request.read;
+
+import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlan;
+import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType;
+import org.apache.iotdb.tsfile.common.conf.TSFileConfig;
+import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Objects;
+
+public class GetNodesInSchemaTemplatePlan extends ConfigPhysicalPlan {
+
+  private String templateName;
+
+  public GetNodesInSchemaTemplatePlan() {
+    super(ConfigPhysicalPlanType.ShowNodesInSchemaTemplate);
+  }
+
+  public GetNodesInSchemaTemplatePlan(String templateName) {
+    this();
+    this.templateName = templateName;
+  }
+
+  public String getTemplateName() {
+    return templateName;
+  }
+
+  @Override
+  protected void serializeImpl(DataOutputStream stream) throws IOException {
+    stream.writeInt(ConfigPhysicalPlanType.ShowNodesInSchemaTemplate.ordinal());
+    byte[] bytes = this.getTemplateName().getBytes();
+    int length = bytes.length;
+    stream.writeInt(length);
+    stream.write(bytes);
+  }
+
+  @Override
+  protected void deserializeImpl(ByteBuffer buffer) throws IOException {
+    int length = ReadWriteIOUtils.readInt(buffer);
+    byte[] dataBytes = ReadWriteIOUtils.readBytes(buffer, length);
+    this.templateName = new String(dataBytes, TSFileConfig.STRING_CHARSET);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    GetNodesInSchemaTemplatePlan that = (GetNodesInSchemaTemplatePlan) o;
+    return this.templateName.equalsIgnoreCase(this.templateName);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(templateName);
+  }
+}
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/read/GetSchemaTemplatePlan.java b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/read/GetSchemaTemplatePlan.java
new file mode 100644
index 0000000000..143422dcba
--- /dev/null
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/read/GetSchemaTemplatePlan.java
@@ -0,0 +1,54 @@
+/*
+ * 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.confignode.consensus.request.read;
+
+import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlan;
+import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Objects;
+
+public class GetSchemaTemplatePlan extends ConfigPhysicalPlan {
+
+  public GetSchemaTemplatePlan() {
+    super(ConfigPhysicalPlanType.ShowSchemaTemplate);
+  }
+
+  @Override
+  protected void serializeImpl(DataOutputStream stream) throws IOException {
+    stream.writeInt(ConfigPhysicalPlanType.ShowSchemaTemplate.ordinal());
+  }
+
+  @Override
+  protected void deserializeImpl(ByteBuffer buffer) throws IOException {}
+
+  @Override
+  public boolean equals(Object o) {
+    if (o != null) return true;
+    return false;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(this);
+  }
+}
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/CreateSchemaTemplatePlan.java b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/CreateSchemaTemplatePlan.java
new file mode 100644
index 0000000000..923471ea91
--- /dev/null
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/CreateSchemaTemplatePlan.java
@@ -0,0 +1,74 @@
+/*
+ * 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.confignode.consensus.request.write;
+
+import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlan;
+import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType;
+import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Objects;
+
+public class CreateSchemaTemplatePlan extends ConfigPhysicalPlan {
+
+  private byte[] template;
+
+  public CreateSchemaTemplatePlan() {
+    super(ConfigPhysicalPlanType.CreateSchemaTemplate);
+  }
+
+  public CreateSchemaTemplatePlan(byte[] template) {
+    this();
+    this.template = template;
+  }
+
+  public byte[] getTemplate() {
+    return template;
+  }
+
+  @Override
+  protected void serializeImpl(DataOutputStream stream) throws IOException {
+    stream.writeInt(ConfigPhysicalPlanType.CreateSchemaTemplate.ordinal());
+    stream.writeInt(template.length);
+    stream.write(template);
+  }
+
+  @Override
+  protected void deserializeImpl(ByteBuffer buffer) throws IOException {
+    int length = ReadWriteIOUtils.readInt(buffer);
+    this.template = ReadWriteIOUtils.readBytes(buffer, length);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    CreateSchemaTemplatePlan that = (CreateSchemaTemplatePlan) o;
+    return Arrays.equals(that.template, template);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(template);
+  }
+}
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/TemplateInfoResp.java b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/TemplateInfoResp.java
new file mode 100644
index 0000000000..c427867b48
--- /dev/null
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/TemplateInfoResp.java
@@ -0,0 +1,48 @@
+/*
+ * 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.confignode.consensus.response;
+
+import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.consensus.common.DataSet;
+import org.apache.iotdb.db.metadata.template.Template;
+
+import java.util.List;
+
+public class TemplateInfoResp implements DataSet {
+
+  TSStatus status;
+  List<Template> templateList;
+
+  public TSStatus getStatus() {
+    return status;
+  }
+
+  public void setStatus(TSStatus status) {
+    this.status = status;
+  }
+
+  public List<Template> getTemplateList() {
+    return templateList;
+  }
+
+  public void setTemplateList(List<Template> templateList) {
+    this.templateList = templateList;
+  }
+}
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/ClusterSchemaManager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ClusterSchemaManager.java
index de7af53548..6280bce6ca 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/ClusterSchemaManager.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ClusterSchemaManager.java
@@ -28,18 +28,25 @@ import org.apache.iotdb.confignode.client.AsyncDataNodeClientPool;
 import org.apache.iotdb.confignode.client.handlers.SetTTLHandler;
 import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor;
 import org.apache.iotdb.confignode.consensus.request.read.CountStorageGroupPlan;
+import org.apache.iotdb.confignode.consensus.request.read.GetNodesInSchemaTemplatePlan;
+import org.apache.iotdb.confignode.consensus.request.read.GetSchemaTemplatePlan;
 import org.apache.iotdb.confignode.consensus.request.read.GetStorageGroupPlan;
 import org.apache.iotdb.confignode.consensus.request.write.AdjustMaxRegionGroupCountPlan;
+import org.apache.iotdb.confignode.consensus.request.write.CreateSchemaTemplatePlan;
 import org.apache.iotdb.confignode.consensus.request.write.DeleteStorageGroupPlan;
 import org.apache.iotdb.confignode.consensus.request.write.SetDataReplicationFactorPlan;
 import org.apache.iotdb.confignode.consensus.request.write.SetSchemaReplicationFactorPlan;
 import org.apache.iotdb.confignode.consensus.request.write.SetStorageGroupPlan;
 import org.apache.iotdb.confignode.consensus.request.write.SetTTLPlan;
 import org.apache.iotdb.confignode.consensus.request.write.SetTimePartitionIntervalPlan;
+import org.apache.iotdb.confignode.consensus.response.TemplateInfoResp;
 import org.apache.iotdb.confignode.exception.StorageGroupNotExistsException;
 import org.apache.iotdb.confignode.persistence.ClusterSchemaInfo;
+import org.apache.iotdb.confignode.rpc.thrift.TGetAllTemplatesResp;
+import org.apache.iotdb.confignode.rpc.thrift.TGetTemplateResp;
 import org.apache.iotdb.confignode.rpc.thrift.TStorageGroupSchema;
 import org.apache.iotdb.consensus.common.DataSet;
+import org.apache.iotdb.db.metadata.template.Template;
 import org.apache.iotdb.rpc.RpcUtils;
 import org.apache.iotdb.rpc.TSStatusCode;
 import org.apache.iotdb.tsfile.utils.Pair;
@@ -47,6 +54,9 @@ import org.apache.iotdb.tsfile.utils.Pair;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -297,6 +307,72 @@ public class ClusterSchemaManager {
     return clusterSchemaInfo.getMaxRegionGroupCount(storageGroup, consensusGroupType);
   }
 
+  /**
+   * create schema template
+   *
+   * @param createSchemaTemplatePlan CreateSchemaTemplatePlan
+   * @return TSStatus
+   */
+  public TSStatus createTemplate(CreateSchemaTemplatePlan createSchemaTemplatePlan) {
+    return getConsensusManager().write(createSchemaTemplatePlan).getStatus();
+  }
+
+  /**
+   * show schema template
+   *
+   * @return TGetAllTemplatesResp
+   */
+  public TGetAllTemplatesResp getAllTemplates() {
+    GetSchemaTemplatePlan getSchemaTemplatePlan = new GetSchemaTemplatePlan();
+    TemplateInfoResp templateResp =
+        (TemplateInfoResp) getConsensusManager().read(getSchemaTemplatePlan).getDataset();
+    TGetAllTemplatesResp resp = new TGetAllTemplatesResp();
+    resp.setStatus(templateResp.getStatus());
+    if (resp.getStatus().getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
+      if (templateResp.getTemplateList() != null) {
+        List<ByteBuffer> list = new ArrayList<ByteBuffer>();
+        templateResp.getTemplateList().stream()
+            .forEach(
+                item -> {
+                  try {
+                    list.add(Template.template2ByteBuffer(item));
+                  } catch (IOException e) {
+                    e.printStackTrace();
+                  }
+                });
+        resp.setTemplateList(list);
+      }
+    }
+    return resp;
+  }
+
+  /**
+   * show nodes in schema template
+   *
+   * @param req
+   * @return
+   */
+  public TGetTemplateResp getTemplate(String req) {
+    GetNodesInSchemaTemplatePlan getNodesInSchemaTemplatePlan =
+        new GetNodesInSchemaTemplatePlan(req);
+    TemplateInfoResp templateResp =
+        (TemplateInfoResp) getConsensusManager().read(getNodesInSchemaTemplatePlan).getDataset();
+    TGetTemplateResp resp = new TGetTemplateResp();
+    try {
+      if (templateResp.getStatus().getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
+        if (templateResp.getTemplateList() != null && !templateResp.getTemplateList().isEmpty()) {
+          ByteBuffer byteBuffer =
+              Template.template2ByteBuffer(templateResp.getTemplateList().get(0));
+          resp.setTemplate(byteBuffer);
+        }
+      }
+      resp.setStatus(templateResp.getStatus());
+    } catch (IOException e) {
+      resp.setStatus(new TSStatus(TSStatusCode.TEMPLATE_IMCOMPATIBLE.getStatusCode()));
+    }
+    return resp;
+  }
+
   private NodeManager getNodeManager() {
     return configManager.getNodeManager();
   }
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java
index c529119ae6..6417569e55 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java
@@ -48,6 +48,7 @@ import org.apache.iotdb.confignode.consensus.request.read.GetRegionInfoListPlan;
 import org.apache.iotdb.confignode.consensus.request.read.GetSchemaPartitionPlan;
 import org.apache.iotdb.confignode.consensus.request.read.GetStorageGroupPlan;
 import org.apache.iotdb.confignode.consensus.request.write.ActivateDataNodePlan;
+import org.apache.iotdb.confignode.consensus.request.write.CreateSchemaTemplatePlan;
 import org.apache.iotdb.confignode.consensus.request.write.RegisterDataNodePlan;
 import org.apache.iotdb.confignode.consensus.request.write.RemoveConfigNodePlan;
 import org.apache.iotdb.confignode.consensus.request.write.SetDataReplicationFactorPlan;
@@ -76,8 +77,11 @@ import org.apache.iotdb.confignode.persistence.partition.PartitionInfo;
 import org.apache.iotdb.confignode.rpc.thrift.TClusterNodeInfos;
 import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeRegisterReq;
 import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeRegisterResp;
+import org.apache.iotdb.confignode.rpc.thrift.TCreateSchemaTemplateReq;
 import org.apache.iotdb.confignode.rpc.thrift.TDataPartitionResp;
 import org.apache.iotdb.confignode.rpc.thrift.TDataPartitionTableResp;
+import org.apache.iotdb.confignode.rpc.thrift.TGetAllTemplatesResp;
+import org.apache.iotdb.confignode.rpc.thrift.TGetTemplateResp;
 import org.apache.iotdb.confignode.rpc.thrift.TPermissionInfoResp;
 import org.apache.iotdb.confignode.rpc.thrift.TRegionRouteMapResp;
 import org.apache.iotdb.confignode.rpc.thrift.TSchemaNodeManagementResp;
@@ -933,4 +937,36 @@ public class ConfigManager implements IManager {
     partitionManager.addMetrics();
     nodeManager.addMetrics();
   }
+
+  @Override
+  public TSStatus createSchemaTemplate(TCreateSchemaTemplateReq req) {
+    TSStatus status = confirmLeader();
+    if (status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
+      CreateSchemaTemplatePlan createSchemaTemplatePlan =
+          new CreateSchemaTemplatePlan(req.getSerializedTemplate());
+      return clusterSchemaManager.createTemplate(createSchemaTemplatePlan);
+    } else {
+      return status;
+    }
+  }
+
+  @Override
+  public TGetAllTemplatesResp getAllTemplates() {
+    TSStatus status = confirmLeader();
+    if (status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
+      return clusterSchemaManager.getAllTemplates();
+    } else {
+      return new TGetAllTemplatesResp().setStatus(status);
+    }
+  }
+
+  @Override
+  public TGetTemplateResp getTemplate(String req) {
+    TSStatus status = confirmLeader();
+    if (status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
+      return clusterSchemaManager.getTemplate(req);
+    } else {
+      return new TGetTemplateResp().setStatus(status);
+    }
+  }
 }
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/IManager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/IManager.java
index 3c75879daf..5e901f3cf2 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/IManager.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/IManager.java
@@ -41,6 +41,9 @@ import org.apache.iotdb.confignode.manager.load.LoadManager;
 import org.apache.iotdb.confignode.rpc.thrift.TClusterNodeInfos;
 import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeRegisterReq;
 import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeRegisterResp;
+import org.apache.iotdb.confignode.rpc.thrift.TCreateSchemaTemplateReq;
+import org.apache.iotdb.confignode.rpc.thrift.TGetAllTemplatesResp;
+import org.apache.iotdb.confignode.rpc.thrift.TGetTemplateResp;
 import org.apache.iotdb.confignode.rpc.thrift.TPermissionInfoResp;
 import org.apache.iotdb.confignode.rpc.thrift.TRegionRouteMapResp;
 import org.apache.iotdb.confignode.rpc.thrift.TSchemaNodeManagementResp;
@@ -270,4 +273,27 @@ public interface IManager {
 
   /** Show datanodes */
   DataSet showDataNodes();
+
+  /**
+   * create schema template
+   *
+   * @param req TCreateSchemaTemplateReq
+   * @return TSStatus
+   */
+  TSStatus createSchemaTemplate(TCreateSchemaTemplateReq req);
+
+  /**
+   * show schema templates
+   *
+   * @return
+   */
+  TGetAllTemplatesResp getAllTemplates();
+
+  /**
+   * show nodes in schema template
+   *
+   * @param req String
+   * @return
+   */
+  TGetTemplateResp getTemplate(String req);
 }
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfo.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfo.java
index 7053f405a9..6e6b7d604b 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfo.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/ClusterSchemaInfo.java
@@ -27,6 +27,7 @@ import org.apache.iotdb.commons.utils.TestOnly;
 import org.apache.iotdb.confignode.consensus.request.read.CountStorageGroupPlan;
 import org.apache.iotdb.confignode.consensus.request.read.GetStorageGroupPlan;
 import org.apache.iotdb.confignode.consensus.request.write.AdjustMaxRegionGroupCountPlan;
+import org.apache.iotdb.confignode.consensus.request.write.CreateSchemaTemplatePlan;
 import org.apache.iotdb.confignode.consensus.request.write.DeleteStorageGroupPlan;
 import org.apache.iotdb.confignode.consensus.request.write.SetDataReplicationFactorPlan;
 import org.apache.iotdb.confignode.consensus.request.write.SetSchemaReplicationFactorPlan;
@@ -35,9 +36,14 @@ import org.apache.iotdb.confignode.consensus.request.write.SetTTLPlan;
 import org.apache.iotdb.confignode.consensus.request.write.SetTimePartitionIntervalPlan;
 import org.apache.iotdb.confignode.consensus.response.CountStorageGroupResp;
 import org.apache.iotdb.confignode.consensus.response.StorageGroupSchemaResp;
+import org.apache.iotdb.confignode.consensus.response.TemplateInfoResp;
 import org.apache.iotdb.confignode.exception.StorageGroupNotExistsException;
+import org.apache.iotdb.confignode.persistence.schema.TemplateTable;
+import org.apache.iotdb.confignode.rpc.thrift.TGetAllTemplatesResp;
+import org.apache.iotdb.confignode.rpc.thrift.TGetTemplateResp;
 import org.apache.iotdb.confignode.rpc.thrift.TStorageGroupSchema;
 import org.apache.iotdb.db.metadata.mtree.MTreeAboveSG;
+import org.apache.iotdb.db.metadata.template.Template;
 import org.apache.iotdb.rpc.TSStatusCode;
 import org.apache.iotdb.tsfile.utils.Pair;
 
@@ -50,6 +56,7 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -73,11 +80,14 @@ public class ClusterSchemaInfo implements SnapshotProcessor {
 
   private final String snapshotFileName = "cluster_schema.bin";
 
+  private final TemplateTable templateTable;
+
   public ClusterSchemaInfo() throws IOException {
     storageGroupReadWriteLock = new ReentrantReadWriteLock();
 
     try {
       mTree = new MTreeAboveSG();
+      templateTable = new TemplateTable();
     } catch (MetadataException e) {
       LOGGER.error("Can't construct StorageGroupInfo", e);
       throw new IOException(e);
@@ -436,7 +446,11 @@ public class ClusterSchemaInfo implements SnapshotProcessor {
 
   @Override
   public boolean processTakeSnapshot(File snapshotDir) throws IOException {
+    processMtreeTakeSnapshot(snapshotDir);
+    return templateTable.processTakeSnapshot(snapshotDir);
+  }
 
+  public boolean processMtreeTakeSnapshot(File snapshotDir) throws IOException {
     File snapshotFile = new File(snapshotDir, snapshotFileName);
     if (snapshotFile.exists() && snapshotFile.isFile()) {
       LOGGER.error(
@@ -472,7 +486,11 @@ public class ClusterSchemaInfo implements SnapshotProcessor {
 
   @Override
   public void processLoadSnapshot(File snapshotDir) throws IOException {
+    processMtreeLoadSnapshot(snapshotDir);
+    templateTable.processLoadSnapshot(snapshotDir);
+  }
 
+  public void processMtreeLoadSnapshot(File snapshotDir) throws IOException {
     File snapshotFile = new File(snapshotDir, snapshotFileName);
     if (!snapshotFile.exists() || !snapshotFile.isFile()) {
       LOGGER.error(
@@ -534,6 +552,51 @@ public class ClusterSchemaInfo implements SnapshotProcessor {
     return matchedNamesInNextLevel;
   }
 
+  public TSStatus createSchemaTemplate(CreateSchemaTemplatePlan createSchemaTemplatePlan) {
+    return templateTable.createTemplate(createSchemaTemplatePlan);
+  }
+
+  public TemplateInfoResp getAllTemplates() {
+    TemplateInfoResp result = new TemplateInfoResp();
+    TGetAllTemplatesResp resp = templateTable.getAllTemplate();
+    result.setStatus(resp.getStatus());
+    if (resp.status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
+      if (resp.getTemplateList() != null) {
+        List<Template> list = new ArrayList<Template>();
+        resp.getTemplateList().stream()
+            .forEach(
+                item -> {
+                  try {
+                    list.add(Template.byteBuffer2Template(item));
+                  } catch (IOException | ClassNotFoundException e) {
+                    throw new RuntimeException("template deserialization error.", e);
+                  }
+                });
+        result.setTemplateList(list);
+      }
+    }
+    return result;
+  }
+
+  public TemplateInfoResp getTemplate(String req) {
+    TemplateInfoResp result = new TemplateInfoResp();
+    TGetTemplateResp resp = templateTable.getMatchedTemplateByName(req);
+    result.setStatus(resp.getStatus());
+    if (resp.status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
+      if (resp.getTemplate() != null) {
+        List<Template> list = new ArrayList<Template>();
+        try {
+          Template template = Template.byteBuffer2Template(ByteBuffer.wrap(resp.getTemplate()));
+          list.add(template);
+          result.setTemplateList(list);
+        } catch (IOException | ClassNotFoundException e) {
+          throw new RuntimeException("template deserialization error.", e);
+        }
+      }
+    }
+    return result;
+  }
+
   @TestOnly
   public void clear() {
     mTree.clear();
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigPlanExecutor.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigPlanExecutor.java
index dc2e5c6e68..2f323911af 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigPlanExecutor.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigPlanExecutor.java
@@ -28,6 +28,7 @@ import org.apache.iotdb.confignode.consensus.request.read.CountStorageGroupPlan;
 import org.apache.iotdb.confignode.consensus.request.read.GetDataNodeInfoPlan;
 import org.apache.iotdb.confignode.consensus.request.read.GetDataPartitionPlan;
 import org.apache.iotdb.confignode.consensus.request.read.GetNodePathsPartitionPlan;
+import org.apache.iotdb.confignode.consensus.request.read.GetNodesInSchemaTemplatePlan;
 import org.apache.iotdb.confignode.consensus.request.read.GetRegionInfoListPlan;
 import org.apache.iotdb.confignode.consensus.request.read.GetSchemaPartitionPlan;
 import org.apache.iotdb.confignode.consensus.request.read.GetStorageGroupPlan;
@@ -38,6 +39,7 @@ import org.apache.iotdb.confignode.consensus.request.write.CreateDataPartitionPl
 import org.apache.iotdb.confignode.consensus.request.write.CreateFunctionPlan;
 import org.apache.iotdb.confignode.consensus.request.write.CreateRegionGroupsPlan;
 import org.apache.iotdb.confignode.consensus.request.write.CreateSchemaPartitionPlan;
+import org.apache.iotdb.confignode.consensus.request.write.CreateSchemaTemplatePlan;
 import org.apache.iotdb.confignode.consensus.request.write.DeleteProcedurePlan;
 import org.apache.iotdb.confignode.consensus.request.write.DeleteStorageGroupPlan;
 import org.apache.iotdb.confignode.consensus.request.write.DropFunctionPlan;
@@ -136,6 +138,11 @@ public class ConfigPlanExecutor {
         return getSchemaNodeManagementPartition(req);
       case GetRegionInfoList:
         return partitionInfo.getRegionInfoList((GetRegionInfoListPlan) req);
+      case ShowSchemaTemplate:
+        return clusterSchemaInfo.getAllTemplates();
+      case ShowNodesInSchemaTemplate:
+        GetNodesInSchemaTemplatePlan plan = (GetNodesInSchemaTemplatePlan) req;
+        return clusterSchemaInfo.getTemplate(plan.getTemplateName());
       default:
         throw new UnknownPhysicalPlanTypeException(req.getType());
     }
@@ -199,6 +206,8 @@ public class ConfigPlanExecutor {
         return udfInfo.createFunction((CreateFunctionPlan) req);
       case DropFunction:
         return udfInfo.dropFunction((DropFunctionPlan) req);
+      case CreateSchemaTemplate:
+        return clusterSchemaInfo.createSchemaTemplate((CreateSchemaTemplatePlan) req);
       default:
         throw new UnknownPhysicalPlanTypeException(req.getType());
     }
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/schema/TemplateTable.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/schema/TemplateTable.java
new file mode 100644
index 0000000000..9b16afb14a
--- /dev/null
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/schema/TemplateTable.java
@@ -0,0 +1,219 @@
+/*
+ * 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.confignode.persistence.schema;
+
+import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.commons.utils.TestOnly;
+import org.apache.iotdb.confignode.consensus.request.write.CreateSchemaTemplatePlan;
+import org.apache.iotdb.confignode.rpc.thrift.TGetAllTemplatesResp;
+import org.apache.iotdb.confignode.rpc.thrift.TGetTemplateResp;
+import org.apache.iotdb.db.metadata.template.Template;
+import org.apache.iotdb.rpc.TSStatusCode;
+import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils;
+
+import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+public class TemplateTable {
+
+  private static final Logger LOGGER = LoggerFactory.getLogger(TemplateTable.class);
+
+  // StorageGroup read write lock
+  private final ReentrantReadWriteLock templateReadWriteLock;
+
+  private final Map<String, Template> templateMap = new ConcurrentHashMap<>();
+
+  private final String snapshotFileName = "template_info.bin";
+
+  public TemplateTable() throws IOException {
+    templateReadWriteLock = new ReentrantReadWriteLock();
+  }
+
+  public TGetTemplateResp getMatchedTemplateByName(String name) {
+    TGetTemplateResp resp = new TGetTemplateResp();
+    try {
+      templateReadWriteLock.readLock().lock();
+      Template template = templateMap.get(name);
+      resp.setStatus(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()));
+      resp.setTemplate(Template.template2ByteBuffer(template));
+    } catch (IOException e) {
+      LOGGER.warn("Error TemplateInfo name", e);
+      resp.setStatus(new TSStatus(TSStatusCode.TEMPLATE_NOT_EXIST.getStatusCode()));
+    } finally {
+      templateReadWriteLock.readLock().unlock();
+    }
+    return resp;
+  }
+
+  public TGetAllTemplatesResp getAllTemplate() {
+    TGetAllTemplatesResp resp = new TGetAllTemplatesResp();
+    try {
+      templateReadWriteLock.readLock().lock();
+      List<ByteBuffer> templates = new ArrayList<>();
+      this.templateMap.values().stream()
+          .forEach(
+              item -> {
+                try {
+                  templates.add(Template.template2ByteBuffer(item));
+                } catch (IOException e) {
+                  resp.setStatus(new TSStatus(TSStatusCode.TEMPLATE_IMCOMPATIBLE.getStatusCode()));
+                  throw new RuntimeException(e);
+                }
+              });
+      resp.setTemplateList(templates);
+      resp.setStatus(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()));
+    } catch (RuntimeException e) {
+      LOGGER.warn("Error TemplateInfo name", e);
+      resp.setStatus(new TSStatus(TSStatusCode.TEMPLATE_IMCOMPATIBLE.getStatusCode()));
+    } finally {
+      templateReadWriteLock.readLock().unlock();
+    }
+    return resp;
+  }
+
+  public TSStatus createTemplate(CreateSchemaTemplatePlan createSchemaTemplatePlan) {
+    try {
+      templateReadWriteLock.readLock().lock();
+      Template template =
+          Template.byteBuffer2Template(ByteBuffer.wrap(createSchemaTemplatePlan.getTemplate()));
+      Template temp = this.templateMap.get(template.getName());
+      if (temp != null && template.getName().equalsIgnoreCase(temp.getName())) {
+        LOGGER.error(
+            "Failed to create template, because template name {} is exists", template.getName());
+        return new TSStatus(TSStatusCode.DUPLICATED_TEMPLATE.getStatusCode());
+      }
+      this.templateMap.put(template.getName(), template);
+      return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode());
+    } catch (IOException | ClassNotFoundException e) {
+      LOGGER.warn("Error to create template", e);
+      return new TSStatus(TSStatusCode.CREATE_TEMPLATE_ERROR.getStatusCode());
+    } finally {
+      templateReadWriteLock.readLock().unlock();
+    }
+  }
+
+  private void serialize(OutputStream outputStream) throws IOException {
+    ReadWriteIOUtils.write(templateMap.size(), outputStream);
+    for (Map.Entry<String, Template> entry : templateMap.entrySet()) {
+      serializeTemplate(entry.getValue(), outputStream);
+    }
+  }
+
+  private void serializeTemplate(Template template, OutputStream outputStream) {
+    try {
+      ByteBuffer dataBuffer = template.serialize();
+      ReadWriteIOUtils.write(dataBuffer, outputStream);
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+  }
+
+  private void deserialize(InputStream inputStream) throws IOException {
+    ByteBuffer byteBuffer = ByteBuffer.wrap(IOUtils.toByteArray(inputStream));
+    int size = ReadWriteIOUtils.readInt(byteBuffer);
+    while (size > 0) {
+      Template template = deserializeTemplate(byteBuffer);
+      templateMap.put(template.getName(), template);
+      size--;
+    }
+  }
+
+  private Template deserializeTemplate(ByteBuffer byteBuffer) {
+    Template template = new Template();
+    int length = ReadWriteIOUtils.readInt(byteBuffer);
+    byte[] data = ReadWriteIOUtils.readBytes(byteBuffer, length);
+    template.deserialize(ByteBuffer.wrap(data));
+    return template;
+  }
+
+  public boolean processTakeSnapshot(File snapshotDir) throws IOException {
+    File snapshotFile = new File(snapshotDir, snapshotFileName);
+    if (snapshotFile.exists() && snapshotFile.isFile()) {
+      LOGGER.error(
+          "template failed to take snapshot, because snapshot file [{}] is already exist.",
+          snapshotFile.getAbsolutePath());
+      return false;
+    }
+    File tmpFile = new File(snapshotFile.getAbsolutePath() + "-" + UUID.randomUUID());
+    templateReadWriteLock.writeLock().lock();
+    try (FileOutputStream fileOutputStream = new FileOutputStream(tmpFile);
+        BufferedOutputStream outputStream = new BufferedOutputStream(fileOutputStream)) {
+      serialize(outputStream);
+      outputStream.flush();
+      fileOutputStream.flush();
+      outputStream.close();
+      fileOutputStream.close();
+      return tmpFile.renameTo(snapshotFile);
+    } finally {
+      for (int retry = 0; retry < 5; retry++) {
+        if (!tmpFile.exists() || tmpFile.delete()) {
+          break;
+        } else {
+          LOGGER.warn(
+              "Can't delete temporary snapshot file: {}, retrying...", tmpFile.getAbsolutePath());
+        }
+      }
+      templateReadWriteLock.writeLock().unlock();
+    }
+  }
+
+  public void processLoadSnapshot(File snapshotDir) throws IOException {
+    File snapshotFile = new File(snapshotDir, snapshotFileName);
+    if (!snapshotFile.exists() || !snapshotFile.isFile()) {
+      LOGGER.error(
+          "Failed to load snapshot,snapshot file [{}] is not exist.",
+          snapshotFile.getAbsolutePath());
+      return;
+    }
+    templateReadWriteLock.writeLock().lock();
+    try (FileInputStream fileInputStream = new FileInputStream(snapshotFile);
+        BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream)) {
+      // Load snapshot of template
+      this.templateMap.clear();
+      deserialize(bufferedInputStream);
+      bufferedInputStream.close();
+      fileInputStream.close();
+    } finally {
+      templateReadWriteLock.writeLock().unlock();
+    }
+  }
+
+  @TestOnly
+  public void clear() {
+    this.templateMap.clear();
+  }
+}
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java b/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java
index c797ede063..8667f80c40 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java
@@ -65,6 +65,7 @@ import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeRegisterReq;
 import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeRegisterResp;
 import org.apache.iotdb.confignode.rpc.thrift.TCountStorageGroupResp;
 import org.apache.iotdb.confignode.rpc.thrift.TCreateFunctionReq;
+import org.apache.iotdb.confignode.rpc.thrift.TCreateSchemaTemplateReq;
 import org.apache.iotdb.confignode.rpc.thrift.TDataNodeActiveReq;
 import org.apache.iotdb.confignode.rpc.thrift.TDataNodeInfoResp;
 import org.apache.iotdb.confignode.rpc.thrift.TDataNodeRegisterReq;
@@ -75,6 +76,8 @@ import org.apache.iotdb.confignode.rpc.thrift.TDataPartitionTableResp;
 import org.apache.iotdb.confignode.rpc.thrift.TDeleteStorageGroupReq;
 import org.apache.iotdb.confignode.rpc.thrift.TDeleteStorageGroupsReq;
 import org.apache.iotdb.confignode.rpc.thrift.TDropFunctionReq;
+import org.apache.iotdb.confignode.rpc.thrift.TGetAllTemplatesResp;
+import org.apache.iotdb.confignode.rpc.thrift.TGetTemplateResp;
 import org.apache.iotdb.confignode.rpc.thrift.TLoginReq;
 import org.apache.iotdb.confignode.rpc.thrift.TPermissionInfoResp;
 import org.apache.iotdb.confignode.rpc.thrift.TRegionRouteMapResp;
@@ -527,4 +530,19 @@ public class ConfigNodeRPCServiceProcessor implements IConfigNodeRPCService.Ifac
   public void handleClientExit() {}
 
   // TODO: Interfaces for data operations
+
+  @Override
+  public TSStatus createSchemaTemplate(TCreateSchemaTemplateReq req) throws TException {
+    return configManager.createSchemaTemplate(req);
+  }
+
+  @Override
+  public TGetAllTemplatesResp getAllTemplates() throws TException {
+    return configManager.getAllTemplates();
+  }
+
+  @Override
+  public TGetTemplateResp getTemplate(String req) throws TException {
+    return configManager.getTemplate(req);
+  }
 }
diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanSerDeTest.java b/confignode/src/test/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanSerDeTest.java
index 3c5859e674..8f35519eb4 100644
--- a/confignode/src/test/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanSerDeTest.java
+++ b/confignode/src/test/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanSerDeTest.java
@@ -29,6 +29,7 @@ import org.apache.iotdb.common.rpc.thrift.TSeriesPartitionSlot;
 import org.apache.iotdb.common.rpc.thrift.TTimePartitionSlot;
 import org.apache.iotdb.commons.auth.AuthException;
 import org.apache.iotdb.commons.auth.entity.PrivilegeType;
+import org.apache.iotdb.commons.exception.IllegalPathException;
 import org.apache.iotdb.commons.partition.DataPartitionTable;
 import org.apache.iotdb.commons.partition.SchemaPartitionTable;
 import org.apache.iotdb.commons.partition.SeriesPartitionTable;
@@ -36,10 +37,12 @@ import org.apache.iotdb.confignode.consensus.request.auth.AuthorPlan;
 import org.apache.iotdb.confignode.consensus.request.read.CountStorageGroupPlan;
 import org.apache.iotdb.confignode.consensus.request.read.GetDataNodeInfoPlan;
 import org.apache.iotdb.confignode.consensus.request.read.GetDataPartitionPlan;
+import org.apache.iotdb.confignode.consensus.request.read.GetNodesInSchemaTemplatePlan;
 import org.apache.iotdb.confignode.consensus.request.read.GetOrCreateDataPartitionPlan;
 import org.apache.iotdb.confignode.consensus.request.read.GetOrCreateSchemaPartitionPlan;
 import org.apache.iotdb.confignode.consensus.request.read.GetRegionInfoListPlan;
 import org.apache.iotdb.confignode.consensus.request.read.GetSchemaPartitionPlan;
+import org.apache.iotdb.confignode.consensus.request.read.GetSchemaTemplatePlan;
 import org.apache.iotdb.confignode.consensus.request.read.GetStorageGroupPlan;
 import org.apache.iotdb.confignode.consensus.request.write.ActivateDataNodePlan;
 import org.apache.iotdb.confignode.consensus.request.write.AdjustMaxRegionGroupCountPlan;
@@ -47,6 +50,7 @@ import org.apache.iotdb.confignode.consensus.request.write.ApplyConfigNodePlan;
 import org.apache.iotdb.confignode.consensus.request.write.CreateDataPartitionPlan;
 import org.apache.iotdb.confignode.consensus.request.write.CreateRegionGroupsPlan;
 import org.apache.iotdb.confignode.consensus.request.write.CreateSchemaPartitionPlan;
+import org.apache.iotdb.confignode.consensus.request.write.CreateSchemaTemplatePlan;
 import org.apache.iotdb.confignode.consensus.request.write.DeleteProcedurePlan;
 import org.apache.iotdb.confignode.consensus.request.write.DeleteRegionsPlan;
 import org.apache.iotdb.confignode.consensus.request.write.DeleteStorageGroupPlan;
@@ -61,6 +65,11 @@ import org.apache.iotdb.confignode.consensus.request.write.UpdateProcedurePlan;
 import org.apache.iotdb.confignode.procedure.Procedure;
 import org.apache.iotdb.confignode.procedure.impl.DeleteStorageGroupProcedure;
 import org.apache.iotdb.confignode.rpc.thrift.TStorageGroupSchema;
+import org.apache.iotdb.db.metadata.template.Template;
+import org.apache.iotdb.db.mpp.plan.statement.metadata.template.CreateSchemaTemplateStatement;
+import org.apache.iotdb.tsfile.file.metadata.enums.CompressionType;
+import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
+import org.apache.iotdb.tsfile.file.metadata.enums.TSEncoding;
 import org.apache.iotdb.tsfile.utils.Pair;
 
 import org.junit.Assert;
@@ -593,4 +602,50 @@ public class ConfigPhysicalPlanSerDeTest {
     Assert.assertEquals(req0.getType(), req1.getType());
     Assert.assertEquals(req0.getRegionType(), req1.getRegionType());
   }
+
+  @Test
+  public void CreateSchemaTemplatePlanTest() throws IOException, IllegalPathException {
+    Template template = new Template(newCreateSchemaTemplateStatement("template_name"));
+    CreateSchemaTemplatePlan createSchemaTemplatePlan0 =
+        new CreateSchemaTemplatePlan(Template.template2ByteBuffer(template).array());
+    CreateSchemaTemplatePlan createSchemaTemplatePlan1 =
+        (CreateSchemaTemplatePlan)
+            ConfigPhysicalPlan.Factory.create(createSchemaTemplatePlan0.serializeToByteBuffer());
+    Assert.assertEquals(createSchemaTemplatePlan0, createSchemaTemplatePlan1);
+  }
+
+  private CreateSchemaTemplateStatement newCreateSchemaTemplateStatement(String name) {
+    List<List<String>> measurements =
+        Arrays.asList(
+            Arrays.asList(name + "_" + "temperature"), Arrays.asList(name + "_" + "status"));
+    List<List<TSDataType>> dataTypes =
+        Arrays.asList(Arrays.asList(TSDataType.FLOAT), Arrays.asList(TSDataType.BOOLEAN));
+    List<List<TSEncoding>> encodings =
+        Arrays.asList(Arrays.asList(TSEncoding.RLE), Arrays.asList(TSEncoding.PLAIN));
+    List<List<CompressionType>> compressors =
+        Arrays.asList(Arrays.asList(CompressionType.SNAPPY), Arrays.asList(CompressionType.SNAPPY));
+    CreateSchemaTemplateStatement createSchemaTemplateStatement =
+        new CreateSchemaTemplateStatement(name, measurements, dataTypes, encodings, compressors);
+    return createSchemaTemplateStatement;
+  }
+
+  @Test
+  public void GetSchemaTemplatePlanTest() throws IOException {
+    GetSchemaTemplatePlan getSchemaTemplatePlan0 = new GetSchemaTemplatePlan();
+    GetSchemaTemplatePlan getSchemaTemplatePlan1 =
+        (GetSchemaTemplatePlan)
+            ConfigPhysicalPlan.Factory.create(getSchemaTemplatePlan0.serializeToByteBuffer());
+    Assert.assertEquals(getSchemaTemplatePlan0, getSchemaTemplatePlan1);
+  }
+
+  @Test
+  public void GetNodesInSchemaTemplatePlanTest() throws IOException {
+    GetNodesInSchemaTemplatePlan getNodesInSchemaTemplatePlan0 =
+        new GetNodesInSchemaTemplatePlan("template_name_test");
+    GetNodesInSchemaTemplatePlan getNodesInSchemaTemplatePlan1 =
+        (GetNodesInSchemaTemplatePlan)
+            ConfigPhysicalPlan.Factory.create(
+                getNodesInSchemaTemplatePlan0.serializeToByteBuffer());
+    Assert.assertEquals(getNodesInSchemaTemplatePlan0, getNodesInSchemaTemplatePlan1);
+  }
 }
diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/persistence/TemplateTableTest.java b/confignode/src/test/java/org/apache/iotdb/confignode/persistence/TemplateTableTest.java
new file mode 100644
index 0000000000..944060d30b
--- /dev/null
+++ b/confignode/src/test/java/org/apache/iotdb/confignode/persistence/TemplateTableTest.java
@@ -0,0 +1,135 @@
+/*
+ * 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.confignode.persistence;
+
+import org.apache.iotdb.commons.exception.IllegalPathException;
+import org.apache.iotdb.confignode.consensus.request.write.CreateSchemaTemplatePlan;
+import org.apache.iotdb.confignode.persistence.schema.TemplateTable;
+import org.apache.iotdb.confignode.rpc.thrift.TGetTemplateResp;
+import org.apache.iotdb.db.metadata.template.Template;
+import org.apache.iotdb.db.mpp.plan.statement.metadata.template.CreateSchemaTemplateStatement;
+import org.apache.iotdb.tsfile.file.metadata.enums.CompressionType;
+import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
+import org.apache.iotdb.tsfile.file.metadata.enums.TSEncoding;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.thrift.TException;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.apache.iotdb.db.constant.TestConstant.BASE_OUTPUT_PATH;
+
+public class TemplateTableTest {
+
+  private static TemplateTable templateTable;
+  private static final File snapshotDir = new File(BASE_OUTPUT_PATH, "snapshot");
+
+  @BeforeClass
+  public static void setup() throws IOException {
+    templateTable = new TemplateTable();
+    if (!snapshotDir.exists()) {
+      snapshotDir.mkdirs();
+    }
+  }
+
+  @AfterClass
+  public static void cleanup() throws IOException {
+    templateTable.clear();
+    if (snapshotDir.exists()) {
+      FileUtils.deleteDirectory(snapshotDir);
+    }
+  }
+
+  @Test
+  public void testSnapshot()
+      throws IOException, TException, IllegalPathException, ClassNotFoundException {
+    int n = 2;
+    String templateName = "template_test";
+
+    List<Template> templates = new ArrayList<>();
+    // create schema template
+    for (int i = 0; i < n; i++) {
+      String templateNameTmp = templateName + "_" + i;
+      CreateSchemaTemplateStatement statement = null;
+      if (i == 1) {
+        statement = newCreateSchemaTemplateStatementAlign(templateNameTmp);
+      } else {
+        statement = newCreateSchemaTemplateStatement(templateNameTmp);
+      }
+      Template template = new Template(statement);
+      templates.add(template);
+      CreateSchemaTemplatePlan createSchemaTemplatePlan =
+          new CreateSchemaTemplatePlan(Template.template2ByteBuffer(template).array());
+      templateTable.createTemplate(createSchemaTemplatePlan);
+    }
+
+    templateTable.processTakeSnapshot(snapshotDir);
+    templateTable.clear();
+    templateTable.processLoadSnapshot(snapshotDir);
+
+    // show nodes in schema template
+    for (int i = 0; i < n; i++) {
+      String templateNameTmp = templateName + "_" + i;
+      TGetTemplateResp templateResp = templateTable.getMatchedTemplateByName(templateNameTmp);
+      Template template = templates.get(i);
+      Template serTemplate =
+          Template.byteBuffer2Template(ByteBuffer.wrap(templateResp.getTemplate()));
+      Assert.assertEquals(template, serTemplate);
+    }
+  }
+
+  private CreateSchemaTemplateStatement newCreateSchemaTemplateStatement(String name) {
+    List<List<String>> measurements =
+        Arrays.asList(
+            Arrays.asList(name + "_" + "temperature"), Arrays.asList(name + "_" + "status"));
+    List<List<TSDataType>> dataTypes =
+        Arrays.asList(Arrays.asList(TSDataType.FLOAT), Arrays.asList(TSDataType.BOOLEAN));
+    List<List<TSEncoding>> encodings =
+        Arrays.asList(Arrays.asList(TSEncoding.RLE), Arrays.asList(TSEncoding.PLAIN));
+    List<List<CompressionType>> compressors =
+        Arrays.asList(Arrays.asList(CompressionType.SNAPPY), Arrays.asList(CompressionType.SNAPPY));
+    CreateSchemaTemplateStatement createSchemaTemplateStatement =
+        new CreateSchemaTemplateStatement(name, measurements, dataTypes, encodings, compressors);
+    return createSchemaTemplateStatement;
+  }
+
+  private CreateSchemaTemplateStatement newCreateSchemaTemplateStatementAlign(String name) {
+    List<List<String>> measurements =
+        Arrays.asList(Arrays.asList(name + "_" + "lat", name + "_" + "lon"));
+    List<List<TSDataType>> dataTypes =
+        Arrays.asList(Arrays.asList(TSDataType.FLOAT, TSDataType.FLOAT));
+    List<List<TSEncoding>> encodings =
+        Arrays.asList(Arrays.asList(TSEncoding.GORILLA, TSEncoding.GORILLA));
+    List<List<CompressionType>> compressors =
+        Arrays.asList(Arrays.asList(CompressionType.SNAPPY, CompressionType.SNAPPY));
+    CreateSchemaTemplateStatement createSchemaTemplateStatement =
+        new CreateSchemaTemplateStatement(name, measurements, dataTypes, encodings, compressors);
+    return createSchemaTemplateStatement;
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/client/ConfigNodeClient.java b/server/src/main/java/org/apache/iotdb/db/client/ConfigNodeClient.java
index 61aa287bd5..f3d722af16 100644
--- a/server/src/main/java/org/apache/iotdb/db/client/ConfigNodeClient.java
+++ b/server/src/main/java/org/apache/iotdb/db/client/ConfigNodeClient.java
@@ -40,6 +40,7 @@ import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeRegisterReq;
 import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeRegisterResp;
 import org.apache.iotdb.confignode.rpc.thrift.TCountStorageGroupResp;
 import org.apache.iotdb.confignode.rpc.thrift.TCreateFunctionReq;
+import org.apache.iotdb.confignode.rpc.thrift.TCreateSchemaTemplateReq;
 import org.apache.iotdb.confignode.rpc.thrift.TDataNodeActiveReq;
 import org.apache.iotdb.confignode.rpc.thrift.TDataNodeInfoResp;
 import org.apache.iotdb.confignode.rpc.thrift.TDataNodeRegisterReq;
@@ -50,6 +51,8 @@ import org.apache.iotdb.confignode.rpc.thrift.TDataPartitionTableResp;
 import org.apache.iotdb.confignode.rpc.thrift.TDeleteStorageGroupReq;
 import org.apache.iotdb.confignode.rpc.thrift.TDeleteStorageGroupsReq;
 import org.apache.iotdb.confignode.rpc.thrift.TDropFunctionReq;
+import org.apache.iotdb.confignode.rpc.thrift.TGetAllTemplatesResp;
+import org.apache.iotdb.confignode.rpc.thrift.TGetTemplateResp;
 import org.apache.iotdb.confignode.rpc.thrift.TLoginReq;
 import org.apache.iotdb.confignode.rpc.thrift.TPermissionInfoResp;
 import org.apache.iotdb.confignode.rpc.thrift.TRegionRouteMapResp;
@@ -804,6 +807,54 @@ public class ConfigNodeClient
     throw new TException(MSG_RECONNECTION_FAIL);
   }
 
+  @Override
+  public TSStatus createSchemaTemplate(TCreateSchemaTemplateReq req) throws TException {
+    for (int i = 0; i < RETRY_NUM; i++) {
+      try {
+        TSStatus tsStatus = client.createSchemaTemplate(req);
+        if (!updateConfigNodeLeader(tsStatus)) {
+          return tsStatus;
+        }
+      } catch (TException e) {
+        configLeader = null;
+      }
+      reconnect();
+    }
+    throw new TException(MSG_RECONNECTION_FAIL);
+  }
+
+  @Override
+  public TGetAllTemplatesResp getAllTemplates() throws TException {
+    for (int i = 0; i < RETRY_NUM; i++) {
+      try {
+        TGetAllTemplatesResp resp = client.getAllTemplates();
+        if (!updateConfigNodeLeader(resp.getStatus())) {
+          return resp;
+        }
+      } catch (TException e) {
+        configLeader = null;
+      }
+      reconnect();
+    }
+    throw new TException(MSG_RECONNECTION_FAIL);
+  }
+
+  @Override
+  public TGetTemplateResp getTemplate(String req) throws TException {
+    for (int i = 0; i < RETRY_NUM; i++) {
+      try {
+        TGetTemplateResp resp = client.getTemplate(req);
+        if (!updateConfigNodeLeader(resp.getStatus())) {
+          return resp;
+        }
+      } catch (TException e) {
+        configLeader = null;
+      }
+      reconnect();
+    }
+    throw new TException(MSG_RECONNECTION_FAIL);
+  }
+
   public static class Factory extends BaseClientFactory<PartitionRegionId, ConfigNodeClient> {
 
     public Factory(
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/template/ClusterTemplateManager.java b/server/src/main/java/org/apache/iotdb/db/metadata/template/ClusterTemplateManager.java
new file mode 100644
index 0000000000..08beb9ef48
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/template/ClusterTemplateManager.java
@@ -0,0 +1,161 @@
+/*
+ * 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.template;
+
+import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.commons.client.IClientManager;
+import org.apache.iotdb.commons.consensus.PartitionRegionId;
+import org.apache.iotdb.commons.exception.IllegalPathException;
+import org.apache.iotdb.commons.exception.IoTDBException;
+import org.apache.iotdb.confignode.rpc.thrift.TCreateSchemaTemplateReq;
+import org.apache.iotdb.confignode.rpc.thrift.TGetAllTemplatesResp;
+import org.apache.iotdb.confignode.rpc.thrift.TGetTemplateResp;
+import org.apache.iotdb.db.client.ConfigNodeClient;
+import org.apache.iotdb.db.client.ConfigNodeInfo;
+import org.apache.iotdb.db.client.DataNodeClientPoolFactory;
+import org.apache.iotdb.db.mpp.plan.statement.metadata.template.CreateSchemaTemplateStatement;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import org.apache.thrift.TException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+public class ClusterTemplateManager implements ITemplateManager {
+
+  private static final Logger LOGGER = LoggerFactory.getLogger(ClusterTemplateManager.class);
+
+  private static final class ClusterTemplateManagerHolder {
+    private static final ClusterTemplateManager INSTANCE = new ClusterTemplateManager();
+
+    private ClusterTemplateManagerHolder() {}
+  }
+
+  public static ClusterTemplateManager getInstance() {
+    return ClusterTemplateManager.ClusterTemplateManagerHolder.INSTANCE;
+  }
+
+  private static final IClientManager<PartitionRegionId, ConfigNodeClient>
+      CONFIG_NODE_CLIENT_MANAGER =
+          new IClientManager.Factory<PartitionRegionId, ConfigNodeClient>()
+              .createClientManager(new DataNodeClientPoolFactory.ConfigNodeClientPoolFactory());
+
+  @Override
+  public TSStatus createSchemaTemplate(CreateSchemaTemplateStatement statement) {
+    TCreateSchemaTemplateReq req = constructTCreateSchemaTemplateReq(statement);
+    try (ConfigNodeClient configNodeClient =
+        CONFIG_NODE_CLIENT_MANAGER.borrowClient(ConfigNodeInfo.partitionRegionId)) {
+      // Send request to some API server
+      TSStatus tsStatus = configNodeClient.createSchemaTemplate(req);
+      // Get response or throw exception
+      if (TSStatusCode.SUCCESS_STATUS.getStatusCode() != tsStatus.getCode()) {
+        LOGGER.error(
+            "Failed to execute create schema template {} in config node, status is {}.",
+            statement.getName(),
+            tsStatus);
+      }
+      return tsStatus;
+    } catch (TException | IOException e) {
+      throw new RuntimeException(
+          new IoTDBException(
+              "create template error.", e, TSStatusCode.CREATE_TEMPLATE_ERROR.getStatusCode()));
+    }
+  }
+
+  private TCreateSchemaTemplateReq constructTCreateSchemaTemplateReq(
+      CreateSchemaTemplateStatement statement) {
+    TCreateSchemaTemplateReq req = new TCreateSchemaTemplateReq();
+    try {
+      Template template = new Template(statement);
+      req.setName(template.getName());
+      req.setSerializedTemplate(Template.template2ByteBuffer(template));
+    } catch (IOException | IllegalPathException e) {
+      throw new RuntimeException(e);
+    }
+    return req;
+  }
+
+  @Override
+  public List<Template> getAllTemplates() {
+    List<Template> templatesList = new ArrayList<>();
+    try (ConfigNodeClient configNodeClient =
+        CONFIG_NODE_CLIENT_MANAGER.borrowClient(ConfigNodeInfo.partitionRegionId)) {
+      TGetAllTemplatesResp tGetAllTemplatesResp = configNodeClient.getAllTemplates();
+      // Get response or throw exception
+      if (tGetAllTemplatesResp.getStatus().getCode()
+          == TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
+        List<ByteBuffer> list = tGetAllTemplatesResp.getTemplateList();
+        Optional<List<ByteBuffer>> optional = Optional.ofNullable(list);
+        optional.orElse(new ArrayList<>()).stream()
+            .forEach(
+                item -> {
+                  try {
+                    Template template = Template.byteBuffer2Template(item);
+                    templatesList.add(template);
+                  } catch (IOException | ClassNotFoundException e) {
+                    throw new RuntimeException(
+                        new IoTDBException(
+                            "deserialize template error.",
+                            e,
+                            TSStatusCode.TEMPLATE_IMCOMPATIBLE.getStatusCode()));
+                  }
+                });
+      } else {
+        throw new RuntimeException(
+            new IoTDBException(
+                tGetAllTemplatesResp.getStatus().getMessage(),
+                tGetAllTemplatesResp.getStatus().getCode()));
+      }
+    } catch (TException | IOException e) {
+      throw new RuntimeException(
+          new IoTDBException(
+              "get all template error.", TSStatusCode.UNDEFINED_TEMPLATE.getStatusCode()));
+    }
+    return templatesList;
+  }
+
+  @Override
+  public Template getTemplate(String name) {
+    Template template = null;
+    try (ConfigNodeClient configNodeClient =
+        CONFIG_NODE_CLIENT_MANAGER.borrowClient(ConfigNodeInfo.partitionRegionId)) {
+      TGetTemplateResp resp = configNodeClient.getTemplate(name);
+      if (resp.getStatus().getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
+        byte[] templateBytes = resp.getTemplate();
+        if (templateBytes != null && templateBytes.length > 0) {
+          template = Template.byteBuffer2Template(ByteBuffer.wrap(templateBytes));
+        }
+      } else {
+        throw new RuntimeException(
+            new IoTDBException(resp.status.getMessage(), resp.status.getCode()));
+      }
+    } catch (Exception e) {
+      throw new RuntimeException(
+          new IoTDBException(
+              "get template info error.", TSStatusCode.UNDEFINED_TEMPLATE.getStatusCode()));
+    }
+    return template;
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/template/ITemplateManager.java b/server/src/main/java/org/apache/iotdb/db/metadata/template/ITemplateManager.java
new file mode 100644
index 0000000000..d2abfe8eeb
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/template/ITemplateManager.java
@@ -0,0 +1,49 @@
+/*
+ * 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.template;
+
+import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.db.mpp.plan.statement.metadata.template.CreateSchemaTemplateStatement;
+
+import java.util.List;
+
+public interface ITemplateManager {
+
+  /**
+   * @param statement CreateSchemaTemplateStatement
+   * @return TSStatus
+   */
+  TSStatus createSchemaTemplate(CreateSchemaTemplateStatement statement);
+
+  /**
+   * show schema templates
+   *
+   * @return List<Template>
+   */
+  List<Template> getAllTemplates();
+
+  /**
+   * show nodes in schema template xx
+   *
+   * @param name
+   * @return Template
+   */
+  Template getTemplate(String name);
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/template/Template.java b/server/src/main/java/org/apache/iotdb/db/metadata/template/Template.java
index 68f01446bb..ba5928bbcd 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/template/Template.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/template/Template.java
@@ -30,6 +30,7 @@ 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.MeasurementMNode;
+import org.apache.iotdb.db.mpp.plan.statement.metadata.template.CreateSchemaTemplateStatement;
 import org.apache.iotdb.db.qp.physical.sys.CreateTemplatePlan;
 import org.apache.iotdb.tsfile.common.constant.TsFileConstant;
 import org.apache.iotdb.tsfile.file.metadata.enums.CompressionType;
@@ -42,9 +43,13 @@ import org.apache.iotdb.tsfile.write.schema.VectorMeasurementSchema;
 
 import org.apache.commons.lang3.builder.HashCodeBuilder;
 
+import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.DataOutputStream;
 import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
 import java.nio.ByteBuffer;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
@@ -59,7 +64,7 @@ import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
-public class Template {
+public class Template implements Serializable {
   private String name;
   private Map<String, IMNode> directNodes;
   private boolean isDirectAligned;
@@ -137,6 +142,21 @@ public class Template {
     }
   }
 
+  /**
+   * build a template from a CreateSchemaTemplateStatement
+   *
+   * @param statement CreateSchemaTemplateStatement
+   */
+  public Template(CreateSchemaTemplateStatement statement) throws IllegalPathException {
+    this(
+        new CreateTemplatePlan(
+            statement.getName(),
+            statement.getMeasurements(),
+            statement.getDataTypes(),
+            statement.getEncodings(),
+            statement.getCompressors()));
+  }
+
   public String getName() {
     return name;
   }
@@ -713,4 +733,19 @@ public class Template {
   public void setRehash(int code) {
     rehashCode = code;
   }
+
+  public static ByteBuffer template2ByteBuffer(Template template) throws IOException {
+    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+    ObjectOutputStream dataOutputStream = new ObjectOutputStream(byteArrayOutputStream);
+    dataOutputStream.writeObject(template);
+    return ByteBuffer.wrap(byteArrayOutputStream.toByteArray());
+  }
+
+  public static Template byteBuffer2Template(ByteBuffer byteBuffer)
+      throws IOException, ClassNotFoundException {
+    ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteBuffer.array());
+    ObjectInputStream ois = new ObjectInputStream(byteArrayInputStream);
+    Template template = (Template) ois.readObject();
+    return template;
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/common/header/HeaderConstant.java b/server/src/main/java/org/apache/iotdb/db/mpp/common/header/HeaderConstant.java
index 4fb07662ab..616072b1a0 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/common/header/HeaderConstant.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/common/header/HeaderConstant.java
@@ -81,6 +81,9 @@ public class HeaderConstant {
   public static final String COLUMN_DATA_REGION_NUM = "DataRegionNum";
   public static final String COLUMN_SCHEMA_REGION_NUM = "SchemaRegionNum";
 
+  // column names for show schema template statement
+  public static final String COLUMN_TEMPLATE_NAME = "template name";
+
   // dataset header for schema statement
   public static final DatasetHeader showTimeSeriesHeader;
   public static final DatasetHeader showDevicesHeader;
@@ -113,6 +116,12 @@ public class HeaderConstant {
   // dataset header for show datanodes
   public static final DatasetHeader showDataNodesHeader;
 
+  // dataset header for show nodes in schema template
+  public static final DatasetHeader showNodesInSchemaTemplate;
+
+  // dataset header for show schma template
+  public static final DatasetHeader showSchemaTemplate;
+
   static {
     countStorageGroupHeader =
         new DatasetHeader(
@@ -251,4 +260,18 @@ public class HeaderConstant {
                 new ColumnHeader(COLUMN_SCHEMA_REGION_NUM, TSDataType.INT32)),
             true);
   }
+
+  static {
+    showSchemaTemplate =
+        new DatasetHeader(
+            Arrays.asList(new ColumnHeader(COLUMN_TEMPLATE_NAME, TSDataType.TEXT)), true);
+    showNodesInSchemaTemplate =
+        new DatasetHeader(
+            Arrays.asList(
+                new ColumnHeader(COLUMN_CHILDNODES, TSDataType.TEXT),
+                new ColumnHeader(COLUMN_TIMESERIES_DATATYPE, TSDataType.TEXT),
+                new ColumnHeader(COLUMN_TIMESERIES_ENCODING, TSDataType.TEXT),
+                new ColumnHeader(COLUMN_TIMESERIES_COMPRESSION, TSDataType.TEXT)),
+            true);
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analyzer.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analyzer.java
index 8a9a063865..ccbe7a1b11 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analyzer.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analyzer.java
@@ -79,6 +79,9 @@ import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowDevicesStatement;
 import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowStorageGroupStatement;
 import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowTTLStatement;
 import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowTimeSeriesStatement;
+import org.apache.iotdb.db.mpp.plan.statement.metadata.template.CreateSchemaTemplateStatement;
+import org.apache.iotdb.db.mpp.plan.statement.metadata.template.ShowNodesInSchemaTemplateStatement;
+import org.apache.iotdb.db.mpp.plan.statement.metadata.template.ShowSchemaTemplateStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.ExplainStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.ShowVersionStatement;
 import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
@@ -1433,6 +1436,43 @@ public class Analyzer {
 
       return analysis;
     }
+
+    @Override
+    public Analysis visitCreateSchemaTemplate(
+        CreateSchemaTemplateStatement createTemplateStatement, MPPQueryContext context) {
+
+      context.setQueryType(QueryType.WRITE);
+      List<List<String>> measurementsList = createTemplateStatement.getMeasurements();
+      for (List measurements : measurementsList) {
+        Set<String> measurementsSet = new HashSet<>(measurements);
+        if (measurementsSet.size() < measurements.size()) {
+          throw new SemanticException(
+              "Measurement under an aligned device is not allowed to have the same measurement name");
+        }
+      }
+      Analysis analysis = new Analysis();
+      analysis.setStatement(createTemplateStatement);
+      return analysis;
+    }
+
+    @Override
+    public Analysis visitShowNodesInSchemaTemplate(
+        ShowNodesInSchemaTemplateStatement showNodesInSchemaTemplateStatement,
+        MPPQueryContext context) {
+      Analysis analysis = new Analysis();
+      analysis.setStatement(showNodesInSchemaTemplateStatement);
+      analysis.setRespDatasetHeader(HeaderConstant.showNodesInSchemaTemplate);
+      return analysis;
+    }
+
+    @Override
+    public Analysis visitShowSchemaTemplate(
+        ShowSchemaTemplateStatement showSchemaTemplateStatement, MPPQueryContext context) {
+      Analysis analysis = new Analysis();
+      analysis.setStatement(showSchemaTemplateStatement);
+      analysis.setRespDatasetHeader(HeaderConstant.showSchemaTemplate);
+      return analysis;
+    }
   }
 
   private GroupByFilter initGroupByFilter(GroupByTimeComponent groupByTimeComponent) {
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/constant/StatementType.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/constant/StatementType.java
index c4e14cc331..cd94366880 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/constant/StatementType.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/constant/StatementType.java
@@ -129,6 +129,8 @@ public enum StatementType {
   PRUNE_TEMPLATE,
   APPEND_TEMPLATE,
   DROP_TEMPLATE,
+  SHOW_SCHEMA_TEMPLATE,
+  SHOW_NODES_IN_SCHEMA_TEMPLATE,
 
   SHOW_QUERY_RESOURCE,
 
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/ConfigTaskVisitor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/ConfigTaskVisitor.java
index fd4da7abbc..0b1f6107fa 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/ConfigTaskVisitor.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/ConfigTaskVisitor.java
@@ -35,6 +35,9 @@ import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowRegionStatement;
 import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowStorageGroupStatement;
 import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowTTLStatement;
 import org.apache.iotdb.db.mpp.plan.statement.metadata.UnSetTTLStatement;
+import org.apache.iotdb.db.mpp.plan.statement.metadata.template.CreateSchemaTemplateStatement;
+import org.apache.iotdb.db.mpp.plan.statement.metadata.template.ShowNodesInSchemaTemplateStatement;
+import org.apache.iotdb.db.mpp.plan.statement.metadata.template.ShowSchemaTemplateStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.AuthorStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.FlushStatement;
 import org.apache.iotdb.tsfile.exception.NotImplementedException;
@@ -130,6 +133,24 @@ public class ConfigTaskVisitor
     return new ShowRegionTask(showRegionStatement);
   }
 
+  @Override
+  public IConfigTask visitCreateSchemaTemplate(
+      CreateSchemaTemplateStatement createSchemaTemplateStatement, TaskContext context) {
+    return new CreateSchemaTemplateTask(createSchemaTemplateStatement);
+  }
+
+  @Override
+  public IConfigTask visitShowNodesInSchemaTemplate(
+      ShowNodesInSchemaTemplateStatement showNodesInSchemaTemplateStatement, TaskContext context) {
+    return new ShowNodesInSchemaTemplateTask(showNodesInSchemaTemplateStatement);
+  }
+
+  @Override
+  public IConfigTask visitShowSchemaTemplate(
+      ShowSchemaTemplateStatement showSchemaTemplateStatement, TaskContext context) {
+    return new ShowSchemaTemplateTask(showSchemaTemplateStatement);
+  }
+
   @Override
   public IConfigTask visitShowDataNodes(
       ShowDataNodesStatement showDataNodesStatement, TaskContext context) {
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/CreateSchemaTemplateTask.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/CreateSchemaTemplateTask.java
new file mode 100644
index 0000000000..231f7de5e9
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/CreateSchemaTemplateTask.java
@@ -0,0 +1,40 @@
+/*
+ * 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.mpp.plan.execution.config;
+
+import org.apache.iotdb.db.mpp.plan.execution.config.executor.IConfigTaskExecutor;
+import org.apache.iotdb.db.mpp.plan.statement.metadata.template.CreateSchemaTemplateStatement;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+public class CreateSchemaTemplateTask implements IConfigTask {
+
+  private final CreateSchemaTemplateStatement createSchemaTemplateStatement;
+
+  public CreateSchemaTemplateTask(CreateSchemaTemplateStatement createSchemaTemplateStatement) {
+    this.createSchemaTemplateStatement = createSchemaTemplateStatement;
+  }
+
+  @Override
+  public ListenableFuture<ConfigTaskResult> execute(IConfigTaskExecutor configTaskExecutor)
+      throws InterruptedException {
+    return configTaskExecutor.createSchemaTemplate(this.createSchemaTemplateStatement);
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/ShowNodesInSchemaTemplateTask.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/ShowNodesInSchemaTemplateTask.java
new file mode 100644
index 0000000000..7faaec9372
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/ShowNodesInSchemaTemplateTask.java
@@ -0,0 +1,79 @@
+/*
+ * 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.mpp.plan.execution.config;
+
+import org.apache.iotdb.db.metadata.template.Template;
+import org.apache.iotdb.db.mpp.common.header.DatasetHeader;
+import org.apache.iotdb.db.mpp.common.header.HeaderConstant;
+import org.apache.iotdb.db.mpp.plan.execution.config.executor.IConfigTaskExecutor;
+import org.apache.iotdb.db.mpp.plan.statement.metadata.template.ShowNodesInSchemaTemplateStatement;
+import org.apache.iotdb.rpc.TSStatusCode;
+import org.apache.iotdb.tsfile.read.common.block.TsBlockBuilder;
+import org.apache.iotdb.tsfile.utils.Binary;
+import org.apache.iotdb.tsfile.write.schema.IMeasurementSchema;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+
+import java.util.Map;
+
+public class ShowNodesInSchemaTemplateTask implements IConfigTask {
+
+  private final ShowNodesInSchemaTemplateStatement showNodesInSchemaTemplateStatement;
+
+  public ShowNodesInSchemaTemplateTask(
+      ShowNodesInSchemaTemplateStatement showNodesInSchemaTemplateStatement) {
+    this.showNodesInSchemaTemplateStatement = showNodesInSchemaTemplateStatement;
+  }
+
+  @Override
+  public ListenableFuture<ConfigTaskResult> execute(IConfigTaskExecutor configTaskExecutor)
+      throws InterruptedException {
+    return configTaskExecutor.showNodesInSchemaTemplate(this.showNodesInSchemaTemplateStatement);
+  }
+
+  public static void buildTSBlock(Template template, SettableFuture<ConfigTaskResult> future) {
+    TsBlockBuilder builder =
+        new TsBlockBuilder(HeaderConstant.showNodesInSchemaTemplate.getRespDataTypes());
+    try {
+      if (template != null) {
+        // template.get
+        for (Map.Entry<String, IMeasurementSchema> entry : template.getSchemaMap().entrySet()) {
+          String keyName = entry.getKey();
+          IMeasurementSchema measurementSchema = entry.getValue();
+          builder.getTimeColumnBuilder().writeLong(0L);
+          builder.getColumnBuilder(0).writeBinary(new Binary(keyName));
+          builder.getColumnBuilder(1).writeBinary(new Binary(measurementSchema.getType().name()));
+          builder
+              .getColumnBuilder(2)
+              .writeBinary(new Binary(measurementSchema.getEncodingType().name()));
+          builder
+              .getColumnBuilder(3)
+              .writeBinary(new Binary(measurementSchema.getCompressor().name()));
+          builder.declarePosition();
+        }
+      }
+    } catch (Exception e) {
+
+    }
+    DatasetHeader datasetHeader = HeaderConstant.showNodesInSchemaTemplate;
+    future.set(new ConfigTaskResult(TSStatusCode.SUCCESS_STATUS, builder.build(), datasetHeader));
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/ShowSchemaTemplateTask.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/ShowSchemaTemplateTask.java
new file mode 100644
index 0000000000..9ceca337a9
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/ShowSchemaTemplateTask.java
@@ -0,0 +1,67 @@
+/*
+ * 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.mpp.plan.execution.config;
+
+import org.apache.iotdb.db.metadata.template.Template;
+import org.apache.iotdb.db.mpp.common.header.DatasetHeader;
+import org.apache.iotdb.db.mpp.common.header.HeaderConstant;
+import org.apache.iotdb.db.mpp.plan.execution.config.executor.IConfigTaskExecutor;
+import org.apache.iotdb.db.mpp.plan.statement.metadata.template.ShowSchemaTemplateStatement;
+import org.apache.iotdb.rpc.TSStatusCode;
+import org.apache.iotdb.tsfile.read.common.block.TsBlockBuilder;
+import org.apache.iotdb.tsfile.utils.Binary;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+public class ShowSchemaTemplateTask implements IConfigTask {
+
+  private final ShowSchemaTemplateStatement showSchemaTemplateStatement;
+
+  public ShowSchemaTemplateTask(ShowSchemaTemplateStatement showSchemaTemplateStatement) {
+    this.showSchemaTemplateStatement = showSchemaTemplateStatement;
+  }
+
+  @Override
+  public ListenableFuture<ConfigTaskResult> execute(IConfigTaskExecutor configTaskExecutor)
+      throws InterruptedException {
+    return configTaskExecutor.showSchemaTemplate(this.showSchemaTemplateStatement);
+  }
+
+  public static void buildTSBlock(
+      List<Template> templateList, SettableFuture<ConfigTaskResult> future) {
+    TsBlockBuilder builder =
+        new TsBlockBuilder(HeaderConstant.showSchemaTemplate.getRespDataTypes());
+    Optional<List<Template>> optional = Optional.ofNullable(templateList);
+    optional.orElse(new ArrayList<>()).stream()
+        .forEach(
+            template -> {
+              builder.getTimeColumnBuilder().writeLong(0L);
+              builder.getColumnBuilder(0).writeBinary(new Binary(template.getName()));
+              builder.declarePosition();
+            });
+    DatasetHeader datasetHeader = HeaderConstant.showSchemaTemplate;
+    future.set(new ConfigTaskResult(TSStatusCode.SUCCESS_STATUS, builder.build(), datasetHeader));
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/executor/ClusterConfigTaskExecutor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/executor/ClusterConfigTaskExecutor.java
index 7f5a0d51ab..0d04cf2792 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/executor/ClusterConfigTaskExecutor.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/executor/ClusterConfigTaskExecutor.java
@@ -31,6 +31,7 @@ import org.apache.iotdb.confignode.rpc.thrift.TCountStorageGroupResp;
 import org.apache.iotdb.confignode.rpc.thrift.TCreateFunctionReq;
 import org.apache.iotdb.confignode.rpc.thrift.TDeleteStorageGroupsReq;
 import org.apache.iotdb.confignode.rpc.thrift.TDropFunctionReq;
+import org.apache.iotdb.confignode.rpc.thrift.TGetTemplateResp;
 import org.apache.iotdb.confignode.rpc.thrift.TSetStorageGroupReq;
 import org.apache.iotdb.confignode.rpc.thrift.TShowDataNodesResp;
 import org.apache.iotdb.confignode.rpc.thrift.TShowRegionReq;
@@ -40,12 +41,16 @@ import org.apache.iotdb.confignode.rpc.thrift.TStorageGroupSchemaResp;
 import org.apache.iotdb.db.client.ConfigNodeClient;
 import org.apache.iotdb.db.client.ConfigNodeInfo;
 import org.apache.iotdb.db.client.DataNodeClientPoolFactory;
+import org.apache.iotdb.db.metadata.template.ClusterTemplateManager;
+import org.apache.iotdb.db.metadata.template.Template;
 import org.apache.iotdb.db.mpp.plan.execution.config.ConfigTaskResult;
 import org.apache.iotdb.db.mpp.plan.execution.config.CountStorageGroupTask;
 import org.apache.iotdb.db.mpp.plan.execution.config.SetStorageGroupTask;
 import org.apache.iotdb.db.mpp.plan.execution.config.ShowClusterTask;
 import org.apache.iotdb.db.mpp.plan.execution.config.ShowDataNodesTask;
+import org.apache.iotdb.db.mpp.plan.execution.config.ShowNodesInSchemaTemplateTask;
 import org.apache.iotdb.db.mpp.plan.execution.config.ShowRegionTask;
+import org.apache.iotdb.db.mpp.plan.execution.config.ShowSchemaTemplateTask;
 import org.apache.iotdb.db.mpp.plan.execution.config.ShowStorageGroupTask;
 import org.apache.iotdb.db.mpp.plan.execution.config.ShowTTLTask;
 import org.apache.iotdb.db.mpp.plan.statement.metadata.CountStorageGroupStatement;
@@ -56,6 +61,9 @@ import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowDataNodesStatement;
 import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowRegionStatement;
 import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowStorageGroupStatement;
 import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowTTLStatement;
+import org.apache.iotdb.db.mpp.plan.statement.metadata.template.CreateSchemaTemplateStatement;
+import org.apache.iotdb.db.mpp.plan.statement.metadata.template.ShowNodesInSchemaTemplateStatement;
+import org.apache.iotdb.db.mpp.plan.statement.metadata.template.ShowSchemaTemplateStatement;
 import org.apache.iotdb.rpc.StatementExecutionException;
 import org.apache.iotdb.rpc.TSStatusCode;
 
@@ -364,4 +372,63 @@ public class ClusterConfigTaskExecutor implements IConfigTaskExecutor {
     ShowDataNodesTask.buildTSBlock(showDataNodesResp, future);
     return future;
   }
+
+  @Override
+  public SettableFuture<ConfigTaskResult> createSchemaTemplate(
+      CreateSchemaTemplateStatement createSchemaTemplateStatement) {
+    SettableFuture<ConfigTaskResult> future = SettableFuture.create();
+    // Construct request using statement
+    try {
+      // Send request to some API server
+      TSStatus tsStatus =
+          ClusterTemplateManager.getInstance().createSchemaTemplate(createSchemaTemplateStatement);
+      // Get response or throw exception
+      if (TSStatusCode.SUCCESS_STATUS.getStatusCode() != tsStatus.getCode()) {
+        LOGGER.error(
+            "Failed to execute create schema template {} in config node, status is {}.",
+            createSchemaTemplateStatement.getName(),
+            tsStatus);
+        future.setException(new StatementExecutionException(tsStatus));
+      } else {
+        future.set(new ConfigTaskResult(TSStatusCode.SUCCESS_STATUS));
+      }
+    } catch (Exception e) {
+      future.setException(e);
+    }
+    return future;
+  }
+
+  @Override
+  public SettableFuture<ConfigTaskResult> showSchemaTemplate(
+      ShowSchemaTemplateStatement showSchemaTemplateStatement) {
+    SettableFuture<ConfigTaskResult> future = SettableFuture.create();
+    try (ConfigNodeClient configNodeClient =
+        CONFIG_NODE_CLIENT_MANAGER.borrowClient(ConfigNodeInfo.partitionRegionId)) {
+      // Send request to some API server
+      List<Template> templateList = ClusterTemplateManager.getInstance().getAllTemplates();
+      // build TSBlock
+      ShowSchemaTemplateTask.buildTSBlock(templateList, future);
+    } catch (Exception e) {
+      future.setException(e);
+    }
+    return future;
+  }
+
+  @Override
+  public SettableFuture<ConfigTaskResult> showNodesInSchemaTemplate(
+      ShowNodesInSchemaTemplateStatement showNodesInSchemaTemplateStatement) {
+    SettableFuture<ConfigTaskResult> future = SettableFuture.create();
+    String req = showNodesInSchemaTemplateStatement.getTemplateName();
+    TGetTemplateResp tGetTemplateResp = new TGetTemplateResp();
+    try (ConfigNodeClient configNodeClient =
+        CONFIG_NODE_CLIENT_MANAGER.borrowClient(ConfigNodeInfo.partitionRegionId)) {
+      // Send request to some API server
+      Template template = ClusterTemplateManager.getInstance().getTemplate(req);
+      // build TSBlock
+      ShowNodesInSchemaTemplateTask.buildTSBlock(template, future);
+    } catch (Exception e) {
+      future.setException(e);
+    }
+    return future;
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/executor/IConfigTaskExecutor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/executor/IConfigTaskExecutor.java
index 2eadef660c..ed2eba46a5 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/executor/IConfigTaskExecutor.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/executor/IConfigTaskExecutor.java
@@ -29,6 +29,9 @@ import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowDataNodesStatement;
 import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowRegionStatement;
 import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowStorageGroupStatement;
 import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowTTLStatement;
+import org.apache.iotdb.db.mpp.plan.statement.metadata.template.CreateSchemaTemplateStatement;
+import org.apache.iotdb.db.mpp.plan.statement.metadata.template.ShowNodesInSchemaTemplateStatement;
+import org.apache.iotdb.db.mpp.plan.statement.metadata.template.ShowSchemaTemplateStatement;
 
 import com.google.common.util.concurrent.SettableFuture;
 
@@ -64,4 +67,13 @@ public interface IConfigTaskExecutor {
   SettableFuture<ConfigTaskResult> showRegion(ShowRegionStatement showRegionStatement);
 
   SettableFuture<ConfigTaskResult> showDataNodes(ShowDataNodesStatement showDataNodesStatement);
+
+  SettableFuture<ConfigTaskResult> createSchemaTemplate(
+      CreateSchemaTemplateStatement createSchemaTemplateStatement);
+
+  SettableFuture<ConfigTaskResult> showSchemaTemplate(
+      ShowSchemaTemplateStatement showSchemaTemplateStatement);
+
+  SettableFuture<ConfigTaskResult> showNodesInSchemaTemplate(
+      ShowNodesInSchemaTemplateStatement showNodesInSchemaTemplateStatement);
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/executor/StandaloneConfigTaskExecutor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/executor/StandaloneConfigTaskExecutor.java
index c23fef90af..e4badb3c1a 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/executor/StandaloneConfigTaskExecutor.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/executor/StandaloneConfigTaskExecutor.java
@@ -41,6 +41,9 @@ import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowDataNodesStatement;
 import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowRegionStatement;
 import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowStorageGroupStatement;
 import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowTTLStatement;
+import org.apache.iotdb.db.mpp.plan.statement.metadata.template.CreateSchemaTemplateStatement;
+import org.apache.iotdb.db.mpp.plan.statement.metadata.template.ShowNodesInSchemaTemplateStatement;
+import org.apache.iotdb.db.mpp.plan.statement.metadata.template.ShowSchemaTemplateStatement;
 import org.apache.iotdb.rpc.RpcUtils;
 import org.apache.iotdb.rpc.StatementExecutionException;
 import org.apache.iotdb.rpc.TSStatusCode;
@@ -294,4 +297,40 @@ public class StandaloneConfigTaskExecutor implements IConfigTaskExecutor {
                 "Executing show datanodes in standalone mode is not supported")));
     return future;
   }
+
+  @Override
+  public SettableFuture<ConfigTaskResult> createSchemaTemplate(
+      CreateSchemaTemplateStatement createSchemaTemplateStatement) {
+    SettableFuture<ConfigTaskResult> future = SettableFuture.create();
+    future.setException(
+        new StatementExecutionException(
+            RpcUtils.getStatus(
+                TSStatusCode.EXECUTE_STATEMENT_ERROR,
+                "Executing create schema template is not supported")));
+    return future;
+  }
+
+  @Override
+  public SettableFuture<ConfigTaskResult> showSchemaTemplate(
+      ShowSchemaTemplateStatement showSchemaTemplateStatement) {
+    SettableFuture<ConfigTaskResult> future = SettableFuture.create();
+    future.setException(
+        new StatementExecutionException(
+            RpcUtils.getStatus(
+                TSStatusCode.EXECUTE_STATEMENT_ERROR,
+                "Executing show schema template is not supported")));
+    return future;
+  }
+
+  @Override
+  public SettableFuture<ConfigTaskResult> showNodesInSchemaTemplate(
+      ShowNodesInSchemaTemplateStatement showNodesInSchemaTemplateStatement) {
+    SettableFuture<ConfigTaskResult> future = SettableFuture.create();
+    future.setException(
+        new StatementExecutionException(
+            RpcUtils.getStatus(
+                TSStatusCode.EXECUTE_STATEMENT_ERROR,
+                "Executing show nodes in schema template is not supported")));
+    return future;
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java
index e5347b27e1..7aa614fac7 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java
@@ -104,6 +104,9 @@ import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowStorageGroupStatement
 import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowTTLStatement;
 import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowTimeSeriesStatement;
 import org.apache.iotdb.db.mpp.plan.statement.metadata.UnSetTTLStatement;
+import org.apache.iotdb.db.mpp.plan.statement.metadata.template.CreateSchemaTemplateStatement;
+import org.apache.iotdb.db.mpp.plan.statement.metadata.template.ShowNodesInSchemaTemplateStatement;
+import org.apache.iotdb.db.mpp.plan.statement.metadata.template.ShowSchemaTemplateStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.AuthorStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.ExplainStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.FlushStatement;
@@ -2311,4 +2314,159 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> {
   public Statement visitShowDataNodes(IoTDBSqlParser.ShowDataNodesContext ctx) {
     return new ShowDataNodesStatement();
   }
+
+  @Override
+  public Statement visitCreateSchemaTemplate(IoTDBSqlParser.CreateSchemaTemplateContext ctx) {
+    CreateSchemaTemplateStatement createTemplateStatement = new CreateSchemaTemplateStatement();
+    String name = parseIdentifier(ctx.templateName.getText());
+    List<List<String>> measurementsList = new ArrayList<List<String>>();
+    List<List<TSDataType>> dataTypesList = new ArrayList<List<TSDataType>>();
+    List<List<TSEncoding>> encodingsList = new ArrayList<List<TSEncoding>>();
+    List<List<CompressionType>> compressorsList = new ArrayList<List<CompressionType>>();
+
+    if (ctx.ALIGNED() != null) {
+      // aligned
+      List<String> measurements = new ArrayList<>();
+      List<TSDataType> dataTypes = new ArrayList<>();
+      List<TSEncoding> encodings = new ArrayList<>();
+      List<CompressionType> compressors = new ArrayList<>();
+      for (IoTDBSqlParser.TemplateMeasurementClauseContext templateClauseContext :
+          ctx.templateMeasurementClause()) {
+        measurements.add(
+            parseNodeNameWithoutWildCard(templateClauseContext.nodeNameWithoutWildcard()));
+        parseAttributeClause(
+            templateClauseContext.attributeClauses(), dataTypes, encodings, compressors);
+      }
+      measurementsList.add(measurements);
+      dataTypesList.add(dataTypes);
+      encodingsList.add(encodings);
+      compressorsList.add(compressors);
+    } else {
+      // non-aligned
+      for (IoTDBSqlParser.TemplateMeasurementClauseContext templateClauseContext :
+          ctx.templateMeasurementClause()) {
+        List<String> measurements = new ArrayList<>();
+        List<TSDataType> dataTypes = new ArrayList<>();
+        List<TSEncoding> encodings = new ArrayList<>();
+        List<CompressionType> compressors = new ArrayList<>();
+        measurements.add(
+            parseNodeNameWithoutWildCard(templateClauseContext.nodeNameWithoutWildcard()));
+        parseAttributeClause(
+            templateClauseContext.attributeClauses(), dataTypes, encodings, compressors);
+        measurementsList.add(measurements);
+        dataTypesList.add(dataTypes);
+        encodingsList.add(encodings);
+        compressorsList.add(compressors);
+      }
+    }
+
+    createTemplateStatement =
+        new CreateSchemaTemplateStatement(
+            name, measurementsList, dataTypesList, encodingsList, compressorsList);
+    return createTemplateStatement;
+  }
+
+  void parseAttributeClause(
+      IoTDBSqlParser.AttributeClausesContext ctx,
+      List<TSDataType> dataTypes,
+      List<TSEncoding> encodings,
+      List<CompressionType> compressors) {
+    if (ctx.aliasNodeName() != null) {
+      throw new SQLParserException("schema template: alias is not supported yet.");
+    }
+
+    TSDataType dataType = null;
+    if (ctx.dataType != null) {
+      if (ctx.attributeKey() != null) {
+        if (!parseAttributeKey(ctx.attributeKey())
+            .equalsIgnoreCase(IoTDBConstant.COLUMN_TIMESERIES_DATATYPE)) {
+          throw new SQLParserException("expecting datatype");
+        }
+      }
+      String dataTypeString = ctx.dataType.getText().toUpperCase();
+      try {
+        dataType = TSDataType.valueOf(dataTypeString);
+        dataTypes.add(dataType);
+      } catch (Exception e) {
+        throw new SemanticException(String.format("unsupported datatype: %s", dataTypeString));
+      }
+    }
+
+    Map<String, String> props = new HashMap<>();
+    if (ctx.attributePair() != null) {
+      for (int i = 0; i < ctx.attributePair().size(); i++) {
+        props.put(
+            parseAttributeKey(ctx.attributePair(i).attributeKey()).toLowerCase(),
+            parseAttributeValue(ctx.attributePair(i).attributeValue()));
+      }
+    }
+
+    TSEncoding encoding = IoTDBDescriptor.getInstance().getDefaultEncodingByType(dataType);
+    if (props.containsKey(IoTDBConstant.COLUMN_TIMESERIES_ENCODING.toLowerCase())) {
+      String encodingString =
+          props.get(IoTDBConstant.COLUMN_TIMESERIES_ENCODING.toLowerCase()).toUpperCase();
+      try {
+        encoding = TSEncoding.valueOf(encodingString);
+        encodings.add(encoding);
+        props.remove(IoTDBConstant.COLUMN_TIMESERIES_ENCODING.toLowerCase());
+      } catch (Exception e) {
+        throw new SemanticException(String.format("unsupported encoding: %s", encodingString));
+      }
+    } else {
+      encodings.add(encoding);
+    }
+
+    CompressionType compressor = TSFileDescriptor.getInstance().getConfig().getCompressor();
+    if (props.containsKey(IoTDBConstant.COLUMN_TIMESERIES_COMPRESSOR.toLowerCase())) {
+      String compressorString =
+          props.get(IoTDBConstant.COLUMN_TIMESERIES_COMPRESSOR.toLowerCase()).toUpperCase();
+      try {
+        compressor = CompressionType.valueOf(compressorString);
+        compressors.add(compressor);
+        props.remove(IoTDBConstant.COLUMN_TIMESERIES_COMPRESSOR.toLowerCase());
+      } catch (Exception e) {
+        throw new SemanticException(String.format("unsupported compressor: %s", compressorString));
+      }
+    } else if (props.containsKey(IoTDBConstant.COLUMN_TIMESERIES_COMPRESSION.toLowerCase())) {
+      String compressionString =
+          props.get(IoTDBConstant.COLUMN_TIMESERIES_COMPRESSION.toLowerCase()).toUpperCase();
+      try {
+        compressor = CompressionType.valueOf(compressionString);
+        compressors.add(compressor);
+        props.remove(IoTDBConstant.COLUMN_TIMESERIES_COMPRESSION.toLowerCase());
+      } catch (Exception e) {
+        throw new SemanticException(
+            String.format("unsupported compression: %s", compressionString));
+      }
+    } else {
+      compressors.add(compressor);
+    }
+
+    if (props.size() > 0) {
+      throw new SQLParserException("schema template: property is not supported yet.");
+    }
+
+    if (ctx.tagClause() != null) {
+      throw new SQLParserException("schema template: tag is not supported yet.");
+    }
+
+    if (ctx.attributeClause() != null) {
+      throw new SQLParserException("schema template: attribute is not supported yet.");
+    }
+  }
+
+  @Override
+  public Statement visitShowSchemaTemplates(IoTDBSqlParser.ShowSchemaTemplatesContext ctx) {
+    ShowSchemaTemplateStatement showSchemaTemplateStatement = new ShowSchemaTemplateStatement();
+    return showSchemaTemplateStatement;
+  }
+
+  @Override
+  public Statement visitShowNodesInSchemaTemplate(
+      IoTDBSqlParser.ShowNodesInSchemaTemplateContext ctx) {
+    String templateName = ctx.templateName.children.get(0).getText();
+    ShowNodesInSchemaTemplateStatement showNodesInSchemaTemplateStatement =
+        new ShowNodesInSchemaTemplateStatement(templateName);
+    return showNodesInSchemaTemplateStatement;
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/StatementVisitor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/StatementVisitor.java
index 2cff76f200..f9da937bff 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/StatementVisitor.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/StatementVisitor.java
@@ -56,15 +56,17 @@ import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowStorageGroupStatement
 import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowTTLStatement;
 import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowTimeSeriesStatement;
 import org.apache.iotdb.db.mpp.plan.statement.metadata.UnSetTTLStatement;
+import org.apache.iotdb.db.mpp.plan.statement.metadata.template.CreateSchemaTemplateStatement;
+import org.apache.iotdb.db.mpp.plan.statement.metadata.template.ShowNodesInSchemaTemplateStatement;
+import org.apache.iotdb.db.mpp.plan.statement.metadata.template.ShowSchemaTemplateStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.AuthorStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.ExplainStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.FlushStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.ShowVersionStatement;
 
 /**
- * This class provides a visitor of {@link org.apache.iotdb.db.mpp.plan.statement.StatementNode},
- * which can be extended to create a visitor which only needs to handle a subset of the available
- * methods.
+ * This class provides a visitor of {@link StatementNode}, which can be extended to create a visitor
+ * which only needs to handle a subset of the available methods.
  *
  * @param <R> The return type of the visit operation.
  * @param <C> The context information during visiting.
@@ -266,4 +268,19 @@ public abstract class StatementVisitor<R, C> {
   public R visitShowVersion(ShowVersionStatement showVersionStatement, C context) {
     return visitStatement(showVersionStatement, context);
   }
+
+  public R visitCreateSchemaTemplate(
+      CreateSchemaTemplateStatement createTemplateStatement, C context) {
+    return visitStatement(createTemplateStatement, context);
+  }
+
+  public R visitShowNodesInSchemaTemplate(
+      ShowNodesInSchemaTemplateStatement showNodesInSchemaTemplateStatement, C context) {
+    return visitStatement(showNodesInSchemaTemplateStatement, context);
+  }
+
+  public R visitShowSchemaTemplate(
+      ShowSchemaTemplateStatement showSchemaTemplateStatement, C context) {
+    return visitStatement(showSchemaTemplateStatement, context);
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/metadata/template/CreateSchemaTemplateStatement.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/metadata/template/CreateSchemaTemplateStatement.java
new file mode 100644
index 0000000000..299a96e2e4
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/metadata/template/CreateSchemaTemplateStatement.java
@@ -0,0 +1,181 @@
+/*
+ * 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.mpp.plan.statement.metadata.template;
+
+import org.apache.iotdb.commons.path.PartialPath;
+import org.apache.iotdb.db.mpp.plan.analyze.QueryType;
+import org.apache.iotdb.db.mpp.plan.constant.StatementType;
+import org.apache.iotdb.db.mpp.plan.statement.IConfigStatement;
+import org.apache.iotdb.db.mpp.plan.statement.Statement;
+import org.apache.iotdb.db.mpp.plan.statement.StatementVisitor;
+import org.apache.iotdb.tsfile.file.metadata.enums.CompressionType;
+import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
+import org.apache.iotdb.tsfile.file.metadata.enums.TSEncoding;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class CreateSchemaTemplateStatement extends Statement implements IConfigStatement {
+
+  String name;
+  Set<String> alignedDeviceId;
+  String[][] measurements;
+  TSDataType[][] dataTypes;
+  TSEncoding[][] encodings;
+  CompressionType[][] compressors;
+
+  // constant to help resolve serialized sequence
+  private static final int NEW_PLAN = -1;
+
+  public CreateSchemaTemplateStatement() {
+    super();
+    statementType = StatementType.CREATE_TEMPLATE;
+  }
+
+  public CreateSchemaTemplateStatement(
+      String name,
+      List<List<String>> measurements,
+      List<List<TSDataType>> dataTypes,
+      List<List<TSEncoding>> encodings,
+      List<List<CompressionType>> compressors) {
+    this();
+    this.name = name;
+    this.measurements = new String[measurements.size()][];
+    for (int i = 0; i < measurements.size(); i++) {
+      this.measurements[i] = new String[measurements.get(i).size()];
+      for (int j = 0; j < measurements.get(i).size(); j++) {
+        this.measurements[i][j] = measurements.get(i).get(j);
+      }
+    }
+
+    this.dataTypes = new TSDataType[dataTypes.size()][];
+    for (int i = 0; i < dataTypes.size(); i++) {
+      this.dataTypes[i] = new TSDataType[dataTypes.get(i).size()];
+      for (int j = 0; j < dataTypes.get(i).size(); j++) {
+        this.dataTypes[i][j] = dataTypes.get(i).get(j);
+      }
+    }
+
+    this.encodings = new TSEncoding[dataTypes.size()][];
+    for (int i = 0; i < encodings.size(); i++) {
+      this.encodings[i] = new TSEncoding[dataTypes.get(i).size()];
+      for (int j = 0; j < encodings.get(i).size(); j++) {
+        this.encodings[i][j] = encodings.get(i).get(j);
+      }
+    }
+
+    this.compressors = new CompressionType[dataTypes.size()][];
+    for (int i = 0; i < compressors.size(); i++) {
+      this.compressors[i] = new CompressionType[compressors.get(i).size()];
+      for (int j = 0; j < compressors.get(i).size(); j++) {
+        this.compressors[i][j] = compressors.get(i).get(j);
+      }
+    }
+    this.alignedDeviceId = new HashSet<>();
+  }
+
+  private CreateSchemaTemplateStatement(
+      String name,
+      List<List<String>> measurements,
+      List<List<TSDataType>> dataTypes,
+      List<List<TSEncoding>> encodings,
+      List<List<CompressionType>> compressors,
+      Set<String> alignedDeviceId) {
+    // Only accessed by deserialization, which may cause ambiguity with align designation
+    this(name, measurements, dataTypes, encodings, compressors);
+    this.alignedDeviceId = alignedDeviceId;
+  }
+
+  public CreateSchemaTemplateStatement(
+      String name,
+      String[][] measurements,
+      TSDataType[][] dataTypes,
+      TSEncoding[][] encodings,
+      CompressionType[][] compressors) {
+    this();
+    this.name = name;
+    this.measurements = measurements;
+    this.dataTypes = dataTypes;
+    this.encodings = encodings;
+    this.compressors = compressors;
+  }
+
+  @Override
+  public List<? extends PartialPath> getPaths() {
+    return null;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public Set<String> getAlignedDeviceId() {
+    return alignedDeviceId;
+  }
+
+  public List<List<String>> getMeasurements() {
+    List<List<String>> ret = new ArrayList<>();
+    for (String[] measurement : measurements) {
+      ret.add(Arrays.asList(measurement));
+    }
+    return ret;
+  }
+
+  public List<List<TSDataType>> getDataTypes() {
+    List<List<TSDataType>> ret = new ArrayList<>();
+    for (TSDataType[] alignedDataTypes : dataTypes) {
+      ret.add(Arrays.asList(alignedDataTypes));
+    }
+    return ret;
+  }
+
+  public List<List<TSEncoding>> getEncodings() {
+    List<List<TSEncoding>> ret = new ArrayList<>();
+    for (TSEncoding[] alignedEncodings : encodings) {
+      ret.add(Arrays.asList(alignedEncodings));
+    }
+    return ret;
+  }
+
+  public List<List<CompressionType>> getCompressors() {
+    List<List<CompressionType>> ret = new ArrayList<>();
+    for (CompressionType[] alignedCompressor : compressors) {
+      ret.add(Arrays.asList(alignedCompressor));
+    }
+    return ret;
+  }
+
+  @Override
+  public <R, C> R accept(StatementVisitor<R, C> visitor, C context) {
+    return visitor.visitCreateSchemaTemplate(this, context);
+  }
+
+  @Override
+  public QueryType getQueryType() {
+    return QueryType.WRITE;
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/metadata/template/ShowNodesInSchemaTemplateStatement.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/metadata/template/ShowNodesInSchemaTemplateStatement.java
new file mode 100644
index 0000000000..dacfc93d8f
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/metadata/template/ShowNodesInSchemaTemplateStatement.java
@@ -0,0 +1,55 @@
+/*
+ * 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.mpp.plan.statement.metadata.template;
+
+import org.apache.iotdb.db.mpp.plan.analyze.QueryType;
+import org.apache.iotdb.db.mpp.plan.constant.StatementType;
+import org.apache.iotdb.db.mpp.plan.statement.IConfigStatement;
+import org.apache.iotdb.db.mpp.plan.statement.StatementVisitor;
+import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowStatement;
+
+public class ShowNodesInSchemaTemplateStatement extends ShowStatement implements IConfigStatement {
+
+  private String templateName;
+
+  public ShowNodesInSchemaTemplateStatement(String templateName) {
+    super();
+    statementType = StatementType.SHOW_NODES_IN_SCHEMA_TEMPLATE;
+    this.templateName = templateName;
+  }
+
+  @Override
+  public <R, C> R accept(StatementVisitor<R, C> visitor, C context) {
+    return visitor.visitShowNodesInSchemaTemplate(this, context);
+  }
+
+  public String getTemplateName() {
+    return templateName;
+  }
+
+  public void setTemplateName(String templateName) {
+    this.templateName = templateName;
+  }
+
+  @Override
+  public QueryType getQueryType() {
+    return QueryType.READ;
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/metadata/template/ShowSchemaTemplateStatement.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/metadata/template/ShowSchemaTemplateStatement.java
new file mode 100644
index 0000000000..e2744d8d1b
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/metadata/template/ShowSchemaTemplateStatement.java
@@ -0,0 +1,44 @@
+/*
+ * 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.mpp.plan.statement.metadata.template;
+
+import org.apache.iotdb.db.mpp.plan.analyze.QueryType;
+import org.apache.iotdb.db.mpp.plan.constant.StatementType;
+import org.apache.iotdb.db.mpp.plan.statement.IConfigStatement;
+import org.apache.iotdb.db.mpp.plan.statement.StatementVisitor;
+import org.apache.iotdb.db.mpp.plan.statement.metadata.ShowStatement;
+
+public class ShowSchemaTemplateStatement extends ShowStatement implements IConfigStatement {
+
+  public ShowSchemaTemplateStatement() {
+    super();
+    statementType = StatementType.SHOW_SCHEMA_TEMPLATE;
+  }
+
+  @Override
+  public <R, C> R accept(StatementVisitor<R, C> visitor, C context) {
+    return visitor.visitShowSchemaTemplate(this, context);
+  }
+
+  @Override
+  public QueryType getQueryType() {
+    return QueryType.READ;
+  }
+}
diff --git a/service-rpc/src/main/java/org/apache/iotdb/rpc/TSStatusCode.java b/service-rpc/src/main/java/org/apache/iotdb/rpc/TSStatusCode.java
index 146068ccd7..cc7370a6fd 100644
--- a/service-rpc/src/main/java/org/apache/iotdb/rpc/TSStatusCode.java
+++ b/service-rpc/src/main/java/org/apache/iotdb/rpc/TSStatusCode.java
@@ -68,6 +68,8 @@ public enum TSStatusCode {
   PIPESERVER_ERROR(336),
   SERIES_OVERFLOW(337),
   MEASUREMENT_ALREADY_EXIST(338),
+  TEMPLATE_NOT_EXIST(339),
+  CREATE_TEMPLATE_ERROR(340),
 
   EXECUTE_STATEMENT_ERROR(400),
   SQL_PARSE_ERROR(401),
diff --git a/thrift-confignode/src/main/thrift/confignode.thrift b/thrift-confignode/src/main/thrift/confignode.thrift
index 973b383df7..4ee1753aaa 100644
--- a/thrift-confignode/src/main/thrift/confignode.thrift
+++ b/thrift-confignode/src/main/thrift/confignode.thrift
@@ -264,6 +264,23 @@ struct TRegionRouteMapResp {
   3: optional map<common.TConsensusGroupId, common.TRegionReplicaSet> regionRouteMap
 }
 
+
+// Template
+struct TCreateSchemaTemplateReq {
+  1: required string name
+  2: required binary serializedTemplate
+}
+
+struct TGetAllTemplatesResp {
+  1: required common.TSStatus status
+  2: optional list<binary> templateList
+}
+
+struct TGetTemplateResp {
+  1: required common.TSStatus status
+  2: optional binary template
+}
+
 service IConfigNodeRPCService {
 
   /* DataNode */
@@ -373,5 +390,13 @@ service IConfigNodeRPCService {
 
   TShowDataNodesResp showDataNodes()
 
+   /* Template */
+
+    common.TSStatus createSchemaTemplate(TCreateSchemaTemplateReq req)
+
+    TGetAllTemplatesResp getAllTemplates()
+
+    TGetTemplateResp getTemplate(string req)
+
 }