You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by xi...@apache.org on 2022/09/09 08:39:43 UTC

[iotdb] branch master updated: [IOTDB-3988][IOTDB-3733] Fix reload problem of metric module, and refactor metric module. (#7239)

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

xingtanzjr 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 b692bb70ab [IOTDB-3988][IOTDB-3733] Fix reload problem of metric module, and refactor metric module. (#7239)
b692bb70ab is described below

commit b692bb70ab460103a4868644148fb948184b8b5a
Author: ZhangHongYin <46...@users.noreply.github.com>
AuthorDate: Fri Sep 9 16:39:37 2022 +0800

    [IOTDB-3988][IOTDB-3733] Fix reload problem of metric module, and refactor metric module. (#7239)
---
 .../iotdb/confignode/manager/NodeManager.java      |   4 +-
 .../iotdb/confignode/manager/PartitionManager.java |   4 +-
 .../iotdb/confignode/manager/load/LoadManager.java |   6 +-
 .../manager/load/LoadManagerMetrics.java           | 288 +++++++++++----------
 .../iotdb/confignode/persistence/NodeInfo.java     |  30 ---
 .../persistence/metric/NodeInfoMetrics.java        |  95 +++++++
 .../persistence/metric/PartitionInfoMetrics.java   | 198 ++++++++++++++
 .../persistence/partition/PartitionInfo.java       |  47 +---
 .../partition/StorageGroupPartitionTable.java      |  58 +----
 .../iotdb/confignode/service/ConfigNode.java       |   4 -
 .../service/thrift/ConfigNodeRPCService.java       |  13 +-
 .../thrift/ConfigNodeRPCServiceHandler.java        |  28 +-
 .../thrift/ConfigNodeRPCServiceHandlerMetrics.java |  69 +++++
 .../thrift/ConfigNodeRPCServiceMetrics.java        |  70 +++++
 .../reporter/DropwizardPrometheusReporter.java     |   4 +-
 .../iotdb/metrics/AbstractMetricService.java       |  65 ++++-
 .../iotdb/metrics/DoNothingMetricService.java      |   2 +-
 .../apache/iotdb/metrics/config/MetricConfig.java  |   4 +-
 .../{predefined => metricsets}/IMetricSet.java     |  19 +-
 .../predefined/PredefinedMetric.java               |   2 +-
 .../predefined/jvm/JvmClassLoaderMetrics.java      |  20 +-
 .../predefined/jvm/JvmCompileMetrics.java          |  20 +-
 .../predefined/jvm/JvmGcMetrics.java               | 117 ++++++---
 .../predefined/jvm/JvmMemoryMetrics.java           |  55 +++-
 .../metricsets/predefined/jvm/JvmMetrics.java      |  52 ++++
 .../predefined/jvm/JvmThreadMetrics.java           |  43 ++-
 .../{ => metricsets}/predefined/jvm/JvmUtils.java  |   2 +-
 .../predefined/logback/LogbackMetrics.java         | 104 ++++++++
 .../predefined/logback/MetricsTurboFilter.java     |  98 +++++++
 .../iotdb/metrics/predefined/jvm/JvmMetrics.java   |  49 ----
 .../metrics/predefined/logback/LogbackMetrics.java | 181 -------------
 .../reporter/MicrometerPrometheusReporter.java     |   5 +-
 .../apache/iotdb/db/engine/cache/ChunkCache.java   |  16 +-
 .../iotdb/db/engine/cache/ChunkCacheMetrics.java   |  67 +++++
 .../db/engine/cache/TimeSeriesMetadataCache.java   |  35 +--
 .../cache/TimeSeriesMetadataCacheMetrics.java      |  77 ++++++
 .../apache/iotdb/db/engine/flush/FlushManager.java |  31 +--
 .../iotdb/db/engine/flush/FlushManagerMBean.java   |   2 +
 .../iotdb/db/engine/flush/FlushManagerMetrics.java |  90 +++++++
 .../iotdb/db/engine/storagegroup/DataRegion.java   |  16 +-
 .../db/engine/storagegroup/DataRegionMetrics.java  |  73 ++++++
 .../engine/storagegroup/TsFileProcessorInfo.java   |  32 +--
 .../storagegroup/TsFileProcessorInfoMetrics.java   |  74 ++++++
 .../db/metadata/cache/DataNodeSchemaCache.java     |  16 +-
 .../metadata/cache/DataNodeSchemaCacheMetrics.java |  67 +++++
 .../db/metadata/rescon/SchemaResourceManager.java  |   4 +-
 .../metadata/rescon/SchemaStatisticsManager.java   |  16 --
 .../rescon/SchemaStatisticsManagerMetrics.java     |  67 +++++
 .../execution/exchange/MPPDataExchangeService.java |  12 +-
 .../exchange/MPPDataExchangeServiceMetrics.java    |  65 +++++
 .../MPPDataExchangeServiceThriftHandler.java       |  27 +-
 ...ppDataExchangeServiceThriftHandlerMetrics.java} |  51 ++--
 .../db/query/pool/RawQueryReadTaskPoolManager.java |  32 +--
 .../pool/RawQueryReadTaskPoolManagerMetrics.java   |  92 +++++++
 .../java/org/apache/iotdb/db/service/DataNode.java |   8 +-
 .../db/service/DataNodeInternalRPCService.java     |  12 +-
 .../service/DataNodeInternalRPCServiceMetrics.java |  72 ++++++
 .../java/org/apache/iotdb/db/service/IoTDB.java    |   5 +-
 .../java/org/apache/iotdb/db/service/NewIoTDB.java |   5 +-
 .../org/apache/iotdb/db/service/RPCService.java    |  13 +-
 .../apache/iotdb/db/service/RPCServiceMetrics.java |  71 +++++
 .../iotdb/db/service/metrics/MetricService.java    |  31 +--
 .../db/service/metrics/predefined/FileMetrics.java |  69 +++--
 .../service/metrics/predefined/ProcessMetrics.java |  80 ++++--
 .../service/metrics/predefined/SystemMetrics.java  | 116 ++++++---
 .../handler/InternalServiceThriftHandler.java      |  22 +-
 .../InternalServiceThriftHandlerMetrics.java       |  68 +++++
 .../thrift/handler/RPCServiceThriftHandler.java    |  19 +-
 .../handler/RPCServiceThriftHandlerMetrics.java    |  65 +++++
 .../apache/iotdb/db/metric/MetricServiceTest.java  |   1 -
 70 files changed, 2386 insertions(+), 989 deletions(-)

diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/NodeManager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/NodeManager.java
index 1c94cfd12c..3471c15262 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/NodeManager.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/NodeManager.java
@@ -50,6 +50,7 @@ import org.apache.iotdb.confignode.manager.load.heartbeat.BaseNodeCache;
 import org.apache.iotdb.confignode.manager.load.heartbeat.ConfigNodeHeartbeatCache;
 import org.apache.iotdb.confignode.manager.load.heartbeat.DataNodeHeartbeatCache;
 import org.apache.iotdb.confignode.persistence.NodeInfo;
+import org.apache.iotdb.confignode.persistence.metric.NodeInfoMetrics;
 import org.apache.iotdb.confignode.procedure.env.DataNodeRemoveHandler;
 import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeInfo;
 import org.apache.iotdb.confignode.rpc.thrift.TDataNodeInfo;
@@ -58,6 +59,7 @@ import org.apache.iotdb.confignode.rpc.thrift.TRatisConfig;
 import org.apache.iotdb.consensus.common.DataSet;
 import org.apache.iotdb.consensus.common.Peer;
 import org.apache.iotdb.consensus.common.response.ConsensusGenericResponse;
+import org.apache.iotdb.db.service.metrics.MetricService;
 import org.apache.iotdb.mpp.rpc.thrift.THeartbeatReq;
 import org.apache.iotdb.rpc.TSStatusCode;
 
@@ -312,7 +314,7 @@ public class NodeManager {
   }
 
   public void addMetrics() {
-    nodeInfo.addMetrics();
+    MetricService.getInstance().addMetricSet(new NodeInfoMetrics(nodeInfo));
   }
 
   /**
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/PartitionManager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/PartitionManager.java
index a5bb056a98..5808989e01 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/PartitionManager.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/PartitionManager.java
@@ -51,10 +51,12 @@ import org.apache.iotdb.confignode.exception.NotEnoughDataNodeException;
 import org.apache.iotdb.confignode.exception.StorageGroupNotExistsException;
 import org.apache.iotdb.confignode.manager.load.LoadManager;
 import org.apache.iotdb.confignode.manager.load.heartbeat.IRegionGroupCache;
+import org.apache.iotdb.confignode.persistence.metric.PartitionInfoMetrics;
 import org.apache.iotdb.confignode.persistence.partition.PartitionInfo;
 import org.apache.iotdb.consensus.ConsensusFactory;
 import org.apache.iotdb.consensus.common.DataSet;
 import org.apache.iotdb.consensus.common.response.ConsensusReadResponse;
+import org.apache.iotdb.db.service.metrics.MetricService;
 import org.apache.iotdb.rpc.RpcUtils;
 import org.apache.iotdb.rpc.TSStatusCode;
 import org.apache.iotdb.tsfile.utils.Pair;
@@ -451,7 +453,7 @@ public class PartitionManager {
   }
 
   public void addMetrics() {
-    partitionInfo.addMetrics();
+    MetricService.getInstance().addMetricSet(new PartitionInfoMetrics(partitionInfo));
   }
 
   /**
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/LoadManager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/LoadManager.java
index 62a4280437..63ab1e5f3a 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/LoadManager.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/LoadManager.java
@@ -46,6 +46,7 @@ import org.apache.iotdb.confignode.manager.load.balancer.RegionBalancer;
 import org.apache.iotdb.confignode.manager.load.balancer.RouteBalancer;
 import org.apache.iotdb.confignode.manager.load.heartbeat.DataNodeHeartbeatCache;
 import org.apache.iotdb.consensus.ConsensusFactory;
+import org.apache.iotdb.db.service.metrics.MetricService;
 import org.apache.iotdb.mpp.rpc.thrift.TRegionRouteReq;
 
 import org.slf4j.Logger;
@@ -77,7 +78,6 @@ public class LoadManager {
 
   private final PartitionBalancer partitionBalancer;
   private final RouteBalancer routeBalancer;
-  private final LoadManagerMetrics loadManagerMetrics;
 
   /** Load balancing executor service */
   private Future<?> currentLoadBalancingFuture;
@@ -93,7 +93,7 @@ public class LoadManager {
     this.regionBalancer = new RegionBalancer(configManager);
     this.partitionBalancer = new PartitionBalancer(configManager);
     this.routeBalancer = new RouteBalancer(configManager);
-    this.loadManagerMetrics = new LoadManagerMetrics(configManager);
+    MetricService.getInstance().addMetricSet(new LoadManagerMetrics(configManager));
   }
 
   /**
@@ -159,13 +159,11 @@ public class LoadManager {
                 TimeUnit.MILLISECONDS);
         LOGGER.info("LoadBalancing service is started successfully.");
       }
-      loadManagerMetrics.addMetrics();
     }
   }
 
   /** Stop the load balancing service */
   public void stopLoadBalancingService() {
-    loadManagerMetrics.removeMetrics();
     synchronized (scheduleMonitor) {
       if (currentLoadBalancingFuture != null) {
         currentLoadBalancingFuture.cancel(false);
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/LoadManagerMetrics.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/LoadManagerMetrics.java
index f359435fb2..03e2527923 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/LoadManagerMetrics.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/LoadManagerMetrics.java
@@ -26,14 +26,16 @@ import org.apache.iotdb.commons.utils.NodeUrlUtils;
 import org.apache.iotdb.confignode.manager.IManager;
 import org.apache.iotdb.confignode.manager.NodeManager;
 import org.apache.iotdb.confignode.manager.PartitionManager;
-import org.apache.iotdb.db.service.metrics.MetricService;
 import org.apache.iotdb.db.service.metrics.enums.Metric;
 import org.apache.iotdb.db.service.metrics.enums.Tag;
+import org.apache.iotdb.metrics.AbstractMetricService;
+import org.apache.iotdb.metrics.metricsets.IMetricSet;
 import org.apache.iotdb.metrics.utils.MetricLevel;
 import org.apache.iotdb.metrics.utils.MetricType;
 
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
 
 import static org.apache.iotdb.confignode.conf.ConfigNodeConstant.METRIC_STATUS_ONLINE;
@@ -41,7 +43,7 @@ import static org.apache.iotdb.confignode.conf.ConfigNodeConstant.METRIC_STATUS_
 import static org.apache.iotdb.confignode.conf.ConfigNodeConstant.METRIC_TAG_TOTAL;
 
 /** This class collates metrics about loadManager */
-public class LoadManagerMetrics {
+public class LoadManagerMetrics implements IMetricSet {
 
   private final IManager configManager;
 
@@ -49,12 +51,143 @@ public class LoadManagerMetrics {
     this.configManager = configManager;
   }
 
-  public void addMetrics() {
-    addNodeMetrics();
-    addLeaderCount();
+  @Override
+  public void bindTo(AbstractMetricService metricService) {
+    addNodeMetrics(metricService);
+    addLeaderCount(metricService);
   }
 
-  private int getRunningConfigNodesNum() {
+  private void addNodeMetrics(AbstractMetricService metricService) {
+    metricService.getOrCreateAutoGauge(
+        Metric.CONFIG_NODE.toString(),
+        MetricLevel.CORE,
+        this,
+        o -> getRunningConfigNodesNum(metricService),
+        Tag.NAME.toString(),
+        METRIC_TAG_TOTAL,
+        Tag.STATUS.toString(),
+        METRIC_STATUS_ONLINE);
+
+    metricService.getOrCreateAutoGauge(
+        Metric.DATA_NODE.toString(),
+        MetricLevel.CORE,
+        this,
+        o -> getRunningDataNodesNum(metricService),
+        Tag.NAME.toString(),
+        METRIC_TAG_TOTAL,
+        Tag.STATUS.toString(),
+        METRIC_STATUS_ONLINE);
+
+    metricService.getOrCreateAutoGauge(
+        Metric.CONFIG_NODE.toString(),
+        MetricLevel.CORE,
+        this,
+        o -> getUnknownConfigNodesNum(metricService),
+        Tag.NAME.toString(),
+        METRIC_TAG_TOTAL,
+        Tag.STATUS.toString(),
+        METRIC_STATUS_UNKNOWN);
+
+    metricService.getOrCreateAutoGauge(
+        Metric.DATA_NODE.toString(),
+        MetricLevel.CORE,
+        this,
+        o -> getUnknownDataNodesNum(metricService),
+        Tag.NAME.toString(),
+        METRIC_TAG_TOTAL,
+        Tag.STATUS.toString(),
+        METRIC_STATUS_UNKNOWN);
+  }
+
+  /** Get the LeaderCount of Specific DataNodeId */
+  private Integer getLeadershipCountByDatanode(int dataNodeId) {
+    Map<Integer, Integer> idToCountMap = new ConcurrentHashMap<>();
+
+    getPartitionManager()
+        .getAllLeadership()
+        .forEach((consensusGroupId, nodeId) -> idToCountMap.merge(nodeId, 1, Integer::sum));
+    return idToCountMap.get(dataNodeId);
+  }
+
+  private void addLeaderCount(AbstractMetricService metricService) {
+    getNodeManager()
+        .getRegisteredDataNodes()
+        .forEach(
+            dataNodeInfo -> {
+              TDataNodeLocation dataNodeLocation = dataNodeInfo.getLocation();
+              int dataNodeId = dataNodeLocation.getDataNodeId();
+              String name =
+                  NodeUrlUtils.convertTEndPointUrl(dataNodeLocation.getClientRpcEndPoint());
+
+              metricService.getOrCreateAutoGauge(
+                  Metric.CLUSTER_NODE_LEADER_COUNT.toString(),
+                  MetricLevel.IMPORTANT,
+                  this,
+                  o -> getLeadershipCountByDatanode(dataNodeId),
+                  Tag.NAME.toString(),
+                  name);
+            });
+  }
+
+  @Override
+  public void unbindFrom(AbstractMetricService metricService) {
+    metricService.remove(
+        MetricType.GAUGE,
+        Metric.CONFIG_NODE.toString(),
+        Tag.NAME.toString(),
+        METRIC_TAG_TOTAL,
+        Tag.STATUS.toString(),
+        METRIC_STATUS_ONLINE);
+
+    metricService.remove(
+        MetricType.GAUGE,
+        Metric.DATA_NODE.toString(),
+        Tag.NAME.toString(),
+        METRIC_TAG_TOTAL,
+        Tag.STATUS.toString(),
+        METRIC_STATUS_ONLINE);
+
+    metricService.remove(
+        MetricType.GAUGE,
+        Metric.CONFIG_NODE.toString(),
+        Tag.NAME.toString(),
+        METRIC_TAG_TOTAL,
+        Tag.STATUS.toString(),
+        METRIC_STATUS_UNKNOWN);
+
+    metricService.remove(
+        MetricType.GAUGE,
+        Metric.DATA_NODE.toString(),
+        Tag.NAME.toString(),
+        METRIC_TAG_TOTAL,
+        Tag.STATUS.toString(),
+        METRIC_STATUS_UNKNOWN);
+
+    getNodeManager()
+        .getRegisteredDataNodes()
+        .forEach(
+            dataNodeInfo -> {
+              TDataNodeLocation dataNodeLocation = dataNodeInfo.getLocation();
+              String name =
+                  NodeUrlUtils.convertTEndPointUrl(dataNodeLocation.getClientRpcEndPoint());
+
+              metricService.remove(
+                  MetricType.GAUGE,
+                  Metric.CLUSTER_NODE_LEADER_COUNT.toString(),
+                  Tag.NAME.toString(),
+                  name);
+            });
+  }
+
+  private NodeManager getNodeManager() {
+    return configManager.getNodeManager();
+  }
+
+  private PartitionManager getPartitionManager() {
+    return configManager.getPartitionManager();
+  }
+
+  private int getRunningConfigNodesNum(AbstractMetricService metricService) {
     List<TConfigNodeLocation> runningConfigNodes =
         getNodeManager().filterConfigNodeThroughStatus(NodeStatus.Running);
     if (runningConfigNodes == null) {
@@ -63,7 +196,7 @@ public class LoadManagerMetrics {
     for (TConfigNodeLocation configNodeLocation : runningConfigNodes) {
       String name = NodeUrlUtils.convertTEndPointUrl(configNodeLocation.getInternalEndPoint());
 
-      MetricService.getInstance()
+      metricService
           .getOrCreateGauge(
               Metric.CLUSTER_NODE_STATUS.toString(),
               MetricLevel.IMPORTANT,
@@ -76,7 +209,7 @@ public class LoadManagerMetrics {
     return runningConfigNodes.size();
   }
 
-  private int getRunningDataNodesNum() {
+  private int getRunningDataNodesNum(AbstractMetricService metricService) {
     List<TDataNodeConfiguration> runningDataNodes =
         getNodeManager().filterDataNodeThroughStatus(NodeStatus.Running);
     if (runningDataNodes == null) {
@@ -86,7 +219,7 @@ public class LoadManagerMetrics {
       TDataNodeLocation dataNodeLocation = dataNodeInfo.getLocation();
       String name = NodeUrlUtils.convertTEndPointUrl(dataNodeLocation.getClientRpcEndPoint());
 
-      MetricService.getInstance()
+      metricService
           .getOrCreateGauge(
               Metric.CLUSTER_NODE_STATUS.toString(),
               MetricLevel.IMPORTANT,
@@ -99,7 +232,7 @@ public class LoadManagerMetrics {
     return runningDataNodes.size();
   }
 
-  private int getUnknownConfigNodesNum() {
+  private int getUnknownConfigNodesNum(AbstractMetricService metricService) {
     List<TConfigNodeLocation> unknownConfigNodes =
         getNodeManager().filterConfigNodeThroughStatus(NodeStatus.Unknown);
     if (unknownConfigNodes == null) {
@@ -108,7 +241,7 @@ public class LoadManagerMetrics {
     for (TConfigNodeLocation configNodeLocation : unknownConfigNodes) {
       String name = NodeUrlUtils.convertTEndPointUrl(configNodeLocation.getInternalEndPoint());
 
-      MetricService.getInstance()
+      metricService
           .getOrCreateGauge(
               Metric.CLUSTER_NODE_STATUS.toString(),
               MetricLevel.IMPORTANT,
@@ -121,7 +254,7 @@ public class LoadManagerMetrics {
     return unknownConfigNodes.size();
   }
 
-  private int getUnknownDataNodesNum() {
+  private int getUnknownDataNodesNum(AbstractMetricService metricService) {
     List<TDataNodeConfiguration> unknownDataNodes =
         getNodeManager().filterDataNodeThroughStatus(NodeStatus.Unknown);
     if (unknownDataNodes == null) {
@@ -131,7 +264,7 @@ public class LoadManagerMetrics {
       TDataNodeLocation dataNodeLocation = dataNodeInfo.getLocation();
       String name = NodeUrlUtils.convertTEndPointUrl(dataNodeLocation.getClientRpcEndPoint());
 
-      MetricService.getInstance()
+      metricService
           .getOrCreateGauge(
               Metric.CLUSTER_NODE_STATUS.toString(),
               MetricLevel.IMPORTANT,
@@ -144,127 +277,16 @@ public class LoadManagerMetrics {
     return unknownDataNodes.size();
   }
 
-  public void addNodeMetrics() {
-    MetricService.getInstance()
-        .getOrCreateAutoGauge(
-            Metric.CONFIG_NODE.toString(),
-            MetricLevel.CORE,
-            this,
-            o -> getRunningConfigNodesNum(),
-            Tag.NAME.toString(),
-            METRIC_TAG_TOTAL,
-            Tag.STATUS.toString(),
-            METRIC_STATUS_ONLINE);
-
-    MetricService.getInstance()
-        .getOrCreateAutoGauge(
-            Metric.DATA_NODE.toString(),
-            MetricLevel.CORE,
-            this,
-            o -> getRunningDataNodesNum(),
-            Tag.NAME.toString(),
-            METRIC_TAG_TOTAL,
-            Tag.STATUS.toString(),
-            METRIC_STATUS_ONLINE);
-
-    MetricService.getInstance()
-        .getOrCreateAutoGauge(
-            Metric.CONFIG_NODE.toString(),
-            MetricLevel.CORE,
-            this,
-            o -> getUnknownConfigNodesNum(),
-            Tag.NAME.toString(),
-            METRIC_TAG_TOTAL,
-            Tag.STATUS.toString(),
-            METRIC_STATUS_UNKNOWN);
-
-    MetricService.getInstance()
-        .getOrCreateAutoGauge(
-            Metric.DATA_NODE.toString(),
-            MetricLevel.CORE,
-            this,
-            o -> getUnknownDataNodesNum(),
-            Tag.NAME.toString(),
-            METRIC_TAG_TOTAL,
-            Tag.STATUS.toString(),
-            METRIC_STATUS_UNKNOWN);
-  }
-
-  /**
-   * Get the LeaderCount of Specific DataNodeId
-   *
-   * @return Integer
-   */
-  public Integer getLeadershipCountByDatanode(int dataNodeId) {
-    Map<Integer, Integer> idToCountMap = new ConcurrentHashMap<>();
-
-    getPartitionManager()
-        .getAllLeadership()
-        .forEach((consensusGroupId, nodeId) -> idToCountMap.merge(nodeId, 1, Integer::sum));
-    return idToCountMap.get(dataNodeId);
-  }
-
-  public void addLeaderCount() {
-    getNodeManager()
-        .getRegisteredDataNodes()
-        .forEach(
-            dataNodeInfo -> {
-              TDataNodeLocation dataNodeLocation = dataNodeInfo.getLocation();
-              int dataNodeId = dataNodeLocation.getDataNodeId();
-              String name =
-                  NodeUrlUtils.convertTEndPointUrl(dataNodeLocation.getClientRpcEndPoint());
-
-              MetricService.getInstance()
-                  .getOrCreateAutoGauge(
-                      Metric.CLUSTER_NODE_LEADER_COUNT.toString(),
-                      MetricLevel.IMPORTANT,
-                      this,
-                      o -> getLeadershipCountByDatanode(dataNodeId),
-                      Tag.NAME.toString(),
-                      name);
-            });
-  }
-
-  public void removeMetrics() {
-    MetricService.getInstance()
-        .remove(
-            MetricType.GAUGE,
-            Metric.CONFIG_NODE.toString(),
-            Tag.NAME.toString(),
-            METRIC_TAG_TOTAL,
-            Tag.STATUS.toString(),
-            METRIC_STATUS_ONLINE);
-    MetricService.getInstance()
-        .remove(
-            MetricType.GAUGE,
-            Metric.DATA_NODE.toString(),
-            Tag.NAME.toString(),
-            METRIC_TAG_TOTAL,
-            Tag.STATUS.toString(),
-            METRIC_STATUS_ONLINE);
-    MetricService.getInstance()
-        .remove(
-            MetricType.GAUGE,
-            Metric.CONFIG_NODE.toString(),
-            Tag.NAME.toString(),
-            METRIC_TAG_TOTAL,
-            Tag.STATUS.toString(),
-            METRIC_STATUS_UNKNOWN);
-    MetricService.getInstance()
-        .remove(
-            MetricType.GAUGE,
-            Metric.DATA_NODE.toString(),
-            Tag.NAME.toString(),
-            METRIC_TAG_TOTAL,
-            Tag.STATUS.toString(),
-            METRIC_STATUS_UNKNOWN);
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    LoadManagerMetrics that = (LoadManagerMetrics) o;
+    return Objects.equals(configManager, that.configManager);
   }
 
-  private NodeManager getNodeManager() {
-    return configManager.getNodeManager();
-  }
-
-  private PartitionManager getPartitionManager() {
-    return configManager.getPartitionManager();
+  @Override
+  public int hashCode() {
+    return Objects.hash(configManager);
   }
 }
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java
index 7a0bfa72ca..39f64cfb1d 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/NodeInfo.java
@@ -32,10 +32,6 @@ 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.RemoveDataNodePlan;
 import org.apache.iotdb.confignode.consensus.response.DataNodeConfigurationResp;
-import org.apache.iotdb.db.service.metrics.MetricService;
-import org.apache.iotdb.db.service.metrics.enums.Metric;
-import org.apache.iotdb.db.service.metrics.enums.Tag;
-import org.apache.iotdb.metrics.utils.MetricLevel;
 import org.apache.iotdb.rpc.TSStatusCode;
 import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils;
 
@@ -66,9 +62,6 @@ import java.util.concurrent.ConcurrentSkipListMap;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
-import static org.apache.iotdb.confignode.conf.ConfigNodeConstant.METRIC_STATUS_REGISTER;
-import static org.apache.iotdb.confignode.conf.ConfigNodeConstant.METRIC_TAG_TOTAL;
-
 /**
  * The NodeInfo stores cluster node information. The cluster node information including: 1. DataNode
  * information 2. ConfigNode information
@@ -103,29 +96,6 @@ public class NodeInfo implements SnapshotProcessor {
     this.registeredConfigNodes = new HashSet<>();
   }
 
-  public void addMetrics() {
-    MetricService.getInstance()
-        .getOrCreateAutoGauge(
-            Metric.CONFIG_NODE.toString(),
-            MetricLevel.CORE,
-            registeredConfigNodes,
-            o -> getRegisteredConfigNodeCount(),
-            Tag.NAME.toString(),
-            METRIC_TAG_TOTAL,
-            Tag.STATUS.toString(),
-            METRIC_STATUS_REGISTER);
-    MetricService.getInstance()
-        .getOrCreateAutoGauge(
-            Metric.DATA_NODE.toString(),
-            MetricLevel.CORE,
-            registeredDataNodes,
-            Map::size,
-            Tag.NAME.toString(),
-            METRIC_TAG_TOTAL,
-            Tag.STATUS.toString(),
-            METRIC_STATUS_REGISTER);
-  }
-
   /**
    * Only leader use this interface
    *
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/metric/NodeInfoMetrics.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/metric/NodeInfoMetrics.java
new file mode 100644
index 0000000000..19acf736bc
--- /dev/null
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/metric/NodeInfoMetrics.java
@@ -0,0 +1,95 @@
+/*
+ * 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.metric;
+
+import org.apache.iotdb.confignode.persistence.NodeInfo;
+import org.apache.iotdb.db.service.metrics.enums.Metric;
+import org.apache.iotdb.db.service.metrics.enums.Tag;
+import org.apache.iotdb.metrics.AbstractMetricService;
+import org.apache.iotdb.metrics.metricsets.IMetricSet;
+import org.apache.iotdb.metrics.utils.MetricLevel;
+import org.apache.iotdb.metrics.utils.MetricType;
+
+import java.util.Objects;
+
+import static org.apache.iotdb.confignode.conf.ConfigNodeConstant.METRIC_STATUS_REGISTER;
+import static org.apache.iotdb.confignode.conf.ConfigNodeConstant.METRIC_TAG_TOTAL;
+
+public class NodeInfoMetrics implements IMetricSet {
+
+  private NodeInfo nodeInfo;
+
+  public NodeInfoMetrics(NodeInfo nodeInfo) {
+    this.nodeInfo = nodeInfo;
+  }
+
+  @Override
+  public void bindTo(AbstractMetricService metricService) {
+    metricService.getOrCreateAutoGauge(
+        Metric.CONFIG_NODE.toString(),
+        MetricLevel.CORE,
+        this,
+        o -> nodeInfo.getRegisteredConfigNodeCount(),
+        Tag.NAME.toString(),
+        METRIC_TAG_TOTAL,
+        Tag.STATUS.toString(),
+        METRIC_STATUS_REGISTER);
+    metricService.getOrCreateAutoGauge(
+        Metric.DATA_NODE.toString(),
+        MetricLevel.CORE,
+        this,
+        o -> nodeInfo.getRegisteredDataNodeCount(),
+        Tag.NAME.toString(),
+        METRIC_TAG_TOTAL,
+        Tag.STATUS.toString(),
+        METRIC_STATUS_REGISTER);
+  }
+
+  @Override
+  public void unbindFrom(AbstractMetricService metricService) {
+    metricService.remove(
+        MetricType.GAUGE,
+        Metric.CONFIG_NODE.toString(),
+        Tag.NAME.toString(),
+        METRIC_TAG_TOTAL,
+        Tag.STATUS.toString(),
+        METRIC_STATUS_REGISTER);
+    metricService.remove(
+        MetricType.GAUGE,
+        Metric.DATA_NODE.toString(),
+        Tag.NAME.toString(),
+        METRIC_TAG_TOTAL,
+        Tag.STATUS.toString(),
+        METRIC_STATUS_REGISTER);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    NodeInfoMetrics that = (NodeInfoMetrics) o;
+    return Objects.equals(nodeInfo, that.nodeInfo);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(nodeInfo);
+  }
+}
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/metric/PartitionInfoMetrics.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/metric/PartitionInfoMetrics.java
new file mode 100644
index 0000000000..a72b52e28b
--- /dev/null
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/metric/PartitionInfoMetrics.java
@@ -0,0 +1,198 @@
+/*
+ * 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.metric;
+
+import org.apache.iotdb.common.rpc.thrift.TConsensusGroupType;
+import org.apache.iotdb.confignode.persistence.partition.PartitionInfo;
+import org.apache.iotdb.confignode.persistence.partition.StorageGroupPartitionTable;
+import org.apache.iotdb.db.service.metrics.enums.Metric;
+import org.apache.iotdb.db.service.metrics.enums.Tag;
+import org.apache.iotdb.metrics.AbstractMetricService;
+import org.apache.iotdb.metrics.metricsets.IMetricSet;
+import org.apache.iotdb.metrics.utils.MetricLevel;
+import org.apache.iotdb.metrics.utils.MetricType;
+
+import java.util.Objects;
+
+public class PartitionInfoMetrics implements IMetricSet {
+  private PartitionInfo partitionInfo;
+
+  public PartitionInfoMetrics(PartitionInfo partitionInfo) {
+    this.partitionInfo = partitionInfo;
+  }
+
+  @Override
+  public void bindTo(AbstractMetricService metricService) {
+    metricService.getOrCreateAutoGauge(
+        Metric.QUANTITY.toString(),
+        MetricLevel.IMPORTANT,
+        partitionInfo,
+        PartitionInfo::getStorageGroupPartitionTableSize,
+        Tag.NAME.toString(),
+        "storageGroup");
+    metricService.getOrCreateAutoGauge(
+        Metric.REGION.toString(),
+        MetricLevel.IMPORTANT,
+        partitionInfo,
+        o -> o.updateRegionGroupMetric(TConsensusGroupType.SchemaRegion),
+        Tag.NAME.toString(),
+        "total",
+        Tag.TYPE.toString(),
+        TConsensusGroupType.SchemaRegion.toString());
+    metricService.getOrCreateAutoGauge(
+        Metric.REGION.toString(),
+        MetricLevel.IMPORTANT,
+        partitionInfo,
+        o -> o.updateRegionGroupMetric(TConsensusGroupType.DataRegion),
+        Tag.NAME.toString(),
+        "total",
+        Tag.TYPE.toString(),
+        TConsensusGroupType.DataRegion.toString());
+  }
+
+  @Override
+  public void unbindFrom(AbstractMetricService metricService) {
+    metricService.remove(
+        MetricType.GAUGE, Metric.QUANTITY.toString(), Tag.NAME.toString(), "storageGroup");
+    metricService.remove(
+        MetricType.GAUGE,
+        Metric.REGION.toString(),
+        Tag.NAME.toString(),
+        "total",
+        Tag.TYPE.toString(),
+        TConsensusGroupType.SchemaRegion.toString());
+    metricService.remove(
+        MetricType.GAUGE,
+        Metric.REGION.toString(),
+        Tag.NAME.toString(),
+        "total",
+        Tag.TYPE.toString(),
+        TConsensusGroupType.DataRegion.toString());
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    PartitionInfoMetrics that = (PartitionInfoMetrics) o;
+    return Objects.equals(partitionInfo, that.partitionInfo);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(partitionInfo);
+  }
+
+  public static class StorageGroupPartitionTableMetrics implements IMetricSet {
+    private StorageGroupPartitionTable storageGroupPartitionTable;
+
+    public StorageGroupPartitionTableMetrics(
+        StorageGroupPartitionTable storageGroupPartitionTable) {
+      this.storageGroupPartitionTable = storageGroupPartitionTable;
+    }
+
+    @Override
+    public void bindTo(AbstractMetricService metricService) {
+      metricService.getOrCreateAutoGauge(
+          Metric.REGION.toString(),
+          MetricLevel.NORMAL,
+          storageGroupPartitionTable,
+          o -> o.getRegionGroupCount(TConsensusGroupType.SchemaRegion),
+          Tag.NAME.toString(),
+          storageGroupPartitionTable.getStorageGroupName(),
+          Tag.TYPE.toString(),
+          TConsensusGroupType.SchemaRegion.toString());
+      metricService.getOrCreateAutoGauge(
+          Metric.REGION.toString(),
+          MetricLevel.NORMAL,
+          storageGroupPartitionTable,
+          o -> o.getRegionGroupCount(TConsensusGroupType.DataRegion),
+          Tag.NAME.toString(),
+          storageGroupPartitionTable.getStorageGroupName(),
+          Tag.TYPE.toString(),
+          TConsensusGroupType.DataRegion.toString());
+      // TODO slot will be updated in the future
+      metricService.getOrCreateAutoGauge(
+          Metric.SLOT.toString(),
+          MetricLevel.NORMAL,
+          storageGroupPartitionTable,
+          StorageGroupPartitionTable::getSchemaPartitionMapSize,
+          Tag.NAME.toString(),
+          storageGroupPartitionTable.getStorageGroupName(),
+          Tag.TYPE.toString(),
+          "schemaSlotNumber");
+      metricService.getOrCreateAutoGauge(
+          Metric.SLOT.toString(),
+          MetricLevel.NORMAL,
+          storageGroupPartitionTable,
+          StorageGroupPartitionTable::getDataPartitionMapSize,
+          Tag.NAME.toString(),
+          storageGroupPartitionTable.getStorageGroupName(),
+          Tag.TYPE.toString(),
+          "dataSlotNumber");
+    }
+
+    @Override
+    public void unbindFrom(AbstractMetricService metricService) {
+      metricService.remove(
+          MetricType.GAUGE,
+          Metric.REGION.toString(),
+          Tag.NAME.toString(),
+          storageGroupPartitionTable.getStorageGroupName(),
+          Tag.TYPE.toString(),
+          TConsensusGroupType.SchemaRegion.toString());
+      metricService.remove(
+          MetricType.GAUGE,
+          Metric.REGION.toString(),
+          Tag.NAME.toString(),
+          storageGroupPartitionTable.getStorageGroupName(),
+          Tag.TYPE.toString(),
+          TConsensusGroupType.DataRegion.toString());
+      // TODO slot will be updated in the future
+      metricService.remove(
+          MetricType.GAUGE,
+          Metric.SLOT.toString(),
+          Tag.NAME.toString(),
+          storageGroupPartitionTable.getStorageGroupName(),
+          Tag.TYPE.toString(),
+          "schemaSlotNumber");
+      metricService.remove(
+          MetricType.GAUGE,
+          Metric.SLOT.toString(),
+          Tag.NAME.toString(),
+          storageGroupPartitionTable.getStorageGroupName(),
+          Tag.TYPE.toString(),
+          "dataSlotNumber");
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+      StorageGroupPartitionTableMetrics that = (StorageGroupPartitionTableMetrics) o;
+      return Objects.equals(storageGroupPartitionTable, that.storageGroupPartitionTable);
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(storageGroupPartitionTable);
+    }
+  }
+}
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/partition/PartitionInfo.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/partition/PartitionInfo.java
index 93a3d69b1f..c757b9f8ba 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/partition/PartitionInfo.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/partition/PartitionInfo.java
@@ -45,6 +45,7 @@ import org.apache.iotdb.confignode.consensus.response.RegionInfoListResp;
 import org.apache.iotdb.confignode.consensus.response.SchemaNodeManagementResp;
 import org.apache.iotdb.confignode.consensus.response.SchemaPartitionResp;
 import org.apache.iotdb.confignode.exception.StorageGroupNotExistsException;
+import org.apache.iotdb.confignode.persistence.metric.PartitionInfoMetrics;
 import org.apache.iotdb.confignode.rpc.thrift.TRegionInfo;
 import org.apache.iotdb.confignode.rpc.thrift.TShowRegionReq;
 import org.apache.iotdb.consensus.common.DataSet;
@@ -108,37 +109,6 @@ public class PartitionInfo implements SnapshotProcessor {
     this.deletedRegionSet = Collections.synchronizedSet(new HashSet<>());
   }
 
-  public void addMetrics() {
-    MetricService.getInstance()
-        .getOrCreateAutoGauge(
-            Metric.QUANTITY.toString(),
-            MetricLevel.IMPORTANT,
-            storageGroupPartitionTables,
-            ConcurrentHashMap::size,
-            Tag.NAME.toString(),
-            "storageGroup");
-    MetricService.getInstance()
-        .getOrCreateAutoGauge(
-            Metric.REGION.toString(),
-            MetricLevel.IMPORTANT,
-            this,
-            o -> o.updateRegionGroupMetric(TConsensusGroupType.SchemaRegion),
-            Tag.NAME.toString(),
-            "total",
-            Tag.TYPE.toString(),
-            TConsensusGroupType.SchemaRegion.toString());
-    MetricService.getInstance()
-        .getOrCreateAutoGauge(
-            Metric.REGION.toString(),
-            MetricLevel.IMPORTANT,
-            this,
-            o -> o.updateRegionGroupMetric(TConsensusGroupType.DataRegion),
-            Tag.NAME.toString(),
-            "total",
-            Tag.TYPE.toString(),
-            TConsensusGroupType.DataRegion.toString());
-  }
-
   public int generateNextRegionGroupId() {
     return nextRegionGroupId.incrementAndGet();
   }
@@ -155,8 +125,12 @@ public class PartitionInfo implements SnapshotProcessor {
    */
   public TSStatus setStorageGroup(SetStorageGroupPlan plan) {
     String storageGroupName = plan.getSchema().getName();
-    storageGroupPartitionTables.put(
-        storageGroupName, new StorageGroupPartitionTable(storageGroupName));
+    StorageGroupPartitionTable storageGroupPartitionTable =
+        new StorageGroupPartitionTable(storageGroupName);
+    storageGroupPartitionTables.put(storageGroupName, storageGroupPartitionTable);
+    MetricService.getInstance()
+        .addMetricSet(
+            new PartitionInfoMetrics.StorageGroupPartitionTableMetrics(storageGroupPartitionTable));
     return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode());
   }
 
@@ -648,7 +622,7 @@ public class PartitionInfo implements SnapshotProcessor {
    * @param type SchemaRegion or DataRegion
    * @return the number of SchemaRegion or DataRegion
    */
-  private int updateRegionGroupMetric(TConsensusGroupType type) {
+  public int updateRegionGroupMetric(TConsensusGroupType type) {
     Set<RegionGroup> regionGroups = new HashSet<>();
     for (Map.Entry<String, StorageGroupPartitionTable> entry :
         storageGroupPartitionTables.entrySet()) {
@@ -676,6 +650,7 @@ public class PartitionInfo implements SnapshotProcessor {
               + ":"
               + dataNodeLocation.getClientRpcEndPoint().port
               + ")";
+      // TODO: this metric can be optimized
       MetricService.getInstance()
           .getOrCreateGauge(
               Metric.REGION.toString(),
@@ -780,6 +755,10 @@ public class PartitionInfo implements SnapshotProcessor {
     }
   }
 
+  public int getStorageGroupPartitionTableSize() {
+    return storageGroupPartitionTables.size();
+  }
+
   public void clear() {
     nextRegionGroupId.set(-1);
     storageGroupPartitionTables.clear();
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/partition/StorageGroupPartitionTable.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/partition/StorageGroupPartitionTable.java
index 1cffbb9b72..aadabe9c1d 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/partition/StorageGroupPartitionTable.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/partition/StorageGroupPartitionTable.java
@@ -30,10 +30,6 @@ import org.apache.iotdb.commons.partition.SchemaPartitionTable;
 import org.apache.iotdb.confignode.consensus.request.read.GetRegionInfoListPlan;
 import org.apache.iotdb.confignode.rpc.thrift.TRegionInfo;
 import org.apache.iotdb.confignode.rpc.thrift.TShowRegionReq;
-import org.apache.iotdb.db.service.metrics.MetricService;
-import org.apache.iotdb.db.service.metrics.enums.Metric;
-import org.apache.iotdb.db.service.metrics.enums.Tag;
-import org.apache.iotdb.metrics.utils.MetricLevel;
 import org.apache.iotdb.tsfile.utils.Pair;
 import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils;
 
@@ -82,52 +78,6 @@ public class StorageGroupPartitionTable {
 
     this.schemaPartitionTable = new SchemaPartitionTable();
     this.dataPartitionTable = new DataPartitionTable();
-
-    addMetrics();
-  }
-
-  private void addMetrics() {
-    MetricService.getInstance()
-        .getOrCreateAutoGauge(
-            Metric.REGION.toString(),
-            MetricLevel.NORMAL,
-            this,
-            o -> o.getRegionGroupCount(TConsensusGroupType.SchemaRegion),
-            Tag.NAME.toString(),
-            storageGroupName,
-            Tag.TYPE.toString(),
-            TConsensusGroupType.SchemaRegion.toString());
-    MetricService.getInstance()
-        .getOrCreateAutoGauge(
-            Metric.REGION.toString(),
-            MetricLevel.NORMAL,
-            this,
-            o -> o.getRegionGroupCount(TConsensusGroupType.DataRegion),
-            Tag.NAME.toString(),
-            storageGroupName,
-            Tag.TYPE.toString(),
-            TConsensusGroupType.DataRegion.toString());
-    // TODO slot will be updated in the future
-    MetricService.getInstance()
-        .getOrCreateAutoGauge(
-            Metric.SLOT.toString(),
-            MetricLevel.NORMAL,
-            schemaPartitionTable,
-            o -> o.getSchemaPartitionMap().size(),
-            Tag.NAME.toString(),
-            storageGroupName,
-            Tag.TYPE.toString(),
-            "schemaSlotNumber");
-    MetricService.getInstance()
-        .getOrCreateAutoGauge(
-            Metric.SLOT.toString(),
-            MetricLevel.NORMAL,
-            dataPartitionTable,
-            o -> o.getDataPartitionMap().size(),
-            Tag.NAME.toString(),
-            storageGroupName,
-            Tag.TYPE.toString(),
-            "dataSlotNumber");
   }
 
   public boolean isPredeleted() {
@@ -482,6 +432,14 @@ public class StorageGroupPartitionTable {
     return storageGroupName;
   }
 
+  public int getDataPartitionMapSize() {
+    return dataPartitionTable.getDataPartitionMap().size();
+  }
+
+  public int getSchemaPartitionMapSize() {
+    return schemaPartitionTable.getSchemaPartitionMap().size();
+  }
+
   @Override
   public boolean equals(Object o) {
     if (this == o) return true;
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/service/ConfigNode.java b/confignode/src/main/java/org/apache/iotdb/confignode/service/ConfigNode.java
index fafa4f3e68..10d993fbab 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/service/ConfigNode.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/service/ConfigNode.java
@@ -139,7 +139,6 @@ public class ConfigNode implements ConfigNodeMBean {
             "The current ConfigNode can't joined the cluster because leader's scheduling failed. The possible cause is that the ip:port configuration is incorrect.");
         stop();
       }
-
     } catch (StartupException | IOException e) {
       LOGGER.error("Meet error while starting up.", e);
       try {
@@ -177,10 +176,7 @@ public class ConfigNode implements ConfigNodeMBean {
     registerManager.register(UDFClassLoaderManager.setupAndGetInstance(CONF.getUdfLibDir()));
     registerManager.register(UDFRegistrationService.setupAndGetInstance(CONF.getSystemUdfDir()));
 
-    // Setup MetricService
     registerManager.register(MetricService.getInstance());
-    MetricService.getInstance().startAllReporter();
-
     LOGGER.info("Successfully setup internal services.");
   }
 
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCService.java b/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCService.java
index 134a528229..605d47e109 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCService.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCService.java
@@ -23,7 +23,6 @@ import org.apache.iotdb.commons.conf.CommonConfig;
 import org.apache.iotdb.commons.conf.CommonDescriptor;
 import org.apache.iotdb.commons.conf.IoTDBConstant;
 import org.apache.iotdb.commons.exception.runtime.RPCServiceException;
-import org.apache.iotdb.commons.service.AbstractThriftServiceThread;
 import org.apache.iotdb.commons.service.ServiceType;
 import org.apache.iotdb.commons.service.ThriftService;
 import org.apache.iotdb.commons.service.ThriftServiceThread;
@@ -31,9 +30,6 @@ import org.apache.iotdb.confignode.conf.ConfigNodeConfig;
 import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor;
 import org.apache.iotdb.confignode.rpc.thrift.IConfigNodeRPCService;
 import org.apache.iotdb.db.service.metrics.MetricService;
-import org.apache.iotdb.db.service.metrics.enums.Metric;
-import org.apache.iotdb.db.service.metrics.enums.Tag;
-import org.apache.iotdb.metrics.utils.MetricLevel;
 
 /** ConfigNodeRPCServer exposes the interface that interacts with the DataNode */
 public class ConfigNodeRPCService extends ThriftService implements ConfigNodeRPCServiceMBean {
@@ -83,14 +79,7 @@ public class ConfigNodeRPCService extends ThriftService implements ConfigNodeRPC
       throw new IllegalAccessException(e.getMessage());
     }
     thriftServiceThread.setName(ThreadName.CONFIGNODE_RPC_SERVICE.getName());
-    MetricService.getInstance()
-        .getOrCreateAutoGauge(
-            Metric.THRIFT_ACTIVE_THREADS.toString(),
-            MetricLevel.CORE,
-            thriftServiceThread,
-            AbstractThriftServiceThread::getActiveThreadCount,
-            Tag.NAME.toString(),
-            ThreadName.CONFIGNODE_RPC_SERVICE.getName());
+    MetricService.getInstance().addMetricSet(new ConfigNodeRPCServiceMetrics(thriftServiceThread));
   }
 
   @Override
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceHandler.java b/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceHandler.java
index 1ace76c612..21e8f4bf8b 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceHandler.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceHandler.java
@@ -14,43 +14,35 @@
  * express or implied. See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.iotdb.confignode.service.thrift;
 
 import org.apache.iotdb.db.service.metrics.MetricService;
-import org.apache.iotdb.db.service.metrics.enums.Metric;
-import org.apache.iotdb.db.service.metrics.enums.Tag;
-import org.apache.iotdb.metrics.utils.MetricLevel;
 
 import org.apache.thrift.protocol.TProtocol;
 import org.apache.thrift.server.ServerContext;
 import org.apache.thrift.server.TServerEventHandler;
 import org.apache.thrift.transport.TTransport;
 
+import java.util.concurrent.atomic.AtomicLong;
+
 public class ConfigNodeRPCServiceHandler implements TServerEventHandler {
+  private AtomicLong thriftConnectionNumber = new AtomicLong(0);
 
-  public ConfigNodeRPCServiceHandler() {}
+  public ConfigNodeRPCServiceHandler() {
+    MetricService.getInstance()
+        .addMetricSet(new ConfigNodeRPCServiceHandlerMetrics(thriftConnectionNumber));
+  }
 
   @Override
   public ServerContext createContext(TProtocol arg0, TProtocol arg1) {
-    MetricService.getInstance()
-        .getOrCreateGauge(
-            Metric.THRIFT_CONNECTIONS.toString(),
-            MetricLevel.CORE,
-            Tag.NAME.toString(),
-            "ConfigNodeRPC")
-        .incr(1L);
+    thriftConnectionNumber.incrementAndGet();
     return null;
   }
 
   @Override
   public void deleteContext(ServerContext arg0, TProtocol arg1, TProtocol arg2) {
-    MetricService.getInstance()
-        .getOrCreateGauge(
-            Metric.THRIFT_CONNECTIONS.toString(),
-            MetricLevel.CORE,
-            Tag.NAME.toString(),
-            "ConfigNodeRPC")
-        .decr(1L);
+    thriftConnectionNumber.decrementAndGet();
   }
 
   @Override
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceHandlerMetrics.java b/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceHandlerMetrics.java
new file mode 100644
index 0000000000..8526847d58
--- /dev/null
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceHandlerMetrics.java
@@ -0,0 +1,69 @@
+/*
+ * 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
+ *
+ * <p>http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * <p>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.service.thrift;
+
+import org.apache.iotdb.db.service.metrics.enums.Metric;
+import org.apache.iotdb.db.service.metrics.enums.Tag;
+import org.apache.iotdb.metrics.AbstractMetricService;
+import org.apache.iotdb.metrics.metricsets.IMetricSet;
+import org.apache.iotdb.metrics.utils.MetricLevel;
+import org.apache.iotdb.metrics.utils.MetricType;
+
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicLong;
+
+public class ConfigNodeRPCServiceHandlerMetrics implements IMetricSet {
+  private AtomicLong thriftConnectionNumber;
+
+  public ConfigNodeRPCServiceHandlerMetrics(AtomicLong thriftConnectionNumber) {
+    this.thriftConnectionNumber = thriftConnectionNumber;
+  }
+
+  @Override
+  public void bindTo(AbstractMetricService metricService) {
+    metricService.getOrCreateAutoGauge(
+        Metric.THRIFT_CONNECTIONS.toString(),
+        MetricLevel.CORE,
+        thriftConnectionNumber,
+        AtomicLong::get,
+        Tag.NAME.toString(),
+        "ConfigNodeRPC");
+  }
+
+  @Override
+  public void unbindFrom(AbstractMetricService metricService) {
+    metricService.remove(
+        MetricType.GAUGE,
+        Metric.THRIFT_CONNECTIONS.toString(),
+        Tag.NAME.toString(),
+        "ConfigNodeRPC");
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    ConfigNodeRPCServiceHandlerMetrics that = (ConfigNodeRPCServiceHandlerMetrics) o;
+    return Objects.equals(thriftConnectionNumber, that.thriftConnectionNumber);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(thriftConnectionNumber);
+  }
+}
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceMetrics.java b/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceMetrics.java
new file mode 100644
index 0000000000..4cedf4c9e6
--- /dev/null
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceMetrics.java
@@ -0,0 +1,70 @@
+/*
+ * 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
+ *
+ * <p>http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * <p>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.service.thrift;
+
+import org.apache.iotdb.commons.concurrent.ThreadName;
+import org.apache.iotdb.commons.service.AbstractThriftServiceThread;
+import org.apache.iotdb.db.service.metrics.enums.Metric;
+import org.apache.iotdb.db.service.metrics.enums.Tag;
+import org.apache.iotdb.metrics.AbstractMetricService;
+import org.apache.iotdb.metrics.metricsets.IMetricSet;
+import org.apache.iotdb.metrics.utils.MetricLevel;
+import org.apache.iotdb.metrics.utils.MetricType;
+
+import java.util.Objects;
+
+public class ConfigNodeRPCServiceMetrics implements IMetricSet {
+  private AbstractThriftServiceThread thriftServiceThread;
+
+  public ConfigNodeRPCServiceMetrics(AbstractThriftServiceThread thriftServiceThread) {
+    this.thriftServiceThread = thriftServiceThread;
+  }
+
+  @Override
+  public void bindTo(AbstractMetricService metricService) {
+    metricService.getOrCreateAutoGauge(
+        Metric.THRIFT_ACTIVE_THREADS.toString(),
+        MetricLevel.CORE,
+        thriftServiceThread,
+        AbstractThriftServiceThread::getActiveThreadCount,
+        Tag.NAME.toString(),
+        ThreadName.CONFIGNODE_RPC_SERVICE.getName());
+  }
+
+  @Override
+  public void unbindFrom(AbstractMetricService metricService) {
+    metricService.remove(
+        MetricType.GAUGE,
+        Metric.THRIFT_ACTIVE_THREADS.toString(),
+        Tag.NAME.toString(),
+        ThreadName.CONFIGNODE_RPC_SERVICE.getName());
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    ConfigNodeRPCServiceMetrics that = (ConfigNodeRPCServiceMetrics) o;
+    return Objects.equals(thriftServiceThread, that.thriftServiceThread);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(thriftServiceThread);
+  }
+}
diff --git a/metrics/dropwizard-metrics/src/main/java/org/apache/iotdb/metrics/dropwizard/reporter/DropwizardPrometheusReporter.java b/metrics/dropwizard-metrics/src/main/java/org/apache/iotdb/metrics/dropwizard/reporter/DropwizardPrometheusReporter.java
index 4706dc0fa0..e3c1570562 100644
--- a/metrics/dropwizard-metrics/src/main/java/org/apache/iotdb/metrics/dropwizard/reporter/DropwizardPrometheusReporter.java
+++ b/metrics/dropwizard-metrics/src/main/java/org/apache/iotdb/metrics/dropwizard/reporter/DropwizardPrometheusReporter.java
@@ -47,13 +47,11 @@ public class DropwizardPrometheusReporter implements Reporter {
   @Override
   public boolean start() {
     if (httpServer != null) {
-      LOGGER.warn("Dropwizard Prometheus Reporter already start!");
       return false;
     }
     int port = MetricConfigDescriptor.getInstance().getMetricConfig().getPrometheusExporterPort();
     httpServer =
         HttpServer.create()
-            .idleTimeout(Duration.ofMillis(30_000L))
             .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 2000)
             .port(port)
             .route(
@@ -95,7 +93,7 @@ public class DropwizardPrometheusReporter implements Reporter {
   public boolean stop() {
     if (httpServer != null) {
       try {
-        httpServer.disposeNow();
+        httpServer.disposeNow(Duration.ofSeconds(10));
         httpServer = null;
       } catch (Exception e) {
         LOGGER.error("failed to stop server", e);
diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/AbstractMetricService.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/AbstractMetricService.java
index f00d7f1c4a..30f13c5198 100644
--- a/metrics/interface/src/main/java/org/apache/iotdb/metrics/AbstractMetricService.java
+++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/AbstractMetricService.java
@@ -23,8 +23,8 @@ import org.apache.iotdb.metrics.config.MetricConfig;
 import org.apache.iotdb.metrics.config.MetricConfigDescriptor;
 import org.apache.iotdb.metrics.config.ReloadLevel;
 import org.apache.iotdb.metrics.impl.DoNothingMetricManager;
-import org.apache.iotdb.metrics.predefined.IMetricSet;
-import org.apache.iotdb.metrics.predefined.PredefinedMetric;
+import org.apache.iotdb.metrics.metricsets.IMetricSet;
+import org.apache.iotdb.metrics.metricsets.predefined.PredefinedMetric;
 import org.apache.iotdb.metrics.reporter.CompositeReporter;
 import org.apache.iotdb.metrics.reporter.Reporter;
 import org.apache.iotdb.metrics.type.Counter;
@@ -54,7 +54,7 @@ public abstract class AbstractMetricService {
   /** The config of metric service */
   private final MetricConfig metricConfig = MetricConfigDescriptor.getInstance().getMetricConfig();
   /** Is the first initialization of metric service */
-  private final AtomicBoolean isFirstInitialization = new AtomicBoolean(true);
+  protected AtomicBoolean isFirstInitialization = new AtomicBoolean(true);
   /** The metric manager of metric service */
   protected AbstractMetricManager metricManager = new DoNothingMetricManager();
   /** The metric reporter of metric service */
@@ -68,31 +68,52 @@ public abstract class AbstractMetricService {
 
   /** start metric service */
   public void startService() {
+    startCoreModule();
+    for (IMetricSet metricSet : metricSets) {
+      metricSet.bindTo(this);
+    }
+  }
+
+  /** restart metric service */
+  public void restartService() {
+    logger.info("Restart Core Module");
+    stopCoreModule();
+    startCoreModule();
+    for (IMetricSet metricSet : metricSets) {
+      logger.info("Restart metricSet: {}", metricSet.getClass().getName());
+      metricSet.unbindFrom(this);
+      metricSet.bindTo(this);
+    }
+  }
+
+  /** stop metric service */
+  public void stopService() {
+    for (IMetricSet metricSet : metricSets) {
+      metricSet.unbindFrom(this);
+    }
+    stopCoreModule();
+  }
+  /** start metric core module */
+  private void startCoreModule() {
     logger.info("Start metric service at level: {}", metricConfig.getMetricLevel().name());
     // load metric manager
     loadManager();
     // load metric reporter
     loadReporter();
     // do start all reporter without first time
-    if (!isFirstInitialization.getAndSet(false)) {
-      startAllReporter();
-    }
+    startAllReporter();
+    logger.info("Start predefined metrics: {}", metricConfig.getPredefinedMetrics());
     for (PredefinedMetric predefinedMetric : metricConfig.getPredefinedMetrics()) {
       enablePredefinedMetrics(predefinedMetric);
     }
-    logger.info("Start predefined metrics: {}", metricConfig.getPredefinedMetrics());
   }
 
-  /** stop metric service */
-  public void stopService() {
-    compositeReporter.stopAll();
+  /** stop metric core module */
+  private void stopCoreModule() {
+    stopAllReporter();
     metricManager.stop();
     metricManager = new DoNothingMetricManager();
     compositeReporter = new CompositeReporter();
-    for (IMetricSet metricSet : metricSets) {
-      metricSet.stopAsyncCollectedMetrics();
-    }
-    metricSets = new ArrayList<>();
   }
 
   /** Load metric manager according to configuration */
@@ -156,6 +177,14 @@ public abstract class AbstractMetricService {
     compositeReporter.startAll();
   }
 
+  /** Stop all reporters */
+  public void stopAllReporter() {
+    if (!isEnable()) {
+      return;
+    }
+    compositeReporter.stopAll();
+  }
+
   /** Start reporter according to type */
   public void start(ReporterType type) {
     if (!isEnable()) {
@@ -259,4 +288,12 @@ public abstract class AbstractMetricService {
   public boolean isEnable() {
     return isEnableMetric;
   }
+
+  /** bind metrics and store metric set */
+  public void addMetricSet(IMetricSet metricSet) {
+    if (!metricSets.contains(metricSet)) {
+      metricSet.bindTo(this);
+      metricSets.add(metricSet);
+    }
+  }
 }
diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/DoNothingMetricService.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/DoNothingMetricService.java
index 8f07e299fb..b6e4baf066 100644
--- a/metrics/interface/src/main/java/org/apache/iotdb/metrics/DoNothingMetricService.java
+++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/DoNothingMetricService.java
@@ -20,7 +20,7 @@
 package org.apache.iotdb.metrics;
 
 import org.apache.iotdb.metrics.config.ReloadLevel;
-import org.apache.iotdb.metrics.predefined.PredefinedMetric;
+import org.apache.iotdb.metrics.metricsets.predefined.PredefinedMetric;
 
 public class DoNothingMetricService extends AbstractMetricService {
 
diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/config/MetricConfig.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/config/MetricConfig.java
index 687e5ba627..1b5a625b8f 100644
--- a/metrics/interface/src/main/java/org/apache/iotdb/metrics/config/MetricConfig.java
+++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/config/MetricConfig.java
@@ -19,7 +19,7 @@
 
 package org.apache.iotdb.metrics.config;
 
-import org.apache.iotdb.metrics.predefined.PredefinedMetric;
+import org.apache.iotdb.metrics.metricsets.predefined.PredefinedMetric;
 import org.apache.iotdb.metrics.utils.MetricLevel;
 import org.apache.iotdb.metrics.utils.MonitorType;
 import org.apache.iotdb.metrics.utils.ReporterType;
@@ -36,7 +36,7 @@ public class MetricConfig {
   private Boolean enablePerformanceStat = false;
 
   /** The type of the implementation of metric service */
-  private MonitorType monitorType = MonitorType.DROPWIZARD;
+  private MonitorType monitorType = MonitorType.MICROMETER;
 
   /** The list of reporters provide data for external system */
   private List<ReporterType> metricReporterList =
diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/IMetricSet.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/IMetricSet.java
similarity index 66%
rename from metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/IMetricSet.java
rename to metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/IMetricSet.java
index b566d2dd53..9b66fcec4f 100644
--- a/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/IMetricSet.java
+++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/IMetricSet.java
@@ -17,20 +17,15 @@
  * under the License.
  */
 
-package org.apache.iotdb.metrics.predefined;
+package org.apache.iotdb.metrics.metricsets;
 
-import org.apache.iotdb.metrics.AbstractMetricManager;
+import org.apache.iotdb.metrics.AbstractMetricService;
 
+/** Notice that IMetricSet should be stateless */
 public interface IMetricSet {
-  /** bind related metric to metric manager */
-  void bindTo(AbstractMetricManager metricManager);
+  /** bind metrics to metricManager and init environment */
+  void bindTo(AbstractMetricService metricService);
 
-  /** get type of metric set */
-  PredefinedMetric getType();
-
-  /** start async collectd metric */
-  default void startAsyncCollectedMetrics() {}
-
-  /** stop async collectd metric */
-  default void stopAsyncCollectedMetrics() {}
+  /** remove metrics from metricManager and clear environment */
+  void unbindFrom(AbstractMetricService metricService);
 }
diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/PredefinedMetric.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/predefined/PredefinedMetric.java
similarity index 94%
rename from metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/PredefinedMetric.java
rename to metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/predefined/PredefinedMetric.java
index c6580f69e8..c520afc1f0 100644
--- a/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/PredefinedMetric.java
+++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/predefined/PredefinedMetric.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package org.apache.iotdb.metrics.predefined;
+package org.apache.iotdb.metrics.metricsets.predefined;
 
 public enum PredefinedMetric {
   JVM,
diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmClassLoaderMetrics.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/predefined/jvm/JvmClassLoaderMetrics.java
similarity index 72%
rename from metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmClassLoaderMetrics.java
rename to metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/predefined/jvm/JvmClassLoaderMetrics.java
index 8579717f85..daa7cf5206 100644
--- a/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmClassLoaderMetrics.java
+++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/predefined/jvm/JvmClassLoaderMetrics.java
@@ -17,12 +17,12 @@
  * under the License.
  */
 
-package org.apache.iotdb.metrics.predefined.jvm;
+package org.apache.iotdb.metrics.metricsets.predefined.jvm;
 
-import org.apache.iotdb.metrics.AbstractMetricManager;
-import org.apache.iotdb.metrics.predefined.IMetricSet;
-import org.apache.iotdb.metrics.predefined.PredefinedMetric;
+import org.apache.iotdb.metrics.AbstractMetricService;
+import org.apache.iotdb.metrics.metricsets.IMetricSet;
 import org.apache.iotdb.metrics.utils.MetricLevel;
+import org.apache.iotdb.metrics.utils.MetricType;
 
 import java.lang.management.ClassLoadingMXBean;
 import java.lang.management.ManagementFactory;
@@ -30,15 +30,14 @@ import java.lang.management.ManagementFactory;
 /** This file is modified from io.micrometer.core.instrument.binder.jvm.ClassLoaderMetrics */
 public class JvmClassLoaderMetrics implements IMetricSet {
   @Override
-  public void bindTo(AbstractMetricManager metricManager) {
+  public void bindTo(AbstractMetricService metricService) {
     ClassLoadingMXBean classLoadingBean = ManagementFactory.getClassLoadingMXBean();
-
-    metricManager.getOrCreateAutoGauge(
+    metricService.getOrCreateAutoGauge(
         "jvm.classes.loaded.classes",
         MetricLevel.IMPORTANT,
         classLoadingBean,
         ClassLoadingMXBean::getLoadedClassCount);
-    metricManager.getOrCreateAutoGauge(
+    metricService.getOrCreateAutoGauge(
         "jvm.classes.unloaded.classes",
         MetricLevel.IMPORTANT,
         classLoadingBean,
@@ -46,7 +45,8 @@ public class JvmClassLoaderMetrics implements IMetricSet {
   }
 
   @Override
-  public PredefinedMetric getType() {
-    return PredefinedMetric.JVM;
+  public void unbindFrom(AbstractMetricService metricService) {
+    metricService.remove(MetricType.GAUGE, "jvm.classes.loaded.classes");
+    metricService.remove(MetricType.GAUGE, "jvm.classes.unloaded.classes");
   }
 }
diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmCompileMetrics.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/predefined/jvm/JvmCompileMetrics.java
similarity index 69%
rename from metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmCompileMetrics.java
rename to metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/predefined/jvm/JvmCompileMetrics.java
index 86adf2dbad..fcf8801983 100644
--- a/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmCompileMetrics.java
+++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/predefined/jvm/JvmCompileMetrics.java
@@ -17,12 +17,12 @@
  * under the License.
  */
 
-package org.apache.iotdb.metrics.predefined.jvm;
+package org.apache.iotdb.metrics.metricsets.predefined.jvm;
 
-import org.apache.iotdb.metrics.AbstractMetricManager;
-import org.apache.iotdb.metrics.predefined.IMetricSet;
-import org.apache.iotdb.metrics.predefined.PredefinedMetric;
+import org.apache.iotdb.metrics.AbstractMetricService;
+import org.apache.iotdb.metrics.metricsets.IMetricSet;
 import org.apache.iotdb.metrics.utils.MetricLevel;
+import org.apache.iotdb.metrics.utils.MetricType;
 
 import java.lang.management.CompilationMXBean;
 import java.lang.management.ManagementFactory;
@@ -30,10 +30,10 @@ import java.lang.management.ManagementFactory;
 /** This file is modified from io.micrometer.core.instrument.binder.jvm.JvmCompilationMetrics */
 public class JvmCompileMetrics implements IMetricSet {
   @Override
-  public void bindTo(AbstractMetricManager metricManager) {
+  public void bindTo(AbstractMetricService metricService) {
     CompilationMXBean compilationBean = ManagementFactory.getCompilationMXBean();
     if (compilationBean != null && compilationBean.isCompilationTimeMonitoringSupported()) {
-      metricManager.getOrCreateAutoGauge(
+      metricService.getOrCreateAutoGauge(
           "jvm.compilation.time.ms",
           MetricLevel.IMPORTANT,
           compilationBean,
@@ -44,7 +44,11 @@ public class JvmCompileMetrics implements IMetricSet {
   }
 
   @Override
-  public PredefinedMetric getType() {
-    return PredefinedMetric.JVM;
+  public void unbindFrom(AbstractMetricService metricService) {
+    CompilationMXBean compilationBean = ManagementFactory.getCompilationMXBean();
+    if (compilationBean != null && compilationBean.isCompilationTimeMonitoringSupported()) {
+      metricService.remove(
+          MetricType.GAUGE, "jvm.compilation.time.ms", "compiler", compilationBean.getName());
+    }
   }
 }
diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmGcMetrics.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/predefined/jvm/JvmGcMetrics.java
similarity index 79%
rename from metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmGcMetrics.java
rename to metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/predefined/jvm/JvmGcMetrics.java
index 27cd6a670b..703f3b1e32 100644
--- a/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmGcMetrics.java
+++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/predefined/jvm/JvmGcMetrics.java
@@ -17,14 +17,14 @@
  * under the License.
  */
 
-package org.apache.iotdb.metrics.predefined.jvm;
+package org.apache.iotdb.metrics.metricsets.predefined.jvm;
 
-import org.apache.iotdb.metrics.AbstractMetricManager;
-import org.apache.iotdb.metrics.predefined.IMetricSet;
-import org.apache.iotdb.metrics.predefined.PredefinedMetric;
+import org.apache.iotdb.metrics.AbstractMetricService;
+import org.apache.iotdb.metrics.metricsets.IMetricSet;
 import org.apache.iotdb.metrics.type.Counter;
 import org.apache.iotdb.metrics.type.Timer;
 import org.apache.iotdb.metrics.utils.MetricLevel;
+import org.apache.iotdb.metrics.utils.MetricType;
 
 import com.sun.management.GarbageCollectionNotificationInfo;
 import com.sun.management.GcInfo;
@@ -71,23 +71,8 @@ public class JvmGcMetrics implements IMetricSet, AutoCloseable {
   }
 
   @Override
-  public void bindTo(AbstractMetricManager metricManager) {
-    if (ManagementFactory.getMemoryPoolMXBeans().isEmpty()) {
-      logger.warn(
-          "GC notifications will not be available because MemoryPoolMXBeans are not provided by the JVM");
-      return;
-    }
-
-    try {
-      Class.forName(
-          "com.sun.management.GarbageCollectionNotificationInfo",
-          false,
-          MemoryPoolMXBean.class.getClassLoader());
-    } catch (Throwable e) {
-      // We are operating in a JVM without access to this level of detail
-      logger.warn(
-          "GC notifications will not be available because "
-              + "com.sun.management.GarbageCollectionNotificationInfo is not present");
+  public void bindTo(AbstractMetricService metricService) {
+    if (!preCheck()) {
       return;
     }
 
@@ -100,20 +85,20 @@ public class JvmGcMetrics implements IMetricSet, AutoCloseable {
             .orElse(0.0);
 
     AtomicLong maxDataSize = new AtomicLong((long) maxLongLivedPoolBytes);
-    metricManager.getOrCreateAutoGauge(
+    metricService.getOrCreateAutoGauge(
         "jvm.gc.max.data.size.bytes", MetricLevel.IMPORTANT, maxDataSize, AtomicLong::get);
 
     AtomicLong liveDataSize = new AtomicLong();
-    metricManager.getOrCreateAutoGauge(
+    metricService.getOrCreateAutoGauge(
         "jvm.gc.live.data.size.bytes", MetricLevel.IMPORTANT, liveDataSize, AtomicLong::get);
 
     Counter allocatedBytes =
-        metricManager.getOrCreateCounter("jvm.gc.memory.allocated.bytes", MetricLevel.IMPORTANT);
+        metricService.getOrCreateCounter("jvm.gc.memory.allocated.bytes", MetricLevel.IMPORTANT);
 
     Counter promotedBytes =
         (oldGenPoolName == null)
             ? null
-            : metricManager.getOrCreateCounter(
+            : metricService.getOrCreateCounter(
                 "jvm.gc.memory.promoted.bytes", MetricLevel.IMPORTANT);
 
     // start watching for GC notifications
@@ -140,7 +125,7 @@ public class JvmGcMetrics implements IMetricSet, AutoCloseable {
               timerName = "jvm.gc.pause";
             }
             Timer timer =
-                metricManager.getOrCreateTimer(
+                metricService.getOrCreateTimer(
                     timerName, MetricLevel.IMPORTANT, "action", gcAction, "cause", gcCause);
             timer.update(duration, TimeUnit.MILLISECONDS);
 
@@ -213,6 +198,81 @@ public class JvmGcMetrics implements IMetricSet, AutoCloseable {
     }
   }
 
+  @Override
+  public void unbindFrom(AbstractMetricService metricService) {
+    if (!preCheck()) {
+      return;
+    }
+
+    metricService.remove(MetricType.GAUGE, "jvm.gc.max.data.size.bytes");
+    metricService.remove(MetricType.GAUGE, "jvm.gc.live.data.size.bytes");
+    metricService.remove(MetricType.COUNTER, "jvm.gc.memory.allocated.bytes");
+
+    if (oldGenPoolName != null) {
+      metricService.remove(MetricType.COUNTER, "jvm.gc.memory.promoted.bytes");
+    }
+
+    // start watching for GC notifications
+    for (GarbageCollectorMXBean mbean : ManagementFactory.getGarbageCollectorMXBeans()) {
+      if (!(mbean instanceof NotificationEmitter)) {
+        continue;
+      }
+      NotificationListener notificationListener =
+          (notification, ref) -> {
+            CompositeData cd = (CompositeData) notification.getUserData();
+            GarbageCollectionNotificationInfo notificationInfo =
+                GarbageCollectionNotificationInfo.from(cd);
+
+            String gcCause = notificationInfo.getGcCause();
+            String gcAction = notificationInfo.getGcAction();
+            String timerName;
+            if (isConcurrentPhase(gcCause, notificationInfo.getGcName())) {
+              timerName = "jvm.gc.concurrent.phase.time";
+            } else {
+              timerName = "jvm.gc.pause";
+            }
+            metricService.remove(MetricType.TIMER, timerName, "action", gcAction, "cause", gcCause);
+          };
+      NotificationEmitter notificationEmitter = (NotificationEmitter) mbean;
+      notificationEmitter.addNotificationListener(
+          notificationListener,
+          notification ->
+              notification
+                  .getType()
+                  .equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION),
+          null);
+      notificationListenerCleanUpRunnables.add(
+          () -> {
+            try {
+              notificationEmitter.removeNotificationListener(notificationListener);
+            } catch (ListenerNotFoundException ignore) {
+            }
+          });
+    }
+  }
+
+  private boolean preCheck() {
+    if (ManagementFactory.getMemoryPoolMXBeans().isEmpty()) {
+      logger.warn(
+          "GC notifications will not be available because MemoryPoolMXBeans are not provided by the JVM");
+      return false;
+    }
+
+    try {
+      Class.forName(
+          "com.sun.management.GarbageCollectionNotificationInfo",
+          false,
+          MemoryPoolMXBean.class.getClassLoader());
+    } catch (Throwable e) {
+      // We are operating in a JVM without access to this level of detail
+      logger.warn(
+          "GC notifications will not be available because "
+              + "com.sun.management.GarbageCollectionNotificationInfo is not present");
+      return false;
+    }
+    return true;
+  }
+
   private void countPoolSizeDelta(
       Map<String, MemoryUsage> before,
       Map<String, MemoryUsage> after,
@@ -233,11 +293,6 @@ public class JvmGcMetrics implements IMetricSet, AutoCloseable {
     notificationListenerCleanUpRunnables.forEach(Runnable::run);
   }
 
-  @Override
-  public PredefinedMetric getType() {
-    return PredefinedMetric.JVM;
-  }
-
   enum GcGenerationAge {
     OLD,
     YOUNG,
diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmMemoryMetrics.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/predefined/jvm/JvmMemoryMetrics.java
similarity index 63%
rename from metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmMemoryMetrics.java
rename to metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/predefined/jvm/JvmMemoryMetrics.java
index 1e28133961..da13ef12ac 100644
--- a/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmMemoryMetrics.java
+++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/predefined/jvm/JvmMemoryMetrics.java
@@ -17,12 +17,12 @@
  * under the License.
  */
 
-package org.apache.iotdb.metrics.predefined.jvm;
+package org.apache.iotdb.metrics.metricsets.predefined.jvm;
 
-import org.apache.iotdb.metrics.AbstractMetricManager;
-import org.apache.iotdb.metrics.predefined.IMetricSet;
-import org.apache.iotdb.metrics.predefined.PredefinedMetric;
+import org.apache.iotdb.metrics.AbstractMetricService;
+import org.apache.iotdb.metrics.metricsets.IMetricSet;
 import org.apache.iotdb.metrics.utils.MetricLevel;
+import org.apache.iotdb.metrics.utils.MetricType;
 
 import java.lang.management.BufferPoolMXBean;
 import java.lang.management.ManagementFactory;
@@ -33,10 +33,10 @@ import java.lang.management.MemoryUsage;
 /** This file is modified from io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics */
 public class JvmMemoryMetrics implements IMetricSet {
   @Override
-  public void bindTo(AbstractMetricManager metricManager) {
+  public void bindTo(AbstractMetricService metricService) {
     for (BufferPoolMXBean bufferPoolBean :
         ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class)) {
-      metricManager.getOrCreateAutoGauge(
+      metricService.getOrCreateAutoGauge(
           "jvm.buffer.count.buffers",
           MetricLevel.IMPORTANT,
           bufferPoolBean,
@@ -44,7 +44,7 @@ public class JvmMemoryMetrics implements IMetricSet {
           "id",
           bufferPoolBean.getName());
 
-      metricManager.getOrCreateAutoGauge(
+      metricService.getOrCreateAutoGauge(
           "jvm.buffer.memory.used.bytes",
           MetricLevel.IMPORTANT,
           bufferPoolBean,
@@ -52,7 +52,7 @@ public class JvmMemoryMetrics implements IMetricSet {
           "id",
           bufferPoolBean.getName());
 
-      metricManager.getOrCreateAutoGauge(
+      metricService.getOrCreateAutoGauge(
           "jvm.buffer.total.capacity.bytes",
           MetricLevel.IMPORTANT,
           bufferPoolBean,
@@ -65,7 +65,7 @@ public class JvmMemoryMetrics implements IMetricSet {
         ManagementFactory.getPlatformMXBeans(MemoryPoolMXBean.class)) {
       String area = MemoryType.HEAP.equals(memoryPoolBean.getType()) ? "heap" : "nonheap";
 
-      metricManager.getOrCreateAutoGauge(
+      metricService.getOrCreateAutoGauge(
           "jvm.memory.used.bytes",
           MetricLevel.IMPORTANT,
           memoryPoolBean,
@@ -75,7 +75,7 @@ public class JvmMemoryMetrics implements IMetricSet {
           "area",
           area);
 
-      metricManager.getOrCreateAutoGauge(
+      metricService.getOrCreateAutoGauge(
           "jvm.memory.committed.bytes",
           MetricLevel.IMPORTANT,
           memoryPoolBean,
@@ -85,7 +85,7 @@ public class JvmMemoryMetrics implements IMetricSet {
           "area",
           area);
 
-      metricManager.getOrCreateAutoGauge(
+      metricService.getOrCreateAutoGauge(
           "jvm.memory.max.bytes",
           MetricLevel.IMPORTANT,
           memoryPoolBean,
@@ -98,7 +98,36 @@ public class JvmMemoryMetrics implements IMetricSet {
   }
 
   @Override
-  public PredefinedMetric getType() {
-    return PredefinedMetric.JVM;
+  public void unbindFrom(AbstractMetricService metricService) {
+    for (BufferPoolMXBean bufferPoolBean :
+        ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class)) {
+      metricService.remove(
+          MetricType.GAUGE, "jvm.buffer.count.buffers", "id", bufferPoolBean.getName());
+
+      metricService.remove(
+          MetricType.GAUGE, "jvm.buffer.memory.used.bytes", "id", bufferPoolBean.getName());
+
+      metricService.remove(
+          MetricType.GAUGE, "jvm.buffer.total.capacity.bytes", "id", bufferPoolBean.getName());
+    }
+
+    for (MemoryPoolMXBean memoryPoolBean :
+        ManagementFactory.getPlatformMXBeans(MemoryPoolMXBean.class)) {
+      String area = MemoryType.HEAP.equals(memoryPoolBean.getType()) ? "heap" : "nonheap";
+
+      metricService.remove(
+          MetricType.GAUGE, "jvm.memory.used.bytes", "id", memoryPoolBean.getName(), "area", area);
+
+      metricService.remove(
+          MetricType.GAUGE,
+          "jvm.memory.committed.bytes",
+          "id",
+          memoryPoolBean.getName(),
+          "area",
+          area);
+
+      metricService.remove(
+          MetricType.GAUGE, "jvm.memory.max.bytes", "id", memoryPoolBean.getName(), "area", area);
+    }
   }
 }
diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/predefined/jvm/JvmMetrics.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/predefined/jvm/JvmMetrics.java
new file mode 100644
index 0000000000..0d63d6a3a9
--- /dev/null
+++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/predefined/jvm/JvmMetrics.java
@@ -0,0 +1,52 @@
+/*
+ * 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.metrics.metricsets.predefined.jvm;
+
+import org.apache.iotdb.metrics.AbstractMetricService;
+import org.apache.iotdb.metrics.metricsets.IMetricSet;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class JvmMetrics implements IMetricSet {
+  private List<IMetricSet> metricSets = new ArrayList<>();
+
+  public JvmMetrics() {
+    metricSets.add(new JvmClassLoaderMetrics());
+    metricSets.add(new JvmCompileMetrics());
+    metricSets.add(new JvmGcMetrics());
+    metricSets.add(new JvmMemoryMetrics());
+    metricSets.add(new JvmThreadMetrics());
+  }
+
+  @Override
+  public void bindTo(AbstractMetricService metricService) {
+    for (IMetricSet metricSet : metricSets) {
+      metricSet.bindTo(metricService);
+    }
+  }
+
+  @Override
+  public void unbindFrom(AbstractMetricService metricService) {
+    for (IMetricSet metricSet : metricSets) {
+      metricSet.unbindFrom(metricService);
+    }
+  }
+}
diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmThreadMetrics.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/predefined/jvm/JvmThreadMetrics.java
similarity index 68%
rename from metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmThreadMetrics.java
rename to metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/predefined/jvm/JvmThreadMetrics.java
index 8a7428942f..f0a5cd8b7d 100644
--- a/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmThreadMetrics.java
+++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/predefined/jvm/JvmThreadMetrics.java
@@ -17,12 +17,12 @@
  * under the License.
  */
 
-package org.apache.iotdb.metrics.predefined.jvm;
+package org.apache.iotdb.metrics.metricsets.predefined.jvm;
 
-import org.apache.iotdb.metrics.AbstractMetricManager;
-import org.apache.iotdb.metrics.predefined.IMetricSet;
-import org.apache.iotdb.metrics.predefined.PredefinedMetric;
+import org.apache.iotdb.metrics.AbstractMetricService;
+import org.apache.iotdb.metrics.metricsets.IMetricSet;
 import org.apache.iotdb.metrics.utils.MetricLevel;
+import org.apache.iotdb.metrics.utils.MetricType;
 
 import java.lang.management.ManagementFactory;
 import java.lang.management.ThreadMXBean;
@@ -31,22 +31,22 @@ import java.util.Arrays;
 /** This file is modified from io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics */
 public class JvmThreadMetrics implements IMetricSet {
   @Override
-  public void bindTo(AbstractMetricManager metricManager) {
+  public void bindTo(AbstractMetricService metricService) {
     ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
 
-    metricManager.getOrCreateAutoGauge(
+    metricService.getOrCreateAutoGauge(
         "jvm.threads.peak.threads",
         MetricLevel.IMPORTANT,
         threadBean,
         ThreadMXBean::getPeakThreadCount);
 
-    metricManager.getOrCreateAutoGauge(
+    metricService.getOrCreateAutoGauge(
         "jvm.threads.daemon.threads",
         MetricLevel.IMPORTANT,
         threadBean,
         ThreadMXBean::getDaemonThreadCount);
 
-    metricManager.getOrCreateAutoGauge(
+    metricService.getOrCreateAutoGauge(
         "jvm.threads.live.threads",
         MetricLevel.IMPORTANT,
         threadBean,
@@ -55,7 +55,7 @@ public class JvmThreadMetrics implements IMetricSet {
     try {
       threadBean.getAllThreadIds();
       for (Thread.State state : Thread.State.values()) {
-        metricManager.getOrCreateAutoGauge(
+        metricService.getOrCreateAutoGauge(
             "jvm.threads.states.threads",
             MetricLevel.IMPORTANT,
             threadBean,
@@ -69,6 +69,26 @@ public class JvmThreadMetrics implements IMetricSet {
     }
   }
 
+  @Override
+  public void unbindFrom(AbstractMetricService metricService) {
+    ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
+
+    metricService.remove(MetricType.GAUGE, "jvm.threads.peak.threads");
+    metricService.remove(MetricType.GAUGE, "jvm.threads.daemon.threads");
+    metricService.remove(MetricType.GAUGE, "jvm.threads.live.threads");
+
+    try {
+      threadBean.getAllThreadIds();
+      for (Thread.State state : Thread.State.values()) {
+        metricService.remove(
+            MetricType.GAUGE, "jvm.threads.states.threads", "state", getStateTagValue(state));
+      }
+    } catch (Error error) {
+      // An error will be thrown for unsupported operations
+      // e.g. SubstrateVM does not support getAllThreadIds
+    }
+  }
+
   // VisibleForTesting
   static long getThreadStateCount(ThreadMXBean threadBean, Thread.State state) {
     return Arrays.stream(threadBean.getThreadInfo(threadBean.getAllThreadIds()))
@@ -79,9 +99,4 @@ public class JvmThreadMetrics implements IMetricSet {
   private static String getStateTagValue(Thread.State state) {
     return state.name().toLowerCase().replace("_", "-");
   }
-
-  @Override
-  public PredefinedMetric getType() {
-    return PredefinedMetric.JVM;
-  }
 }
diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmUtils.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/predefined/jvm/JvmUtils.java
similarity index 96%
rename from metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmUtils.java
rename to metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/predefined/jvm/JvmUtils.java
index 4b7f9d3062..79a7b4a54c 100644
--- a/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmUtils.java
+++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/predefined/jvm/JvmUtils.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package org.apache.iotdb.metrics.predefined.jvm;
+package org.apache.iotdb.metrics.metricsets.predefined.jvm;
 
 import java.lang.management.MemoryPoolMXBean;
 import java.lang.management.MemoryUsage;
diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/predefined/logback/LogbackMetrics.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/predefined/logback/LogbackMetrics.java
new file mode 100644
index 0000000000..94464699c0
--- /dev/null
+++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/predefined/logback/LogbackMetrics.java
@@ -0,0 +1,104 @@
+/*
+ * 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.metrics.metricsets.predefined.logback;
+
+import org.apache.iotdb.metrics.AbstractMetricService;
+import org.apache.iotdb.metrics.metricsets.IMetricSet;
+import org.apache.iotdb.metrics.utils.MetricType;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.spi.LoggerContextListener;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/** This file is modified from io.micrometer.core.instrument.binder.logging.LogbackMetrics */
+public class LogbackMetrics implements IMetricSet {
+  private static final org.slf4j.Logger logger = LoggerFactory.getLogger(LogbackMetrics.class);
+  static ThreadLocal<Boolean> ignoreMetrics = new ThreadLocal<>();
+  private final LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
+  private final Map<AbstractMetricService, MetricsTurboFilter> metricsTurboFilters =
+      new HashMap<>();
+
+  public LogbackMetrics() {
+    loggerContext.addListener(
+        new LoggerContextListener() {
+          @Override
+          public boolean isResetResistant() {
+            return true;
+          }
+
+          @Override
+          public void onReset(LoggerContext context) {
+            // re-add turbo filter because reset clears the turbo filter list
+            synchronized (metricsTurboFilters) {
+              for (MetricsTurboFilter addMetricsTurboFilter : metricsTurboFilters.values()) {
+                loggerContext.addTurboFilter(addMetricsTurboFilter);
+              }
+            }
+          }
+
+          @Override
+          public void onStart(LoggerContext context) {
+            // no-op
+          }
+
+          @Override
+          public void onStop(LoggerContext context) {
+            // no-op
+          }
+
+          @Override
+          public void onLevelChange(Logger logger, Level level) {
+            // no-op
+          }
+        });
+  }
+
+  @Override
+  public void bindTo(AbstractMetricService metricService) {
+    MetricsTurboFilter filter = new MetricsTurboFilter(metricService);
+    synchronized (metricsTurboFilters) {
+      metricsTurboFilters.put(metricService, filter);
+      loggerContext.addTurboFilter(filter);
+    }
+  }
+
+  @Override
+  public void unbindFrom(AbstractMetricService metricService) {
+    try {
+      synchronized (metricsTurboFilters) {
+        for (MetricsTurboFilter addMetricsTurboFilter : metricsTurboFilters.values()) {
+          loggerContext.getTurboFilterList().remove(addMetricsTurboFilter);
+        }
+        metricService.remove(MetricType.COUNTER, "logback.events", "level", "error");
+        metricService.remove(MetricType.COUNTER, "logback.events", "level", "warn");
+        metricService.remove(MetricType.COUNTER, "logback.events", "level", "info");
+        metricService.remove(MetricType.COUNTER, "logback.events", "level", "debug");
+        metricService.remove(MetricType.COUNTER, "logback.events", "level", "trace");
+      }
+    } catch (Exception e) {
+      logger.error("Failed to remove LogBackMetrics.");
+    }
+  }
+}
diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/predefined/logback/MetricsTurboFilter.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/predefined/logback/MetricsTurboFilter.java
new file mode 100644
index 0000000000..173ea3b5e7
--- /dev/null
+++ b/metrics/interface/src/main/java/org/apache/iotdb/metrics/metricsets/predefined/logback/MetricsTurboFilter.java
@@ -0,0 +1,98 @@
+/*
+ * 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.metrics.metricsets.predefined.logback;
+
+import org.apache.iotdb.metrics.AbstractMetricService;
+import org.apache.iotdb.metrics.type.Counter;
+import org.apache.iotdb.metrics.utils.MetricLevel;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.turbo.TurboFilter;
+import ch.qos.logback.core.spi.FilterReply;
+import org.slf4j.Marker;
+
+public class MetricsTurboFilter extends TurboFilter {
+  private Counter errorCounter;
+  private Counter warnCounter;
+  private Counter infoCounter;
+  private Counter debugCounter;
+  private Counter traceCounter;
+
+  MetricsTurboFilter(AbstractMetricService metricService) {
+    errorCounter =
+        metricService.getOrCreateCounter("logback.events", MetricLevel.IMPORTANT, "level", "error");
+
+    warnCounter =
+        metricService.getOrCreateCounter("logback.events", MetricLevel.IMPORTANT, "level", "warn");
+
+    infoCounter =
+        metricService.getOrCreateCounter("logback.events", MetricLevel.IMPORTANT, "level", "info");
+
+    debugCounter =
+        metricService.getOrCreateCounter("logback.events", MetricLevel.IMPORTANT, "level", "debug");
+
+    traceCounter =
+        metricService.getOrCreateCounter("logback.events", MetricLevel.IMPORTANT, "level", "trace");
+  }
+
+  @Override
+  public FilterReply decide(
+      Marker marker, Logger logger, Level level, String format, Object[] params, Throwable t) {
+    // When filter is asked for decision for an isDebugEnabled call or similar test, there is no
+    // message (ie format)
+    // and no intention to log anything with this call. We will not increment counters and can
+    // return immediately and
+    // avoid the relatively expensive ThreadLocal access below. See also logbacks
+    // Logger.callTurboFilters().
+    if (format == null) {
+      return FilterReply.NEUTRAL;
+    }
+
+    Boolean ignored = LogbackMetrics.ignoreMetrics.get();
+    if (ignored != null && ignored) {
+      return FilterReply.NEUTRAL;
+    }
+
+    // cannot use logger.isEnabledFor(level), as it would cause a StackOverflowError by calling this
+    // filter again!
+    if (level.isGreaterOrEqual(logger.getEffectiveLevel())) {
+      switch (level.toInt()) {
+        case Level.ERROR_INT:
+          errorCounter.inc();
+          break;
+        case Level.WARN_INT:
+          warnCounter.inc();
+          break;
+        case Level.INFO_INT:
+          infoCounter.inc();
+          break;
+        case Level.DEBUG_INT:
+          debugCounter.inc();
+          break;
+        case Level.TRACE_INT:
+          traceCounter.inc();
+          break;
+      }
+    }
+
+    return FilterReply.NEUTRAL;
+  }
+}
diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmMetrics.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmMetrics.java
deleted file mode 100644
index 3d2a744861..0000000000
--- a/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/jvm/JvmMetrics.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.iotdb.metrics.predefined.jvm;
-
-import org.apache.iotdb.metrics.AbstractMetricManager;
-import org.apache.iotdb.metrics.predefined.IMetricSet;
-import org.apache.iotdb.metrics.predefined.PredefinedMetric;
-
-public class JvmMetrics implements IMetricSet {
-  @Override
-  public void bindTo(AbstractMetricManager metricManager) {
-    JvmClassLoaderMetrics jvmClassLoaderMetricSet = new JvmClassLoaderMetrics();
-    jvmClassLoaderMetricSet.bindTo(metricManager);
-
-    JvmCompileMetrics jvmCompileMetricSet = new JvmCompileMetrics();
-    jvmCompileMetricSet.bindTo(metricManager);
-
-    JvmGcMetrics jvmGcMetricSet = new JvmGcMetrics();
-    jvmGcMetricSet.bindTo(metricManager);
-
-    JvmMemoryMetrics jvmMemoryMetricSet = new JvmMemoryMetrics();
-    jvmMemoryMetricSet.bindTo(metricManager);
-
-    JvmThreadMetrics jvmThreadMetrics = new JvmThreadMetrics();
-    jvmThreadMetrics.bindTo(metricManager);
-  }
-
-  @Override
-  public PredefinedMetric getType() {
-    return PredefinedMetric.JVM;
-  }
-}
diff --git a/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/logback/LogbackMetrics.java b/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/logback/LogbackMetrics.java
deleted file mode 100644
index b2dde91411..0000000000
--- a/metrics/interface/src/main/java/org/apache/iotdb/metrics/predefined/logback/LogbackMetrics.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.iotdb.metrics.predefined.logback;
-
-import org.apache.iotdb.metrics.AbstractMetricManager;
-import org.apache.iotdb.metrics.predefined.IMetricSet;
-import org.apache.iotdb.metrics.predefined.PredefinedMetric;
-import org.apache.iotdb.metrics.type.Counter;
-import org.apache.iotdb.metrics.utils.MetricLevel;
-
-import ch.qos.logback.classic.Level;
-import ch.qos.logback.classic.Logger;
-import ch.qos.logback.classic.LoggerContext;
-import ch.qos.logback.classic.spi.LoggerContextListener;
-import ch.qos.logback.classic.turbo.TurboFilter;
-import ch.qos.logback.core.spi.FilterReply;
-import org.slf4j.LoggerFactory;
-import org.slf4j.Marker;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/** This file is modified from io.micrometer.core.instrument.binder.logging.LogbackMetrics */
-public class LogbackMetrics implements IMetricSet, AutoCloseable {
-  static ThreadLocal<Boolean> ignoreMetrics = new ThreadLocal<>();
-  private final LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
-  private final Map<AbstractMetricManager, MetricsTurboFilter> metricsTurboFilters =
-      new HashMap<>();
-
-  public LogbackMetrics() {
-    loggerContext.addListener(
-        new LoggerContextListener() {
-          @Override
-          public boolean isResetResistant() {
-            return true;
-          }
-
-          @Override
-          public void onReset(LoggerContext context) {
-            // re-add turbo filter because reset clears the turbo filter list
-            synchronized (metricsTurboFilters) {
-              for (MetricsTurboFilter metricsTurboFilter : metricsTurboFilters.values()) {
-                loggerContext.addTurboFilter(metricsTurboFilter);
-              }
-            }
-          }
-
-          @Override
-          public void onStart(LoggerContext context) {
-            // no-op
-          }
-
-          @Override
-          public void onStop(LoggerContext context) {
-            // no-op
-          }
-
-          @Override
-          public void onLevelChange(Logger logger, Level level) {
-            // no-op
-          }
-        });
-  }
-
-  @Override
-  public void bindTo(AbstractMetricManager metricManager) {
-    MetricsTurboFilter filter = new MetricsTurboFilter(metricManager);
-    synchronized (metricsTurboFilters) {
-      metricsTurboFilters.put(metricManager, filter);
-      loggerContext.addTurboFilter(filter);
-    }
-  }
-
-  public static void ignoreMetrics(Runnable r) {
-    ignoreMetrics.set(true);
-    try {
-      r.run();
-    } finally {
-      ignoreMetrics.remove();
-    }
-  }
-
-  @Override
-  public PredefinedMetric getType() {
-    return PredefinedMetric.LOGBACK;
-  }
-
-  @Override
-  public void close() throws Exception {
-    synchronized (metricsTurboFilters) {
-      for (MetricsTurboFilter metricsTurboFilter : metricsTurboFilters.values()) {
-        loggerContext.getTurboFilterList().remove(metricsTurboFilter);
-      }
-    }
-  }
-}
-
-class MetricsTurboFilter extends TurboFilter {
-  private Counter errorCounter;
-  private Counter warnCounter;
-  private Counter infoCounter;
-  private Counter debugCounter;
-  private Counter traceCounter;
-
-  MetricsTurboFilter(AbstractMetricManager metricManager) {
-    errorCounter =
-        metricManager.getOrCreateCounter("logback.events", MetricLevel.IMPORTANT, "level", "error");
-
-    warnCounter =
-        metricManager.getOrCreateCounter("logback.events", MetricLevel.IMPORTANT, "level", "warn");
-
-    infoCounter =
-        metricManager.getOrCreateCounter("logback.events", MetricLevel.IMPORTANT, "level", "info");
-
-    debugCounter =
-        metricManager.getOrCreateCounter("logback.events", MetricLevel.IMPORTANT, "level", "debug");
-
-    traceCounter =
-        metricManager.getOrCreateCounter("logback.events", MetricLevel.IMPORTANT, "level", "trace");
-  }
-
-  @Override
-  public FilterReply decide(
-      Marker marker, Logger logger, Level level, String format, Object[] params, Throwable t) {
-    // When filter is asked for decision for an isDebugEnabled call or similar test, there is no
-    // message (ie format)
-    // and no intention to log anything with this call. We will not increment counters and can
-    // return immediately and
-    // avoid the relatively expensive ThreadLocal access below. See also logbacks
-    // Logger.callTurboFilters().
-    if (format == null) {
-      return FilterReply.NEUTRAL;
-    }
-
-    Boolean ignored = LogbackMetrics.ignoreMetrics.get();
-    if (ignored != null && ignored) {
-      return FilterReply.NEUTRAL;
-    }
-
-    // cannot use logger.isEnabledFor(level), as it would cause a StackOverflowError by calling this
-    // filter again!
-    if (level.isGreaterOrEqual(logger.getEffectiveLevel())) {
-      switch (level.toInt()) {
-        case Level.ERROR_INT:
-          errorCounter.inc();
-          break;
-        case Level.WARN_INT:
-          warnCounter.inc();
-          break;
-        case Level.INFO_INT:
-          infoCounter.inc();
-          break;
-        case Level.DEBUG_INT:
-          debugCounter.inc();
-          break;
-        case Level.TRACE_INT:
-          traceCounter.inc();
-          break;
-      }
-    }
-
-    return FilterReply.NEUTRAL;
-  }
-}
diff --git a/metrics/micrometer-metrics/src/main/java/org/apache/iotdb/metrics/micrometer/reporter/MicrometerPrometheusReporter.java b/metrics/micrometer-metrics/src/main/java/org/apache/iotdb/metrics/micrometer/reporter/MicrometerPrometheusReporter.java
index e163b53820..709063da3d 100644
--- a/metrics/micrometer-metrics/src/main/java/org/apache/iotdb/metrics/micrometer/reporter/MicrometerPrometheusReporter.java
+++ b/metrics/micrometer-metrics/src/main/java/org/apache/iotdb/metrics/micrometer/reporter/MicrometerPrometheusReporter.java
@@ -36,7 +36,6 @@ import reactor.core.publisher.Mono;
 import reactor.netty.DisposableServer;
 import reactor.netty.http.server.HttpServer;
 
-import java.time.Duration;
 import java.util.Set;
 import java.util.stream.Collectors;
 
@@ -50,6 +49,9 @@ public class MicrometerPrometheusReporter implements Reporter {
 
   @Override
   public boolean start() {
+    if (httpServer != null) {
+      return false;
+    }
     Set<MeterRegistry> meterRegistrySet =
         Metrics.globalRegistry.getRegistries().stream()
             .filter(reporter -> reporter instanceof PrometheusMeterRegistry)
@@ -64,7 +66,6 @@ public class MicrometerPrometheusReporter implements Reporter {
     }
     httpServer =
         HttpServer.create()
-            .idleTimeout(Duration.ofMillis(30_000L))
             .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 2000)
             .port(metricConfig.getPrometheusExporterPort())
             .route(
diff --git a/server/src/main/java/org/apache/iotdb/db/engine/cache/ChunkCache.java b/server/src/main/java/org/apache/iotdb/db/engine/cache/ChunkCache.java
index 1b74456ad3..bac3869e6c 100644
--- a/server/src/main/java/org/apache/iotdb/db/engine/cache/ChunkCache.java
+++ b/server/src/main/java/org/apache/iotdb/db/engine/cache/ChunkCache.java
@@ -24,9 +24,6 @@ import org.apache.iotdb.db.conf.IoTDBConfig;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
 import org.apache.iotdb.db.query.control.FileReaderManager;
 import org.apache.iotdb.db.service.metrics.MetricService;
-import org.apache.iotdb.db.service.metrics.enums.Metric;
-import org.apache.iotdb.db.service.metrics.enums.Tag;
-import org.apache.iotdb.metrics.utils.MetricLevel;
 import org.apache.iotdb.tsfile.file.metadata.ChunkMetadata;
 import org.apache.iotdb.tsfile.read.TsFileSequenceReader;
 import org.apache.iotdb.tsfile.read.common.Chunk;
@@ -86,14 +83,11 @@ public class ChunkCache {
                 });
 
     // add metrics
-    MetricService.getInstance()
-        .getOrCreateAutoGauge(
-            Metric.CACHE_HIT.toString(),
-            MetricLevel.IMPORTANT,
-            lruCache,
-            l -> (long) (l.stats().hitRate() * 100),
-            Tag.NAME.toString(),
-            "chunk");
+    MetricService.getInstance().addMetricSet(new ChunkCacheMetrics(this));
+  }
+
+  public double getHitRate() {
+    return lruCache.stats().hitRate() * 100;
   }
 
   public static ChunkCache getInstance() {
diff --git a/server/src/main/java/org/apache/iotdb/db/engine/cache/ChunkCacheMetrics.java b/server/src/main/java/org/apache/iotdb/db/engine/cache/ChunkCacheMetrics.java
new file mode 100644
index 0000000000..64f40bb46a
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/engine/cache/ChunkCacheMetrics.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.engine.cache;
+
+import org.apache.iotdb.db.service.metrics.enums.Metric;
+import org.apache.iotdb.db.service.metrics.enums.Tag;
+import org.apache.iotdb.metrics.AbstractMetricService;
+import org.apache.iotdb.metrics.metricsets.IMetricSet;
+import org.apache.iotdb.metrics.utils.MetricLevel;
+import org.apache.iotdb.metrics.utils.MetricType;
+
+import java.util.Objects;
+
+public class ChunkCacheMetrics implements IMetricSet {
+  private ChunkCache chunkCache;
+
+  public ChunkCacheMetrics(ChunkCache chunkCache) {
+    this.chunkCache = chunkCache;
+  }
+
+  @Override
+  public void bindTo(AbstractMetricService metricService) {
+    metricService.getOrCreateAutoGauge(
+        Metric.CACHE_HIT.toString(),
+        MetricLevel.IMPORTANT,
+        chunkCache,
+        o -> (long) o.getHitRate(),
+        Tag.NAME.toString(),
+        "chunk");
+  }
+
+  @Override
+  public void unbindFrom(AbstractMetricService metricService) {
+    metricService.remove(
+        MetricType.GAUGE, Metric.CACHE_HIT.toString(), Tag.NAME.toString(), "chunk");
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    ChunkCacheMetrics that = (ChunkCacheMetrics) o;
+    return Objects.equals(chunkCache, that.chunkCache);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(chunkCache);
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/engine/cache/TimeSeriesMetadataCache.java b/server/src/main/java/org/apache/iotdb/db/engine/cache/TimeSeriesMetadataCache.java
index 576d178429..b64e3541d4 100644
--- a/server/src/main/java/org/apache/iotdb/db/engine/cache/TimeSeriesMetadataCache.java
+++ b/server/src/main/java/org/apache/iotdb/db/engine/cache/TimeSeriesMetadataCache.java
@@ -25,9 +25,6 @@ import org.apache.iotdb.db.conf.IoTDBConfig;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
 import org.apache.iotdb.db.query.control.FileReaderManager;
 import org.apache.iotdb.db.service.metrics.MetricService;
-import org.apache.iotdb.db.service.metrics.enums.Metric;
-import org.apache.iotdb.db.service.metrics.enums.Tag;
-import org.apache.iotdb.metrics.utils.MetricLevel;
 import org.apache.iotdb.tsfile.file.metadata.ChunkMetadata;
 import org.apache.iotdb.tsfile.file.metadata.TimeseriesMetadata;
 import org.apache.iotdb.tsfile.read.TsFileSequenceReader;
@@ -106,30 +103,8 @@ public class TimeSeriesMetadataCache {
                                 + RamUsageEstimator.shallowSizeOf(value.getChunkMetadataList())))
             .recordStats()
             .build();
-
     // add metrics
-    MetricService.getInstance()
-        .getOrCreateAutoGauge(
-            Metric.CACHE_HIT.toString(),
-            MetricLevel.IMPORTANT,
-            lruCache,
-            l -> (long) (l.stats().hitRate() * 100),
-            Tag.NAME.toString(),
-            "timeSeriesMeta");
-    MetricService.getInstance()
-        .getOrCreateAutoGauge(
-            Metric.CACHE_HIT.toString(),
-            MetricLevel.IMPORTANT,
-            bloomFilterPreventCount,
-            prevent -> {
-              if (bloomFilterRequestCount.get() == 0L) {
-                return 1L;
-              }
-              return (long)
-                  ((double) prevent.get() / (double) bloomFilterRequestCount.get() * 100L);
-            },
-            Tag.NAME.toString(),
-            "bloomFilter");
+    MetricService.getInstance().addMetricSet(new TimeSeriesMetadataCacheMetrics(this));
   }
 
   public static TimeSeriesMetadataCache getInstance() {
@@ -243,6 +218,14 @@ public class TimeSeriesMetadataCache {
     return entryAverageSize.get();
   }
 
+  public long calculateBloomFilterHitRatio() {
+    if (bloomFilterRequestCount.get() == 0L) {
+      return 1L;
+    }
+    return (long)
+        ((double) bloomFilterPreventCount.get() / (double) bloomFilterRequestCount.get() * 100L);
+  }
+
   /** clear LRUCache. */
   public void clear() {
     lruCache.invalidateAll();
diff --git a/server/src/main/java/org/apache/iotdb/db/engine/cache/TimeSeriesMetadataCacheMetrics.java b/server/src/main/java/org/apache/iotdb/db/engine/cache/TimeSeriesMetadataCacheMetrics.java
new file mode 100644
index 0000000000..d7344b7687
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/engine/cache/TimeSeriesMetadataCacheMetrics.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.db.engine.cache;
+
+import org.apache.iotdb.db.service.metrics.enums.Metric;
+import org.apache.iotdb.db.service.metrics.enums.Tag;
+import org.apache.iotdb.metrics.AbstractMetricService;
+import org.apache.iotdb.metrics.metricsets.IMetricSet;
+import org.apache.iotdb.metrics.utils.MetricLevel;
+import org.apache.iotdb.metrics.utils.MetricType;
+
+import java.util.Objects;
+
+public class TimeSeriesMetadataCacheMetrics implements IMetricSet {
+
+  private TimeSeriesMetadataCache timeSeriesMetadataCache;
+
+  public TimeSeriesMetadataCacheMetrics(TimeSeriesMetadataCache timeSeriesMetadataCache) {
+    this.timeSeriesMetadataCache = timeSeriesMetadataCache;
+  }
+
+  @Override
+  public void bindTo(AbstractMetricService metricService) {
+    metricService.getOrCreateAutoGauge(
+        Metric.CACHE_HIT.toString(),
+        MetricLevel.IMPORTANT,
+        timeSeriesMetadataCache,
+        l -> (long) timeSeriesMetadataCache.calculateTimeSeriesMetadataHitRatio(),
+        Tag.NAME.toString(),
+        "timeSeriesMeta");
+    metricService.getOrCreateAutoGauge(
+        Metric.CACHE_HIT.toString(),
+        MetricLevel.IMPORTANT,
+        timeSeriesMetadataCache,
+        TimeSeriesMetadataCache::calculateBloomFilterHitRatio,
+        Tag.NAME.toString(),
+        "bloomFilter");
+  }
+
+  @Override
+  public void unbindFrom(AbstractMetricService metricService) {
+    metricService.remove(
+        MetricType.GAUGE, Metric.CACHE_HIT.toString(), Tag.NAME.toString(), "timeSeriesMeta");
+    metricService.remove(
+        MetricType.GAUGE, Metric.CACHE_HIT.toString(), Tag.NAME.toString(), "bloomFilter");
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    TimeSeriesMetadataCacheMetrics that = (TimeSeriesMetadataCacheMetrics) o;
+    return Objects.equals(timeSeriesMetadataCache, that.timeSeriesMetadataCache);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(timeSeriesMetadataCache);
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/engine/flush/FlushManager.java b/server/src/main/java/org/apache/iotdb/db/engine/flush/FlushManager.java
index 3eef0d9fcb..dcf31495b0 100644
--- a/server/src/main/java/org/apache/iotdb/db/engine/flush/FlushManager.java
+++ b/server/src/main/java/org/apache/iotdb/db/engine/flush/FlushManager.java
@@ -16,6 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
 package org.apache.iotdb.db.engine.flush;
 
 import org.apache.iotdb.commons.concurrent.WrappedRunnable;
@@ -28,11 +29,7 @@ import org.apache.iotdb.db.conf.IoTDBDescriptor;
 import org.apache.iotdb.db.engine.flush.pool.FlushSubTaskPoolManager;
 import org.apache.iotdb.db.engine.flush.pool.FlushTaskPoolManager;
 import org.apache.iotdb.db.engine.storagegroup.TsFileProcessor;
-import org.apache.iotdb.db.rescon.AbstractPoolManager;
 import org.apache.iotdb.db.service.metrics.MetricService;
-import org.apache.iotdb.db.service.metrics.enums.Metric;
-import org.apache.iotdb.db.service.metrics.enums.Tag;
-import org.apache.iotdb.metrics.utils.MetricLevel;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -55,26 +52,7 @@ public class FlushManager implements FlushManagerMBean, IService {
     flushPool.start();
     try {
       JMXService.registerMBean(this, ServiceType.FLUSH_SERVICE.getJmxName());
-      MetricService.getInstance()
-          .getOrCreateAutoGauge(
-              Metric.QUEUE.toString(),
-              MetricLevel.IMPORTANT,
-              flushPool,
-              AbstractPoolManager::getWaitingTasksNumber,
-              Tag.NAME.toString(),
-              "flush",
-              Tag.STATUS.toString(),
-              "waiting");
-      MetricService.getInstance()
-          .getOrCreateAutoGauge(
-              Metric.QUEUE.toString(),
-              MetricLevel.IMPORTANT,
-              flushPool,
-              AbstractPoolManager::getWorkingTasksNumber,
-              Tag.NAME.toString(),
-              "flush",
-              Tag.STATUS.toString(),
-              "running");
+      MetricService.getInstance().addMetricSet(new FlushManagerMetrics(this));
     } catch (Exception e) {
       throw new StartupException(this.getID().getName(), e.getMessage());
     }
@@ -92,6 +70,11 @@ public class FlushManager implements FlushManagerMBean, IService {
     return ServiceType.FLUSH_SERVICE;
   }
 
+  @Override
+  public int getNumberOfWaitingTasks() {
+    return flushPool.getWaitingTasksNumber();
+  }
+
   @Override
   public int getNumberOfWorkingTasks() {
     return flushPool.getWorkingTasksNumber();
diff --git a/server/src/main/java/org/apache/iotdb/db/engine/flush/FlushManagerMBean.java b/server/src/main/java/org/apache/iotdb/db/engine/flush/FlushManagerMBean.java
index 7f474902d6..a78b190910 100644
--- a/server/src/main/java/org/apache/iotdb/db/engine/flush/FlushManagerMBean.java
+++ b/server/src/main/java/org/apache/iotdb/db/engine/flush/FlushManagerMBean.java
@@ -21,6 +21,8 @@ package org.apache.iotdb.db.engine.flush;
 
 public interface FlushManagerMBean {
 
+  int getNumberOfWaitingTasks();
+
   int getNumberOfWorkingTasks();
 
   int getNumberOfPendingTasks();
diff --git a/server/src/main/java/org/apache/iotdb/db/engine/flush/FlushManagerMetrics.java b/server/src/main/java/org/apache/iotdb/db/engine/flush/FlushManagerMetrics.java
new file mode 100644
index 0000000000..3e69678b67
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/engine/flush/FlushManagerMetrics.java
@@ -0,0 +1,90 @@
+/*
+ * 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.engine.flush;
+
+import org.apache.iotdb.db.service.metrics.enums.Metric;
+import org.apache.iotdb.db.service.metrics.enums.Tag;
+import org.apache.iotdb.metrics.AbstractMetricService;
+import org.apache.iotdb.metrics.metricsets.IMetricSet;
+import org.apache.iotdb.metrics.utils.MetricLevel;
+import org.apache.iotdb.metrics.utils.MetricType;
+
+import java.util.Objects;
+
+public class FlushManagerMetrics implements IMetricSet {
+  private FlushManager flushManager;
+
+  public FlushManagerMetrics(FlushManager flushManager) {
+    this.flushManager = flushManager;
+  }
+
+  @Override
+  public void bindTo(AbstractMetricService metricService) {
+    metricService.getOrCreateAutoGauge(
+        Metric.QUEUE.toString(),
+        MetricLevel.IMPORTANT,
+        flushManager,
+        FlushManager::getNumberOfWaitingTasks,
+        Tag.NAME.toString(),
+        "flush",
+        Tag.STATUS.toString(),
+        "waiting");
+    metricService.getOrCreateAutoGauge(
+        Metric.QUEUE.toString(),
+        MetricLevel.IMPORTANT,
+        flushManager,
+        FlushManager::getNumberOfWorkingTasks,
+        Tag.NAME.toString(),
+        "flush",
+        Tag.STATUS.toString(),
+        "running");
+  }
+
+  @Override
+  public void unbindFrom(AbstractMetricService metricService) {
+    metricService.remove(
+        MetricType.GAUGE,
+        Metric.QUEUE.toString(),
+        Tag.NAME.toString(),
+        "flush",
+        Tag.STATUS.toString(),
+        "waiting");
+    metricService.remove(
+        MetricType.GAUGE,
+        Metric.QUEUE.toString(),
+        Tag.NAME.toString(),
+        "flush",
+        Tag.STATUS.toString(),
+        "running");
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    FlushManagerMetrics that = (FlushManagerMetrics) o;
+    return Objects.equals(flushManager, that.flushManager);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(flushManager);
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/DataRegion.java b/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/DataRegion.java
index 18093dec11..1780dcb55f 100755
--- a/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/DataRegion.java
+++ b/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/DataRegion.java
@@ -86,8 +86,6 @@ import org.apache.iotdb.db.rescon.TsFileResourceManager;
 import org.apache.iotdb.db.service.IoTDB;
 import org.apache.iotdb.db.service.SettleService;
 import org.apache.iotdb.db.service.metrics.MetricService;
-import org.apache.iotdb.db.service.metrics.enums.Metric;
-import org.apache.iotdb.db.service.metrics.enums.Tag;
 import org.apache.iotdb.db.sync.SyncService;
 import org.apache.iotdb.db.sync.sender.manager.ISyncManager;
 import org.apache.iotdb.db.tools.settle.TsFileAndModSettleTool;
@@ -101,7 +99,6 @@ import org.apache.iotdb.db.wal.recover.file.UnsealedTsFileRecoverPerformer;
 import org.apache.iotdb.db.wal.utils.WALMode;
 import org.apache.iotdb.db.wal.utils.listener.WALFlushListener;
 import org.apache.iotdb.db.wal.utils.listener.WALRecoverListener;
-import org.apache.iotdb.metrics.utils.MetricLevel;
 import org.apache.iotdb.rpc.RpcUtils;
 import org.apache.iotdb.rpc.TSStatusCode;
 import org.apache.iotdb.tsfile.file.metadata.ChunkMetadata;
@@ -346,14 +343,7 @@ public class DataRegion {
       recover();
     }
 
-    MetricService.getInstance()
-        .getOrCreateAutoGauge(
-            Metric.MEM.toString(),
-            MetricLevel.IMPORTANT,
-            storageGroupInfo,
-            StorageGroupInfo::getMemCost,
-            Tag.NAME.toString(),
-            "storageGroup_" + getStorageGroupName());
+    MetricService.getInstance().addMetricSet(new DataRegionMetrics(this));
   }
 
   @TestOnly
@@ -3798,6 +3788,10 @@ public class DataRegion {
     }
   }
 
+  public long getMemCost() {
+    return storageGroupInfo.getMemCost();
+  }
+
   @TestOnly
   public ILastFlushTimeManager getLastFlushTimeManager() {
     return lastFlushTimeManager;
diff --git a/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/DataRegionMetrics.java b/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/DataRegionMetrics.java
new file mode 100644
index 0000000000..866b5cea32
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/DataRegionMetrics.java
@@ -0,0 +1,73 @@
+/*
+ * 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.engine.storagegroup;
+
+import org.apache.iotdb.db.service.metrics.enums.Metric;
+import org.apache.iotdb.db.service.metrics.enums.Tag;
+import org.apache.iotdb.metrics.AbstractMetricService;
+import org.apache.iotdb.metrics.metricsets.IMetricSet;
+import org.apache.iotdb.metrics.utils.MetricLevel;
+import org.apache.iotdb.metrics.utils.MetricType;
+
+import java.util.Objects;
+
+public class DataRegionMetrics implements IMetricSet {
+  private DataRegion dataRegion;
+  private String storageGroupName;
+
+  public DataRegionMetrics(DataRegion dataRegion) {
+    this.dataRegion = dataRegion;
+    this.storageGroupName = dataRegion.getStorageGroupName();
+  }
+
+  @Override
+  public void bindTo(AbstractMetricService metricService) {
+    metricService.getOrCreateAutoGauge(
+        Metric.MEM.toString(),
+        MetricLevel.IMPORTANT,
+        dataRegion,
+        DataRegion::getMemCost,
+        Tag.NAME.toString(),
+        "storageGroup_" + storageGroupName);
+  }
+
+  @Override
+  public void unbindFrom(AbstractMetricService metricService) {
+    metricService.remove(
+        MetricType.GAUGE,
+        Metric.MEM.toString(),
+        Tag.NAME.toString(),
+        "storageGroup_" + storageGroupName);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    DataRegionMetrics that = (DataRegionMetrics) o;
+    return Objects.equals(dataRegion, that.dataRegion)
+        && Objects.equals(storageGroupName, that.storageGroupName);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(dataRegion, storageGroupName);
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/TsFileProcessorInfo.java b/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/TsFileProcessorInfo.java
index 4aafb030c8..7d0b419a39 100644
--- a/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/TsFileProcessorInfo.java
+++ b/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/TsFileProcessorInfo.java
@@ -19,9 +19,6 @@
 package org.apache.iotdb.db.engine.storagegroup;
 
 import org.apache.iotdb.db.service.metrics.MetricService;
-import org.apache.iotdb.db.service.metrics.enums.Metric;
-import org.apache.iotdb.db.service.metrics.enums.Tag;
-import org.apache.iotdb.metrics.utils.MetricLevel;
 
 /** The TsFileProcessorInfo records the memory cost of this TsFileProcessor. */
 public class TsFileProcessorInfo {
@@ -35,46 +32,29 @@ public class TsFileProcessorInfo {
   public TsFileProcessorInfo(StorageGroupInfo storageGroupInfo) {
     this.storageGroupInfo = storageGroupInfo;
     this.memCost = 0L;
+    if (null != storageGroupInfo.getDataRegion()) {
+      MetricService.getInstance()
+          .addMetricSet(
+              new TsFileProcessorInfoMetrics(
+                  storageGroupInfo.getDataRegion().getStorageGroupName(), memCost));
+    }
   }
 
   /** called in each insert */
   public void addTSPMemCost(long cost) {
     memCost += cost;
     storageGroupInfo.addStorageGroupMemCost(cost);
-    if (null != storageGroupInfo.getDataRegion()) {
-      MetricService.getInstance()
-          .getOrCreateGauge(
-              Metric.MEM.toString(),
-              MetricLevel.IMPORTANT,
-              Tag.NAME.toString(),
-              "chunkMetaData_" + storageGroupInfo.getDataRegion().getStorageGroupName())
-          .incr(cost);
-    }
   }
 
   /** called when meet exception */
   public void releaseTSPMemCost(long cost) {
     storageGroupInfo.releaseStorageGroupMemCost(cost);
     memCost -= cost;
-    MetricService.getInstance()
-        .getOrCreateGauge(
-            Metric.MEM.toString(),
-            MetricLevel.IMPORTANT,
-            Tag.NAME.toString(),
-            "chunkMetaData_" + storageGroupInfo.getDataRegion().getStorageGroupName())
-        .decr(cost);
   }
 
   /** called when closing TSP */
   public void clear() {
     storageGroupInfo.releaseStorageGroupMemCost(memCost);
-    MetricService.getInstance()
-        .getOrCreateGauge(
-            Metric.MEM.toString(),
-            MetricLevel.IMPORTANT,
-            Tag.NAME.toString(),
-            "chunkMetaData_" + storageGroupInfo.getDataRegion().getStorageGroupName())
-        .decr(memCost);
     memCost = 0L;
   }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/TsFileProcessorInfoMetrics.java b/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/TsFileProcessorInfoMetrics.java
new file mode 100644
index 0000000000..866068d936
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/TsFileProcessorInfoMetrics.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.db.engine.storagegroup;
+
+import org.apache.iotdb.db.service.metrics.MetricService;
+import org.apache.iotdb.db.service.metrics.enums.Metric;
+import org.apache.iotdb.db.service.metrics.enums.Tag;
+import org.apache.iotdb.metrics.AbstractMetricService;
+import org.apache.iotdb.metrics.metricsets.IMetricSet;
+import org.apache.iotdb.metrics.utils.MetricLevel;
+import org.apache.iotdb.metrics.utils.MetricType;
+
+import java.util.Objects;
+
+public class TsFileProcessorInfoMetrics implements IMetricSet {
+  private String storageGroupName;
+  private long memCost;
+
+  public TsFileProcessorInfoMetrics(String storageGroupName, long memCost) {
+    this.storageGroupName = storageGroupName;
+    this.memCost = memCost;
+  }
+
+  @Override
+  public void bindTo(AbstractMetricService metricService) {
+    MetricService.getInstance()
+        .getOrCreateAutoGauge(
+            Metric.MEM.toString(),
+            MetricLevel.IMPORTANT,
+            memCost,
+            o -> o,
+            Tag.NAME.toString(),
+            "chunkMetaData_" + storageGroupName);
+  }
+
+  @Override
+  public void unbindFrom(AbstractMetricService metricService) {
+    MetricService.getInstance()
+        .remove(
+            MetricType.GAUGE,
+            Metric.MEM.toString(),
+            Tag.NAME.toString(),
+            "chunkMetaData_" + storageGroupName);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    TsFileProcessorInfoMetrics that = (TsFileProcessorInfoMetrics) o;
+    return memCost == that.memCost && Objects.equals(storageGroupName, that.storageGroupName);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(storageGroupName, memCost);
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/cache/DataNodeSchemaCache.java b/server/src/main/java/org/apache/iotdb/db/metadata/cache/DataNodeSchemaCache.java
index 5306817e8c..6c88cbb870 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/cache/DataNodeSchemaCache.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/cache/DataNodeSchemaCache.java
@@ -26,9 +26,6 @@ import org.apache.iotdb.db.metadata.path.MeasurementPath;
 import org.apache.iotdb.db.mpp.common.schematree.ClusterSchemaTree;
 import org.apache.iotdb.db.mpp.common.schematree.ISchemaTree;
 import org.apache.iotdb.db.service.metrics.MetricService;
-import org.apache.iotdb.db.service.metrics.enums.Metric;
-import org.apache.iotdb.db.service.metrics.enums.Tag;
-import org.apache.iotdb.metrics.utils.MetricLevel;
 import org.apache.iotdb.tsfile.read.TimeValuePair;
 import org.apache.iotdb.tsfile.write.schema.MeasurementSchema;
 
@@ -56,14 +53,11 @@ public class DataNodeSchemaCache {
                 (PartialPath key, SchemaCacheEntry value) ->
                     PartialPath.estimateSize(key) + SchemaCacheEntry.estimateSize(value))
             .build();
-    MetricService.getInstance()
-        .getOrCreateAutoGauge(
-            Metric.CACHE_HIT.toString(),
-            MetricLevel.IMPORTANT,
-            cache,
-            l -> (long) (l.stats().hitRate() * 100),
-            Tag.NAME.toString(),
-            "schemaCache");
+    MetricService.getInstance().addMetricSet(new DataNodeSchemaCacheMetrics(this));
+  }
+
+  public double getHitRate() {
+    return cache.stats().hitRate() * 100;
   }
 
   public static DataNodeSchemaCache getInstance() {
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/cache/DataNodeSchemaCacheMetrics.java b/server/src/main/java/org/apache/iotdb/db/metadata/cache/DataNodeSchemaCacheMetrics.java
new file mode 100644
index 0000000000..5cf6d4111b
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/cache/DataNodeSchemaCacheMetrics.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.metadata.cache;
+
+import org.apache.iotdb.db.service.metrics.enums.Metric;
+import org.apache.iotdb.db.service.metrics.enums.Tag;
+import org.apache.iotdb.metrics.AbstractMetricService;
+import org.apache.iotdb.metrics.metricsets.IMetricSet;
+import org.apache.iotdb.metrics.utils.MetricLevel;
+import org.apache.iotdb.metrics.utils.MetricType;
+
+import java.util.Objects;
+
+public class DataNodeSchemaCacheMetrics implements IMetricSet {
+  private DataNodeSchemaCache dataNodeSchemaCache;
+
+  public DataNodeSchemaCacheMetrics(DataNodeSchemaCache dataNodeSchemaCache) {
+    this.dataNodeSchemaCache = dataNodeSchemaCache;
+  }
+
+  @Override
+  public void bindTo(AbstractMetricService metricService) {
+    metricService.getOrCreateAutoGauge(
+        Metric.CACHE_HIT.toString(),
+        MetricLevel.IMPORTANT,
+        dataNodeSchemaCache,
+        o -> (long) o.getHitRate(),
+        Tag.NAME.toString(),
+        "schemaCache");
+  }
+
+  @Override
+  public void unbindFrom(AbstractMetricService metricService) {
+    metricService.remove(
+        MetricType.GAUGE, Metric.CACHE_HIT.toString(), Tag.NAME.toString(), "schemaCache");
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    DataNodeSchemaCacheMetrics that = (DataNodeSchemaCacheMetrics) o;
+    return Objects.equals(dataNodeSchemaCache, that.dataNodeSchemaCache);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(dataNodeSchemaCache);
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/rescon/SchemaResourceManager.java b/server/src/main/java/org/apache/iotdb/db/metadata/rescon/SchemaResourceManager.java
index c5df8ff9a7..b1f6f11a56 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/rescon/SchemaResourceManager.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/rescon/SchemaResourceManager.java
@@ -24,13 +24,15 @@ import org.apache.iotdb.db.metadata.mtree.store.disk.MTreeFlushTaskManager;
 import org.apache.iotdb.db.metadata.mtree.store.disk.MTreeReleaseTaskManager;
 import org.apache.iotdb.db.metadata.mtree.store.disk.memcontrol.MemManagerHolder;
 import org.apache.iotdb.db.metadata.schemaregion.SchemaEngineMode;
+import org.apache.iotdb.db.service.metrics.MetricService;
 
 public class SchemaResourceManager {
 
   private SchemaResourceManager() {}
 
   public static void initSchemaResource() {
-    SchemaStatisticsManager.getInstance().init();
+    MetricService.getInstance()
+        .addMetricSet(new SchemaStatisticsManagerMetrics(SchemaStatisticsManager.getInstance()));
     MemoryStatistics.getInstance().init();
     if (IoTDBDescriptor.getInstance()
         .getConfig()
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/rescon/SchemaStatisticsManager.java b/server/src/main/java/org/apache/iotdb/db/metadata/rescon/SchemaStatisticsManager.java
index 6d63cdfe85..e33342d403 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/rescon/SchemaStatisticsManager.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/rescon/SchemaStatisticsManager.java
@@ -18,11 +18,6 @@
  */
 package org.apache.iotdb.db.metadata.rescon;
 
-import org.apache.iotdb.db.service.metrics.MetricService;
-import org.apache.iotdb.db.service.metrics.enums.Metric;
-import org.apache.iotdb.db.service.metrics.enums.Tag;
-import org.apache.iotdb.metrics.utils.MetricLevel;
-
 import java.util.concurrent.atomic.AtomicLong;
 
 public class SchemaStatisticsManager {
@@ -43,17 +38,6 @@ public class SchemaStatisticsManager {
     return SchemaStatisticsHolder.INSTANCE;
   }
 
-  public void init() {
-    MetricService.getInstance()
-        .getOrCreateAutoGauge(
-            Metric.QUANTITY.toString(),
-            MetricLevel.IMPORTANT,
-            totalSeriesNumber,
-            AtomicLong::get,
-            Tag.NAME.toString(),
-            "timeSeries");
-  }
-
   public long getTotalSeriesNumber() {
     return totalSeriesNumber.get();
   }
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/rescon/SchemaStatisticsManagerMetrics.java b/server/src/main/java/org/apache/iotdb/db/metadata/rescon/SchemaStatisticsManagerMetrics.java
new file mode 100644
index 0000000000..7d2e2a3e29
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/rescon/SchemaStatisticsManagerMetrics.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.metadata.rescon;
+
+import org.apache.iotdb.db.service.metrics.enums.Metric;
+import org.apache.iotdb.db.service.metrics.enums.Tag;
+import org.apache.iotdb.metrics.AbstractMetricService;
+import org.apache.iotdb.metrics.metricsets.IMetricSet;
+import org.apache.iotdb.metrics.utils.MetricLevel;
+import org.apache.iotdb.metrics.utils.MetricType;
+
+import java.util.Objects;
+
+public class SchemaStatisticsManagerMetrics implements IMetricSet {
+  private SchemaStatisticsManager schemaStatisticsManager;
+
+  public SchemaStatisticsManagerMetrics(SchemaStatisticsManager schemaStatisticsManager) {
+    this.schemaStatisticsManager = schemaStatisticsManager;
+  }
+
+  @Override
+  public void bindTo(AbstractMetricService metricService) {
+    metricService.getOrCreateAutoGauge(
+        Metric.QUANTITY.toString(),
+        MetricLevel.IMPORTANT,
+        schemaStatisticsManager,
+        SchemaStatisticsManager::getTotalSeriesNumber,
+        Tag.NAME.toString(),
+        "timeSeries");
+  }
+
+  @Override
+  public void unbindFrom(AbstractMetricService metricService) {
+    metricService.remove(
+        MetricType.GAUGE, Metric.QUANTITY.toString(), Tag.NAME.toString(), "timeSeries");
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    SchemaStatisticsManagerMetrics that = (SchemaStatisticsManagerMetrics) o;
+    return Objects.equals(schemaStatisticsManager, that.schemaStatisticsManager);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(schemaStatisticsManager);
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/exchange/MPPDataExchangeService.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/exchange/MPPDataExchangeService.java
index 56df15a6ff..4a0d35cbf4 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/execution/exchange/MPPDataExchangeService.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/exchange/MPPDataExchangeService.java
@@ -26,7 +26,6 @@ import org.apache.iotdb.commons.concurrent.IoTDBThreadPoolFactory;
 import org.apache.iotdb.commons.concurrent.IoTThreadFactory;
 import org.apache.iotdb.commons.concurrent.ThreadName;
 import org.apache.iotdb.commons.exception.runtime.RPCServiceException;
-import org.apache.iotdb.commons.service.AbstractThriftServiceThread;
 import org.apache.iotdb.commons.service.ServiceType;
 import org.apache.iotdb.commons.service.ThriftService;
 import org.apache.iotdb.commons.service.ThriftServiceThread;
@@ -35,9 +34,6 @@ import org.apache.iotdb.db.conf.IoTDBConfig;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
 import org.apache.iotdb.db.mpp.execution.memory.LocalMemoryManager;
 import org.apache.iotdb.db.service.metrics.MetricService;
-import org.apache.iotdb.db.service.metrics.enums.Metric;
-import org.apache.iotdb.db.service.metrics.enums.Tag;
-import org.apache.iotdb.metrics.utils.MetricLevel;
 import org.apache.iotdb.mpp.rpc.thrift.MPPDataExchangeService.Processor;
 
 import org.slf4j.Logger;
@@ -111,13 +107,7 @@ public class MPPDataExchangeService extends ThriftService implements MPPDataExch
     }
     thriftServiceThread.setName(ThreadName.MPP_DATA_EXCHANGE_RPC_SERVICE.getName());
     MetricService.getInstance()
-        .getOrCreateAutoGauge(
-            Metric.THRIFT_ACTIVE_THREADS.toString(),
-            MetricLevel.CORE,
-            thriftServiceThread,
-            AbstractThriftServiceThread::getActiveThreadCount,
-            Tag.NAME.toString(),
-            ThreadName.MPP_DATA_EXCHANGE_RPC_SERVICE.getName());
+        .addMetricSet(new MPPDataExchangeServiceMetrics(thriftServiceThread));
   }
 
   @Override
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/exchange/MPPDataExchangeServiceMetrics.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/exchange/MPPDataExchangeServiceMetrics.java
new file mode 100644
index 0000000000..7d87b402c5
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/exchange/MPPDataExchangeServiceMetrics.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.mpp.execution.exchange;
+
+import org.apache.iotdb.commons.concurrent.ThreadName;
+import org.apache.iotdb.commons.service.AbstractThriftServiceThread;
+import org.apache.iotdb.db.service.metrics.enums.Metric;
+import org.apache.iotdb.db.service.metrics.enums.Tag;
+import org.apache.iotdb.metrics.AbstractMetricService;
+import org.apache.iotdb.metrics.metricsets.IMetricSet;
+import org.apache.iotdb.metrics.utils.MetricLevel;
+import org.apache.iotdb.metrics.utils.MetricType;
+
+public class MPPDataExchangeServiceMetrics implements IMetricSet {
+  private AbstractThriftServiceThread thriftServiceThread;
+
+  public MPPDataExchangeServiceMetrics(AbstractThriftServiceThread thriftServiceThread) {
+    this.thriftServiceThread = thriftServiceThread;
+  }
+
+  @Override
+  public void bindTo(AbstractMetricService metricService) {
+    metricService.getOrCreateAutoGauge(
+        Metric.THRIFT_ACTIVE_THREADS.toString(),
+        MetricLevel.CORE,
+        thriftServiceThread,
+        AbstractThriftServiceThread::getActiveThreadCount,
+        Tag.NAME.toString(),
+        ThreadName.MPP_DATA_EXCHANGE_RPC_SERVICE.getName());
+  }
+
+  @Override
+  public void unbindFrom(AbstractMetricService metricService) {
+    metricService.remove(
+        MetricType.GAUGE,
+        Metric.THRIFT_ACTIVE_THREADS.toString(),
+        Tag.NAME.toString(),
+        ThreadName.MPP_DATA_EXCHANGE_RPC_SERVICE.getName());
+  }
+
+  public AbstractThriftServiceThread getThriftServiceThread() {
+    return thriftServiceThread;
+  }
+
+  public void setThriftServiceThread(AbstractThriftServiceThread thriftServiceThread) {
+    this.thriftServiceThread = thriftServiceThread;
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/exchange/MPPDataExchangeServiceThriftHandler.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/exchange/MPPDataExchangeServiceThriftHandler.java
index 60d50729d9..4321320974 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/execution/exchange/MPPDataExchangeServiceThriftHandler.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/exchange/MPPDataExchangeServiceThriftHandler.java
@@ -20,42 +20,35 @@
 package org.apache.iotdb.db.mpp.execution.exchange;
 
 import org.apache.iotdb.db.service.metrics.MetricService;
-import org.apache.iotdb.db.service.metrics.enums.Metric;
-import org.apache.iotdb.db.service.metrics.enums.Tag;
-import org.apache.iotdb.metrics.utils.MetricLevel;
 
 import org.apache.thrift.protocol.TProtocol;
 import org.apache.thrift.server.ServerContext;
 import org.apache.thrift.server.TServerEventHandler;
 import org.apache.thrift.transport.TTransport;
 
+import java.util.concurrent.atomic.AtomicLong;
+
 public class MPPDataExchangeServiceThriftHandler implements TServerEventHandler {
+  private AtomicLong thriftConnectionNumber = new AtomicLong(0);
+
+  public MPPDataExchangeServiceThriftHandler() {
+    MetricService.getInstance()
+        .addMetricSet(new MppDataExchangeServiceThriftHandlerMetrics(thriftConnectionNumber));
+  }
 
   @Override
   public void preServe() {}
 
   @Override
   public ServerContext createContext(TProtocol tProtocol, TProtocol tProtocol1) {
-    MetricService.getInstance()
-        .getOrCreateGauge(
-            Metric.THRIFT_CONNECTIONS.toString(),
-            MetricLevel.CORE,
-            Tag.NAME.toString(),
-            "MPPDataExchange")
-        .incr(1L);
+    thriftConnectionNumber.incrementAndGet();
     return null;
   }
 
   @Override
   public void deleteContext(
       ServerContext serverContext, TProtocol tProtocol, TProtocol tProtocol1) {
-    MetricService.getInstance()
-        .getOrCreateGauge(
-            Metric.THRIFT_CONNECTIONS.toString(),
-            MetricLevel.CORE,
-            Tag.NAME.toString(),
-            "MPPDataExchange")
-        .decr(1L);
+    thriftConnectionNumber.decrementAndGet();
   }
 
   @Override
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/exchange/MPPDataExchangeServiceThriftHandler.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/exchange/MppDataExchangeServiceThriftHandlerMetrics.java
similarity index 53%
copy from server/src/main/java/org/apache/iotdb/db/mpp/execution/exchange/MPPDataExchangeServiceThriftHandler.java
copy to server/src/main/java/org/apache/iotdb/db/mpp/execution/exchange/MppDataExchangeServiceThriftHandlerMetrics.java
index 60d50729d9..e86c909ee1 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/execution/exchange/MPPDataExchangeServiceThriftHandler.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/exchange/MppDataExchangeServiceThriftHandlerMetrics.java
@@ -22,43 +22,54 @@ package org.apache.iotdb.db.mpp.execution.exchange;
 import org.apache.iotdb.db.service.metrics.MetricService;
 import org.apache.iotdb.db.service.metrics.enums.Metric;
 import org.apache.iotdb.db.service.metrics.enums.Tag;
+import org.apache.iotdb.metrics.AbstractMetricService;
+import org.apache.iotdb.metrics.metricsets.IMetricSet;
 import org.apache.iotdb.metrics.utils.MetricLevel;
+import org.apache.iotdb.metrics.utils.MetricType;
 
-import org.apache.thrift.protocol.TProtocol;
-import org.apache.thrift.server.ServerContext;
-import org.apache.thrift.server.TServerEventHandler;
-import org.apache.thrift.transport.TTransport;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicLong;
 
-public class MPPDataExchangeServiceThriftHandler implements TServerEventHandler {
+public class MppDataExchangeServiceThriftHandlerMetrics implements IMetricSet {
+  private AtomicLong thriftConnectionNumber;
 
-  @Override
-  public void preServe() {}
+  public MppDataExchangeServiceThriftHandlerMetrics(AtomicLong thriftConnectionNumber) {
+    this.thriftConnectionNumber = thriftConnectionNumber;
+  }
 
   @Override
-  public ServerContext createContext(TProtocol tProtocol, TProtocol tProtocol1) {
+  public void bindTo(AbstractMetricService metricService) {
     MetricService.getInstance()
-        .getOrCreateGauge(
+        .getOrCreateAutoGauge(
             Metric.THRIFT_CONNECTIONS.toString(),
             MetricLevel.CORE,
+            thriftConnectionNumber,
+            AtomicLong::get,
             Tag.NAME.toString(),
-            "MPPDataExchange")
-        .incr(1L);
-    return null;
+            "MPPDataExchange");
   }
 
   @Override
-  public void deleteContext(
-      ServerContext serverContext, TProtocol tProtocol, TProtocol tProtocol1) {
+  public void unbindFrom(AbstractMetricService metricService) {
     MetricService.getInstance()
-        .getOrCreateGauge(
+        .remove(
+            MetricType.GAUGE,
             Metric.THRIFT_CONNECTIONS.toString(),
-            MetricLevel.CORE,
             Tag.NAME.toString(),
-            "MPPDataExchange")
-        .decr(1L);
+            "MPPDataExchange");
   }
 
   @Override
-  public void processContext(
-      ServerContext serverContext, TTransport tTransport, TTransport tTransport1) {}
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    MppDataExchangeServiceThriftHandlerMetrics that =
+        (MppDataExchangeServiceThriftHandlerMetrics) o;
+    return Objects.equals(thriftConnectionNumber, that.thriftConnectionNumber);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(thriftConnectionNumber);
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/query/pool/RawQueryReadTaskPoolManager.java b/server/src/main/java/org/apache/iotdb/db/query/pool/RawQueryReadTaskPoolManager.java
index f2fbf7c92b..92cae2641c 100644
--- a/server/src/main/java/org/apache/iotdb/db/query/pool/RawQueryReadTaskPoolManager.java
+++ b/server/src/main/java/org/apache/iotdb/db/query/pool/RawQueryReadTaskPoolManager.java
@@ -24,9 +24,6 @@ import org.apache.iotdb.commons.concurrent.ThreadName;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
 import org.apache.iotdb.db.rescon.AbstractPoolManager;
 import org.apache.iotdb.db.service.metrics.MetricService;
-import org.apache.iotdb.db.service.metrics.enums.Metric;
-import org.apache.iotdb.db.service.metrics.enums.Tag;
-import org.apache.iotdb.metrics.utils.MetricLevel;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -50,26 +47,15 @@ public class RawQueryReadTaskPoolManager extends AbstractPoolManager {
     pool =
         IoTDBThreadPoolFactory.newFixedThreadPool(
             threadCnt, ThreadName.SUB_RAW_QUERY_SERVICE.getName());
-    MetricService.getInstance()
-        .getOrCreateAutoGauge(
-            Metric.QUEUE.toString(),
-            MetricLevel.IMPORTANT,
-            pool,
-            p -> ((ThreadPoolExecutor) p).getActiveCount(),
-            Tag.NAME.toString(),
-            ThreadName.SUB_RAW_QUERY_SERVICE.getName(),
-            Tag.STATUS.toString(),
-            "running");
-    MetricService.getInstance()
-        .getOrCreateAutoGauge(
-            Metric.QUEUE.toString(),
-            MetricLevel.IMPORTANT,
-            pool,
-            p -> ((ThreadPoolExecutor) p).getQueue().size(),
-            Tag.NAME.toString(),
-            ThreadName.SUB_RAW_QUERY_SERVICE.getName(),
-            Tag.STATUS.toString(),
-            "waiting");
+    MetricService.getInstance().addMetricSet(new RawQueryReadTaskPoolManagerMetrics(this));
+  }
+
+  public long getActiveCount() {
+    return ((ThreadPoolExecutor) pool).getActiveCount();
+  }
+
+  public long getWaitingCount() {
+    return ((ThreadPoolExecutor) pool).getQueue().size();
   }
 
   public static RawQueryReadTaskPoolManager getInstance() {
diff --git a/server/src/main/java/org/apache/iotdb/db/query/pool/RawQueryReadTaskPoolManagerMetrics.java b/server/src/main/java/org/apache/iotdb/db/query/pool/RawQueryReadTaskPoolManagerMetrics.java
new file mode 100644
index 0000000000..db51dfbd6e
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/query/pool/RawQueryReadTaskPoolManagerMetrics.java
@@ -0,0 +1,92 @@
+/*
+ * 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.query.pool;
+
+import org.apache.iotdb.commons.concurrent.ThreadName;
+import org.apache.iotdb.db.service.metrics.enums.Metric;
+import org.apache.iotdb.db.service.metrics.enums.Tag;
+import org.apache.iotdb.metrics.AbstractMetricService;
+import org.apache.iotdb.metrics.metricsets.IMetricSet;
+import org.apache.iotdb.metrics.utils.MetricLevel;
+import org.apache.iotdb.metrics.utils.MetricType;
+
+import java.util.Objects;
+
+public class RawQueryReadTaskPoolManagerMetrics implements IMetricSet {
+  private RawQueryReadTaskPoolManager rawQueryReadTaskPoolManager;
+
+  public RawQueryReadTaskPoolManagerMetrics(
+      RawQueryReadTaskPoolManager rawQueryReadTaskPoolManager) {
+    this.rawQueryReadTaskPoolManager = rawQueryReadTaskPoolManager;
+  }
+
+  @Override
+  public void bindTo(AbstractMetricService metricService) {
+    metricService.getOrCreateAutoGauge(
+        Metric.QUEUE.toString(),
+        MetricLevel.IMPORTANT,
+        rawQueryReadTaskPoolManager,
+        RawQueryReadTaskPoolManager::getActiveCount,
+        Tag.NAME.toString(),
+        ThreadName.SUB_RAW_QUERY_SERVICE.getName(),
+        Tag.STATUS.toString(),
+        "running");
+    metricService.getOrCreateAutoGauge(
+        Metric.QUEUE.toString(),
+        MetricLevel.IMPORTANT,
+        rawQueryReadTaskPoolManager,
+        RawQueryReadTaskPoolManager::getWaitingCount,
+        Tag.NAME.toString(),
+        ThreadName.SUB_RAW_QUERY_SERVICE.getName(),
+        Tag.STATUS.toString(),
+        "waiting");
+  }
+
+  @Override
+  public void unbindFrom(AbstractMetricService metricService) {
+    metricService.remove(
+        MetricType.GAUGE,
+        Metric.QUEUE.toString(),
+        Tag.NAME.toString(),
+        ThreadName.SUB_RAW_QUERY_SERVICE.getName(),
+        Tag.STATUS.toString(),
+        "running");
+    metricService.remove(
+        MetricType.GAUGE,
+        Metric.QUEUE.toString(),
+        Tag.NAME.toString(),
+        ThreadName.SUB_RAW_QUERY_SERVICE.getName(),
+        Tag.STATUS.toString(),
+        "waiting");
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    RawQueryReadTaskPoolManagerMetrics that = (RawQueryReadTaskPoolManagerMetrics) o;
+    return Objects.equals(rawQueryReadTaskPoolManager, that.rawQueryReadTaskPoolManager);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(rawQueryReadTaskPoolManager);
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/service/DataNode.java b/server/src/main/java/org/apache/iotdb/db/service/DataNode.java
index 9f62ce7cdc..390bc52045 100644
--- a/server/src/main/java/org/apache/iotdb/db/service/DataNode.java
+++ b/server/src/main/java/org/apache/iotdb/db/service/DataNode.java
@@ -266,9 +266,6 @@ public class DataNode implements DataNodeMBean {
     setUncaughtExceptionHandler();
     initServiceProvider();
 
-    // init metric service
-    registerManager.register(MetricService.getInstance());
-
     logger.info("recover the schema...");
     initSchemaEngine();
     registerManager.register(new JMXService());
@@ -312,11 +309,10 @@ public class DataNode implements DataNodeMBean {
     registerManager.register(TriggerRegistrationService.getInstance());
     registerManager.register(ContinuousQueryService.getInstance());
 
-    // start reporter
-    MetricService.getInstance().startAllReporter();
-
     // start region migrate service
     registerManager.register(RegionMigrateService.getInstance());
+
+    registerManager.register(MetricService.getInstance());
   }
 
   /** set up RPC and protocols after DataNode is available */
diff --git a/server/src/main/java/org/apache/iotdb/db/service/DataNodeInternalRPCService.java b/server/src/main/java/org/apache/iotdb/db/service/DataNodeInternalRPCService.java
index 1aed9d999e..78d1761271 100644
--- a/server/src/main/java/org/apache/iotdb/db/service/DataNodeInternalRPCService.java
+++ b/server/src/main/java/org/apache/iotdb/db/service/DataNodeInternalRPCService.java
@@ -21,18 +21,14 @@ package org.apache.iotdb.db.service;
 
 import org.apache.iotdb.commons.concurrent.ThreadName;
 import org.apache.iotdb.commons.exception.runtime.RPCServiceException;
-import org.apache.iotdb.commons.service.AbstractThriftServiceThread;
 import org.apache.iotdb.commons.service.ServiceType;
 import org.apache.iotdb.commons.service.ThriftService;
 import org.apache.iotdb.commons.service.ThriftServiceThread;
 import org.apache.iotdb.db.conf.IoTDBConfig;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
 import org.apache.iotdb.db.service.metrics.MetricService;
-import org.apache.iotdb.db.service.metrics.enums.Metric;
-import org.apache.iotdb.db.service.metrics.enums.Tag;
 import org.apache.iotdb.db.service.thrift.handler.InternalServiceThriftHandler;
 import org.apache.iotdb.db.service.thrift.impl.DataNodeInternalRPCServiceImpl;
-import org.apache.iotdb.metrics.utils.MetricLevel;
 import org.apache.iotdb.mpp.rpc.thrift.IDataNodeRPCService.Processor;
 
 public class DataNodeInternalRPCService extends ThriftService
@@ -77,13 +73,7 @@ public class DataNodeInternalRPCService extends ThriftService
     }
     thriftServiceThread.setName(ThreadName.DATANODE_INTERNAL_RPC_SERVICE.getName());
     MetricService.getInstance()
-        .getOrCreateAutoGauge(
-            Metric.THRIFT_ACTIVE_THREADS.toString(),
-            MetricLevel.CORE,
-            thriftServiceThread,
-            AbstractThriftServiceThread::getActiveThreadCount,
-            Tag.NAME.toString(),
-            ThreadName.DATANODE_INTERNAL_RPC_SERVICE.getName());
+        .addMetricSet(new DataNodeInternalRPCServiceMetrics(thriftServiceThread));
   }
 
   @Override
diff --git a/server/src/main/java/org/apache/iotdb/db/service/DataNodeInternalRPCServiceMetrics.java b/server/src/main/java/org/apache/iotdb/db/service/DataNodeInternalRPCServiceMetrics.java
new file mode 100644
index 0000000000..a6727223e5
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/service/DataNodeInternalRPCServiceMetrics.java
@@ -0,0 +1,72 @@
+/*
+ * 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.service;
+
+import org.apache.iotdb.commons.concurrent.ThreadName;
+import org.apache.iotdb.commons.service.AbstractThriftServiceThread;
+import org.apache.iotdb.db.service.metrics.enums.Metric;
+import org.apache.iotdb.db.service.metrics.enums.Tag;
+import org.apache.iotdb.metrics.AbstractMetricService;
+import org.apache.iotdb.metrics.metricsets.IMetricSet;
+import org.apache.iotdb.metrics.utils.MetricLevel;
+import org.apache.iotdb.metrics.utils.MetricType;
+
+import java.util.Objects;
+
+public class DataNodeInternalRPCServiceMetrics implements IMetricSet {
+  AbstractThriftServiceThread thriftServiceThread;
+
+  public DataNodeInternalRPCServiceMetrics(AbstractThriftServiceThread thriftServiceThread) {
+    this.thriftServiceThread = thriftServiceThread;
+  }
+
+  @Override
+  public void bindTo(AbstractMetricService metricService) {
+    metricService.getOrCreateAutoGauge(
+        Metric.THRIFT_ACTIVE_THREADS.toString(),
+        MetricLevel.CORE,
+        thriftServiceThread,
+        AbstractThriftServiceThread::getActiveThreadCount,
+        Tag.NAME.toString(),
+        ThreadName.DATANODE_INTERNAL_RPC_SERVICE.getName());
+  }
+
+  @Override
+  public void unbindFrom(AbstractMetricService metricService) {
+    metricService.remove(
+        MetricType.GAUGE,
+        Metric.THRIFT_ACTIVE_THREADS.toString(),
+        Tag.NAME.toString(),
+        ThreadName.DATANODE_INTERNAL_RPC_SERVICE.getName());
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    DataNodeInternalRPCServiceMetrics that = (DataNodeInternalRPCServiceMetrics) o;
+    return Objects.equals(thriftServiceThread, that.thriftServiceThread);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(thriftServiceThread);
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/service/IoTDB.java b/server/src/main/java/org/apache/iotdb/db/service/IoTDB.java
index 7850de4b43..3a15ac2a6e 100644
--- a/server/src/main/java/org/apache/iotdb/db/service/IoTDB.java
+++ b/server/src/main/java/org/apache/iotdb/db/service/IoTDB.java
@@ -141,7 +141,6 @@ public class IoTDB implements IoTDBMBean {
     setUncaughtExceptionHandler();
     initServiceProvider();
 
-    registerManager.register(MetricService.getInstance());
     logger.info("recover the schema...");
     initConfigManager();
     registerManager.register(new JMXService());
@@ -195,9 +194,7 @@ public class IoTDB implements IoTDBMBean {
     registerManager.register(SettleService.getINSTANCE());
     registerManager.register(TriggerRegistrationService.getInstance());
     registerManager.register(ContinuousQueryService.getInstance());
-
-    // start reporter
-    MetricService.getInstance().startAllReporter();
+    registerManager.register(MetricService.getInstance());
 
     logger.info("IoTDB configuration: " + config.getConfigMessage());
     logger.info("Congratulation, IoTDB is set up successfully. Now, enjoy yourself!");
diff --git a/server/src/main/java/org/apache/iotdb/db/service/NewIoTDB.java b/server/src/main/java/org/apache/iotdb/db/service/NewIoTDB.java
index 4cf7ac0a0b..1aff419e56 100644
--- a/server/src/main/java/org/apache/iotdb/db/service/NewIoTDB.java
+++ b/server/src/main/java/org/apache/iotdb/db/service/NewIoTDB.java
@@ -125,7 +125,6 @@ public class NewIoTDB implements NewIoTDBMBean {
         .getConfig()
         .setRpcImplClassName(ClientRPCServiceImpl.class.getName());
 
-    registerManager.register(MetricService.getInstance());
     logger.info("recover the schema...");
     initConfigManager();
     registerManager.register(new JMXService());
@@ -178,9 +177,7 @@ public class NewIoTDB implements NewIoTDBMBean {
     }
     registerManager.register(TriggerRegistrationService.getInstance());
     registerManager.register(ContinuousQueryService.getInstance());
-
-    // start reporter
-    MetricService.getInstance().startAllReporter();
+    registerManager.register(MetricService.getInstance());
 
     logger.info("IoTDB configuration: " + config.getConfigMessage());
     logger.info("Congratulation, IoTDB is set up successfully. Now, enjoy yourself!");
diff --git a/server/src/main/java/org/apache/iotdb/db/service/RPCService.java b/server/src/main/java/org/apache/iotdb/db/service/RPCService.java
index 47b77d0606..05e68f16bd 100644
--- a/server/src/main/java/org/apache/iotdb/db/service/RPCService.java
+++ b/server/src/main/java/org/apache/iotdb/db/service/RPCService.java
@@ -20,20 +20,16 @@ package org.apache.iotdb.db.service;
 
 import org.apache.iotdb.commons.concurrent.ThreadName;
 import org.apache.iotdb.commons.exception.runtime.RPCServiceException;
-import org.apache.iotdb.commons.service.AbstractThriftServiceThread;
 import org.apache.iotdb.commons.service.ServiceType;
 import org.apache.iotdb.commons.service.ThriftService;
 import org.apache.iotdb.commons.service.ThriftServiceThread;
 import org.apache.iotdb.db.conf.IoTDBConfig;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
 import org.apache.iotdb.db.service.metrics.MetricService;
-import org.apache.iotdb.db.service.metrics.enums.Metric;
-import org.apache.iotdb.db.service.metrics.enums.Tag;
 import org.apache.iotdb.db.service.thrift.ProcessorWithMetrics;
 import org.apache.iotdb.db.service.thrift.handler.RPCServiceThriftHandler;
 import org.apache.iotdb.db.service.thrift.impl.IClientRPCServiceWithHandler;
 import org.apache.iotdb.metrics.config.MetricConfigDescriptor;
-import org.apache.iotdb.metrics.utils.MetricLevel;
 import org.apache.iotdb.service.rpc.thrift.IClientRPCService.Processor;
 
 import java.lang.reflect.InvocationTargetException;
@@ -83,14 +79,7 @@ public class RPCService extends ThriftService implements RPCServiceMBean {
       throw new IllegalAccessException(e.getMessage());
     }
     thriftServiceThread.setName(ThreadName.CLIENT_RPC_SERVICE.getName());
-    MetricService.getInstance()
-        .getOrCreateAutoGauge(
-            Metric.THRIFT_ACTIVE_THREADS.toString(),
-            MetricLevel.CORE,
-            thriftServiceThread,
-            AbstractThriftServiceThread::getActiveThreadCount,
-            Tag.NAME.toString(),
-            ThreadName.CLIENT_RPC_SERVICE.getName());
+    MetricService.getInstance().addMetricSet(new RPCServiceMetrics(thriftServiceThread));
   }
 
   @Override
diff --git a/server/src/main/java/org/apache/iotdb/db/service/RPCServiceMetrics.java b/server/src/main/java/org/apache/iotdb/db/service/RPCServiceMetrics.java
new file mode 100644
index 0000000000..ef9435a1bf
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/service/RPCServiceMetrics.java
@@ -0,0 +1,71 @@
+/*
+ * 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.service;
+
+import org.apache.iotdb.commons.concurrent.ThreadName;
+import org.apache.iotdb.commons.service.AbstractThriftServiceThread;
+import org.apache.iotdb.db.service.metrics.enums.Metric;
+import org.apache.iotdb.db.service.metrics.enums.Tag;
+import org.apache.iotdb.metrics.AbstractMetricService;
+import org.apache.iotdb.metrics.metricsets.IMetricSet;
+import org.apache.iotdb.metrics.utils.MetricLevel;
+import org.apache.iotdb.metrics.utils.MetricType;
+
+import java.util.Objects;
+
+public class RPCServiceMetrics implements IMetricSet {
+  private AbstractThriftServiceThread thriftServiceThread;
+
+  public RPCServiceMetrics(AbstractThriftServiceThread thriftServiceThread) {
+    this.thriftServiceThread = thriftServiceThread;
+  }
+
+  @Override
+  public void bindTo(AbstractMetricService metricService) {
+    metricService.getOrCreateAutoGauge(
+        Metric.THRIFT_ACTIVE_THREADS.toString(),
+        MetricLevel.CORE,
+        thriftServiceThread,
+        AbstractThriftServiceThread::getActiveThreadCount,
+        Tag.NAME.toString(),
+        ThreadName.CLIENT_RPC_SERVICE.getName());
+  }
+
+  @Override
+  public void unbindFrom(AbstractMetricService metricService) {
+    metricService.remove(
+        MetricType.GAUGE,
+        Metric.THRIFT_ACTIVE_THREADS.toString(),
+        Tag.NAME.toString(),
+        ThreadName.CLIENT_RPC_SERVICE.getName());
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    RPCServiceMetrics that = (RPCServiceMetrics) o;
+    return Objects.equals(thriftServiceThread, that.thriftServiceThread);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(thriftServiceThread);
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/service/metrics/MetricService.java b/server/src/main/java/org/apache/iotdb/db/service/metrics/MetricService.java
index 75dd84a77d..1a998004fd 100644
--- a/server/src/main/java/org/apache/iotdb/db/service/metrics/MetricService.java
+++ b/server/src/main/java/org/apache/iotdb/db/service/metrics/MetricService.java
@@ -29,10 +29,10 @@ import org.apache.iotdb.db.service.metrics.predefined.ProcessMetrics;
 import org.apache.iotdb.db.service.metrics.predefined.SystemMetrics;
 import org.apache.iotdb.metrics.AbstractMetricService;
 import org.apache.iotdb.metrics.config.ReloadLevel;
-import org.apache.iotdb.metrics.predefined.IMetricSet;
-import org.apache.iotdb.metrics.predefined.PredefinedMetric;
-import org.apache.iotdb.metrics.predefined.jvm.JvmMetrics;
-import org.apache.iotdb.metrics.predefined.logback.LogbackMetrics;
+import org.apache.iotdb.metrics.metricsets.IMetricSet;
+import org.apache.iotdb.metrics.metricsets.predefined.PredefinedMetric;
+import org.apache.iotdb.metrics.metricsets.predefined.jvm.JvmMetrics;
+import org.apache.iotdb.metrics.metricsets.predefined.logback.LogbackMetrics;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -60,6 +60,12 @@ public class MetricService extends AbstractMetricService implements MetricServic
     }
   }
 
+  public void restart() {
+    logger.info("Restart metric Service.");
+    restartService();
+    logger.info("Finish restart metric Service");
+  }
+
   @Override
   public void stop() {
     if (isEnable()) {
@@ -70,12 +76,6 @@ public class MetricService extends AbstractMetricService implements MetricServic
     }
   }
 
-  @Override
-  public void restartService() throws StartupException {
-    stopService();
-    startService();
-  }
-
   @Override
   public void enablePredefinedMetrics(PredefinedMetric metric) {
     IMetricSet metricSet;
@@ -99,9 +99,7 @@ public class MetricService extends AbstractMetricService implements MetricServic
         logger.error("Unknown predefined metrics: {}", metric);
         return;
     }
-    metricSet.bindTo(metricManager);
-    metricSet.startAsyncCollectedMetrics();
-    metricSets.add(metricSet);
+    metricSet.bindTo(this);
   }
 
   @Override
@@ -119,14 +117,13 @@ public class MetricService extends AbstractMetricService implements MetricServic
             isEnableMetric = false;
             break;
           case RESTART_METRIC:
-            stop();
             isEnableMetric = true;
-            start();
+            restart();
             break;
           case RESTART_REPORTER:
-            compositeReporter.stopAll();
+            stopAllReporter();
             loadReporter();
-            compositeReporter.startAll();
+            startAllReporter();
             logger.info("Finish restart metric reporters.");
             break;
           case NOTHING:
diff --git a/server/src/main/java/org/apache/iotdb/db/service/metrics/predefined/FileMetrics.java b/server/src/main/java/org/apache/iotdb/db/service/metrics/predefined/FileMetrics.java
index d92eab30c3..b2a6c1faa0 100644
--- a/server/src/main/java/org/apache/iotdb/db/service/metrics/predefined/FileMetrics.java
+++ b/server/src/main/java/org/apache/iotdb/db/service/metrics/predefined/FileMetrics.java
@@ -26,11 +26,11 @@ import org.apache.iotdb.db.conf.IoTDBDescriptor;
 import org.apache.iotdb.db.service.metrics.enums.Metric;
 import org.apache.iotdb.db.service.metrics.enums.Tag;
 import org.apache.iotdb.db.wal.WALManager;
-import org.apache.iotdb.metrics.AbstractMetricManager;
+import org.apache.iotdb.metrics.AbstractMetricService;
 import org.apache.iotdb.metrics.config.MetricConfigDescriptor;
-import org.apache.iotdb.metrics.predefined.IMetricSet;
-import org.apache.iotdb.metrics.predefined.PredefinedMetric;
+import org.apache.iotdb.metrics.metricsets.IMetricSet;
 import org.apache.iotdb.metrics.utils.MetricLevel;
+import org.apache.iotdb.metrics.utils.MetricType;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -38,14 +38,14 @@ import org.slf4j.LoggerFactory;
 import java.io.File;
 import java.io.UncheckedIOException;
 import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Stream;
 
 public class FileMetrics implements IMetricSet {
   private static final Logger logger = LoggerFactory.getLogger(FileMetrics.class);
-  private final String[] walDirs = IoTDBDescriptor.getInstance().getConfig().getWalDirs();
-  private final String[] dataDirs = IoTDBDescriptor.getInstance().getConfig().getDataDirs();
+  private Future<?> currentServiceFuture;
   private final ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
   private long walFileTotalSize = 0L;
   private long walFileTotalCount = 0L;
@@ -55,69 +55,82 @@ public class FileMetrics implements IMetricSet {
   private long unsequenceFileTotalCount = 0L;
 
   @Override
-  public void bindTo(AbstractMetricManager metricManager) {
-    metricManager.getOrCreateAutoGauge(
+  public void bindTo(AbstractMetricService metricService) {
+    metricService.getOrCreateAutoGauge(
         Metric.FILE_SIZE.toString(),
         MetricLevel.IMPORTANT,
         this,
         FileMetrics::getWalFileTotalSize,
         Tag.NAME.toString(),
         "wal");
-    metricManager.getOrCreateAutoGauge(
+    metricService.getOrCreateAutoGauge(
         Metric.FILE_SIZE.toString(),
         MetricLevel.IMPORTANT,
         this,
         FileMetrics::getSequenceFileTotalSize,
         Tag.NAME.toString(),
         "seq");
-    metricManager.getOrCreateAutoGauge(
+    metricService.getOrCreateAutoGauge(
         Metric.FILE_SIZE.toString(),
         MetricLevel.IMPORTANT,
         this,
         FileMetrics::getUnsequenceFileTotalSize,
         Tag.NAME.toString(),
         "unseq");
-    metricManager.getOrCreateAutoGauge(
+    metricService.getOrCreateAutoGauge(
         Metric.FILE_COUNT.toString(),
         MetricLevel.IMPORTANT,
         this,
         FileMetrics::getWalFileTotalCount,
         Tag.NAME.toString(),
         "wal");
-    metricManager.getOrCreateAutoGauge(
+    metricService.getOrCreateAutoGauge(
         Metric.FILE_COUNT.toString(),
         MetricLevel.IMPORTANT,
         this,
         FileMetrics::getSequenceFileTotalCount,
         Tag.NAME.toString(),
         "seq");
-    metricManager.getOrCreateAutoGauge(
+    metricService.getOrCreateAutoGauge(
         Metric.FILE_COUNT.toString(),
         MetricLevel.IMPORTANT,
         this,
         FileMetrics::getUnsequenceFileTotalCount,
         Tag.NAME.toString(),
         "unseq");
-  }
 
-  @Override
-  public PredefinedMetric getType() {
-    return PredefinedMetric.FILE;
-  }
-
-  @Override
-  public void startAsyncCollectedMetrics() {
-    ScheduledExecutorUtil.safelyScheduleAtFixedRate(
-        service,
-        this::collect,
-        1,
-        MetricConfigDescriptor.getInstance().getMetricConfig().getAsyncCollectPeriodInSecond(),
-        TimeUnit.SECONDS);
+    // finally start to update the value of some metrics in async way
+    if (metricService.isEnable() && null != currentServiceFuture) {
+      currentServiceFuture =
+          ScheduledExecutorUtil.safelyScheduleAtFixedRate(
+              service,
+              this::collect,
+              1,
+              MetricConfigDescriptor.getInstance()
+                  .getMetricConfig()
+                  .getAsyncCollectPeriodInSecond(),
+              TimeUnit.SECONDS);
+    }
   }
 
   @Override
-  public void stopAsyncCollectedMetrics() {
-    service.shutdown();
+  public void unbindFrom(AbstractMetricService metricService) {
+    // first stop to update the value of some metrics in async way
+    if (currentServiceFuture != null) {
+      currentServiceFuture.cancel(false);
+      currentServiceFuture = null;
+    }
+
+    metricService.remove(MetricType.GAUGE, Metric.FILE_SIZE.toString(), Tag.NAME.toString(), "wal");
+    metricService.remove(MetricType.GAUGE, Metric.FILE_SIZE.toString(), Tag.NAME.toString(), "seq");
+    metricService.remove(
+        MetricType.GAUGE, Metric.FILE_SIZE.toString(), Tag.NAME.toString(), "unseq");
+    metricService.remove(
+        MetricType.GAUGE, Metric.FILE_COUNT.toString(), Tag.NAME.toString(), "wal");
+    metricService.remove(
+        MetricType.GAUGE, Metric.FILE_COUNT.toString(), Tag.NAME.toString(), "seq");
+    metricService.remove(
+        MetricType.GAUGE, Metric.FILE_COUNT.toString(), Tag.NAME.toString(), "unseq");
   }
 
   private void collect() {
diff --git a/server/src/main/java/org/apache/iotdb/db/service/metrics/predefined/ProcessMetrics.java b/server/src/main/java/org/apache/iotdb/db/service/metrics/predefined/ProcessMetrics.java
index 2ab82ee686..1397523d53 100644
--- a/server/src/main/java/org/apache/iotdb/db/service/metrics/predefined/ProcessMetrics.java
+++ b/server/src/main/java/org/apache/iotdb/db/service/metrics/predefined/ProcessMetrics.java
@@ -21,10 +21,10 @@ package org.apache.iotdb.db.service.metrics.predefined;
 
 import org.apache.iotdb.db.service.metrics.enums.Metric;
 import org.apache.iotdb.db.service.metrics.enums.Tag;
-import org.apache.iotdb.metrics.AbstractMetricManager;
-import org.apache.iotdb.metrics.predefined.IMetricSet;
-import org.apache.iotdb.metrics.predefined.PredefinedMetric;
+import org.apache.iotdb.metrics.AbstractMetricService;
+import org.apache.iotdb.metrics.metricsets.IMetricSet;
 import org.apache.iotdb.metrics.utils.MetricLevel;
+import org.apache.iotdb.metrics.utils.MetricType;
 
 import com.sun.management.OperatingSystemMXBean;
 
@@ -41,20 +41,23 @@ public class ProcessMetrics implements IMetricSet {
   }
 
   @Override
-  public void bindTo(AbstractMetricManager metricManager) {
-    collectProcessCPUInfo(metricManager);
-    collectProcessMemInfo(metricManager);
-    collectProcessStatusInfo(metricManager);
-    collectThreadInfo(metricManager);
+  public void bindTo(AbstractMetricService metricService) {
+    collectProcessCPUInfo(metricService);
+    collectProcessMemInfo(metricService);
+    collectProcessStatusInfo(metricService);
+    collectThreadInfo(metricService);
   }
 
   @Override
-  public PredefinedMetric getType() {
-    return PredefinedMetric.PROCESS;
+  public void unbindFrom(AbstractMetricService metricService) {
+    removeProcessCPUInfo(metricService);
+    removeProcessMemInfo(metricService);
+    removeProcessStatusInfo(metricService);
+    removeThreadInfo(metricService);
   }
 
-  private void collectProcessCPUInfo(AbstractMetricManager metricManager) {
-    metricManager.getOrCreateAutoGauge(
+  private void collectProcessCPUInfo(AbstractMetricService metricService) {
+    metricService.getOrCreateAutoGauge(
         Metric.PROCESS_CPU_LOAD.toString(),
         MetricLevel.CORE,
         sunOsMXBean,
@@ -62,7 +65,7 @@ public class ProcessMetrics implements IMetricSet {
         Tag.NAME.toString(),
         "process");
 
-    metricManager.getOrCreateAutoGauge(
+    metricService.getOrCreateAutoGauge(
         Metric.PROCESS_CPU_TIME.toString(),
         MetricLevel.CORE,
         sunOsMXBean,
@@ -71,37 +74,45 @@ public class ProcessMetrics implements IMetricSet {
         "process");
   }
 
-  private void collectProcessMemInfo(AbstractMetricManager metricManager) {
+  private void removeProcessCPUInfo(AbstractMetricService metricService) {
+    metricService.remove(
+        MetricType.GAUGE, Metric.PROCESS_CPU_LOAD.toString(), Tag.NAME.toString(), "process");
+
+    metricService.remove(
+        MetricType.GAUGE, Metric.PROCESS_CPU_TIME.toString(), Tag.NAME.toString(), "process");
+  }
+
+  private void collectProcessMemInfo(AbstractMetricService metricService) {
     Runtime runtime = Runtime.getRuntime();
-    metricManager.getOrCreateAutoGauge(
+    metricService.getOrCreateAutoGauge(
         Metric.PROCESS_MAX_MEM.toString(),
         MetricLevel.CORE,
         runtime,
         a -> runtime.maxMemory(),
         Tag.NAME.toString(),
         "process");
-    metricManager.getOrCreateAutoGauge(
+    metricService.getOrCreateAutoGauge(
         Metric.PROCESS_TOTAL_MEM.toString(),
         MetricLevel.CORE,
         runtime,
         a -> runtime.totalMemory(),
         Tag.NAME.toString(),
         "process");
-    metricManager.getOrCreateAutoGauge(
+    metricService.getOrCreateAutoGauge(
         Metric.PROCESS_FREE_MEM.toString(),
         MetricLevel.CORE,
         runtime,
         a -> runtime.freeMemory(),
         Tag.NAME.toString(),
         "process");
-    metricManager.getOrCreateAutoGauge(
+    metricService.getOrCreateAutoGauge(
         Metric.PROCESS_USED_MEM.toString(),
         MetricLevel.CORE,
         this,
         a -> getProcessUsedMemory(),
         Tag.NAME.toString(),
         "process");
-    metricManager.getOrCreateAutoGauge(
+    metricService.getOrCreateAutoGauge(
         Metric.PROCESS_MEM_RATIO.toString(),
         MetricLevel.CORE,
         this,
@@ -110,8 +121,21 @@ public class ProcessMetrics implements IMetricSet {
         "process");
   }
 
-  private void collectThreadInfo(AbstractMetricManager metricManager) {
-    metricManager.getOrCreateAutoGauge(
+  private void removeProcessMemInfo(AbstractMetricService metricService) {
+    metricService.remove(
+        MetricType.GAUGE, Metric.PROCESS_MAX_MEM.toString(), Tag.NAME.toString(), "process");
+    metricService.remove(
+        MetricType.GAUGE, Metric.PROCESS_TOTAL_MEM.toString(), Tag.NAME.toString(), "process");
+    metricService.remove(
+        MetricType.GAUGE, Metric.PROCESS_FREE_MEM.toString(), Tag.NAME.toString(), "process");
+    metricService.remove(
+        MetricType.GAUGE, Metric.PROCESS_USED_MEM.toString(), Tag.NAME.toString(), "process");
+    metricService.remove(
+        MetricType.GAUGE, Metric.PROCESS_MEM_RATIO.toString(), Tag.NAME.toString(), "process");
+  }
+
+  private void collectThreadInfo(AbstractMetricService metricService) {
+    metricService.getOrCreateAutoGauge(
         Metric.PROCESS_THREADS_COUNT.toString(),
         MetricLevel.CORE,
         this,
@@ -120,8 +144,13 @@ public class ProcessMetrics implements IMetricSet {
         "process");
   }
 
-  private void collectProcessStatusInfo(AbstractMetricManager metricManager) {
-    metricManager.getOrCreateAutoGauge(
+  private void removeThreadInfo(AbstractMetricService metricService) {
+    metricService.remove(
+        MetricType.GAUGE, Metric.PROCESS_THREADS_COUNT.toString(), Tag.NAME.toString(), "process");
+  }
+
+  private void collectProcessStatusInfo(AbstractMetricService metricService) {
+    metricService.getOrCreateAutoGauge(
         Metric.PROCESS_STATUS.toString(),
         MetricLevel.CORE,
         this,
@@ -130,6 +159,11 @@ public class ProcessMetrics implements IMetricSet {
         "process");
   }
 
+  private void removeProcessStatusInfo(AbstractMetricService metricService) {
+    metricService.remove(
+        MetricType.GAUGE, Metric.PROCESS_STATUS.toString(), Tag.NAME.toString(), "process");
+  }
+
   private long getProcessUsedMemory() {
     return runtime.totalMemory() - runtime.freeMemory();
   }
diff --git a/server/src/main/java/org/apache/iotdb/db/service/metrics/predefined/SystemMetrics.java b/server/src/main/java/org/apache/iotdb/db/service/metrics/predefined/SystemMetrics.java
index b4f52a2515..69f070d028 100644
--- a/server/src/main/java/org/apache/iotdb/db/service/metrics/predefined/SystemMetrics.java
+++ b/server/src/main/java/org/apache/iotdb/db/service/metrics/predefined/SystemMetrics.java
@@ -21,22 +21,24 @@ package org.apache.iotdb.db.service.metrics.predefined;
 import org.apache.iotdb.commons.concurrent.threadpool.ScheduledExecutorUtil;
 import org.apache.iotdb.db.service.metrics.enums.Metric;
 import org.apache.iotdb.db.service.metrics.enums.Tag;
-import org.apache.iotdb.metrics.AbstractMetricManager;
+import org.apache.iotdb.metrics.AbstractMetricService;
 import org.apache.iotdb.metrics.config.MetricConfigDescriptor;
-import org.apache.iotdb.metrics.predefined.IMetricSet;
-import org.apache.iotdb.metrics.predefined.PredefinedMetric;
+import org.apache.iotdb.metrics.metricsets.IMetricSet;
 import org.apache.iotdb.metrics.utils.MetricLevel;
+import org.apache.iotdb.metrics.utils.MetricType;
 
 import com.sun.management.OperatingSystemMXBean;
 
 import java.io.File;
 import java.lang.management.ManagementFactory;
 import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 
 public class SystemMetrics implements IMetricSet {
   private com.sun.management.OperatingSystemMXBean osMXBean;
+  private Future<?> currentServiceFuture;
   private final ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
   private long systemDiskTotalSpace = 0L;
   private long systemDiskFreeSpace = 0L;
@@ -46,19 +48,40 @@ public class SystemMetrics implements IMetricSet {
   }
 
   @Override
-  public void bindTo(AbstractMetricManager metricManager) {
-    collectSystemCpuInfo(metricManager);
-    collectSystemDiskInfo(metricManager);
-    collectSystemMEMInfo(metricManager);
+  public void bindTo(AbstractMetricService metricService) {
+    collectSystemCpuInfo(metricService);
+    collectSystemDiskInfo(metricService);
+    collectSystemMemInfo(metricService);
+
+    // finally start to update the value of some metrics in async way
+    if (metricService.isEnable() && null != currentServiceFuture) {
+      currentServiceFuture =
+          ScheduledExecutorUtil.safelyScheduleAtFixedRate(
+              service,
+              this::collect,
+              1,
+              MetricConfigDescriptor.getInstance()
+                  .getMetricConfig()
+                  .getAsyncCollectPeriodInSecond(),
+              TimeUnit.SECONDS);
+    }
   }
 
   @Override
-  public PredefinedMetric getType() {
-    return PredefinedMetric.SYSTEM;
+  public void unbindFrom(AbstractMetricService metricService) {
+    // first stop to update the value of some metrics in async way
+    if (currentServiceFuture != null) {
+      currentServiceFuture.cancel(false);
+      currentServiceFuture = null;
+    }
+
+    removeSystemCpuInfo(metricService);
+    removeSystemDiskInfo(metricService);
+    removeSystemMemInfo(metricService);
   }
 
-  private void collectSystemCpuInfo(AbstractMetricManager metricManager) {
-    metricManager.getOrCreateAutoGauge(
+  private void collectSystemCpuInfo(AbstractMetricService metricService) {
+    metricService.getOrCreateAutoGauge(
         Metric.SYS_CPU_LOAD.toString(),
         MetricLevel.CORE,
         osMXBean,
@@ -66,42 +89,50 @@ public class SystemMetrics implements IMetricSet {
         Tag.NAME.toString(),
         "system");
 
-    metricManager
+    metricService
         .getOrCreateGauge(
             Metric.SYS_CPU_CORES.toString(), MetricLevel.IMPORTANT, Tag.NAME.toString(), "system")
         .set(osMXBean.getAvailableProcessors());
   }
 
-  private void collectSystemMEMInfo(AbstractMetricManager metricManager) {
-    metricManager
+  private void removeSystemCpuInfo(AbstractMetricService metricService) {
+    metricService.remove(
+        MetricType.GAUGE, Metric.SYS_CPU_LOAD.toString(), Tag.NAME.toString(), "system");
+
+    metricService.remove(
+        MetricType.GAUGE, Metric.SYS_CPU_CORES.toString(), Tag.NAME.toString(), "system");
+  }
+
+  private void collectSystemMemInfo(AbstractMetricService metricService) {
+    metricService
         .getOrCreateGauge(
             Metric.SYS_TOTAL_PHYSICAL_MEMORY_SIZE.toString(),
             MetricLevel.CORE,
             Tag.NAME.toString(),
             "system")
         .set(osMXBean.getTotalPhysicalMemorySize());
-    metricManager.getOrCreateAutoGauge(
+    metricService.getOrCreateAutoGauge(
         Metric.SYS_FREE_PHYSICAL_MEMORY_SIZE.toString(),
         MetricLevel.CORE,
         osMXBean,
         a -> osMXBean.getFreePhysicalMemorySize(),
         Tag.NAME.toString(),
         "system");
-    metricManager.getOrCreateAutoGauge(
+    metricService.getOrCreateAutoGauge(
         Metric.SYS_TOTAL_SWAP_SPACE_SIZE.toString(),
         MetricLevel.CORE,
         osMXBean,
         a -> osMXBean.getTotalSwapSpaceSize(),
         Tag.NAME.toString(),
         "system");
-    metricManager.getOrCreateAutoGauge(
+    metricService.getOrCreateAutoGauge(
         Metric.SYS_FREE_SWAP_SPACE_SIZE.toString(),
         MetricLevel.CORE,
         osMXBean,
         a -> osMXBean.getFreeSwapSpaceSize(),
         Tag.NAME.toString(),
         "system");
-    metricManager.getOrCreateAutoGauge(
+    metricService.getOrCreateAutoGauge(
         Metric.SYS_COMMITTED_VM_SIZE.toString(),
         MetricLevel.CORE,
         osMXBean,
@@ -110,15 +141,40 @@ public class SystemMetrics implements IMetricSet {
         "system");
   }
 
-  private void collectSystemDiskInfo(AbstractMetricManager metricManager) {
-    metricManager.getOrCreateAutoGauge(
+  private void removeSystemMemInfo(AbstractMetricService metricService) {
+    metricService.remove(
+        MetricType.GAUGE,
+        Metric.SYS_TOTAL_PHYSICAL_MEMORY_SIZE.toString(),
+        Tag.NAME.toString(),
+        "system");
+    metricService.remove(
+        MetricType.GAUGE,
+        Metric.SYS_FREE_PHYSICAL_MEMORY_SIZE.toString(),
+        Tag.NAME.toString(),
+        "system");
+    metricService.remove(
+        MetricType.GAUGE,
+        Metric.SYS_TOTAL_SWAP_SPACE_SIZE.toString(),
+        Tag.NAME.toString(),
+        "system");
+    metricService.remove(
+        MetricType.GAUGE,
+        Metric.SYS_FREE_SWAP_SPACE_SIZE.toString(),
+        Tag.NAME.toString(),
+        "system");
+    metricService.remove(
+        MetricType.GAUGE, Metric.SYS_COMMITTED_VM_SIZE.toString(), Tag.NAME.toString(), "system");
+  }
+
+  private void collectSystemDiskInfo(AbstractMetricService metricService) {
+    metricService.getOrCreateAutoGauge(
         Metric.SYS_DISK_TOTAL_SPACE.toString(),
         MetricLevel.CORE,
         this,
         SystemMetrics::getSystemDiskTotalSpace,
         Tag.NAME.toString(),
         "system");
-    metricManager.getOrCreateAutoGauge(
+    metricService.getOrCreateAutoGauge(
         Metric.SYS_DISK_FREE_SPACE.toString(),
         MetricLevel.CORE,
         this,
@@ -127,19 +183,11 @@ public class SystemMetrics implements IMetricSet {
         "system");
   }
 
-  @Override
-  public void startAsyncCollectedMetrics() {
-    ScheduledExecutorUtil.safelyScheduleAtFixedRate(
-        service,
-        this::collect,
-        1,
-        MetricConfigDescriptor.getInstance().getMetricConfig().getAsyncCollectPeriodInSecond(),
-        TimeUnit.SECONDS);
-  }
-
-  @Override
-  public void stopAsyncCollectedMetrics() {
-    service.shutdown();
+  private void removeSystemDiskInfo(AbstractMetricService metricService) {
+    metricService.remove(
+        MetricType.GAUGE, Metric.SYS_DISK_TOTAL_SPACE.toString(), Tag.NAME.toString(), "system");
+    metricService.remove(
+        MetricType.GAUGE, Metric.SYS_DISK_FREE_SPACE.toString(), Tag.NAME.toString(), "system");
   }
 
   private void collect() {
diff --git a/server/src/main/java/org/apache/iotdb/db/service/thrift/handler/InternalServiceThriftHandler.java b/server/src/main/java/org/apache/iotdb/db/service/thrift/handler/InternalServiceThriftHandler.java
index 65ac4d0f0b..d0e72ed39e 100644
--- a/server/src/main/java/org/apache/iotdb/db/service/thrift/handler/InternalServiceThriftHandler.java
+++ b/server/src/main/java/org/apache/iotdb/db/service/thrift/handler/InternalServiceThriftHandler.java
@@ -20,36 +20,36 @@
 package org.apache.iotdb.db.service.thrift.handler;
 
 import org.apache.iotdb.db.service.metrics.MetricService;
-import org.apache.iotdb.db.service.metrics.enums.Metric;
-import org.apache.iotdb.db.service.metrics.enums.Tag;
-import org.apache.iotdb.metrics.utils.MetricLevel;
 
 import org.apache.thrift.protocol.TProtocol;
 import org.apache.thrift.server.ServerContext;
 import org.apache.thrift.server.TServerEventHandler;
 import org.apache.thrift.transport.TTransport;
 
+import java.util.concurrent.atomic.AtomicLong;
+
 public class InternalServiceThriftHandler implements TServerEventHandler {
 
+  private AtomicLong thriftConnectionNumber = new AtomicLong(0);
+
+  public InternalServiceThriftHandler() {
+    MetricService.getInstance()
+        .addMetricSet(new InternalServiceThriftHandlerMetrics(thriftConnectionNumber));
+  }
+
   @Override
   public void preServe() {}
 
   @Override
   public ServerContext createContext(TProtocol tProtocol, TProtocol tProtocol1) {
-    MetricService.getInstance()
-        .getOrCreateGauge(
-            Metric.THRIFT_CONNECTIONS.toString(), MetricLevel.CORE, Tag.NAME.toString(), "Internal")
-        .incr(1L);
+    thriftConnectionNumber.incrementAndGet();
     return null;
   }
 
   @Override
   public void deleteContext(
       ServerContext serverContext, TProtocol tProtocol, TProtocol tProtocol1) {
-    MetricService.getInstance()
-        .getOrCreateGauge(
-            Metric.THRIFT_CONNECTIONS.toString(), MetricLevel.CORE, Tag.NAME.toString(), "Internal")
-        .decr(1L);
+    thriftConnectionNumber.decrementAndGet();
   }
 
   @Override
diff --git a/server/src/main/java/org/apache/iotdb/db/service/thrift/handler/InternalServiceThriftHandlerMetrics.java b/server/src/main/java/org/apache/iotdb/db/service/thrift/handler/InternalServiceThriftHandlerMetrics.java
new file mode 100644
index 0000000000..c9eef53faf
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/service/thrift/handler/InternalServiceThriftHandlerMetrics.java
@@ -0,0 +1,68 @@
+/*
+ * 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.service.thrift.handler;
+
+import org.apache.iotdb.db.service.metrics.enums.Metric;
+import org.apache.iotdb.db.service.metrics.enums.Tag;
+import org.apache.iotdb.metrics.AbstractMetricService;
+import org.apache.iotdb.metrics.metricsets.IMetricSet;
+import org.apache.iotdb.metrics.utils.MetricLevel;
+import org.apache.iotdb.metrics.utils.MetricType;
+
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicLong;
+
+public class InternalServiceThriftHandlerMetrics implements IMetricSet {
+  private AtomicLong thriftConnectionNumber;
+
+  public InternalServiceThriftHandlerMetrics(AtomicLong thriftConnectionNumber) {
+    this.thriftConnectionNumber = thriftConnectionNumber;
+  }
+
+  @Override
+  public void bindTo(AbstractMetricService metricService) {
+    metricService.getOrCreateAutoGauge(
+        Metric.THRIFT_CONNECTIONS.toString(),
+        MetricLevel.CORE,
+        thriftConnectionNumber,
+        AtomicLong::get,
+        Tag.NAME.toString(),
+        "Internal");
+  }
+
+  @Override
+  public void unbindFrom(AbstractMetricService metricService) {
+    metricService.remove(
+        MetricType.GAUGE, Metric.THRIFT_CONNECTIONS.toString(), Tag.NAME.toString(), "Internal");
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    InternalServiceThriftHandlerMetrics that = (InternalServiceThriftHandlerMetrics) o;
+    return Objects.equals(thriftConnectionNumber, that.thriftConnectionNumber);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(thriftConnectionNumber);
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/service/thrift/handler/RPCServiceThriftHandler.java b/server/src/main/java/org/apache/iotdb/db/service/thrift/handler/RPCServiceThriftHandler.java
index 8938fe16d8..c03045591f 100644
--- a/server/src/main/java/org/apache/iotdb/db/service/thrift/handler/RPCServiceThriftHandler.java
+++ b/server/src/main/java/org/apache/iotdb/db/service/thrift/handler/RPCServiceThriftHandler.java
@@ -17,30 +17,29 @@
 package org.apache.iotdb.db.service.thrift.handler;
 
 import org.apache.iotdb.db.service.metrics.MetricService;
-import org.apache.iotdb.db.service.metrics.enums.Metric;
-import org.apache.iotdb.db.service.metrics.enums.Tag;
 import org.apache.iotdb.db.service.thrift.impl.IClientRPCServiceWithHandler;
-import org.apache.iotdb.metrics.utils.MetricLevel;
 
 import org.apache.thrift.protocol.TProtocol;
 import org.apache.thrift.server.ServerContext;
 import org.apache.thrift.server.TServerEventHandler;
 import org.apache.thrift.transport.TTransport;
 
+import java.util.concurrent.atomic.AtomicLong;
+
 public class RPCServiceThriftHandler implements TServerEventHandler {
 
+  private AtomicLong thriftConnectionNumber = new AtomicLong(0);
   private final IClientRPCServiceWithHandler eventHandler;
 
   public RPCServiceThriftHandler(IClientRPCServiceWithHandler eventHandler) {
     this.eventHandler = eventHandler;
+    MetricService.getInstance()
+        .addMetricSet(new RPCServiceThriftHandlerMetrics(thriftConnectionNumber));
   }
 
   @Override
   public ServerContext createContext(TProtocol arg0, TProtocol arg1) {
-    MetricService.getInstance()
-        .getOrCreateGauge(
-            Metric.THRIFT_CONNECTIONS.toString(), MetricLevel.CORE, Tag.NAME.toString(), "RPC")
-        .incr(1L);
+    thriftConnectionNumber.incrementAndGet();
     return null;
   }
 
@@ -48,11 +47,7 @@ public class RPCServiceThriftHandler implements TServerEventHandler {
   public void deleteContext(ServerContext arg0, TProtocol arg1, TProtocol arg2) {
     // release query resources.
     eventHandler.handleClientExit();
-
-    MetricService.getInstance()
-        .getOrCreateGauge(
-            Metric.THRIFT_CONNECTIONS.toString(), MetricLevel.CORE, Tag.NAME.toString(), "RPC")
-        .decr(1L);
+    thriftConnectionNumber.decrementAndGet();
   }
 
   @Override
diff --git a/server/src/main/java/org/apache/iotdb/db/service/thrift/handler/RPCServiceThriftHandlerMetrics.java b/server/src/main/java/org/apache/iotdb/db/service/thrift/handler/RPCServiceThriftHandlerMetrics.java
new file mode 100644
index 0000000000..863b8e6315
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/service/thrift/handler/RPCServiceThriftHandlerMetrics.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * <p>http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * <p>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.service.thrift.handler;
+
+import org.apache.iotdb.db.service.metrics.enums.Metric;
+import org.apache.iotdb.db.service.metrics.enums.Tag;
+import org.apache.iotdb.metrics.AbstractMetricService;
+import org.apache.iotdb.metrics.metricsets.IMetricSet;
+import org.apache.iotdb.metrics.utils.MetricLevel;
+import org.apache.iotdb.metrics.utils.MetricType;
+
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicLong;
+
+public class RPCServiceThriftHandlerMetrics implements IMetricSet {
+  private AtomicLong thriftConnectionNumber;
+
+  public RPCServiceThriftHandlerMetrics(AtomicLong thriftConnectionNumber) {
+    this.thriftConnectionNumber = thriftConnectionNumber;
+  }
+
+  @Override
+  public void bindTo(AbstractMetricService metricService) {
+    metricService.getOrCreateAutoGauge(
+        Metric.THRIFT_CONNECTIONS.toString(),
+        MetricLevel.CORE,
+        thriftConnectionNumber,
+        AtomicLong::get,
+        Tag.NAME.toString(),
+        "RPC");
+  }
+
+  @Override
+  public void unbindFrom(AbstractMetricService metricService) {
+    metricService.remove(
+        MetricType.GAUGE, Metric.THRIFT_CONNECTIONS.toString(), Tag.NAME.toString(), "RPC");
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    RPCServiceThriftHandlerMetrics that = (RPCServiceThriftHandlerMetrics) o;
+    return Objects.equals(thriftConnectionNumber, that.thriftConnectionNumber);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(thriftConnectionNumber);
+  }
+}
diff --git a/server/src/test/java/org/apache/iotdb/db/metric/MetricServiceTest.java b/server/src/test/java/org/apache/iotdb/db/metric/MetricServiceTest.java
index f1bca32b99..a7db0093f5 100644
--- a/server/src/test/java/org/apache/iotdb/db/metric/MetricServiceTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/metric/MetricServiceTest.java
@@ -61,7 +61,6 @@ public class MetricServiceTest {
       metricConfig.setMetricLevel(MetricLevel.IMPORTANT);
       metricService = new DoNothingMetricService();
       metricService.startService();
-      metricService.startAllReporter();
 
       // test metric service
       assertTrue(metricService.isEnable());