You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by qi...@apache.org on 2019/07/10 08:03:43 UTC

[incubator-iotdb] branch master updated: [ IOTDB-116]Performance statistics module (#195)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 2f5e7ff  [ IOTDB-116]Performance statistics module (#195)
2f5e7ff is described below

commit 2f5e7ffe34a19bbdf79b29ce75ab16bff3bfee85
Author: suyue <23...@qq.com>
AuthorDate: Wed Jul 10 16:03:38 2019 +0800

    [ IOTDB-116]Performance statistics module (#195)
    
    * add time cost statstic
---
 .../4-Performance Monitor.md                       |  78 ++++
 .../{4-System log.md => 5-System log.md}           |   0
 .../{5-Data Management.md => 6-Data Management.md} |   0
 ...e.md => 7-Build and use IoTDB by Dockerfile.md} |   0
 .../4-Performance Monitor.md                       |  79 ++++
 .../{4-System log.md => 5-System log.md}           |   0
 .../{5-Data Management.md => 6-Data Management.md} |   0
 ...e.md => 7-Build and use IoTDB by Dockerfile.md} |   0
 iotdb/iotdb/conf/iotdb-engine.properties           |  12 +
 iotdb/iotdb/conf/logback.xml                       |  20 +
 .../org/apache/iotdb/db/concurrent/ThreadName.java |   3 +-
 .../java/org/apache/iotdb/db/conf/IoTDBConfig.java |  38 ++
 .../org/apache/iotdb/db/conf/IoTDBDescriptor.java  |  10 +
 .../db/cost/statistic/ConcurrentCircularArray.java |  69 ++++
 .../iotdb/db/cost/statistic/Measurement.java       | 428 +++++++++++++++++++++
 .../iotdb/db/cost/statistic/MeasurementMBean.java  |  62 +++
 .../apache/iotdb/db/cost/statistic/Operation.java  |  36 ++
 .../org/apache/iotdb/db/qp/QueryProcessor.java     |   3 +-
 .../java/org/apache/iotdb/db/service/IoTDB.java    |   2 +
 .../org/apache/iotdb/db/service/ServiceType.java   |   1 +
 .../org/apache/iotdb/db/service/TSServiceImpl.java |  25 +-
 .../db/cost/statistic/PerformanceStatTest.java     |  71 ++++
 .../java/org/apache/iotdb/jdbc/IoTDBStatement.java |   1 -
 23 files changed, 928 insertions(+), 10 deletions(-)

