You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ni...@apache.org on 2020/01/14 06:58:08 UTC

[ignite] branch master updated: IGNITE-11987: Metrics configuration. (#6696)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 03636e6  IGNITE-11987: Metrics configuration. (#6696)
03636e6 is described below

commit 03636e610350f6e24b948751b95251ea1a556b97
Author: Nikolay <ni...@apache.org>
AuthorDate: Tue Jan 14 09:57:43 2020 +0300

    IGNITE-11987: Metrics configuration. (#6696)
---
 .../benchmarks/jol/GridMetricsJolBenchmark.java    |   2 +-
 .../org/apache/ignite/IgniteSystemProperties.java  |   3 +
 .../configuration/DataRegionConfiguration.java     |   9 +
 .../configuration/DataStorageConfiguration.java    |   9 +
 .../configuration/MemoryPolicyConfiguration.java   |   9 +
 .../PersistentStoreConfiguration.java              |   9 +
 .../org/apache/ignite/internal/IgniteKernal.java   |  12 -
 .../internal/managers/IgniteMBeansManager.java     |   6 +
 .../ignite/internal/metric/IoStatisticsHolder.java |   5 +
 .../internal/metric/IoStatisticsHolderCache.java   |  21 +-
 .../internal/metric/IoStatisticsHolderIndex.java   |  25 +-
 .../internal/metric/IoStatisticsHolderNoOp.java    |   5 +
 .../internal/metric/IoStatisticsHolderQuery.java   |   5 +
 .../ignite/internal/metric/IoStatisticsType.java   |  10 +-
 .../processors/cache/CacheGroupContext.java        |  11 +
 .../processors/cache/GridCacheAdapter.java         |   5 +-
 .../processors/cache/GridCacheProcessor.java       |   2 +
 .../cache/persistence/DataRegionMetricsImpl.java   |   5 +
 .../metastorage/DistributedMetaStorage.java        |  10 +
 .../persistence/DistributedMetaStorageImpl.java    |   5 +
 .../processors/metric/GridMetricManager.java       | 218 +++++++++++-
 .../internal/processors/metric/MetricRegistry.java |  39 ++-
 .../processors/metric/MetricsMxBeanImpl.java       |  75 ++++
 .../processors/metric/impl/HitRateMetric.java      |  18 +-
 .../processors/metric/impl/MetricUtils.java        |  13 +
 .../ignite/internal/util/GridArgumentCheck.java    |  13 +
 .../ignite/mxbean/DataRegionMetricsMXBean.java     |   4 +
 .../ignite/mxbean/DataStorageMetricsMXBean.java    |   4 +
 .../org/apache/ignite/mxbean/IgniteMXBean.java     |  10 -
 .../org/apache/ignite/mxbean/MetricsMxBean.java    |  64 ++++
 ...IoStatisticsMetricsLocalMXBeanImplSelfTest.java |  25 +-
 .../ignite/internal/metric/JmxExporterSpiTest.java |   4 +-
 .../internal/metric/MetricsConfigurationTest.java  | 382 +++++++++++++++++++++
 .../ignite/internal/metric/MetricsSelfTest.java    |  14 +-
 .../ignite/testsuites/IgniteCacheTestSuite9.java   |   6 +-
 .../processors/query/h2/database/H2TreeIndex.java  |   8 +-
 .../index/IoStatisticsBasicIndexSelfTest.java      |  42 +++
 37 files changed, 1019 insertions(+), 88 deletions(-)

diff --git a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jol/GridMetricsJolBenchmark.java b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jol/GridMetricsJolBenchmark.java
index 46efb03..3539c18 100644
--- a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jol/GridMetricsJolBenchmark.java
+++ b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jol/GridMetricsJolBenchmark.java
@@ -111,7 +111,7 @@ public class GridMetricsJolBenchmark {
      * Calculates and prints the size of metric registry of {@code TOTAL} size;
      */
     private static void measureMetricRegistry() {
-        MetricRegistry mreg = new MetricRegistry("test", null);
+        MetricRegistry mreg = new MetricRegistry("test", name -> null, name -> null, null);
 
         for(int i=0; i<BOOLEAN_CNT; i++)
             mreg.booleanMetric(BOOLEAN_METRIC + i, null);
diff --git a/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java b/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
index 3388071..5fe892c 100644
--- a/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
+++ b/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
@@ -36,6 +36,7 @@ import org.apache.ignite.internal.processors.metastorage.DistributedMetaStorage;
 import org.apache.ignite.internal.processors.metric.GridMetricManager;
 import org.apache.ignite.internal.processors.rest.GridRestCommand;
 import org.apache.ignite.internal.util.GridLogThrottle;
+import org.apache.ignite.mxbean.MetricsMxBean;
 import org.apache.ignite.stream.StreamTransformer;
 import org.jetbrains.annotations.Nullable;
 
@@ -695,7 +696,9 @@ public final class IgniteSystemProperties {
 
     /**
      * Time interval for calculating rebalance rate statistics, in milliseconds. Defaults to 60000.
+     * @deprecated Use {@link MetricsMxBean#configureHitRateMetric(String, long)} instead.
      */
+    @Deprecated
     public static final String IGNITE_REBALANCE_STATISTICS_TIME_INTERVAL = "IGNITE_REBALANCE_STATISTICS_TIME_INTERVAL";
 
     /**
diff --git a/modules/core/src/main/java/org/apache/ignite/configuration/DataRegionConfiguration.java b/modules/core/src/main/java/org/apache/ignite/configuration/DataRegionConfiguration.java
index 8591000..994b1e0 100644
--- a/modules/core/src/main/java/org/apache/ignite/configuration/DataRegionConfiguration.java
+++ b/modules/core/src/main/java/org/apache/ignite/configuration/DataRegionConfiguration.java
@@ -21,6 +21,7 @@ import org.apache.ignite.DataRegionMetrics;
 import org.apache.ignite.internal.mem.IgniteOutOfMemoryException;
 import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.mxbean.DataRegionMetricsMXBean;
+import org.apache.ignite.mxbean.MetricsMxBean;
 
 import static org.apache.ignite.configuration.DataStorageConfiguration.DFLT_DATA_REG_DEFAULT_NAME;
 
@@ -362,7 +363,9 @@ public final class DataRegionConfiguration implements Serializable {
      * will return average allocation rate (pages per second) for the last minute.
      *
      * @return Time interval over which allocation rate is calculated.
+     * @deprecated Use {@link MetricsMxBean#configureHitRateMetric(String, long)} instead.
      */
+    @Deprecated
     public long getMetricsRateTimeInterval() {
         return metricsRateTimeInterval;
     }
@@ -377,7 +380,9 @@ public final class DataRegionConfiguration implements Serializable {
      *
      * @param metricsRateTimeInterval Time interval used for allocation and eviction rates calculations.
      * @return {@code this} for chaining.
+     * @deprecated Use {@link MetricsMxBean#configureHitRateMetric(String, long)} instead.
      */
+    @Deprecated
     public DataRegionConfiguration setMetricsRateTimeInterval(long metricsRateTimeInterval) {
         this.metricsRateTimeInterval = metricsRateTimeInterval;
 
@@ -394,7 +399,9 @@ public final class DataRegionConfiguration implements Serializable {
      * calculation overhead.
      *
      * @return number of sub intervals.
+     * @deprecated Use {@link MetricsMxBean#configureHitRateMetric(String, long)} instead.
      */
+    @Deprecated
     public int getMetricsSubIntervalCount() {
         return metricsSubIntervalCount;
     }
@@ -409,7 +416,9 @@ public final class DataRegionConfiguration implements Serializable {
      *
      * @param metricsSubIntervalCnt A number of sub-intervals.
      * @return {@code this} for chaining.
+     * @deprecated Use {@link MetricsMxBean#configureHitRateMetric(String, long)} instead.
      */
+    @Deprecated
     public DataRegionConfiguration setMetricsSubIntervalCount(int metricsSubIntervalCnt) {
         this.metricsSubIntervalCount = metricsSubIntervalCnt;
 
diff --git a/modules/core/src/main/java/org/apache/ignite/configuration/DataStorageConfiguration.java b/modules/core/src/main/java/org/apache/ignite/configuration/DataStorageConfiguration.java
index 3805954..a705e49 100644
--- a/modules/core/src/main/java/org/apache/ignite/configuration/DataStorageConfiguration.java
+++ b/modules/core/src/main/java/org/apache/ignite/configuration/DataStorageConfiguration.java
@@ -27,6 +27,7 @@ import org.apache.ignite.internal.util.tostring.GridToStringInclude;
 import org.apache.ignite.internal.util.typedef.internal.A;
 import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.mxbean.MetricsMxBean;
 
 import static org.apache.ignite.IgniteSystemProperties.IGNITE_DEFAULT_DATA_STORAGE_PAGE_SIZE;
 
@@ -731,7 +732,9 @@ public class DataStorageConfiguration implements Serializable {
      * hits will be tracked. Default value is {@link #DFLT_RATE_TIME_INTERVAL_MILLIS}.
      *
      * @return Time interval in milliseconds.
+     * @deprecated Use {@link MetricsMxBean#configureHitRateMetric(String, long)} instead.
      */
+    @Deprecated
     public long getMetricsRateTimeInterval() {
         return metricsRateTimeInterval;
     }
@@ -741,7 +744,9 @@ public class DataStorageConfiguration implements Serializable {
      * hits will be tracked.
      *
      * @param metricsRateTimeInterval Time interval in milliseconds.
+     * @deprecated Use {@link MetricsMxBean#configureHitRateMetric(String, long)} instead.
      */
+    @Deprecated
     public DataStorageConfiguration setMetricsRateTimeInterval(long metricsRateTimeInterval) {
         this.metricsRateTimeInterval = metricsRateTimeInterval;
 
@@ -753,7 +758,9 @@ public class DataStorageConfiguration implements Serializable {
      * Default value is {@link #DFLT_SUB_INTERVALS}.
      *
      * @return The number of sub-intervals for history tracking.
+     * @deprecated Use {@link MetricsMxBean#configureHitRateMetric(String, long)} instead.
      */
+    @Deprecated
     public int getMetricsSubIntervalCount() {
         return metricsSubIntervalCnt;
     }
@@ -762,7 +769,9 @@ public class DataStorageConfiguration implements Serializable {
      * Sets the number of sub-intervals to split the {@link #getMetricsRateTimeInterval()} into to track the update history.
      *
      * @param metricsSubIntervalCnt The number of sub-intervals for history tracking.
+     * @deprecated Use {@link MetricsMxBean#configureHitRateMetric(String, long)} instead.
      */
+    @Deprecated
     public DataStorageConfiguration setMetricsSubIntervalCount(int metricsSubIntervalCnt) {
         this.metricsSubIntervalCnt = metricsSubIntervalCnt;
 
diff --git a/modules/core/src/main/java/org/apache/ignite/configuration/MemoryPolicyConfiguration.java b/modules/core/src/main/java/org/apache/ignite/configuration/MemoryPolicyConfiguration.java
index 0213b7f..fb32d1a 100644
--- a/modules/core/src/main/java/org/apache/ignite/configuration/MemoryPolicyConfiguration.java
+++ b/modules/core/src/main/java/org/apache/ignite/configuration/MemoryPolicyConfiguration.java
@@ -21,6 +21,7 @@ import org.apache.ignite.MemoryMetrics;
 import org.apache.ignite.internal.mem.IgniteOutOfMemoryException;
 import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.mxbean.DataRegionMetricsMXBean;
+import org.apache.ignite.mxbean.MetricsMxBean;
 
 import static org.apache.ignite.configuration.MemoryConfiguration.DFLT_MEM_PLC_DEFAULT_NAME;
 
@@ -324,7 +325,9 @@ public final class MemoryPolicyConfiguration implements Serializable {
      * will return average allocation rate (pages per second) for the last minute.
      *
      * @return Time interval over which allocation rate is calculated.
+     * @deprecated Use {@link MetricsMxBean#configureHitRateMetric(String, long)} instead.
      */
+    @Deprecated
     public long getRateTimeInterval() {
         return rateTimeInterval;
     }
@@ -339,7 +342,9 @@ public final class MemoryPolicyConfiguration implements Serializable {
      *
      * @param rateTimeInterval Time interval used for allocation and eviction rates calculations.
      * @return {@code this} for chaining.
+     * @deprecated Use {@link MetricsMxBean#configureHitRateMetric(String, long)} instead.
      */
+    @Deprecated
     public MemoryPolicyConfiguration setRateTimeInterval(long rateTimeInterval) {
         this.rateTimeInterval = rateTimeInterval;
 
@@ -356,7 +361,9 @@ public final class MemoryPolicyConfiguration implements Serializable {
      * calculation overhead.
      *
      * @return number of sub intervals.
+     * @deprecated Use {@link MetricsMxBean#configureHitRateMetric(String, long)} instead.
      */
+    @Deprecated
     public int getSubIntervals() {
         return subIntervals;
     }
@@ -371,7 +378,9 @@ public final class MemoryPolicyConfiguration implements Serializable {
      *
      * @param subIntervals A number of sub-intervals.
      * @return {@code this} for chaining.
+     * @deprecated Use {@link MetricsMxBean#configureHitRateMetric(String, long)} instead.
      */
+    @Deprecated
     public MemoryPolicyConfiguration setSubIntervals(int subIntervals) {
         this.subIntervals = subIntervals;
 
diff --git a/modules/core/src/main/java/org/apache/ignite/configuration/PersistentStoreConfiguration.java b/modules/core/src/main/java/org/apache/ignite/configuration/PersistentStoreConfiguration.java
index 2dfa4d5..07bdd9d 100644
--- a/modules/core/src/main/java/org/apache/ignite/configuration/PersistentStoreConfiguration.java
+++ b/modules/core/src/main/java/org/apache/ignite/configuration/PersistentStoreConfiguration.java
@@ -22,6 +22,7 @@ import org.apache.ignite.internal.processors.cache.persistence.file.AsyncFileIOF
 import org.apache.ignite.internal.processors.cache.persistence.file.FileIOFactory;
 import org.apache.ignite.internal.processors.cache.persistence.file.RandomAccessFileIOFactory;
 import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.mxbean.MetricsMxBean;
 
 /**
  * Configures Apache Ignite Persistent store.
@@ -430,7 +431,9 @@ public class PersistentStoreConfiguration implements Serializable {
      * hits will be tracked. Default value is {@link #DFLT_RATE_TIME_INTERVAL_MILLIS}.
      *
      * @return Time interval in milliseconds.
+     * @deprecated Use {@link MetricsMxBean#configureHitRateMetric(String, long)} instead.
      */
+    @Deprecated
     public long getRateTimeInterval() {
         return rateTimeInterval;
     }
@@ -440,7 +443,9 @@ public class PersistentStoreConfiguration implements Serializable {
      * hits will be tracked.
      *
      * @param rateTimeInterval Time interval in milliseconds.
+     * @deprecated Use {@link MetricsMxBean#configureHitRateMetric(String, long)} instead.
      */
+    @Deprecated
     public PersistentStoreConfiguration setRateTimeInterval(long rateTimeInterval) {
         this.rateTimeInterval = rateTimeInterval;
 
@@ -452,7 +457,9 @@ public class PersistentStoreConfiguration implements Serializable {
      * Default value is {@link #DFLT_SUB_INTERVALS}.
      *
      * @return The number of sub-intervals for history tracking.
+     * @deprecated Use {@link MetricsMxBean#configureHitRateMetric(String, long)} instead.
      */
+    @Deprecated
     public int getSubIntervals() {
         return subIntervals;
     }
@@ -461,7 +468,9 @@ public class PersistentStoreConfiguration implements Serializable {
      * Sets the number of sub-intervals to split the {@link #getRateTimeInterval()} into to track the update history.
      *
      * @param subIntervals The number of sub-intervals for history tracking.
+     * @deprecated Use {@link MetricsMxBean#configureHitRateMetric(String, long)} instead.
      */
+    @Deprecated
     public PersistentStoreConfiguration setSubIntervals(int subIntervals) {
         this.subIntervals = subIntervals;
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java b/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java
index a4fdd7c..497d766 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java
@@ -4733,18 +4733,6 @@ public class IgniteKernal implements IgniteEx, IgniteMXBean, Externalizable {
     }
 
     /** {@inheritDoc} */
-    @Override public void resetMetrics(String registry) {
-        assert registry != null;
-
-        MetricRegistry mreg = ctx.metric().registry(registry);
-
-        if (mreg != null)
-            mreg.reset();
-        else if (log.isInfoEnabled())
-            log.info("\"" + registry + "\" not found.");
-    }
-
-    /** {@inheritDoc} */
     @Override public void clusterState(String state) {
         ClusterState newState = ClusterState.valueOf(state);
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/IgniteMBeansManager.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/IgniteMBeansManager.java
index 99ddccc..41034a7 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/managers/IgniteMBeansManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/IgniteMBeansManager.java
@@ -36,6 +36,7 @@ import org.apache.ignite.internal.TransactionsMXBeanImpl;
 import org.apache.ignite.internal.managers.encryption.EncryptionMXBeanImpl;
 import org.apache.ignite.internal.processors.cache.persistence.DataStorageMXBeanImpl;
 import org.apache.ignite.internal.processors.cluster.BaselineAutoAdjustMXBeanImpl;
+import org.apache.ignite.internal.processors.metric.MetricsMxBeanImpl;
 import org.apache.ignite.internal.util.StripedExecutor;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.internal.worker.FailureHandlingMxBeanImpl;
@@ -47,6 +48,7 @@ import org.apache.ignite.mxbean.DataStorageMXBean;
 import org.apache.ignite.mxbean.EncryptionMXBean;
 import org.apache.ignite.mxbean.FailureHandlingMxBean;
 import org.apache.ignite.mxbean.IgniteMXBean;
+import org.apache.ignite.mxbean.MetricsMxBean;
 import org.apache.ignite.mxbean.StripedExecutorMXBean;
 import org.apache.ignite.mxbean.ThreadPoolMXBean;
 import org.apache.ignite.mxbean.TransactionMetricsMxBean;
@@ -159,6 +161,10 @@ public class IgniteMBeansManager {
         registerMBean("Encryption", encryptionMXBean.getClass().getSimpleName(), encryptionMXBean,
             EncryptionMXBean.class);
 
+        // Metrics configuration
+        MetricsMxBean metricsMxBean = new MetricsMxBeanImpl(ctx.metric(), log);
+        registerMBean("Metrics", metricsMxBean.getClass().getSimpleName(), metricsMxBean, MetricsMxBean.class);
+
         // Executors
         registerExecutorMBean("GridUtilityCacheExecutor", utilityCachePool);
         registerExecutorMBean("GridExecutionExecutor", execSvc);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/metric/IoStatisticsHolder.java b/modules/core/src/main/java/org/apache/ignite/internal/metric/IoStatisticsHolder.java
index e6f9f7b..28604d0 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/metric/IoStatisticsHolder.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/metric/IoStatisticsHolder.java
@@ -45,4 +45,9 @@ public interface IoStatisticsHolder {
      * @return Number of physical reads.
      */
     public long physicalReads();
+
+    /**
+     * @return Metric registry name.
+     */
+    public String metricRegistryName();
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/metric/IoStatisticsHolderCache.java b/modules/core/src/main/java/org/apache/ignite/internal/metric/IoStatisticsHolderCache.java
index e6e12d9..27fe585 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/metric/IoStatisticsHolderCache.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/metric/IoStatisticsHolderCache.java
@@ -45,26 +45,26 @@ public class IoStatisticsHolderCache implements IoStatisticsHolder {
     private final LongAdderMetric physicalReadCtr;
 
     /** */
-    private final String cacheName;
+    private final String grpName;
 
     /** */
     private final int grpId;
 
     /**
-     * @param cacheName Name of cache.
+     * @param grpName Name of the group.
      * @param grpId Group id.
      * @param mmgr Metric manager.
      */
-    public IoStatisticsHolderCache(String cacheName, int grpId, GridMetricManager mmgr) {
-        assert cacheName != null;
+    public IoStatisticsHolderCache(String grpName, int grpId, GridMetricManager mmgr) {
+        assert grpName != null;
 
-        this.cacheName = cacheName;
+        this.grpName = grpName;
         this.grpId = grpId;
 
-        MetricRegistry mreg = mmgr.registry(metricName(CACHE_GROUP.metricGroupName(), cacheName));
+        MetricRegistry mreg = mmgr.registry(metricRegistryName());
 
         mreg.longMetric("startTime", null).value(U.currentTimeMillis());
-        mreg.objectMetric("name", String.class, null).value(cacheName);
+        mreg.objectMetric("name", String.class, null).value(grpName);
         mreg.intMetric("grpId", null).value(grpId);
 
         this.logicalReadCtr = mreg.longAdderMetric(LOGICAL_READS, null);
@@ -105,6 +105,11 @@ public class IoStatisticsHolderCache implements IoStatisticsHolder {
         return physicalReadCtr.value();
     }
 
+    /** {@inheritDoc} */
+    @Override public String metricRegistryName() {
+        return metricName(CACHE_GROUP.metricGroupName(), grpName);
+    }
+
     /**
      * @return Cache group id.
      */
@@ -117,6 +122,6 @@ public class IoStatisticsHolderCache implements IoStatisticsHolder {
         return S.toString(IoStatisticsHolderCache.class, this,
             "logicalReadCtr", logicalReadCtr,
             "physicalReadCtr", physicalReadCtr,
-            "cacheName", cacheName);
+            "grpName", grpName);
     }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/metric/IoStatisticsHolderIndex.java b/modules/core/src/main/java/org/apache/ignite/internal/metric/IoStatisticsHolderIndex.java
index a4ec9f6..eb2325e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/metric/IoStatisticsHolderIndex.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/metric/IoStatisticsHolderIndex.java
@@ -59,31 +59,35 @@ public class IoStatisticsHolderIndex implements IoStatisticsHolder {
     private final LongAdderMetric physicalReadInnerCtr;
 
     /** */
-    private final String cacheName;
+    private final String grpName;
 
     /** */
     private final String idxName;
 
+    /** */
+    private IoStatisticsType type;
+
     /**
      * @param type Type of statistics.
-     * @param cacheName Cache name.
+     * @param grpName Group name.
      * @param idxName Index name.
      * @param mmgr Metric manager.
      */
     public IoStatisticsHolderIndex(
         IoStatisticsType type,
-        String cacheName,
+        String grpName,
         String idxName,
         GridMetricManager mmgr) {
-        assert cacheName != null && idxName != null;
+        assert grpName != null && idxName != null;
 
-        this.cacheName = cacheName;
+        this.type = type;
+        this.grpName = grpName;
         this.idxName = idxName;
 
-        MetricRegistry mreg = mmgr.registry(metricName(type.metricGroupName(), cacheName, idxName));
+        MetricRegistry mreg = mmgr.registry(metricRegistryName());
 
         mreg.longMetric("startTime", null).value(U.currentTimeMillis());
-        mreg.objectMetric("name", String.class, null).value(cacheName);
+        mreg.objectMetric("name", String.class, null).value(grpName);
         mreg.objectMetric("indexName", String.class, null).value(idxName);
 
         logicalReadLeafCtr = mreg.longAdderMetric(LOGICAL_READS_LEAF, null);
@@ -148,13 +152,18 @@ public class IoStatisticsHolderIndex implements IoStatisticsHolder {
     }
 
     /** {@inheritDoc} */
+    @Override public String metricRegistryName() {
+        return metricName(type.metricGroupName(), grpName, idxName);
+    }
+
+    /** {@inheritDoc} */
     @Override public String toString() {
         return S.toString(IoStatisticsHolderIndex.class, this,
             "logicalReadLeafCtr", logicalReadLeafCtr,
             "logicalReadInnerCtr", logicalReadInnerCtr,
             "physicalReadLeafCtr", physicalReadLeafCtr,
             "physicalReadInnerCtr", physicalReadInnerCtr,
-            "cacheName", cacheName,
+            "grpName", grpName,
             "idxName", idxName);
     }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/metric/IoStatisticsHolderNoOp.java b/modules/core/src/main/java/org/apache/ignite/internal/metric/IoStatisticsHolderNoOp.java
index 43c75c1..936fb11 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/metric/IoStatisticsHolderNoOp.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/metric/IoStatisticsHolderNoOp.java
@@ -49,4 +49,9 @@ public class IoStatisticsHolderNoOp implements IoStatisticsHolder {
     @Override public long physicalReads() {
         return 0;
     }
+
+    /** {@inheritDoc} */
+    @Override public String metricRegistryName() {
+        return null;
+    }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/metric/IoStatisticsHolderQuery.java b/modules/core/src/main/java/org/apache/ignite/internal/metric/IoStatisticsHolderQuery.java
index bdb5b67..650e5e3 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/metric/IoStatisticsHolderQuery.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/metric/IoStatisticsHolderQuery.java
@@ -72,6 +72,11 @@ public class IoStatisticsHolderQuery implements IoStatisticsHolder {
         return physicalReadCtr.longValue();
     }
 
+    /** {@inheritDoc} */
+    @Override public String metricRegistryName() {
+        return null;
+    }
+
     /**
      * @return Query id.
      */
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/metric/IoStatisticsType.java b/modules/core/src/main/java/org/apache/ignite/internal/metric/IoStatisticsType.java
index 34f0b28..4de638b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/metric/IoStatisticsType.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/metric/IoStatisticsType.java
@@ -35,19 +35,19 @@ public enum IoStatisticsType {
     SQL("io.statistics.sql");
 
     /** Metric group. */
-    private String metricGroupName;
+    private String metricGrpName;
 
     /**
-     * @param monitoringGroup Monitoring group.
+     * @param metricGrpName Metric group name.
      */
-    IoStatisticsType(String metricGroupName) {
-        this.metricGroupName = metricGroupName;
+    IoStatisticsType(String metricGrpName) {
+        this.metricGrpName = metricGrpName;
     }
 
     /**
      * @return Metric group name.
      */
     public String metricGroupName() {
-        return metricGroupName;
+        return metricGrpName;
     }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheGroupContext.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheGroupContext.java
index ad35570..6082789 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheGroupContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheGroupContext.java
@@ -1304,4 +1304,15 @@ public class CacheGroupContext {
     public CacheGroupMetricsImpl metrics() {
         return metrics;
     }
+
+    /**
+     * Removes statistics metrics registries.
+     */
+    public void removeIOStatistic() {
+        if (statHolderData != IoStatisticsHolderNoOp.INSTANCE)
+            ctx.kernalContext().metric().remove(statHolderData.metricRegistryName());
+
+        if (statHolderIdx != IoStatisticsHolderNoOp.INSTANCE)
+            ctx.kernalContext().metric().remove(statHolderIdx.metricRegistryName());
+    }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java
index 2789357..abbbd57 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java
@@ -108,7 +108,6 @@ import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
 import org.apache.ignite.internal.processors.datastreamer.DataStreamerEntry;
 import org.apache.ignite.internal.processors.datastreamer.DataStreamerImpl;
 import org.apache.ignite.internal.processors.dr.IgniteDrDataStreamerCacheUpdater;
-import org.apache.ignite.internal.processors.metric.impl.MetricUtils;
 import org.apache.ignite.internal.processors.platform.cache.PlatformCacheEntryFilter;
 import org.apache.ignite.internal.processors.task.GridInternal;
 import org.apache.ignite.internal.transactions.IgniteTxHeuristicCheckedException;
@@ -164,6 +163,7 @@ import static org.apache.ignite.internal.processors.cache.CacheOperationContext.
 import static org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtPartitionState.OWNING;
 import static org.apache.ignite.internal.processors.dr.GridDrType.DR_LOAD;
 import static org.apache.ignite.internal.processors.dr.GridDrType.DR_NONE;
+import static org.apache.ignite.internal.processors.metric.impl.MetricUtils.cacheMetricsRegistryName;
 import static org.apache.ignite.internal.processors.task.GridTaskThreadContextKey.TC_NO_FAILOVER;
 import static org.apache.ignite.internal.processors.task.GridTaskThreadContextKey.TC_SUBGRID;
 import static org.apache.ignite.transactions.TransactionConcurrency.OPTIMISTIC;
@@ -665,7 +665,8 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
         // no matter what references these futures are holding.
         lastFut = null;
 
-        ctx.kernalContext().metric().remove(MetricUtils.cacheMetricsRegistryName(ctx.name(), isNear()));
+        if (!ctx.kernalContext().isStopping())
+            ctx.kernalContext().metric().remove(cacheMetricsRegistryName(ctx.name(), isNear()));
     }
 
     /**
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java
index 1eabd87..6a34df4 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java
@@ -535,6 +535,8 @@ public class GridCacheProcessor extends GridProcessorAdapter {
 
         grp.metrics().remove();
 
+        grp.removeIOStatistic();
+
         cachesInfo.cleanupRemovedGroup(grp.groupId());
     }
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataRegionMetricsImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataRegionMetricsImpl.java
index c30846c..a6e510d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataRegionMetricsImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataRegionMetricsImpl.java
@@ -28,6 +28,7 @@ import org.apache.ignite.internal.processors.metric.impl.AtomicLongMetric;
 import org.apache.ignite.internal.processors.metric.impl.HitRateMetric;
 import org.apache.ignite.internal.processors.metric.impl.LongAdderMetric;
 import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.mxbean.MetricsMxBean;
 import org.apache.ignite.spi.metric.Metric;
 
 import static org.apache.ignite.internal.processors.cache.CacheGroupMetricsImpl.CACHE_GROUP_METRICS_PREFIX;
@@ -519,7 +520,9 @@ public class DataRegionMetricsImpl implements DataRegionMetrics {
 
     /**
      * @param rateTimeInterval Time interval (in milliseconds) used to calculate allocation/eviction rate.
+     * @deprecated Use {@link MetricsMxBean#configureHitRateMetric(String, long)} instead.
      */
+    @Deprecated
     public void rateTimeInterval(long rateTimeInterval) {
         this.rateTimeInterval = rateTimeInterval;
 
@@ -533,7 +536,9 @@ public class DataRegionMetricsImpl implements DataRegionMetrics {
      * Sets number of subintervals the whole rateTimeInterval will be split into to calculate allocation rate.
      *
      * @param subInts Number of subintervals.
+     * @deprecated Use {@link MetricsMxBean#configureHitRateMetric(String, long)} instead.
      */
+    @Deprecated
     public void subIntervals(int subInts) {
         assert subInts > 0;
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/metastorage/DistributedMetaStorage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/metastorage/DistributedMetaStorage.java
index 09c5e3b..cb70b8c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/metastorage/DistributedMetaStorage.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/metastorage/DistributedMetaStorage.java
@@ -43,11 +43,21 @@ public interface DistributedMetaStorage extends ReadableDistributedMetaStorage {
      *
      * @param key The key.
      * @param val Value to write. Must not be null.
+     * @return Future with the operation result.
      * @throws IgniteCheckedException In case of marshalling error or some other unexpected exception.
      */
     GridFutureAdapter<?> writeAsync(@NotNull String key, @NotNull Serializable val) throws IgniteCheckedException;
 
     /**
+     * Remove value from distributed metastorage asynchronously.
+     *
+     * @param key The key.
+     * @return Future with the operation result.
+     * @throws IgniteCheckedException In case of marshalling error or some other unexpected exception.
+     */
+    GridFutureAdapter<?> removeAsync(@NotNull String key) throws IgniteCheckedException;
+
+    /**
      * Remove value from distributed metastorage.
      *
      * @param key The key.
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/metastorage/persistence/DistributedMetaStorageImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/metastorage/persistence/DistributedMetaStorageImpl.java
index 95ce0df..242c65c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/metastorage/persistence/DistributedMetaStorageImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/metastorage/persistence/DistributedMetaStorageImpl.java
@@ -432,6 +432,11 @@ public class DistributedMetaStorageImpl extends GridProcessorAdapter
     }
 
     /** {@inheritDoc} */
+    @Override public GridFutureAdapter<?> removeAsync(@NotNull String key) throws IgniteCheckedException {
+        return startWrite(key, null);
+    }
+
+    /** {@inheritDoc} */
     @Override public void remove(@NotNull String key) throws IgniteCheckedException {
         startWrite(key, null).get();
     }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/GridMetricManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/GridMetricManager.java
index 49a9c0f..339d0f8 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/GridMetricManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/GridMetricManager.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.internal.processors.metric;
 
+import java.io.Serializable;
 import java.lang.management.GarbageCollectorMXBean;
 import java.lang.management.ManagementFactory;
 import java.lang.management.MemoryMXBean;
@@ -38,13 +39,23 @@ import java.util.function.Consumer;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteException;
 import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.NodeStoppingException;
 import org.apache.ignite.internal.managers.GridManagerAdapter;
+import org.apache.ignite.internal.processors.metastorage.DistributedMetaStorage;
+import org.apache.ignite.internal.processors.metastorage.DistributedMetastorageLifecycleListener;
+import org.apache.ignite.internal.processors.metastorage.ReadableDistributedMetaStorage;
 import org.apache.ignite.internal.processors.metric.impl.AtomicLongMetric;
 import org.apache.ignite.internal.processors.metric.impl.DoubleMetricImpl;
+import org.apache.ignite.internal.processors.metric.impl.HistogramMetric;
+import org.apache.ignite.internal.processors.metric.impl.HitRateMetric;
 import org.apache.ignite.internal.processors.timeout.GridTimeoutProcessor;
 import org.apache.ignite.internal.util.StripedExecutor;
+import org.apache.ignite.internal.util.future.GridCompoundFuture;
+import org.apache.ignite.internal.util.typedef.T2;
+import org.apache.ignite.internal.util.typedef.internal.A;
 import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.spi.metric.Metric;
 import org.apache.ignite.spi.metric.MetricExporterSpi;
 import org.apache.ignite.spi.metric.ReadOnlyMetricRegistry;
 import org.apache.ignite.thread.IgniteStripedThreadPoolExecutor;
@@ -53,6 +64,7 @@ import org.jetbrains.annotations.Nullable;
 
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
 import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_PHY_RAM;
+import static org.apache.ignite.internal.processors.metric.impl.MetricUtils.fromFullName;
 import static org.apache.ignite.internal.processors.metric.impl.MetricUtils.metricName;
 import static org.apache.ignite.internal.util.IgniteUtils.notifyListeners;
 
@@ -178,6 +190,12 @@ public class GridMetricManager extends GridManagerAdapter<MetricExporterSpi> imp
     /** */
     private static final Collection<GarbageCollectorMXBean> gc = ManagementFactory.getGarbageCollectorMXBeans();
 
+    /** Prefix for {@link HitRateMetric} configuration property name. */
+    public static final String HITRATE_CFG_PREFIX = metricName("metrics", "hitrate");
+
+    /** Prefix for {@link HistogramMetric} configuration property name. */
+    public static final String HISTOGRAM_CFG_PREFIX = metricName("metrics", "histogram");
+
     /** Registered metrics registries. */
     private final ConcurrentHashMap<String, MetricRegistry> registries = new ConcurrentHashMap<>();
 
@@ -187,6 +205,12 @@ public class GridMetricManager extends GridManagerAdapter<MetricExporterSpi> imp
     /** Metric registry remove listeners. */
     private final List<Consumer<MetricRegistry>> metricRegRemoveLsnrs = new CopyOnWriteArrayList<>();
 
+    /** Read-only metastorage. */
+    private volatile ReadableDistributedMetaStorage roMetastorage;
+
+    /** Metastorage with the write access. */
+    private volatile DistributedMetaStorage metastorage;
+
     /** Metrics update worker. */
     private GridTimeoutProcessor.CancelableTask metricsUpdateTask;
 
@@ -242,7 +266,7 @@ public class GridMetricManager extends GridManagerAdapter<MetricExporterSpi> imp
     }
 
     /** {@inheritDoc} */
-    @Override protected void onKernalStart0() throws IgniteCheckedException {
+    @Override protected void onKernalStart0() {
         metricsUpdateTask = ctx.timeout().schedule(new MetricsUpdater(), METRICS_UPDATE_FREQ, METRICS_UPDATE_FREQ);
     }
 
@@ -252,6 +276,38 @@ public class GridMetricManager extends GridManagerAdapter<MetricExporterSpi> imp
             spi.setMetricRegistry(this);
 
         startSpi();
+
+        ctx.internalSubscriptionProcessor().registerDistributedMetastorageListener(
+            new DistributedMetastorageLifecycleListener() {
+                /** {@inheritDoc} */
+                @Override public void onReadyForRead(ReadableDistributedMetaStorage metastorage) {
+                    roMetastorage = metastorage;
+
+                    try {
+                        metastorage.iterate(HITRATE_CFG_PREFIX, (name, val) -> onHitRateConfigChanged(
+                            name.substring(HITRATE_CFG_PREFIX.length() + 1), (Long) val));
+
+                        metastorage.iterate(HISTOGRAM_CFG_PREFIX, (name, val) -> onHistogramConfigChanged(
+                            name.substring(HISTOGRAM_CFG_PREFIX.length() + 1), (long[]) val));
+                    }
+                    catch (IgniteCheckedException e) {
+                        throw new IgniteException(e);
+                    }
+
+                    metastorage.listen(n -> n.startsWith(HITRATE_CFG_PREFIX),
+                        (name, oldVal, newVal) -> onHitRateConfigChanged(
+                            name.substring(HITRATE_CFG_PREFIX.length() + 1), (Long) newVal));
+
+                    metastorage.listen(n -> n.startsWith(HISTOGRAM_CFG_PREFIX),
+                        (name, oldVal, newVal) -> onHistogramConfigChanged(
+                            name.substring(HISTOGRAM_CFG_PREFIX.length() + 1), (long[]) newVal));
+                }
+
+                /** {@inheritDoc} */
+                @Override public void onReadyForWrite(DistributedMetaStorage metastorage) {
+                    GridMetricManager.this.metastorage = metastorage;
+                }
+            });
     }
 
     /** {@inheritDoc} */
@@ -270,7 +326,10 @@ public class GridMetricManager extends GridManagerAdapter<MetricExporterSpi> imp
      */
     public MetricRegistry registry(String name) {
         return registries.computeIfAbsent(name, n -> {
-            MetricRegistry mreg = new MetricRegistry(name, log);
+            MetricRegistry mreg = new MetricRegistry(name,
+                mname -> readFromMetastorage(metricName(HITRATE_CFG_PREFIX, mname)),
+                mname -> readFromMetastorage(metricName(HISTOGRAM_CFG_PREFIX, mname)),
+                log);
 
             notifyListeners(mreg, metricRegCreationLsnrs, log);
 
@@ -278,6 +337,25 @@ public class GridMetricManager extends GridManagerAdapter<MetricExporterSpi> imp
         });
     }
 
+    /**
+     * Reads value from {@link #roMetastorage}.
+     *
+     * @param key Key.
+     * @param <T> Key type.
+     * @return Value or {@code null} if not found.
+     */
+    private <T extends Serializable> T readFromMetastorage(String key) {
+        if (roMetastorage == null)
+            return null;
+
+        try {
+            return roMetastorage.read(key);
+        }
+        catch (IgniteCheckedException e) {
+            throw new IgniteException(e);
+        }
+    }
+
     /** {@inheritDoc} */
     @NotNull @Override public Iterator<MetricRegistry> iterator() {
         return registries.values().iterator();
@@ -301,8 +379,140 @@ public class GridMetricManager extends GridManagerAdapter<MetricExporterSpi> imp
     public void remove(String regName) {
         MetricRegistry mreg = registries.remove(regName);
 
-        if (mreg != null)
-            notifyListeners(mreg, metricRegRemoveLsnrs, log);
+        if (mreg == null)
+            return;
+
+        notifyListeners(mreg, metricRegRemoveLsnrs, log);
+
+        try {
+            GridCompoundFuture opsFut = new GridCompoundFuture<>();
+
+            for (Metric m : mreg) {
+                if (m instanceof HitRateMetric)
+                    opsFut.add(metastorage.removeAsync(metricName(HITRATE_CFG_PREFIX, m.name())));
+                else if (m instanceof HistogramMetric)
+                    opsFut.add(metastorage.removeAsync(metricName(HISTOGRAM_CFG_PREFIX, m.name())));
+            }
+
+            opsFut.markInitialized();
+            opsFut.get();
+        }
+        catch (IgniteCheckedException e) {
+            throw new IgniteException(e);
+        }
+    }
+
+    /**
+     * Change {@link HitRateMetric} configuration if it exists.
+     *
+     * @param name Metric name.
+     * @param rateTimeInterval New rate time interval.
+     * @throws IgniteCheckedException If write of configuration failed.
+     * @see HitRateMetric#reset(long, int)
+     */
+    public void configureHitRate(String name, long rateTimeInterval) throws IgniteCheckedException {
+        A.notNullOrEmpty(name, "name");
+        A.ensure(rateTimeInterval > 0, "rateTimeInterval should be positive");
+        A.notNull(metastorage, "Metastorage not ready. Node not started?");
+
+        if (ctx.isStopping())
+            throw new NodeStoppingException("Operation has been cancelled (node is stopping)");
+
+        metastorage.write(metricName(HITRATE_CFG_PREFIX, name), rateTimeInterval);
+    }
+
+    /**
+     * Stores {@link HistogramMetric} configuration in metastorage.
+     *
+     * @param name Metric name.
+     * @param bounds New bounds.
+     * @throws IgniteCheckedException If write of configuration failed.
+     */
+    public void configureHistogram(String name, long[] bounds) throws IgniteCheckedException {
+        A.notNullOrEmpty(name, "name");
+        A.notEmpty(bounds, "bounds");
+        A.notNull(metastorage, "Metastorage not ready. Node not started?");
+
+        if (ctx.isStopping())
+            throw new NodeStoppingException("Operation has been cancelled (node is stopping)");
+
+        metastorage.write(metricName(HISTOGRAM_CFG_PREFIX, name), bounds);
+    }
+
+    /**
+     * Change {@link HitRateMetric} instance configuration.
+     *
+     * @param name Metric name.
+     * @param rateTimeInterval New rateTimeInterval.
+     * @see HistogramMetric#reset(long[])
+     */
+    private void onHitRateConfigChanged(String name, @Nullable Long rateTimeInterval) {
+        if (rateTimeInterval == null)
+            return;
+
+        A.ensure(rateTimeInterval > 0, "rateTimeInterval should be positive");
+
+        HitRateMetric m = find(name, HitRateMetric.class);
+
+        if (m == null)
+            return;
+
+        m.reset(rateTimeInterval);
+    }
+
+    /**
+     * Change {@link HistogramMetric} instance configuration.
+     *
+     * @param name Metric name.
+     * @param bounds New bounds.
+     */
+    private void onHistogramConfigChanged(String name, @Nullable long[] bounds) {
+        if (bounds == null)
+            return;
+
+        HistogramMetric m = find(name, HistogramMetric.class);
+
+        if (m == null)
+            return;
+
+        m.reset(bounds);
+    }
+
+    /**
+     * @param name Metric name.
+     * @param type Metric type.
+     * @return Metric.
+     */
+    private <T extends Metric> T find(String name, Class<T> type) {
+        A.notNull(name, "name");
+
+        T2<String, String> splitted = fromFullName(name);
+
+        MetricRegistry mreg = registries.get(splitted.get1());
+
+        if (mreg == null) {
+            if (log.isInfoEnabled())
+                log.info("Metric registry not found[registry=" + splitted.get1() + ']');
+
+            return null;
+        }
+
+        Metric m = mreg.findMetric(splitted.get2());
+
+        if (m == null) {
+            if (log.isInfoEnabled())
+                log.info("Metric not found[registry=" + splitted.get1() + ", metricName=" + splitted.get2() + ']');
+
+            return null;
+        }
+
+        if (!m.getClass().isAssignableFrom(type)) {
+            log.error("Metric '" + name + "' has wrong type[type=" + m.getClass().getSimpleName() + ']');
+
+            return null;
+        }
+
+        return (T) m;
     }
 
     /**
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/MetricRegistry.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/MetricRegistry.java
index 7dee8e4..5be8a9e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/MetricRegistry.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/MetricRegistry.java
@@ -21,11 +21,13 @@ import java.util.Iterator;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.BooleanSupplier;
 import java.util.function.DoubleSupplier;
+import java.util.function.Function;
 import java.util.function.IntSupplier;
 import java.util.function.LongConsumer;
 import java.util.function.LongSupplier;
 import java.util.function.Supplier;
 import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.internal.processors.metric.impl.AtomicLongMetric;
 import org.apache.ignite.internal.processors.metric.impl.BooleanGauge;
 import org.apache.ignite.internal.processors.metric.impl.BooleanMetricImpl;
 import org.apache.ignite.internal.processors.metric.impl.DoubleGauge;
@@ -37,7 +39,6 @@ import org.apache.ignite.internal.processors.metric.impl.IntMetricImpl;
 import org.apache.ignite.internal.processors.metric.impl.LongAdderMetric;
 import org.apache.ignite.internal.processors.metric.impl.LongAdderWithDelegateMetric;
 import org.apache.ignite.internal.processors.metric.impl.LongGauge;
-import org.apache.ignite.internal.processors.metric.impl.AtomicLongMetric;
 import org.apache.ignite.internal.processors.metric.impl.ObjectGauge;
 import org.apache.ignite.internal.processors.metric.impl.ObjectMetricImpl;
 import org.apache.ignite.spi.metric.BooleanMetric;
@@ -46,6 +47,7 @@ import org.apache.ignite.spi.metric.Metric;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
+import static org.apache.ignite.internal.processors.metric.impl.HitRateMetric.DFLT_SIZE;
 import static org.apache.ignite.internal.processors.metric.impl.MetricUtils.metricName;
 import static org.apache.ignite.internal.util.lang.GridFunc.nonThrowableSupplier;
 
@@ -62,13 +64,24 @@ public class MetricRegistry implements Iterable<Metric> {
     /** Registered metrics. */
     private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
 
+    /** HitRate config provider. */
+    private final Function<String, Long> hitRateCfgProvider;
+
+    /** Histogram config provider. */
+    private final Function<String, long[]> histogramCfgProvider;
+
     /**
      * @param grpName Group name.
+     * @param hitRateCfgProvider HitRate config provider.
+     * @param histogramCfgProvider Histogram config provider.
      * @param log Logger.
      */
-    public MetricRegistry(String grpName, IgniteLogger log) {
+    public MetricRegistry(String grpName, Function<String, Long> hitRateCfgProvider,
+        Function<String, long[]> histogramCfgProvider, IgniteLogger log) {
         this.grpName = grpName;
         this.log = log;
+        this.hitRateCfgProvider = hitRateCfgProvider;
+        this.histogramCfgProvider = histogramCfgProvider;
     }
 
     /**
@@ -252,7 +265,16 @@ public class MetricRegistry implements Iterable<Metric> {
      * @see HitRateMetric
      */
     public HitRateMetric hitRateMetric(String name, @Nullable String desc, long rateTimeInterval, int size) {
-        return addMetric(name, new HitRateMetric(metricName(grpName, name), desc, rateTimeInterval, size));
+        String fullName = metricName(grpName, name);
+
+        HitRateMetric metric = addMetric(name, new HitRateMetric(fullName, desc, rateTimeInterval, size));
+
+        Long cfgRateTimeInterval = hitRateCfgProvider.apply(fullName);
+
+        if (cfgRateTimeInterval != null)
+            metric.reset(cfgRateTimeInterval, DFLT_SIZE);
+
+        return metric;
     }
 
     /**
@@ -276,7 +298,16 @@ public class MetricRegistry implements Iterable<Metric> {
      * @return {@link HistogramMetric}
      */
     public HistogramMetric histogram(String name, long[] bounds, @Nullable String desc) {
-        return addMetric(name, new HistogramMetric(metricName(grpName, name), desc, bounds));
+        String fullName = metricName(grpName, name);
+
+        HistogramMetric metric = addMetric(name, new HistogramMetric(fullName, desc, bounds));
+
+        long[] cfgBounds = histogramCfgProvider.apply(fullName);
+
+        if (cfgBounds != null)
+            metric.reset(cfgBounds);
+
+        return metric;
     }
 
     /**
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/MetricsMxBeanImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/MetricsMxBeanImpl.java
new file mode 100644
index 0000000..11c5242
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/MetricsMxBeanImpl.java
@@ -0,0 +1,75 @@
+/*
+ * 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.ignite.internal.processors.metric;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.mxbean.MetricsMxBean;
+
+/**
+ * {@link MetricsMxBean} implementation.
+ */
+public class MetricsMxBeanImpl implements MetricsMxBean {
+    /** Metric manager. */
+    private final GridMetricManager mmgr;
+
+    /** Logger. */
+    private final IgniteLogger log;
+
+    /**
+     * @param mmgr Metric manager.
+     * @param log Logger.
+     */
+    public MetricsMxBeanImpl(GridMetricManager mmgr, IgniteLogger log) {
+        this.mmgr = mmgr;
+        this.log = log;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void resetMetrics(String registry) {
+        assert registry != null;
+
+        MetricRegistry mreg = mmgr.registry(registry);
+
+        if (mreg != null)
+            mreg.reset();
+        else if (log.isInfoEnabled())
+            log.info("\"" + registry + "\" not found.");
+    }
+
+    /** {@inheritDoc} */
+    @Override public void configureHitRateMetric(String name, long rateTimeInterval) {
+        try {
+            mmgr.configureHitRate(name, rateTimeInterval);
+        }
+        catch (IgniteCheckedException e) {
+            throw new IgniteException(e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void configureHistogramMetric(String name, long[] bounds) {
+        try {
+            mmgr.configureHistogram(name, bounds);
+        }
+        catch (IgniteCheckedException e) {
+            throw new IgniteException(e);
+        }
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/impl/HitRateMetric.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/impl/HitRateMetric.java
index f77fdb6..5ac88a9 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/impl/HitRateMetric.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/impl/HitRateMetric.java
@@ -34,6 +34,9 @@ import org.jetbrains.annotations.Nullable;
  * 2^55 - 1 hits per interval can be accumulated without numeric overflow.
  */
 public class HitRateMetric extends AbstractMetric implements LongMetric {
+    /** Default counters array size. */
+    public static final int DFLT_SIZE = 10;
+
     /** Metric instance. */
     private volatile HitRateMetricImpl cntr;
 
@@ -51,11 +54,22 @@ public class HitRateMetric extends AbstractMetric implements LongMetric {
 
     /** {@inheritDoc} */
     @Override public void reset() {
-        cntr = new HitRateMetricImpl(cntr.rateTimeInterval, cntr.size);
+        HitRateMetricImpl cntr0 = cntr;
+
+        cntr = new HitRateMetricImpl(cntr0.rateTimeInterval, cntr0.size);
+    }
+
+    /**
+     * Resets metric with the new parametes.
+     *
+     * @param rateTimeInterval New rate time interval.
+     */
+    public void reset(long rateTimeInterval) {
+        reset(rateTimeInterval, DFLT_SIZE);
     }
 
     /**
-     * Resets metric with the new paramters.
+     * Resets metric with the new parameters.
      *
      * @param rateTimeInterval New rate time interval.
      * @param size New counters array size.
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/impl/MetricUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/impl/MetricUtils.java
index fad16b5..6be260e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/impl/MetricUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/impl/MetricUtils.java
@@ -74,6 +74,19 @@ public class MetricUtils {
     }
 
     /**
+     * Splits full metric name to registry name and metric name.
+     *
+     * @param name Full metric name.
+     * @return Array consist of registry name and metric name.
+     */
+    public static T2<String, String> fromFullName(String name) {
+        return new T2<> (
+            name.substring(0, name.lastIndexOf(SEPARATOR)),
+            name.substring(name.lastIndexOf(SEPARATOR) + 1)
+        );
+    }
+
+    /**
      * @param cacheName Cache name.
      * @param isNear Is near flag.
      * @return Cache metrics registry name.
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/GridArgumentCheck.java b/modules/core/src/main/java/org/apache/ignite/internal/util/GridArgumentCheck.java
index 5b523f6..529c8f1 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/GridArgumentCheck.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/GridArgumentCheck.java
@@ -154,6 +154,19 @@ public class GridArgumentCheck {
      * @param arr Array.
      * @param name Argument name.
      */
+    public static void notEmpty(long[] arr, String name) {
+        notNull(arr, name);
+
+        if (arr.length == 0)
+            throw new IllegalArgumentException(INVALID_ARG_MSG_PREFIX + name + NOT_EMPTY_SUFFIX);
+    }
+
+    /**
+     * Checks that given array is not empty.
+     *
+     * @param arr Array.
+     * @param name Argument name.
+     */
     public static void notEmpty(double[] arr, String name) {
         notNull(arr, name);
 
diff --git a/modules/core/src/main/java/org/apache/ignite/mxbean/DataRegionMetricsMXBean.java b/modules/core/src/main/java/org/apache/ignite/mxbean/DataRegionMetricsMXBean.java
index 78bc961..65fbf4e 100644
--- a/modules/core/src/main/java/org/apache/ignite/mxbean/DataRegionMetricsMXBean.java
+++ b/modules/core/src/main/java/org/apache/ignite/mxbean/DataRegionMetricsMXBean.java
@@ -136,6 +136,7 @@ public interface DataRegionMetricsMXBean extends DataRegionMetrics {
      * will return average allocation rate (pages per second) for the last minute.
      *
      * @param rateTimeInterval Time interval (in milliseconds) used for allocation and eviction rates calculations.
+     * @deprecated Use {@link MetricsMxBean#configureHitRateMetric(String, long)} instead.
      */
     @MXBeanDescription(
         "Sets time interval for pages allocation and eviction monitoring purposes."
@@ -146,6 +147,7 @@ public interface DataRegionMetricsMXBean extends DataRegionMetrics {
     @MXBeanParametersDescriptions(
         "Time interval (in milliseconds) to set."
     )
+    @Deprecated
     public void rateTimeInterval(long rateTimeInterval);
 
     /**
@@ -157,6 +159,7 @@ public interface DataRegionMetricsMXBean extends DataRegionMetrics {
      * calculation overhead.
      *
      * @param subInts A number of sub-intervals.
+     * @deprecated Use {@link MetricsMxBean#configureHitRateMetric(String, long)} instead.
      */
     @MXBeanDescription(
         "Sets a number of sub-intervals to calculate allocation and eviction rates metrics."
@@ -167,5 +170,6 @@ public interface DataRegionMetricsMXBean extends DataRegionMetrics {
     @MXBeanParametersDescriptions(
         "Number of subintervals to set."
     )
+    @Deprecated
     public void subIntervals(int subInts);
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/mxbean/DataStorageMetricsMXBean.java b/modules/core/src/main/java/org/apache/ignite/mxbean/DataStorageMetricsMXBean.java
index 3eec790..7034d8c 100644
--- a/modules/core/src/main/java/org/apache/ignite/mxbean/DataStorageMetricsMXBean.java
+++ b/modules/core/src/main/java/org/apache/ignite/mxbean/DataStorageMetricsMXBean.java
@@ -149,6 +149,7 @@ public interface DataStorageMetricsMXBean extends DataStorageMetrics {
      * {@link DataStorageConfiguration#setMetricsRateTimeInterval(long)} configuration property.
      *
      * @param rateTimeInterval Time interval (in milliseconds) used for allocation and eviction rates calculations.
+     * @deprecated Use {@link MetricsMxBean#configureHitRateMetric(String, long)} instead.
      */
     @MXBeanDescription(
         "Sets time interval for pages allocation and eviction monitoring purposes."
@@ -159,6 +160,7 @@ public interface DataStorageMetricsMXBean extends DataStorageMetrics {
     @MXBeanParametersDescriptions(
         "Time interval (in milliseconds) to set."
     )
+    @Deprecated
     public void rateTimeInterval(long rateTimeInterval);
 
     /**
@@ -167,6 +169,7 @@ public interface DataStorageMetricsMXBean extends DataStorageMetrics {
      * property.
      *
      * @param subInts A number of sub-intervals.
+     * @deprecated Use {@link MetricsMxBean#configureHitRateMetric(String, long)} instead.
      */
     @MXBeanDescription(
         "Sets a number of sub-intervals to calculate allocation and eviction rates metrics."
@@ -177,6 +180,7 @@ public interface DataStorageMetricsMXBean extends DataStorageMetrics {
     @MXBeanParametersDescriptions(
         "Number of subintervals to set."
     )
+    @Deprecated
     public void subIntervals(int subInts);
 
     /** {@inheritDoc} */
diff --git a/modules/core/src/main/java/org/apache/ignite/mxbean/IgniteMXBean.java b/modules/core/src/main/java/org/apache/ignite/mxbean/IgniteMXBean.java
index 686c333..79dfe61 100644
--- a/modules/core/src/main/java/org/apache/ignite/mxbean/IgniteMXBean.java
+++ b/modules/core/src/main/java/org/apache/ignite/mxbean/IgniteMXBean.java
@@ -685,16 +685,6 @@ public interface IgniteMXBean {
     void clearNodeLocalMap();
 
     /**
-     * Resets metrics for of a given registry.
-     *
-     * @param registry Metrics registry name.
-     */
-    @MXBeanDescription("Resets metrics of a given registry.")
-    @MXBeanParametersNames("registry")
-    @MXBeanParametersDescriptions("Metrics registry.")
-    public void resetMetrics(String registry);
-
-    /**
      * Checks cluster state.
      *
      * @return String representation of current cluster state.
diff --git a/modules/core/src/main/java/org/apache/ignite/mxbean/MetricsMxBean.java b/modules/core/src/main/java/org/apache/ignite/mxbean/MetricsMxBean.java
new file mode 100644
index 0000000..52eb844
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/mxbean/MetricsMxBean.java
@@ -0,0 +1,64 @@
+/*
+ * 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.ignite.mxbean;
+
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.internal.processors.metric.impl.HistogramMetric;
+import org.apache.ignite.internal.processors.metric.impl.HitRateMetric;
+
+/**
+ * Metrics MXBean interface.
+ */
+@MXBeanDescription("MBean that provides access to Ignite metrics management methods.")
+public interface MetricsMxBean {
+    /**
+     * Resets metrics for of a given registry.
+     *
+     * @param registry Metrics registry name.
+     */
+    @MXBeanDescription("Resets metrics of a given registry.")
+    @MXBeanParametersNames("registry")
+    @MXBeanParametersDescriptions("Metrics registry.")
+    public void resetMetrics(String registry);
+
+    /**
+     * Change {@link HitRateMetric} configuration.
+     * Call of this method will change metric configuration across all cluster nodes.
+     *
+     * @param name Metric name.
+     * @param rateTimeInterval New rate time interval.
+     * @throws IgniteException If some error occured.
+     */
+    @MXBeanDescription("Configure hitrate metric.")
+    @MXBeanParametersNames({"name", "cfg"})
+    @MXBeanParametersDescriptions({"Metric name.", "New rate time interval."})
+    public void configureHitRateMetric(String name, long rateTimeInterval) throws IgniteException;
+
+    /**
+     * Change {@link HistogramMetric} configuration.
+     * Call of this method will change metric configuration across all cluster nodes.
+     *
+     * @param name Metric name.
+     * @param bounds New bounds.
+     * @throws IgniteException If some error occured.
+     */
+    @MXBeanDescription("Configure histogram metric.")
+    @MXBeanParametersNames({"name", "cfg"})
+    @MXBeanParametersDescriptions({"Metric name.", "New bounds."})
+    public void configureHistogramMetric(String name, long[] bounds) throws IgniteException;
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/metric/IoStatisticsMetricsLocalMXBeanImplSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/metric/IoStatisticsMetricsLocalMXBeanImplSelfTest.java
index 58e68c7..c32c567 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/metric/IoStatisticsMetricsLocalMXBeanImplSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/metric/IoStatisticsMetricsLocalMXBeanImplSelfTest.java
@@ -18,21 +18,13 @@
 
 package org.apache.ignite.internal.metric;
 
-import java.lang.management.ManagementFactory;
 import java.util.stream.StreamSupport;
-import javax.management.MBeanServer;
-import javax.management.MBeanServerInvocationHandler;
 import javax.management.MalformedObjectNameException;
-import javax.management.ObjectName;
-import org.apache.ignite.IgniteException;
 import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.internal.IgniteEx;
-import org.apache.ignite.internal.IgniteKernal;
 import org.apache.ignite.internal.processors.metric.GridMetricManager;
 import org.apache.ignite.internal.processors.metric.MetricRegistry;
-import org.apache.ignite.internal.util.typedef.internal.U;
-import org.apache.ignite.mxbean.IgniteMXBean;
 import org.apache.ignite.spi.metric.LongMetric;
 import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
 import org.junit.Test;
@@ -46,6 +38,7 @@ import static org.apache.ignite.internal.metric.IoStatisticsHolderQuery.LOGICAL_
 import static org.apache.ignite.internal.metric.IoStatisticsHolderQuery.PHYSICAL_READS;
 import static org.apache.ignite.internal.metric.IoStatisticsType.CACHE_GROUP;
 import static org.apache.ignite.internal.metric.IoStatisticsType.HASH_INDEX;
+import static org.apache.ignite.internal.metric.MetricsConfigurationTest.metricsBean;
 import static org.apache.ignite.internal.processors.metric.impl.MetricUtils.metricName;
 
 /**
@@ -180,20 +173,6 @@ public class IoStatisticsMetricsLocalMXBeanImplSelfTest extends GridCommonAbstra
      * @param grpName Group name to reset metrics.
      */
     public static void resetMetric(IgniteEx ignite, String grpName) {
-        try {
-            ObjectName mbeanName = U.makeMBeanName(ignite.name(), "Kernal",
-                IgniteKernal.class.getSimpleName());
-
-            MBeanServer mbeanSrv = ManagementFactory.getPlatformMBeanServer();
-
-            if (!mbeanSrv.isRegistered(mbeanName))
-                fail("MBean is not registered: " + mbeanName.getCanonicalName());
-
-            IgniteMXBean bean = MBeanServerInvocationHandler.newProxyInstance(mbeanSrv, mbeanName, IgniteMXBean.class, false);
-
-            bean.resetMetrics(grpName);
-        } catch (MalformedObjectNameException e) {
-            throw new IgniteException(e);
-        }
+        metricsBean(ignite).resetMetrics(grpName);
     }
 }
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/metric/JmxExporterSpiTest.java b/modules/core/src/test/java/org/apache/ignite/internal/metric/JmxExporterSpiTest.java
index a768a29..d32a2ba 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/metric/JmxExporterSpiTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/metric/JmxExporterSpiTest.java
@@ -504,7 +504,7 @@ public class JmxExporterSpiTest extends AbstractExporterSpiTest {
     }
 
     /** */
-    public DynamicMBean mbean(IgniteEx g, String grp, String name) throws MalformedObjectNameException {
+    public static DynamicMBean mbean(IgniteEx g, String grp, String name) throws MalformedObjectNameException {
         ObjectName mbeanName = U.makeMBeanName(g.name(), grp, name);
 
         MBeanServer mbeanSrv = ManagementFactory.getPlatformMBeanServer();
@@ -518,7 +518,7 @@ public class JmxExporterSpiTest extends AbstractExporterSpiTest {
     /** */
     @Test
     public void testHistogramSearchByName() throws Exception {
-        MetricRegistry mreg = new MetricRegistry("test", null);
+        MetricRegistry mreg = new MetricRegistry("test", name -> null, name -> null, null);
 
         createTestHistogram(mreg);
 
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/metric/MetricsConfigurationTest.java b/modules/core/src/test/java/org/apache/ignite/internal/metric/MetricsConfigurationTest.java
new file mode 100644
index 0000000..f2cbaec
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/metric/MetricsConfigurationTest.java
@@ -0,0 +1,382 @@
+/*
+ * 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.ignite.internal.metric;
+
+import java.lang.management.ManagementFactory;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerInvocationHandler;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.DataStorageConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.processors.metric.MetricRegistry;
+import org.apache.ignite.internal.processors.metric.MetricsMxBeanImpl;
+import org.apache.ignite.internal.processors.metric.impl.HistogramMetric;
+import org.apache.ignite.internal.processors.metric.impl.HitRateMetric;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.mxbean.MetricsMxBean;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.Test;
+
+import static org.apache.ignite.configuration.WALMode.FSYNC;
+import static org.apache.ignite.internal.processors.cache.transactions.TransactionMetricsAdapter.METRIC_SYSTEM_TIME_HISTOGRAM;
+import static org.apache.ignite.internal.processors.metric.GridMetricManager.HISTOGRAM_CFG_PREFIX;
+import static org.apache.ignite.internal.processors.metric.GridMetricManager.HITRATE_CFG_PREFIX;
+import static org.apache.ignite.internal.processors.metric.GridMetricManager.TX_METRICS;
+import static org.apache.ignite.internal.processors.metric.impl.MetricUtils.cacheMetricsRegistryName;
+import static org.apache.ignite.internal.processors.metric.impl.MetricUtils.metricName;
+import static org.apache.ignite.testframework.GridTestUtils.assertThrowsWithCause;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertNotEquals;
+
+/** Tests metrics configuration. */
+public class MetricsConfigurationTest extends GridCommonAbstractTest {
+    /** Test metric registry. */
+    public static final String TEST_REG = "testReg";
+
+    /** Test hitrate metric name. */
+    public static final String HITRATE_NAME = "hitrate";
+
+    /** Test histogram metric name. */
+    public static final String HISTOGRAM_NAME = "histogram";
+
+    /** Test bounds. */
+    public static final long[] BOUNDS = new long[] {50, 100};
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() throws Exception {
+        cleanPersistenceDir();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+        if (igniteInstanceName.startsWith("persistent")) {
+            DataStorageConfiguration dsCfg = new DataStorageConfiguration();
+
+            dsCfg.getDefaultDataRegionConfiguration().setPersistenceEnabled(true);
+            dsCfg.setWalMode(FSYNC);
+
+            cfg.setDataStorageConfiguration(dsCfg);
+        }
+
+        return cfg;
+    }
+
+    /** Tests configuration of {@link HitRateMetric}. */
+    @Test
+    public void testHitRateConfiguration() throws Exception {
+        try (IgniteEx g = startGrid(0)) {
+            MetricsMxBean bean = metricsBean(g);
+
+            //Empty name.
+            assertThrowsWithCause(
+                () -> bean.configureHitRateMetric(null, 1),
+                NullPointerException.class);
+
+            //Wrong rateTimeInterval value.
+            assertThrowsWithCause(
+                () -> bean.configureHitRateMetric("io.dataregion.default.AllocationRate", 0),
+                IllegalArgumentException.class);
+
+            assertThrowsWithCause(
+                () -> bean.configureHitRateMetric("io.dataregion.default.AllocationRate", -1),
+                IllegalArgumentException.class);
+
+            bean.configureHitRateMetric("io.dataregion.default.AllocationRate", 5000);
+
+            HitRateMetric allocationRate = g.context().metric().registry(metricName("io.dataregion.default"))
+                .findMetric("AllocationRate");
+
+            assertEquals(5000, allocationRate.rateTimeInterval());
+        }
+    }
+
+    /** Tests configuration of {@link HistogramMetric}. */
+    @Test
+    public void testHistogramConfiguration() throws Exception {
+        try (IgniteEx g = startGrid(0)) {
+            MetricsMxBean bean = metricsBean(g);
+
+            //Empty name.
+            assertThrowsWithCause(() -> bean.configureHistogramMetric(null, BOUNDS), NullPointerException.class);
+
+            //Wrong bounds value.
+            assertThrowsWithCause(
+                () -> bean.configureHistogramMetric(metricName(TX_METRICS, METRIC_SYSTEM_TIME_HISTOGRAM), null),
+                NullPointerException.class);
+
+            assertThrowsWithCause(
+                () -> bean.configureHistogramMetric(metricName(TX_METRICS, METRIC_SYSTEM_TIME_HISTOGRAM), new long[0]),
+                IllegalArgumentException.class);
+
+            bean.configureHistogramMetric(metricName(TX_METRICS, METRIC_SYSTEM_TIME_HISTOGRAM), BOUNDS);
+
+            HistogramMetric systemTime = g.context().metric().registry(TX_METRICS)
+                .findMetric(METRIC_SYSTEM_TIME_HISTOGRAM);
+
+            assertArrayEquals(BOUNDS, systemTime.bounds());
+        }
+    }
+
+    /** Tests metric configuration applied on all nodes. */
+    @Test
+    public void testConfigurationSeveralNodes() throws Exception {
+        try (IgniteEx g0 = startGrid(0); IgniteEx g1 = startGrid(1)) {
+            assertNotEquals(BOUNDS.length, g0.context().metric().registry(TX_METRICS)
+                .<HistogramMetric>findMetric(METRIC_SYSTEM_TIME_HISTOGRAM).bounds().length);
+
+            assertNotEquals(BOUNDS.length, g1.context().metric().registry(TX_METRICS)
+                .<HistogramMetric>findMetric(METRIC_SYSTEM_TIME_HISTOGRAM).bounds().length);
+
+            metricsBean(g0).configureHistogramMetric(metricName(TX_METRICS, METRIC_SYSTEM_TIME_HISTOGRAM), BOUNDS);
+
+            assertArrayEquals(BOUNDS, g0.context().metric().registry(TX_METRICS)
+                .<HistogramMetric>findMetric(METRIC_SYSTEM_TIME_HISTOGRAM).bounds());
+
+            assertArrayEquals(BOUNDS, g1.context().metric().registry(TX_METRICS)
+                .<HistogramMetric>findMetric(METRIC_SYSTEM_TIME_HISTOGRAM).bounds());
+        }
+    }
+
+    /** Tests metric configuration applied on all nodes. */
+    @Test
+    public void testNodeRestart() throws Exception {
+        checkOnStartAndRestart((g0, g1) -> {
+            assertNotEquals(BOUNDS.length, g0.context().metric().registry(TX_METRICS)
+                .<HistogramMetric>findMetric(METRIC_SYSTEM_TIME_HISTOGRAM).bounds().length);
+
+            assertNotEquals(BOUNDS.length, g1.context().metric().registry(TX_METRICS)
+                .<HistogramMetric>findMetric(METRIC_SYSTEM_TIME_HISTOGRAM).bounds().length);
+
+            metricsBean(g0).configureHistogramMetric(metricName(TX_METRICS, METRIC_SYSTEM_TIME_HISTOGRAM), BOUNDS);
+
+            assertArrayEquals(BOUNDS, g0.context().metric().registry(TX_METRICS)
+                .<HistogramMetric>findMetric(METRIC_SYSTEM_TIME_HISTOGRAM).bounds());
+
+            assertArrayEquals(BOUNDS, g1.context().metric().registry(TX_METRICS)
+                .<HistogramMetric>findMetric(METRIC_SYSTEM_TIME_HISTOGRAM).bounds());
+        }, (g0, g1) -> {
+            assertArrayEquals(BOUNDS, g0.context().metric().registry(TX_METRICS)
+                .<HistogramMetric>findMetric(METRIC_SYSTEM_TIME_HISTOGRAM).bounds());
+
+            assertArrayEquals(BOUNDS, g1.context().metric().registry(TX_METRICS)
+                .<HistogramMetric>findMetric(METRIC_SYSTEM_TIME_HISTOGRAM).bounds());
+        });
+    }
+
+    /** Tests metric configuration removed on registry remove. */
+    @Test
+    public void testConfigRemovedOnRegistryRemove() throws Exception {
+        checkOnStartAndRestart((g0, g1) -> {
+            MetricRegistry mreg = g0.context().metric().registry(TEST_REG);
+
+            mreg.hitRateMetric(HITRATE_NAME, "test", 10000, 5);
+            mreg.histogram(HISTOGRAM_NAME, new long[] {250, 500}, "test");
+
+            metricsBean(g0).configureHistogramMetric(metricName(TEST_REG, HISTOGRAM_NAME), BOUNDS);
+            metricsBean(g0).configureHitRateMetric(metricName(TEST_REG, HITRATE_NAME), 1000);
+        }, (g0, g1) -> {
+            MetricRegistry mreg = g0.context().metric().registry(TEST_REG);
+
+            HitRateMetric hitRate = mreg.hitRateMetric(HITRATE_NAME, "test", 10000, 5);
+            HistogramMetric histogram = mreg.histogram(HISTOGRAM_NAME, new long[] {250, 500}, "test");
+
+            assertEquals(1000, hitRate.rateTimeInterval());
+            assertArrayEquals(BOUNDS, histogram.bounds());
+
+            assertEquals((Long)1000L,
+                g0.context().distributedMetastorage().read(metricName(HITRATE_CFG_PREFIX, TEST_REG, HITRATE_NAME)));
+
+            assertArrayEquals(BOUNDS,
+                g0.context().distributedMetastorage().read(metricName(HISTOGRAM_CFG_PREFIX, TEST_REG, HISTOGRAM_NAME)));
+
+            assertEquals((Long)1000L,
+                g1.context().distributedMetastorage().read(metricName(HITRATE_CFG_PREFIX, TEST_REG, HITRATE_NAME)));
+
+            assertArrayEquals(BOUNDS,
+                g1.context().distributedMetastorage().read(metricName(HISTOGRAM_CFG_PREFIX, TEST_REG, HISTOGRAM_NAME)));
+
+            g0.context().metric().remove(TEST_REG);
+
+            assertNull(
+                g0.context().distributedMetastorage().read(metricName(HITRATE_CFG_PREFIX, TEST_REG, HITRATE_NAME)));
+            assertNull(
+                g0.context().distributedMetastorage().read(metricName(HISTOGRAM_CFG_PREFIX, TEST_REG, HISTOGRAM_NAME)));
+
+            assertNull(
+                g1.context().distributedMetastorage().read(metricName(HITRATE_CFG_PREFIX, TEST_REG, HITRATE_NAME)));
+            assertNull(
+                g1.context().distributedMetastorage().read(metricName(HISTOGRAM_CFG_PREFIX, TEST_REG, HISTOGRAM_NAME)));
+        });
+    }
+
+    /** Tests metric configuration removed on registry remove. */
+    @Test
+    public void testConfigRemovedOnCacheRemove() throws Exception {
+        String cacheRegName = cacheMetricsRegistryName("test", false);
+
+        checkOnStartAndRestart((g0, g1) -> {
+            g0.createCache("test");
+
+            awaitPartitionMapExchange();
+
+            HistogramMetric getTime = g0.context().metric().registry(cacheRegName).findMetric("GetTime");
+
+            assertNotEquals(BOUNDS.length, getTime.bounds().length);
+
+            metricsBean(g0).configureHistogramMetric(metricName(cacheRegName, "GetTime"), BOUNDS);
+
+            assertArrayEquals(BOUNDS,
+                g0.context().metric().registry(cacheRegName).<HistogramMetric>findMetric("GetTime").bounds());
+
+            assertArrayEquals(BOUNDS,
+                g1.context().metric().registry(cacheRegName).<HistogramMetric>findMetric("GetTime").bounds());
+        }, (g0, g1) -> {
+            assertArrayEquals(BOUNDS,
+                g0.context().metric().registry(cacheRegName).<HistogramMetric>findMetric("GetTime").bounds());
+
+            assertArrayEquals(BOUNDS,
+                g1.context().metric().registry(cacheRegName).<HistogramMetric>findMetric("GetTime").bounds());
+
+            g0.destroyCache("test");
+
+            awaitPartitionMapExchange();
+
+            assertNull(g0.context().distributedMetastorage().read(metricName(cacheRegName, "GetTime")));
+            assertNull(g1.context().distributedMetastorage().read(metricName(cacheRegName, "GetTime")));
+        });
+    }
+
+    /** Tests metric configuration removed on registry remove. */
+    @Test
+    public void testConfigRemovedOnCacheGroupRemove() throws Exception {
+        String cacheRegName = cacheMetricsRegistryName("test", false);
+        String mname = metricName(cacheRegName, "GetTime");
+
+        checkOnStartAndRestart((g0, g1) -> {
+            CacheConfiguration<String, String> ccfg = new CacheConfiguration<>("test");
+
+            ccfg.setGroupName("group");
+
+            g0.createCache(ccfg);
+
+            awaitPartitionMapExchange();
+
+            HistogramMetric getTime = g0.context().metric().registry(cacheRegName).findMetric("GetTime");
+
+            assertNotEquals(BOUNDS.length, getTime.bounds().length);
+
+            metricsBean(g0).configureHistogramMetric(mname, BOUNDS);
+
+            assertArrayEquals(BOUNDS,
+                g0.context().metric().registry(cacheRegName).<HistogramMetric>findMetric("GetTime").bounds());
+
+            assertArrayEquals(BOUNDS,
+                g1.context().metric().registry(cacheRegName).<HistogramMetric>findMetric("GetTime").bounds());
+        }, (g0, g1) -> {
+            assertArrayEquals(BOUNDS,
+                g0.context().metric().registry(cacheRegName).<HistogramMetric>findMetric("GetTime").bounds());
+
+            assertArrayEquals(BOUNDS,
+                g1.context().metric().registry(cacheRegName).<HistogramMetric>findMetric("GetTime").bounds());
+
+            g0.destroyCache("test");
+
+            awaitPartitionMapExchange();
+
+            assertNull(g0.context().distributedMetastorage().read(mname));
+            assertNull(g1.context().distributedMetastorage().read(mname));
+        });
+    }
+
+    /** Executes provided closures after cluster start and after cluster restart. */
+    public void checkOnStartAndRestart(IgniteBiInClosureX<IgniteEx, IgniteEx> afterStart,
+        IgniteBiInClosureX<IgniteEx, IgniteEx> afterRestart) throws Exception {
+
+        IgniteEx g0 = startGrid("persistent-0");
+        IgniteEx g1 = startGrid("persistent-1");
+
+        try {
+            g0.cluster().active(true);
+
+            afterStart.apply(g0, g1);
+
+            g0.close();
+            g1.close();
+
+            g0 = startGrid("persistent-0");
+            g1 = startGrid("persistent-1");
+
+            g0.cluster().active(true);
+
+            afterRestart.apply(g0, g1);
+        }
+        finally {
+            g0.close();
+            g1.close();
+        }
+    }
+
+    /** */
+    public static MetricsMxBean metricsBean(IgniteEx g) {
+        try {
+            ObjectName mbeanName = U.makeMBeanName(g.name(), "Metrics", MetricsMxBeanImpl.class.getSimpleName());
+
+            MBeanServer mbeanSrv = ManagementFactory.getPlatformMBeanServer();
+
+            if (!mbeanSrv.isRegistered(mbeanName))
+                throw new IgniteException("MBean not registered.");
+
+            return MBeanServerInvocationHandler.newProxyInstance(mbeanSrv, mbeanName, MetricsMxBean.class, false);
+        }
+        catch (MalformedObjectNameException e) {
+            throw new IgniteException(e);
+        }
+    }
+
+    /** */
+    public interface IgniteBiInClosureX<E1, E2> {
+        /**
+         * Closure body.
+         *
+         * @param e1 First parameter.
+         * @param e2 Second parameter.
+         */
+        public default void apply(E1 e1, E2 e2) {
+            try {
+                applyx(e1, e2);
+            }
+            catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        /**
+         * Closure body that can throw exception.
+         *
+         * @param e1 First parameter.
+         * @param e2 Second parameter.
+         */
+        public void applyx(E1 e1, E2 e2) throws Exception;
+    }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/metric/MetricsSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/metric/MetricsSelfTest.java
index 28883aa..a8eaf58 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/metric/MetricsSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/metric/MetricsSelfTest.java
@@ -49,6 +49,7 @@ import org.junit.Test;
 
 import static java.util.Arrays.asList;
 import static java.util.stream.Collectors.toSet;
+import static org.apache.ignite.internal.processors.metric.impl.MetricUtils.fromFullName;
 import static org.apache.ignite.internal.processors.metric.impl.MetricUtils.histogramBucketNames;
 import static org.apache.ignite.testframework.GridTestUtils.runAsync;
 import static org.junit.Assert.assertArrayEquals;
@@ -61,7 +62,7 @@ public class MetricsSelfTest extends GridCommonAbstractTest {
     /** */
     @Before
     public void setUp() throws Exception {
-        mreg = new MetricRegistry("group", null);
+        mreg = new MetricRegistry("group", name -> null, name -> null, null);
     }
 
     /** */
@@ -271,7 +272,7 @@ public class MetricsSelfTest extends GridCommonAbstractTest {
     /** */
     @Test
     public void testGetMetrics() throws Exception {
-        MetricRegistry mreg = new MetricRegistry("group", null);
+        MetricRegistry mreg = new MetricRegistry("group", name -> null, name -> null, null);
 
         mreg.longMetric("test1", "");
         mreg.longMetric("test2", "");
@@ -292,7 +293,7 @@ public class MetricsSelfTest extends GridCommonAbstractTest {
     /** */
     @Test
     public void testRemove() throws Exception {
-        MetricRegistry mreg = new MetricRegistry("group", null);
+        MetricRegistry mreg = new MetricRegistry("group", name -> null, name -> null, null);
 
         AtomicLongMetric cntr = mreg.longMetric("my.name", null);
         AtomicLongMetric cntr2 = mreg.longMetric("my.name.x", null);
@@ -358,6 +359,13 @@ public class MetricsSelfTest extends GridCommonAbstractTest {
         assertTrue("Computed values should be cached", names == histogramBucketNames(h, cache));
     }
 
+    /** */
+    @Test
+    public void testFromFullName() {
+        assertEquals(new T2<>("org.apache", "ignite"), fromFullName("org.apache.ignite"));
+
+        assertEquals(new T2<>("org", "apache"), fromFullName("org.apache"));
+    }
 
     /** */
     private void run(Runnable r, int cnt) throws org.apache.ignite.IgniteCheckedException {
diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite9.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite9.java
index d4dcf48..b5b27dd 100644
--- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite9.java
+++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite9.java
@@ -27,6 +27,7 @@ import org.apache.ignite.internal.metric.IoStatisticsMetricsLocalMXBeanImplSelfT
 import org.apache.ignite.internal.metric.IoStatisticsSelfTest;
 import org.apache.ignite.internal.metric.JmxExporterSpiTest;
 import org.apache.ignite.internal.metric.LogExporterSpiTest;
+import org.apache.ignite.internal.metric.MetricsConfigurationTest;
 import org.apache.ignite.internal.metric.MetricsSelfTest;
 import org.apache.ignite.internal.metric.ReadMetricsOnNodeStartupTest;
 import org.apache.ignite.internal.metric.SystemViewSelfTest;
@@ -47,9 +48,9 @@ import org.apache.ignite.internal.processors.cache.transactions.TxDataConsistenc
 import org.apache.ignite.internal.processors.cache.transactions.TxPartitionCounterStateConsistencyHistoryRebalanceTest;
 import org.apache.ignite.internal.processors.cache.transactions.TxPartitionCounterStateConsistencyTest;
 import org.apache.ignite.internal.processors.cache.transactions.TxPartitionCounterStateConsistencyVolatileRebalanceTest;
-import org.apache.ignite.internal.processors.cache.transactions.TxPartitionCounterStateOnePrimaryTwoBackupsFailAllHistoryRebalanceTest;
 import org.apache.ignite.internal.processors.cache.transactions.TxPartitionCounterStateOnePrimaryOneBackupHistoryRebalanceTest;
 import org.apache.ignite.internal.processors.cache.transactions.TxPartitionCounterStateOnePrimaryOneBackupTest;
+import org.apache.ignite.internal.processors.cache.transactions.TxPartitionCounterStateOnePrimaryTwoBackupsFailAllHistoryRebalanceTest;
 import org.apache.ignite.internal.processors.cache.transactions.TxPartitionCounterStateOnePrimaryTwoBackupsFailAllTest;
 import org.apache.ignite.internal.processors.cache.transactions.TxPartitionCounterStateOnePrimaryTwoBackupsHistoryRebalanceTest;
 import org.apache.ignite.internal.processors.cache.transactions.TxPartitionCounterStateOnePrimaryTwoBackupsTest;
@@ -110,12 +111,13 @@ public class IgniteCacheTestSuite9 {
         GridTestUtils.addTestIfNeeded(suite, TxPartitionCounterStateConsistencyVolatileRebalanceTest.class, ignoredTests);
         GridTestUtils.addTestIfNeeded(suite, TxCrossCachePartitionConsistencyTest.class, ignoredTests);
 
-        // IO statistics
+        // IO statistics.
         GridTestUtils.addTestIfNeeded(suite, IoStatisticsCachePersistenceSelfTest.class, ignoredTests);
         GridTestUtils.addTestIfNeeded(suite, IoStatisticsCacheSelfTest.class, ignoredTests);
         GridTestUtils.addTestIfNeeded(suite, IoStatisticsSelfTest.class, ignoredTests);
         GridTestUtils.addTestIfNeeded(suite, IoStatisticsMetricsLocalMXBeanImplSelfTest.class, ignoredTests);
         GridTestUtils.addTestIfNeeded(suite, MetricsSelfTest.class, ignoredTests);
+        GridTestUtils.addTestIfNeeded(suite, MetricsConfigurationTest.class, ignoredTests);
         GridTestUtils.addTestIfNeeded(suite, SystemViewSelfTest.class, ignoredTests);
         GridTestUtils.addTestIfNeeded(suite, CacheMetricsAddRemoveTest.class, ignoredTests);
         GridTestUtils.addTestIfNeeded(suite, JmxExporterSpiTest.class, ignoredTests);
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
index 992f54b..2ff4472 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
@@ -34,7 +34,6 @@ import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.GridTopic;
 import org.apache.ignite.internal.managers.communication.GridIoPolicy;
 import org.apache.ignite.internal.managers.communication.GridMessageListener;
-import org.apache.ignite.internal.metric.IoStatisticsHolder;
 import org.apache.ignite.internal.metric.IoStatisticsHolderIndex;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
@@ -148,6 +147,9 @@ public class H2TreeIndex extends H2TreeIndexBase {
     /** Query context registry. */
     private final QueryContextRegistry qryCtxRegistry;
 
+    /** IO statistics holder. */
+    private final IoStatisticsHolderIndex stats;
+
     /**
      * @param cctx Cache context.
      * @param rowCache Row cache.
@@ -215,7 +217,7 @@ public class H2TreeIndex extends H2TreeIndexBase {
 
         AtomicInteger maxCalculatedInlineSize = new AtomicInteger();
 
-        IoStatisticsHolder stats = new IoStatisticsHolderIndex(
+        stats = new IoStatisticsHolderIndex(
             SORTED_INDEX,
             cctx.name(),
             idxName,
@@ -517,6 +519,8 @@ public class H2TreeIndex extends H2TreeIndexBase {
 
                     dropMetaPage(i);
                 }
+
+                ctx.metric().remove(stats.metricRegistryName());
             }
         }
         catch (IgniteCheckedException e) {
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/IoStatisticsBasicIndexSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/IoStatisticsBasicIndexSelfTest.java
index 0e176bd..809a1df 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/IoStatisticsBasicIndexSelfTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/IoStatisticsBasicIndexSelfTest.java
@@ -30,6 +30,7 @@ import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
+import org.apache.ignite.Ignite;
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.IgniteException;
 import org.apache.ignite.cache.QueryEntity;
@@ -53,6 +54,7 @@ import static org.apache.ignite.internal.metric.IoStatisticsMetricsLocalMXBeanIm
 import static org.apache.ignite.internal.metric.IoStatisticsType.CACHE_GROUP;
 import static org.apache.ignite.internal.metric.IoStatisticsType.HASH_INDEX;
 import static org.apache.ignite.internal.metric.IoStatisticsType.SORTED_INDEX;
+import static org.apache.ignite.internal.processors.cache.index.AbstractSchemaSelfTest.queryProcessor;
 import static org.apache.ignite.internal.processors.metric.impl.MetricUtils.metricName;
 
 /**
@@ -175,6 +177,32 @@ public class IoStatisticsBasicIndexSelfTest extends AbstractIndexingCommonTest {
         checkStat();
     }
 
+    /** Checks that {@link MetricRegistry} with the sorted index IO statistics removed on index drop. */
+    @Test
+    public void testMetricRegistryRemovedOnIndexDrop() throws Exception {
+        indexes = Collections.emptyList();
+
+        startGrid();
+
+        grid().cluster().active(true);
+
+        execute(grid(), "CREATE TABLE t(id int, name varchar, primary key (id))");
+
+        execute(grid(), "CREATE INDEX MY_IDX ON t(name)");
+
+        MetricRegistry mreg =
+            grid().context().metric().registry(metricName(SORTED_INDEX.metricGroupName(), "SQL_PUBLIC_T", "MY_IDX"));
+
+        assertTrue(mreg.iterator().hasNext());
+        assertNotNull(mreg.findMetric("name"));
+
+        execute(grid(), "DROP INDEX MY_IDX");
+
+        mreg = grid().context().metric().registry(metricName(SORTED_INDEX.metricGroupName(), "SQL_PUBLIC_T", "MY_IDX"));
+
+        assertFalse(mreg.iterator().hasNext());
+    }
+
     /** */
     private void checkStat() throws Exception {
         GridMetricManager mmgr = grid().context().metric();
@@ -557,4 +585,18 @@ public class IoStatisticsBasicIndexSelfTest extends AbstractIndexingCommonTest {
             return S.toString(Pojo.class, this);
         }
     }
+
+    /**
+     * Execute query on given node.
+     *
+     * @param node Node.
+     * @param sql Statement.
+     */
+    private List<List<?>> execute(Ignite node, String sql, Object... args) {
+        SqlFieldsQuery qry = new SqlFieldsQuery(sql)
+            .setArgs(args)
+            .setSchema("PUBLIC");
+
+        return queryProcessor(node).querySqlFields(qry, true).getAll();
+    }
 }