diff --git a/docs/Documentation-CHN/UserGuideV0.7.0/4-Deployment and Management/4-Performance Monitor.md b/docs/Documentation-CHN/UserGuideV0.7.0/4-Deployment and Management/4-Performance Monitor.md
new file mode 100644
index 0000000..880564c
--- /dev/null
+++ b/docs/Documentation-CHN/UserGuideV0.7.0/4-Deployment and Management/4-Performance Monitor.md	
@@ -0,0 +1,78 @@
+<!--
+
+    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.
+
+-->
+
+# 第4章 系统部署与管理
+## 性能监控
+### 引言
+
+性能监控模块用来监控IOTDB每一个操作的耗时,以便用户更好的了解数据库的整体性能。此模块会统计每一种操作的平均耗时,以及耗时在一定时间区间内(1ms,4ms,16ms,64ms,256ms,1024ms,以上)的操作的比例。输出文件在log_measure.log中。输出样例如下:
+
+<img style="width:100%; max-width:800px; max-height:600px; margin-left:auto; margin-right:auto; display:block;" src="https://user-images.githubusercontent.com/13203019/60937461-14296f80-a303-11e9-9602-a7bed624bfb3.png">
+ 
+### 配置参数
+
+配置文件位置:conf/iotdb-engine.properties
+
+<center>**表 -配置参数以及描述项**
+
+|参数|默认值|描述|
+|:---|:---|:---|
+|enable\_performance\_stat|false|是否开启性能监控模块|
+|performance\_stat\_display\_interval|60000|打印统计结果的时间延迟,以毫秒为单位|
+|performance_stat_memory_in_kb|20|性能监控模块使用的内存阈值,单位为KB|
+</center>
+ 
+### 利用JMX MBean动态调节参数
+
+通过端口31999连接jconsole,并在上方菜单项中选择‘MBean’. 展开侧边框并选择 'org.apache.iotdb.db.cost.statistic'. 将会得到如下图所示结果:
+
+<img style="width:100%; max-width:800px; max-height:600px; margin-left:auto; margin-right:auto; display:block;" src="https://user-images.githubusercontent.com/13203019/60937484-30c5a780-a303-11e9-8e92-04c413df2088.png">
+ 
+**属性**
+
+1. EnableStat:是否开启性能监控模块,如果被设置为true,则性能监控模块会记录每个操作的耗时并打印结果。这个参数不能直接通过jconsole直接更改,但可通过下方的函数来进行动态设置。
+2. DisplayIntervalInMs:相邻两次打印结果的时间间隔。这个参数可以直接设置,但它要等性能监控模块重启才会生效。重启性能监控模块可以通过先调用 stopStatistic()然后调用startContinuousStatistics()或者直接调用 startOneTimeStatistics()实现。
+3. OperationSwitch:这个属性用来展示针对每一种操作是否开启了监控统计,map的键为操作的名字,值为是否针对这种操作开启性能监控。这个参数不能直接通过jconsole直接更改,但可通过下方的 'changeOperationSwitch()'函数来进行动态设置。
+
+**操作**
+
+1. startContinuousStatistics:开启性能监控并以‘DisplayIntervalInMs’的时间间隔打印统计结果。 
+2. startOneTimeStatistics:开启性能监控并以‘DisplayIntervalInMs’的时间延迟打印一次统计结果。 
+3. stopStatistic:关闭性能监控。
+4. clearStatisticalState(): 清除以统计的结果,从新开始统计。
+5. changeOperationSwitch(String operationName, Boolean operationState):设置是否针对每一种不同的操作开启监控。参数‘operationName是操作的名称,在OperationSwitch属性中展示了所有操作的名称。参数 ‘operationState’是操作的状态,打开或者关闭。如果状态设置成功则此函数会返回true,否则返回false。
+ 
+### 自定义操作类型监控其他区域
+
+**增加操作项**
+
+在org.apache.iotdb.db.cost.statistic.Operation类中增加一个枚举项来表示新增的操作.
+
+**在监控区域增加监控代码**
+
+在监控开始区域增加计时代码:
+
+	long t0 = System. currentTimeMillis();
+
+
+在监控结束区域增加记录代码: 
+
+	Measurement.INSTANCE.addOperationLatency(Operation, t0);
diff --git a/docs/Documentation-CHN/UserGuideV0.7.0/4-Deployment and Management/4-System log.md b/docs/Documentation-CHN/UserGuideV0.7.0/4-Deployment and Management/5-System log.md
similarity index 100%
rename from docs/Documentation-CHN/UserGuideV0.7.0/4-Deployment and Management/4-System log.md
rename to docs/Documentation-CHN/UserGuideV0.7.0/4-Deployment and Management/5-System log.md
diff --git a/docs/Documentation-CHN/UserGuideV0.7.0/4-Deployment and Management/5-Data Management.md b/docs/Documentation-CHN/UserGuideV0.7.0/4-Deployment and Management/6-Data Management.md
similarity index 100%
rename from docs/Documentation-CHN/UserGuideV0.7.0/4-Deployment and Management/5-Data Management.md
rename to docs/Documentation-CHN/UserGuideV0.7.0/4-Deployment and Management/6-Data Management.md
diff --git a/docs/Documentation-CHN/UserGuideV0.7.0/4-Deployment and Management/6-Build and use IoTDB by Dockerfile.md b/docs/Documentation-CHN/UserGuideV0.7.0/4-Deployment and Management/7-Build and use IoTDB by Dockerfile.md
similarity index 100%
rename from docs/Documentation-CHN/UserGuideV0.7.0/4-Deployment and Management/6-Build and use IoTDB by Dockerfile.md
rename to docs/Documentation-CHN/UserGuideV0.7.0/4-Deployment and Management/7-Build and use IoTDB by Dockerfile.md
diff --git a/docs/Documentation/UserGuideV0.7.0/4-Deployment and Management/4-Performance Monitor.md b/docs/Documentation/UserGuideV0.7.0/4-Deployment and Management/4-Performance Monitor.md
new file mode 100644
index 0000000..7128f05
--- /dev/null
+++ b/docs/Documentation/UserGuideV0.7.0/4-Deployment and Management/4-Performance Monitor.md	
@@ -0,0 +1,79 @@
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+
+# Chapter 4: Deployment and Management
+## Performance Monitor
+### Introduction
+
+In order to grasp the performance of iotdb, we add this module to count the time-consuming of each operation. This module can statistic the avg time-consuming of each operation and the proportion of each operation fall into a time range. The output is in log_measure.log file. A output example is in below.  
+
+<img style="width:100%; max-width:800px; max-height:600px; margin-left:auto; margin-right:auto; display:block;" src="https://user-images.githubusercontent.com/13203019/60937461-14296f80-a303-11e9-9602-a7bed624bfb3.png">
+ 
+### Configuration parameter
+
+location:conf/iotdb-engine.properties
+
+<center>**Table -parameter and description**
+
+|Parameter|Default Value|Description|
+|:---|:---|:---|
+|enable\_performance\_stat|false|Is stat performance of sub-module enable.|
+|performance\_stat\_display\_interval|60000|The interval of display statistic result in ms.|
+|performance_stat_memory_in_kb|20|The memory used for performance_stat in kb.|
+</center>
+ 
+### JMX MBean
+
+Connect to jconsole with port 31999,and choose ‘MBean’in menu bar. Expand the sidebar and choose 'org.apache.iotdb.db.cost.statistic'. You can Find:
+ 
+<img style="width:100%; max-width:600px; max-height:200px; margin-left:auto; margin-right:auto; display:block;" src="https://user-images.githubusercontent.com/13203019/60937484-30c5a780-a303-11e9-8e92-04c413df2088.png">
+
+**Attribute**
+
+1. EnableStat:Whether the statistics are enable or not, if it is true, the module records the time-consuming of each operation and prints the results; It can not be set dynamically but changed by function in below.
+
+2. DisplayIntervalInMs:The interval between print results. It can be set dynamically, but will take effect after restart.( First call stopStatistic(), then call startContinuousStatistics() or startOneTimeStatistics())
+3. OperationSwitch:It's a map to indicate whether stat the operation, the key is operation name and the value is stat state. This parameter cannot be changed directly, it's change by operation 'changeOperationSwitch()'. 
+
+**Operation**
+
+1. startContinuousStatistics: Start the statistics and output at interval of ‘DisplayIntervalInMs’.
+2. startOneTimeStatistics:Start the statistics and output in delay of ‘DisplayIntervalInMs’.
+3. stopStatistic:Stop the statistics.
+4. clearStatisticalState(): clear current stat result, reset statistical result.
+5. changeOperationSwitch(String operationName, Boolean operationState):set whether to monitor operation status. The param 'operationName' is the name of operation, defined in attribute operationSwitch. The param operationState is the state of operation. If state-switch successful the function will return true, else return false.
+ 
+### Adding Custom Monitoring Items for developer of IOTDB
+
+**Add Operation**
+
+Add an enumeration in org.apache.iotdb.db.cost.statistic.Operation.
+
+**Add Timing Code in Monitoring Area**
+
+Add timing code in the monitoring start area:
+
+	long t0 = System. currentTimeMillis();
+
+
+Add timing code in the monitoring stop area: 
+
+	Measurement.INSTANCE.addOperationLatency(Operation, t0);
diff --git a/docs/Documentation/UserGuideV0.7.0/4-Deployment and Management/4-System log.md b/docs/Documentation/UserGuideV0.7.0/4-Deployment and Management/5-System log.md
similarity index 100%
rename from docs/Documentation/UserGuideV0.7.0/4-Deployment and Management/4-System log.md
rename to docs/Documentation/UserGuideV0.7.0/4-Deployment and Management/5-System log.md
diff --git a/docs/Documentation/UserGuideV0.7.0/4-Deployment and Management/5-Data Management.md b/docs/Documentation/UserGuideV0.7.0/4-Deployment and Management/6-Data Management.md
similarity index 100%
rename from docs/Documentation/UserGuideV0.7.0/4-Deployment and Management/5-Data Management.md
rename to docs/Documentation/UserGuideV0.7.0/4-Deployment and Management/6-Data Management.md
diff --git a/docs/Documentation/UserGuideV0.7.0/4-Deployment and Management/6-Build and use IoTDB by Dockerfile.md b/docs/Documentation/UserGuideV0.7.0/4-Deployment and Management/7-Build and use IoTDB by Dockerfile.md
similarity index 100%
rename from docs/Documentation/UserGuideV0.7.0/4-Deployment and Management/6-Build and use IoTDB by Dockerfile.md
rename to docs/Documentation/UserGuideV0.7.0/4-Deployment and Management/7-Build and use IoTDB by Dockerfile.md
diff --git a/iotdb/iotdb/conf/iotdb-engine.properties b/iotdb/iotdb/conf/iotdb-engine.properties
index 0466025..aa7ef8e 100644
--- a/iotdb/iotdb/conf/iotdb-engine.properties
+++ b/iotdb/iotdb/conf/iotdb-engine.properties
@@ -181,3 +181,15 @@ ip_white_list=0.0.0.0/0
 # 2. If the sync data accounts for less than 50% of the update of the historical data (compared with the latest timestamp of the local storage group data),then it is recommended to select strategy 2.
 #    Setting the parameter to false, which has little impact on the insert performance of IoTDB system and takes up a large amount of CPU power.
 update_historical_data_possibility=false
+
+
+####################
+### performance statistic configuration
+####################
+
+# Is stat performance of sub-module enable
+enable_performance_stat=false
+# The interval of display statistic result in ms.
+performance_stat_display_interval=60000
+# The memory used for performance_stat in kb.
+performance_stat_memory_in_kb=20
diff --git a/iotdb/iotdb/conf/logback.xml b/iotdb/iotdb/conf/logback.xml
index 2629723..ff7c459 100644
--- a/iotdb/iotdb/conf/logback.xml
+++ b/iotdb/iotdb/conf/logback.xml
@@ -129,6 +129,23 @@
             <level>INFO</level>
         </filter>
     </appender>
+    <appender class="ch.qos.logback.core.rolling.RollingFileAppender" name="FILE_COST_MEASURE">
+        <file>${IOTDB_HOME}/logs/log_measure.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${IOTDB_HOME}/logs/log-measure-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>200MB</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+        </rollingPolicy>
+        <append>true</append>
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>%d [%t] %-5p %C:%L - %m %n</pattern>
+            <charset>utf-8</charset>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>INFO</level>
+        </filter>
+    </appender>
     <root level="info">
         <appender-ref ref="FILEDEBUG"/>
         <appender-ref ref="FILEWARN"/>
@@ -136,4 +153,7 @@
         <appender-ref ref="FILEALL"/>
         <appender-ref ref="stdout"/>
     </root>
+    <logger level="info" name="org.apache.iotdb.db.cost.statistic">
+        <appender-ref ref="FILE_COST_MEASURE"/>
+    </logger>
 </configuration>
diff --git a/iotdb/src/main/java/org/apache/iotdb/db/concurrent/ThreadName.java b/iotdb/src/main/java/org/apache/iotdb/db/concurrent/ThreadName.java
index 132df0d..64b89d1 100644
--- a/iotdb/src/main/java/org/apache/iotdb/db/concurrent/ThreadName.java
+++ b/iotdb/src/main/java/org/apache/iotdb/db/concurrent/ThreadName.java
@@ -38,7 +38,8 @@ public enum ThreadName {
   INDEX_SERVICE("Index-ServerServiceImpl"),
   SYNC_CLIENT("Sync-Client"),
   SYNC_SERVER("Sync-Server"),
-  SYNC_MONITOR("Sync-Monitor");
+  SYNC_MONITOR("Sync-Monitor"),
+  TIME_COST_STATSTIC("TIME_COST_STATSTIC");
 
   private String name;
 
diff --git a/iotdb/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java b/iotdb/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java
index e7543b0..fa402d3 100644
--- a/iotdb/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java
+++ b/iotdb/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java
@@ -176,6 +176,20 @@ public class IoTDBConfig {
   private String rpcImplClassName = TSServiceImpl.class.getName();
 
   /**
+   * Is stat performance of sub-module enable.
+   */
+  private boolean enablePerformanceStat = false;
+
+  /**
+   * The display of stat performance interval in ms.
+   */
+  private long performanceStatDisplayInterval = 60000;
+
+  /**
+   * The memory used for stat performance.
+   */
+  private int performance_stat_memory_in_kb = 20;
+  /**
    * whether use chunkBufferPool.
    */
   private boolean chunkBufferPoolEnable = false;
@@ -487,4 +501,28 @@ public class IoTDBConfig {
   void setChunkBufferPoolEnable(boolean chunkBufferPoolEnable) {
     this.chunkBufferPoolEnable = chunkBufferPoolEnable;
   }
+
+  public boolean isEnablePerformanceStat() {
+    return enablePerformanceStat;
+  }
+
+  public void setEnablePerformanceStat(boolean enablePerformanceStat) {
+    this.enablePerformanceStat = enablePerformanceStat;
+  }
+
+  public long getPerformanceStatDisplayInterval() {
+    return performanceStatDisplayInterval;
+  }
+
+  public void setPerformanceStatDisplayInterval(long performanceStatDisplayInterval) {
+    this.performanceStatDisplayInterval = performanceStatDisplayInterval;
+  }
+
+  public int getPerformance_stat_memory_in_kb() {
+    return performance_stat_memory_in_kb;
+  }
+
+  public void setPerformance_stat_memory_in_kb(int performance_stat_memory_in_kb) {
+    this.performance_stat_memory_in_kb = performance_stat_memory_in_kb;
+  }
 }
diff --git a/iotdb/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java b/iotdb/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java
index 57e941e..4f7e208 100644
--- a/iotdb/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java
+++ b/iotdb/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java
@@ -200,6 +200,16 @@ public class IoTDBDescriptor {
       conf.setZoneID(ZoneId.of(tmpTimeZone.trim()));
       logger.info("Time zone has been set to {}", conf.getZoneID());
 
+      conf.setEnablePerformanceStat(Boolean
+          .parseBoolean(properties.getProperty("enable_performance_stat",
+              Boolean.toString(conf.isEnablePerformanceStat())).trim()));
+
+      conf.setPerformanceStatDisplayInterval(Long
+          .parseLong(properties.getProperty("performance_stat_display_interval",
+              Long.toString(conf.getPerformanceStatDisplayInterval())).trim()));
+      conf.setPerformance_stat_memory_in_kb(Integer
+          .parseInt(properties.getProperty("performance_stat_memory_in_kb",
+              Integer.toString(conf.getPerformance_stat_memory_in_kb())).trim()));
     } catch (IOException e) {
       logger.warn("Cannot load config file because, use default configuration", e);
     } catch (Exception e) {
diff --git a/iotdb/src/main/java/org/apache/iotdb/db/cost/statistic/ConcurrentCircularArray.java b/iotdb/src/main/java/org/apache/iotdb/db/cost/statistic/ConcurrentCircularArray.java
new file mode 100644
index 0000000..535916c
--- /dev/null
+++ b/iotdb/src/main/java/org/apache/iotdb/db/cost/statistic/ConcurrentCircularArray.java
@@ -0,0 +1,69 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.cost.statistic;
+
+public class ConcurrentCircularArray {
+
+  long[] data;
+  int tail;
+  int head;
+
+  public ConcurrentCircularArray(int size) {
+    this.data = new long[size];
+    tail = head = 0;
+  }
+
+  /**
+   * @param d the data
+   * @return true if successfully; false if there is no space.
+   */
+  public synchronized boolean put(long d) {
+    if ((tail + 1) % data.length == head) {
+      return false;
+    }
+    data[tail++] = d;
+    tail = tail % data.length;
+    return true;
+  }
+
+  public synchronized boolean hasData() {
+    return tail != head;
+  }
+
+  /**
+   * @return -1 if there is no data.(However, you should call hasData() frist to avoid returning -1)
+   */
+  public synchronized long take() {
+    if (tail != head) {
+      long result = data[head++];
+      head = head % data.length;
+      return result;
+    } else {
+      return -1;
+    }
+  }
+
+  /**
+   * drop all of the elements in array.
+   */
+  public synchronized void clear(){
+    tail = head = 0;
+  }
+}
diff --git a/iotdb/src/main/java/org/apache/iotdb/db/cost/statistic/Measurement.java b/iotdb/src/main/java/org/apache/iotdb/db/cost/statistic/Measurement.java
new file mode 100644
index 0000000..83c2cdf
--- /dev/null
+++ b/iotdb/src/main/java/org/apache/iotdb/db/cost/statistic/Measurement.java
@@ -0,0 +1,428 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.iotdb.db.cost.statistic;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReentrantLock;
+import org.apache.iotdb.db.concurrent.IoTDBThreadPoolFactory;
+import org.apache.iotdb.db.concurrent.ThreadName;
+import org.apache.iotdb.db.conf.IoTDBConfig;
+import org.apache.iotdb.db.conf.IoTDBConstant;
+import org.apache.iotdb.db.conf.IoTDBDescriptor;
+import org.apache.iotdb.db.exception.StartupException;
+import org.apache.iotdb.db.service.IService;
+import org.apache.iotdb.db.service.JMXService;
+import org.apache.iotdb.db.service.ServiceType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * <p>
+ * Measurement is used to record execution time of operations defined in enum class Operation. It
+ * can display average time of each operation, and proportion of operation whose execution time fall
+ * into time range defined in BUCKET_IN_MS. If you want to change abscissa of histogram, just change
+ * the BUCKET_IN_MS array. For recording a operation, you should:
+ * 1) add a item in enum class Operation.
+ * 2) call <code>startTimeInNano = System.nanoTime()</code> to recode startTime of that operation.
+ * 3) call <code>Measurement.INSTANCE.addOperationLatency(operation, startTimeInNano)</code>
+ * at the end of that operation;
+ *
+ * @see Operation
+ */
+public class Measurement implements MeasurementMBean, IService {
+
+  /**
+   * queue for async store time latencies.
+   */
+  private ConcurrentCircularArray[] operationLatenciesQueue;
+
+  /**
+   * size of each queue, this is calculated by memory.
+   */
+  private final int queueSize;
+
+  /**
+   * latencies sum of each operation.
+   */
+  private long[] operationLatencies;
+
+  /**
+   * the num of each operation.
+   */
+  private long[] operationCnt;
+
+  /**
+   * abscissa of histogram.
+   */
+  private static final int[] BUCKET_IN_MS = {1, 4, 16, 64, 256, 1024, Integer.MAX_VALUE};
+
+  /**
+   * length of BUCKET_IN_MS.
+   */
+  private static final int BUCKET_SIZE = BUCKET_IN_MS.length;
+
+  /**
+   * the num of operation that execution time falls into time range of BUCKET_IN_MS. The outer array
+   * is each operation, the inner array is each time range in BUCKET_IN_MS.
+   */
+  private long[][] operationHistogram;
+
+  /**
+   * display thread and queue consumer thread.
+   */
+  private ScheduledExecutorService service;
+
+  /**
+   * future task of display thread and queue consumer thread.
+   */
+  private List<Future<?>> futureList;
+
+  /**
+   * lock for change state: start() and stopStatistic().
+   */
+  private ReentrantLock stateChangeLock = new ReentrantLock();
+
+  public static final Measurement INSTANCE = AsyncMeasurementHolder.MEASUREMENT;
+
+  private boolean isEnableStat;
+  private long displayIntervalInMs;
+  private Map<String, Boolean> operationSwitch;
+
+  private static final Logger LOGGER = LoggerFactory.getLogger(Measurement.class);
+  private final String mbeanName = String
+      .format("%s:%s=%s", "org.apache.iotdb.db.cost.statistic", IoTDBConstant.JMX_TYPE,
+          getID().getJmxName());
+
+  private Measurement() {
+    IoTDBConfig tdbConfig = IoTDBDescriptor.getInstance().getConfig();
+    isEnableStat = tdbConfig.isEnablePerformanceStat();
+    displayIntervalInMs = tdbConfig.getPerformanceStatDisplayInterval();
+    int memoryInKb = tdbConfig.getPerformance_stat_memory_in_kb();
+
+    queueSize = memoryInKb * 1000 / Operation.values().length / 8;
+    operationLatenciesQueue = new ConcurrentCircularArray[Operation.values().length];
+    operationLatencies = new long[Operation.values().length];
+    operationCnt = new long[Operation.values().length];
+    operationSwitch = new HashMap<>(Operation.values().length);
+    for (Operation op : Operation.values()) {
+      operationLatenciesQueue[op.ordinal()] = new ConcurrentCircularArray(queueSize);
+      operationCnt[op.ordinal()] = 0;
+      operationLatencies[op.ordinal()] = 0;
+      operationSwitch.put(op.getName(), true);
+    }
+    operationHistogram = new long[Operation.values().length][BUCKET_SIZE];
+    for (Operation operation : Operation.values()) {
+      for (int i = 0; i < BUCKET_SIZE; i++) {
+        operationHistogram[operation.ordinal()][i] = 0;
+      }
+    }
+
+    service = IoTDBThreadPoolFactory.newScheduledThreadPool(
+        2, ThreadName.TIME_COST_STATSTIC.getName());
+    futureList = new ArrayList<>();
+  }
+
+  public boolean addOperationLatency(Operation op, long startTime) {
+    if (isEnableStat && operationSwitch.get(op.getName())) {
+      return operationLatenciesQueue[op.ordinal()].put((System.currentTimeMillis() - startTime));
+    }
+    return false;
+  }
+
+  @Override
+  public void startContinuousStatistics() {
+    stateChangeLock.lock();
+    try {
+      if (isEnableStat) {
+        return;
+      }
+      isEnableStat = true;
+      futureList.clear();
+      Future future = service.scheduleWithFixedDelay(
+          new Measurement.DisplayRunnable(), 20, displayIntervalInMs, TimeUnit.MILLISECONDS);
+      futureList.add(future);
+      futureList.add(service.schedule(new QueueConsumerThread(), 0, TimeUnit.MILLISECONDS));
+
+
+    } catch (Exception e) {
+      LOGGER.error("Find error when start performance statistic thread, because {}", e);
+    } finally {
+      stateChangeLock.unlock();
+    }
+  }
+
+  @Override
+  public void startOneTimeStatistics() {
+    stateChangeLock.lock();
+    try {
+      if (isEnableStat) {
+        return;
+      }
+      isEnableStat = true;
+      futureList.clear();
+      futureList.add(service.schedule(new QueueConsumerThread(), 10, TimeUnit.MILLISECONDS));
+      Future future = service.schedule(() -> {
+        showMeasurements();
+        stopStatistic();
+      }, displayIntervalInMs, TimeUnit.MILLISECONDS);
+      futureList.add(future);
+    } catch (Exception e) {
+      LOGGER.error("Find error when start performance statistic thread, because {}", e);
+    } finally {
+      stateChangeLock.unlock();
+    }
+  }
+
+  @Override
+  public void stopStatistic() {
+    stateChangeLock.lock();
+    try {
+      if (!isEnableStat) {
+        return;
+      }
+      isEnableStat = false;
+      for (Future future : futureList) {
+        if (future != null) {
+          future.cancel(true);
+
+        }
+      }
+    } catch (Exception e) {
+      LOGGER.error("Find error when stopStatistic time cost statstic thread, because {}", e);
+    } finally {
+      stateChangeLock.unlock();
+    }
+  }
+
+  @Override
+  public void clearStatisticalState() {
+    for (Operation op : Operation.values()) {
+      operationLatenciesQueue[op.ordinal()].clear();
+      operationCnt[op.ordinal()] = 0;
+      operationLatencies[op.ordinal()] = 0;
+      for (int i = 0; i < BUCKET_SIZE; i++) {
+        operationHistogram[op.ordinal()][i] = 0;
+      }
+    }
+  }
+
+  @Override
+  public boolean changeOperationSwitch(String operationName, Boolean operationState) {
+    if (operationSwitch.containsKey(operationName)) {
+      operationSwitch.put(operationName, operationState);
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  /**
+   * start service.
+   */
+  @Override
+  public void start() throws StartupException {
+    // start display thread and consumer threads.
+    if (isEnableStat) {
+      Future future = service.scheduleWithFixedDelay(
+          new Measurement.DisplayRunnable(), 20, displayIntervalInMs, TimeUnit.MILLISECONDS);
+      futureList.add(future);
+      futureList.add(service.schedule(new QueueConsumerThread(), 10, TimeUnit.MILLISECONDS));
+
+    }
+    try {
+      JMXService.registerMBean(INSTANCE, mbeanName);
+    } catch (Exception e) {
+      String errorMessage = String
+          .format("Failed to start %s because of %s", this.getID().getName(),
+              e.getMessage());
+      throw new StartupException(errorMessage, e);
+    }
+  }
+
+  /**
+   * stop service.
+   */
+  @Override
+  public void stop() {
+    JMXService.deregisterMBean(mbeanName);
+    if (service == null || service.isShutdown()) {
+      return;
+    }
+    stopStatistic();
+    futureList.clear();
+    service.shutdownNow();
+    try {
+      service.awaitTermination(5, TimeUnit.SECONDS);
+    } catch (InterruptedException e) {
+      LOGGER.error("Performance statistic service could not be shutdown, {}", e.getMessage());
+      // Restore interrupted state...
+      Thread.currentThread().interrupt();
+    }
+  }
+
+  @Override
+  public ServiceType getID() {
+    return ServiceType.PERFORMANCE_STATISTIC_SERVICE;
+  }
+
+  @Override
+  public boolean isEnableStat() {
+    return isEnableStat;
+  }
+
+  @Override
+  public long getDisplayIntervalInMs() {
+    return displayIntervalInMs;
+  }
+
+  @Override
+  public void setDisplayIntervalInMs(long displayIntervalInMs) {
+    this.displayIntervalInMs = displayIntervalInMs;
+  }
+
+  @Override
+  public Map<String, Boolean> getOperationSwitch() {
+    return operationSwitch;
+  }
+
+  private static class AsyncMeasurementHolder {
+
+    private static final Measurement MEASUREMENT = new Measurement();
+
+    private AsyncMeasurementHolder() {
+    }
+  }
+
+  private void showMeasurements() {
+    Date date = new Date();
+    LOGGER.info(
+        "====================================={} Measurement (ms)======================================",
+        date);
+    String head = String
+        .format("%-45s%-25s%-25s%-25s", "OPERATION", "COUNT", "TOTAL_TIME", "AVG_TIME");
+    LOGGER.info(head);
+    for (Operation operation : Operation.values()) {
+      if (!operationSwitch.get(operation.getName())) {
+        continue;
+      }
+      long cnt = operationCnt[operation.ordinal()];
+      long totalInMs = operationLatencies[operation.ordinal()];
+      String avg = String.format("%.4f", (totalInMs / (cnt + 1e-9)));
+      String item = String
+          .format("%-45s%-25s%-25s%-25s", operation.name, cnt + "", totalInMs + "", avg);
+      LOGGER.info(item);
+    }
+    LOGGER.info(
+        "==========================================OPERATION HISTOGRAM====================================================");
+    StringBuilder histogramHead = new StringBuilder(String.format("%-45s", "OPERATION"));
+    for (int i = 0; i < BUCKET_SIZE; i++) {
+      histogramHead.append(String.format("%-8s", BUCKET_IN_MS[i] + "ms"));
+    }
+    if (LOGGER.isInfoEnabled()) {
+      LOGGER.info(histogramHead.toString());
+    }
+    for (Operation operation : Operation.values()) {
+      if (!operationSwitch.get(operation.getName())) {
+        continue;
+      }
+      StringBuilder item = new StringBuilder(String.format("%-45s", operation.getName()));
+      long cnt = operationCnt[operation.ordinal()];
+      for (int i = 0; i < BUCKET_SIZE; i++) {
+        String avg = String
+            .format("%.2f", (operationHistogram[operation.ordinal()][i] / (cnt + 1e-9) * 100));
+        item.append(String.format("%-8s", avg + "%"));
+      }
+      if (LOGGER.isInfoEnabled()) {
+        LOGGER.info(item.toString());
+      }
+    }
+
+    LOGGER.info(
+        "=================================================================================================================");
+  }
+
+  class DisplayRunnable implements Runnable {
+
+    @Override
+    public void run() {
+      showMeasurements();
+    }
+  }
+
+  class QueueConsumerThread implements Runnable {
+
+    @Override
+    public void run() {
+      consumer();
+    }
+
+    private void consumer() {
+      boolean allEmpty;
+      while (isEnableStat) {
+        allEmpty = true;
+        for (Operation op : Operation.values()) {
+          if (!operationSwitch.get(op.getName())) {
+            continue;
+          }
+          int idx = op.ordinal();
+          ConcurrentCircularArray queue = operationLatenciesQueue[idx];
+          if (queue.hasData()) {
+            long time = queue.take();
+            operationLatencies[idx] += time;
+            operationCnt[idx]++;
+            operationHistogram[idx][calIndex(time)]++;
+            allEmpty = false;
+          }
+        }
+        if (allEmpty) {
+          try {
+            Thread.sleep(10);
+          } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            break;
+          }
+        }
+      }
+    }
+
+    private int calIndex(long x) {
+      for (int i = 0; i < BUCKET_SIZE; i++) {
+        if (BUCKET_IN_MS[i] >= x) {
+          return i;
+        }
+      }
+      return BUCKET_SIZE - 1;
+    }
+  }
+
+  public long[] getOperationLatencies() {
+    return operationLatencies;
+  }
+
+  public long[] getOperationCnt() {
+    return operationCnt;
+  }
+}
diff --git a/iotdb/src/main/java/org/apache/iotdb/db/cost/statistic/MeasurementMBean.java b/iotdb/src/main/java/org/apache/iotdb/db/cost/statistic/MeasurementMBean.java
new file mode 100644
index 0000000..224956f
--- /dev/null
+++ b/iotdb/src/main/java/org/apache/iotdb/db/cost/statistic/MeasurementMBean.java
@@ -0,0 +1,62 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.cost.statistic;
+
+import java.util.Map;
+
+public interface MeasurementMBean {
+
+  /**
+   * start display performance statistic every interval of displayIntervalInMs.
+   */
+  void startContinuousStatistics();
+
+  /**
+   * start display performance statistic after interval of displayIntervalInMs.
+   */
+  void startOneTimeStatistics();
+
+  /**
+   * stop display performance statistic.
+   */
+  void stopStatistic();
+
+  /**
+   * clear current stat result, reset statistical state.
+   */
+  void clearStatisticalState();
+
+  /**
+   * set whether to monitor operation status.
+   *
+   * @param operationName the name of operation, defined in attribute operationSwitch.
+   * @param operationState state of operation.
+   * @return true if successful, false if fail.
+   */
+  boolean changeOperationSwitch(String operationName, Boolean operationState);
+
+  boolean isEnableStat();
+
+  long getDisplayIntervalInMs();
+
+  void setDisplayIntervalInMs(long displayIntervalInMs);
+
+  Map<String, Boolean> getOperationSwitch();
+}
diff --git a/iotdb/src/main/java/org/apache/iotdb/db/cost/statistic/Operation.java b/iotdb/src/main/java/org/apache/iotdb/db/cost/statistic/Operation.java
new file mode 100644
index 0000000..6f862ba
--- /dev/null
+++ b/iotdb/src/main/java/org/apache/iotdb/db/cost/statistic/Operation.java
@@ -0,0 +1,36 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.iotdb.db.cost.statistic;
+
+public enum Operation {
+  EXECUTE_BATCH("EXECUTE_BATCH"),
+  EXECUTE_ONE_SQL_IN_BATCH("EXECUTE_ONE_SQL_IN_BATCH"),
+  EXECUTE_QUERY("EXECUTE_QUERY");
+
+  public String getName() {
+    return name;
+  }
+
+  String name;
+
+  Operation(String name) {
+    this.name = name;
+  }
+
+}
diff --git a/iotdb/src/main/java/org/apache/iotdb/db/qp/QueryProcessor.java b/iotdb/src/main/java/org/apache/iotdb/db/qp/QueryProcessor.java
index 70ec551..4f2baee 100644
--- a/iotdb/src/main/java/org/apache/iotdb/db/qp/QueryProcessor.java
+++ b/iotdb/src/main/java/org/apache/iotdb/db/qp/QueryProcessor.java
@@ -73,7 +73,8 @@ public class QueryProcessor {
     Operator operator = parseASTToOperator(astNode, zoneId);
     operator = logicalOptimize(operator, executor);
     PhysicalGenerator physicalGenerator = new PhysicalGenerator(executor);
-    return physicalGenerator.transformToPhysicalPlan(operator);
+    PhysicalPlan qp = physicalGenerator.transformToPhysicalPlan(operator);
+    return qp;
   }
 
   /**
diff --git a/iotdb/src/main/java/org/apache/iotdb/db/service/IoTDB.java b/iotdb/src/main/java/org/apache/iotdb/db/service/IoTDB.java
index ba80791..4139d8e 100644
--- a/iotdb/src/main/java/org/apache/iotdb/db/service/IoTDB.java
+++ b/iotdb/src/main/java/org/apache/iotdb/db/service/IoTDB.java
@@ -21,6 +21,7 @@ package org.apache.iotdb.db.service;
 import org.apache.iotdb.db.concurrent.IoTDBDefaultThreadExceptionHandler;
 import org.apache.iotdb.db.conf.IoTDBConstant;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
+import org.apache.iotdb.db.cost.statistic.Measurement;
 import org.apache.iotdb.db.engine.StorageEngine;
 import org.apache.iotdb.db.exception.StartupException;
 import org.apache.iotdb.db.exception.builder.ExceptionBuilder;
@@ -91,6 +92,7 @@ public class IoTDB implements IoTDBMBean {
     registerManager.register(JDBCService.getInstance());
     registerManager.register(Monitor.getInstance());
     registerManager.register(StatMonitor.getInstance());
+    registerManager.register(Measurement.INSTANCE);
     registerManager.register(SyncServerManager.getInstance());
     registerManager.register(TVListAllocator.getInstance());
 
diff --git a/iotdb/src/main/java/org/apache/iotdb/db/service/ServiceType.java b/iotdb/src/main/java/org/apache/iotdb/db/service/ServiceType.java
index 10302b6..4f872fd 100644
--- a/iotdb/src/main/java/org/apache/iotdb/db/service/ServiceType.java
+++ b/iotdb/src/main/java/org/apache/iotdb/db/service/ServiceType.java
@@ -31,6 +31,7 @@ public enum ServiceType {
   AUTHORIZATION_SERVICE("Authorization ServerService", ""),
   FILE_READER_MANAGER_SERVICE("File reader manager ServerService", ""),
   SYNC_SERVICE("SYNC ServerService", ""),
+  PERFORMANCE_STATISTIC_SERVICE("PERFORMANCE_STATISTIC_SERVICE","PERFORMANCE_STATISTIC_SERVICE"),
   TVLIST_ALLOCATOR_SERVICE("TVList Allocator", "");
 
   private String name;
diff --git a/iotdb/src/main/java/org/apache/iotdb/db/service/TSServiceImpl.java b/iotdb/src/main/java/org/apache/iotdb/db/service/TSServiceImpl.java
index 243b3a6..282f8be 100644
--- a/iotdb/src/main/java/org/apache/iotdb/db/service/TSServiceImpl.java
+++ b/iotdb/src/main/java/org/apache/iotdb/db/service/TSServiceImpl.java
@@ -42,6 +42,8 @@ import org.apache.iotdb.db.auth.authorizer.LocalFileAuthorizer;
 import org.apache.iotdb.db.conf.IoTDBConfig;
 import org.apache.iotdb.db.conf.IoTDBConstant;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
+import org.apache.iotdb.db.cost.statistic.Measurement;
+import org.apache.iotdb.db.cost.statistic.Operation;
 import org.apache.iotdb.db.engine.StorageEngine;
 import org.apache.iotdb.db.exception.ArgsErrorException;
 import org.apache.iotdb.db.exception.MetadataErrorException;
@@ -345,7 +347,7 @@ public class TSServiceImpl implements TSIService.Iface, ServerContext {
       case "privilege":
         return TSDataType.TEXT;
       default:
-          // do nothing
+        // do nothing
     }
 
     if (path.contains("(") && !path.startsWith("(") && path.endsWith(")")) {
@@ -399,7 +401,7 @@ public class TSServiceImpl implements TSIService.Iface, ServerContext {
         StorageEngine.getInstance().syncCloseAllProcessor();
         return true;
       case "merge":
-          // TODO change to merge!!!
+        // TODO change to merge!!!
         throw new UnsupportedOperationException("merge not implemented");
       default:
         return false;
@@ -409,6 +411,7 @@ public class TSServiceImpl implements TSIService.Iface, ServerContext {
   @Override
   public TSExecuteBatchStatementResp executeBatchStatement(TSExecuteBatchStatementReq req)
       throws TException {
+    long t1 = System.currentTimeMillis();
     String currStmt = null;
     List<Integer> result = new ArrayList<>();
     try {
@@ -422,14 +425,19 @@ public class TSServiceImpl implements TSIService.Iface, ServerContext {
       StringBuilder batchErrorMessage = new StringBuilder();
 
       for (String statement : statements) {
+        long t2 = System.currentTimeMillis();
         currStmt = statement;
-        isAllSuccessful = isAllSuccessful && executeStatementInBatch(statement, batchErrorMessage, result);
+        isAllSuccessful =
+            isAllSuccessful && executeStatementInBatch(statement, batchErrorMessage, result);
+        Measurement.INSTANCE.addOperationLatency(Operation.EXECUTE_ONE_SQL_IN_BATCH, t2);
       }
+
       if (isAllSuccessful) {
         return getTSBathExecuteStatementResp(TS_StatusCode.SUCCESS_STATUS,
             "Execute batch statements successfully", result);
       } else {
-        return getTSBathExecuteStatementResp(TS_StatusCode.ERROR_STATUS, batchErrorMessage.toString(),
+        return getTSBathExecuteStatementResp(TS_StatusCode.ERROR_STATUS,
+            batchErrorMessage.toString(),
             result);
       }
     } catch (QueryInBatchStmtException e) {
@@ -438,6 +446,8 @@ public class TSServiceImpl implements TSIService.Iface, ServerContext {
     } catch (Exception e) {
       logger.error("{}: error occurs when executing statements", IoTDBConstant.GLOBAL_DB_NAME, e);
       return getTSBathExecuteStatementResp(TS_StatusCode.ERROR_STATUS, e.getMessage(), null);
+    } finally {
+      Measurement.INSTANCE.addOperationLatency(Operation.EXECUTE_BATCH, t1);
     }
   }
 
@@ -472,7 +482,6 @@ public class TSServiceImpl implements TSIService.Iface, ServerContext {
   }
 
 
-
   @Override
   public TSExecuteStatementResp executeStatement(TSExecuteStatementReq req) throws TException {
     try {
@@ -526,7 +535,7 @@ public class TSServiceImpl implements TSIService.Iface, ServerContext {
 
   @Override
   public TSExecuteStatementResp executeQueryStatement(TSExecuteStatementReq req) throws TException {
-
+    long t1 = System.currentTimeMillis();
     try {
       if (!checkLogin()) {
         logger.info(INFO_NOT_LOGIN, IoTDBConstant.GLOBAL_DB_NAME);
@@ -557,6 +566,8 @@ public class TSServiceImpl implements TSIService.Iface, ServerContext {
     } catch (Exception e) {
       logger.error("{}: Internal server error: ", IoTDBConstant.GLOBAL_DB_NAME, e);
       return getTSExecuteStatementResp(TS_StatusCode.ERROR_STATUS, e.getMessage());
+    } finally {
+      Measurement.INSTANCE.addOperationLatency(Operation.EXECUTE_QUERY, t1);
     }
   }
 
@@ -727,7 +738,6 @@ public class TSServiceImpl implements TSIService.Iface, ServerContext {
 
   private TSExecuteStatementResp executeUpdateStatement(PhysicalPlan plan) {
     List<Path> paths = plan.getPaths();
-
     try {
       if (!checkAuthorization(paths, plan)) {
         return getTSExecuteStatementResp(TS_StatusCode.ERROR_STATUS,
@@ -748,6 +758,7 @@ public class TSServiceImpl implements TSIService.Iface, ServerContext {
       logger.debug("meet error while processing non-query. ", e);
       return getTSExecuteStatementResp(TS_StatusCode.ERROR_STATUS, e.getMessage());
     }
+
     TS_StatusCode statusCode = execRet ? TS_StatusCode.SUCCESS_STATUS : TS_StatusCode.ERROR_STATUS;
     String msg = execRet ? "Execute successfully" : "Execute statement error.";
     TSExecuteStatementResp resp = getTSExecuteStatementResp(statusCode, msg);
diff --git a/iotdb/src/test/java/org/apache/iotdb/db/cost/statistic/PerformanceStatTest.java b/iotdb/src/test/java/org/apache/iotdb/db/cost/statistic/PerformanceStatTest.java
new file mode 100644
index 0000000..6764ffc
--- /dev/null
+++ b/iotdb/src/test/java/org/apache/iotdb/db/cost/statistic/PerformanceStatTest.java
@@ -0,0 +1,71 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.iotdb.db.cost.statistic;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class PerformanceStatTest {
+
+  private static final Logger LOGGER = LoggerFactory.getLogger(PerformanceStatTest.class);
+
+  @Test
+  public void test() {
+    Measurement measurement = Measurement.INSTANCE;
+    Operation operation = Operation.EXECUTE_BATCH;
+    measurement.addOperationLatency(operation, System.currentTimeMillis());
+    measurement.addOperationLatency(operation,
+        System.currentTimeMillis() - 8000000);
+
+    long batchOpCnt = measurement.getOperationCnt()[operation.ordinal()];
+    Assert.assertEquals(0L, batchOpCnt);
+    try {
+      measurement.start();
+      measurement.startContinuousStatistics();
+      measurement.addOperationLatency(operation, System.currentTimeMillis());
+      measurement
+          .addOperationLatency(operation, System.currentTimeMillis() - 8000000);
+      Thread.currentThread().sleep(1000);
+      batchOpCnt = measurement.getOperationCnt()[operation.ordinal()];
+      Assert.assertEquals(2L, batchOpCnt);
+      measurement.stopStatistic();
+      measurement.stopStatistic();
+      measurement.stopStatistic();
+      LOGGER.info("After stopStatistic!");
+      Thread.currentThread().sleep(1000);
+      measurement.clearStatisticalState();
+      batchOpCnt = measurement.getOperationCnt()[operation.ordinal()];
+      Assert.assertEquals(0L, batchOpCnt);
+      measurement.startContinuousStatistics();
+      LOGGER.info("ReStart!");
+      Thread.currentThread().sleep(1000);
+      measurement.startContinuousStatistics();
+      LOGGER.info("ReStart2!");
+      Thread.currentThread().sleep(1000);
+      measurement.stopStatistic();
+      LOGGER.info("After stopStatistic2!");
+    } catch (Exception e) {
+      LOGGER.error("find error in stat performance, the message is {}", e.getMessage());
+    } finally {
+      measurement.stop();
+    }
+  }
+}
diff --git a/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBStatement.java b/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBStatement.java
index 66aa7f2..e9340ba 100644
--- a/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBStatement.java
+++ b/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBStatement.java
@@ -48,7 +48,6 @@ import org.apache.thrift.TException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-
 public class IoTDBStatement implements Statement {
 
   private static final String SHOW_TIMESERIES_COMMAND_LOWERCASE = "show timeseries";