You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by ja...@apache.org on 2022/07/15 01:04:24 UTC

[iotdb] branch master updated: [IOTDB-3714] Refactor operators related to aggregate query to support batch processing (#6622)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 8cd42fa076 [IOTDB-3714] Refactor operators related to aggregate query to support batch processing (#6622)
8cd42fa076 is described below

commit 8cd42fa07612378b983ce33321a123ee0796a071
Author: liuminghui233 <36...@users.noreply.github.com>
AuthorDate: Fri Jul 15 09:04:20 2022 +0800

    [IOTDB-3714] Refactor operators related to aggregate query to support batch processing (#6622)
---
 docs/UserGuide/Reference/DataNode-Config-Manual.md |  28 +-
 .../UserGuide/Reference/DataNode-Config-Manual.md  |  28 +-
 .../util/TSFileConfigUtilCompletenessTest.java     |   4 +-
 .../java/org/apache/iotdb/it/env/MppConfig.java    |   6 +
 .../org/apache/iotdb/itbase/env/BaseConfig.java    |   8 +
 .../aligned/IoTDBAggregationWithDeletion2IT.java   |   3 +
 .../it/aligned/IoTDBAggregationWithDeletionIT.java |   4 +
 .../db/it/aligned/IoTDBAlignedSeriesQuery2IT.java  |   3 +
 .../db/it/aligned/IoTDBAlignedSeriesQuery3IT.java  |   4 +-
 .../db/it/aligned/IoTDBAlignedSeriesQueryIT.java   |   4 +
 .../db/it/aligned/IoTDBGroupByLevelQueryIT.java    |   5 +
 ...GroupByQueryWithValueFilterWithDeletion2IT.java |   3 +
 ...BGroupByQueryWithValueFilterWithDeletionIT.java |   4 +
 ...upByQueryWithoutValueFilterWithDeletion2IT.java |   3 +
 ...oupByQueryWithoutValueFilterWithDeletionIT.java |   4 +
 .../iotdb/db/it/env/StandaloneEnvConfig.java       |  11 +
 .../resources/conf/iotdb-datanode.properties       |  12 +
 .../java/org/apache/iotdb/db/conf/IoTDBConfig.java |  12 +
 .../org/apache/iotdb/db/conf/IoTDBDescriptor.java  |  22 ++
 .../iotdb/db/mpp/aggregation/Aggregator.java       |  13 +-
 .../EmptyQueueSlidingWindowAggregator.java         |   1 -
 .../MonotonicQueueSlidingWindowAggregator.java     |   5 +-
 .../NormalQueueSlidingWindowAggregator.java        |   1 -
 .../slidingwindow/SlidingWindowAggregator.java     |   2 +-
 .../db/mpp/execution/operator/AggregationUtil.java | 161 +++++++++
 .../db/mpp/execution/operator/OperatorContext.java |  35 ++
 .../operator/process/AggregationOperator.java      | 144 +++++---
 .../process/RawDataAggregationOperator.java        | 187 ++--------
 .../process/SingleInputAggregationOperator.java    | 149 ++++++++
 .../process/SlidingWindowAggregationOperator.java  | 155 +++------
 .../AbstractSeriesAggregationScanOperator.java     | 350 +++++++++++++++++++
 .../AlignedSeriesAggregationScanOperator.java      | 377 +-------------------
 .../operator/source/AlignedSeriesScanUtil.java     |  38 ++
 .../source/SeriesAggregationScanOperator.java      | 386 +--------------------
 .../execution/operator/source/SeriesScanUtil.java  |  57 +--
 .../mpp/execution/schedule/DriverTaskThread.java   |   6 +-
 .../mpp/execution/timer/ITimeSliceAllocator.java   |  29 ++
 .../timer/RuleBasedTimeSliceAllocator.java         |  66 ++++
 .../db/mpp/plan/planner/LocalExecutionPlanner.java | 184 ++++++----
 .../operator/AggregationOperatorTest.java          |  42 ++-
 .../AlignedSeriesAggregationScanOperatorTest.java  |  92 +++--
 .../operator/LastQueryMergeOperatorTest.java       |  15 +
 .../operator/RawDataAggregationOperatorTest.java   |   8 +
 .../SeriesAggregationScanOperatorTest.java         |  88 +++--
 .../SlidingWindowAggregationOperatorTest.java      |  46 ++-
 .../operator/UpdateLastCacheOperatorTest.java      |   8 +
 .../iotdb/tsfile/common/conf/TSFileConfig.java     |  22 ++
 .../file/metadata/statistics/Statistics.java       |   5 +
 .../iotdb/tsfile/read/common/block/TsBlock.java    |   4 +
 .../tsfile/read/common/block/TsBlockBuilder.java   |   6 +-
 .../read/common/block/TsBlockBuilderStatus.java    |   4 +-
 .../tsfile/read/common/block/TsBlockUtil.java      |  59 ++++
 52 files changed, 1627 insertions(+), 1286 deletions(-)

diff --git a/docs/UserGuide/Reference/DataNode-Config-Manual.md b/docs/UserGuide/Reference/DataNode-Config-Manual.md
index 5e5c5ba473..3199ede743 100644
--- a/docs/UserGuide/Reference/DataNode-Config-Manual.md
+++ b/docs/UserGuide/Reference/DataNode-Config-Manual.md
@@ -199,7 +199,7 @@ The permission definitions are in ${IOTDB\_CONF}/conf/jmx.access.
 |Default| 5 |
 |Effective|After restarting system|
 
-* mpp\_data\_exchange\_core\_pool\_size=1
+* mpp\_data\_exchange\_keep\_alive\_time\_in\_ms
 
 |Name| mpp\_data\_exchange\_keep\_alive\_time\_in\_ms |
 |:---:|:---|
@@ -208,6 +208,32 @@ The permission definitions are in ${IOTDB\_CONF}/conf/jmx.access.
 |Default| 1000 |
 |Effective|After restarting system|
 
+* driver\_task\_execution\_time\_slice\_in\_ms
+
+|Name| driver\_task\_execution\_time\_slice\_in\_ms |
+|:---:|:---|
+|Description| Maximum execution time of a DriverTask |
+|Type| int |
+|Default| 100 |
+|Effective|After restarting system|
+
+* max\_tsblock\_size\_in\_bytes
+
+|Name| max\_tsblock\_size\_in\_bytes |
+|:---:|:---|
+|Description| Maximum capacity of a TsBlock |
+|Type| int |
+|Default| 1024 * 1024 (1 MB) |
+|Effective|After restarting system|
+
+* max\_tsblock\_line\_numbers
+
+|Name| max\_tsblock\_line\_numbers |
+|:---:|:---|
+|Description| Maximum number of lines in a single TsBlock |
+|Type| int |
+|Default| 1000 |
+|Effective|After restarting system|
 
 ### DataNode Internal Service
 
diff --git a/docs/zh/UserGuide/Reference/DataNode-Config-Manual.md b/docs/zh/UserGuide/Reference/DataNode-Config-Manual.md
index c6f8f36671..7b03b9ef36 100644
--- a/docs/zh/UserGuide/Reference/DataNode-Config-Manual.md
+++ b/docs/zh/UserGuide/Reference/DataNode-Config-Manual.md
@@ -181,7 +181,7 @@ IoTDB DataNode 与 Standalone 模式共用一套配置文件,均位于 IoTDB 
 |默认值| 5 |
 |改后生效方式|重启服务生效|
 
-* mpp\_data\_exchange\_core\_pool\_size
+* mpp\_data\_exchange\_keep\_alive\_time\_in\_ms
 
 |名字| mpp\_data\_exchange\_keep\_alive\_time\_in\_ms |
 |:---:|:---|
@@ -190,6 +190,32 @@ IoTDB DataNode 与 Standalone 模式共用一套配置文件,均位于 IoTDB 
 |默认值| 1000 |
 |改后生效方式|重启服务生效|
 
+* driver\_task\_execution\_time\_slice\_in\_ms
+
+|名字| driver\_task\_execution\_time\_slice\_in\_ms |
+|:---:|:---|
+|描述| 单个 DriverTask 最长执行时间 |
+|类型| int |
+|默认值| 100 |
+|改后生效方式|重启服务生效|
+
+* max\_tsblock\_size\_in\_bytes
+
+|名字| max\_tsblock\_size\_in\_bytes |
+|:---:|:---|
+|描述| 单个 TsBlock 的最大容量 |
+|类型| int |
+|默认值| 1024 * 1024 (1 MB) |
+|改后生效方式|重启服务生效|
+
+* max\_tsblock\_line\_numbers
+
+|名字| max\_tsblock\_line\_numbers |
+|:---:|:---|
+|描述| 单个 TsBlock 的最大行数 |
+|类型| int |
+|默认值| 1000 |
+|改后生效方式|重启服务生效|
 
 ### DataNode 内部服务参数
 
diff --git a/flink-tsfile-connector/src/test/java/org/apache/iotdb/flink/util/TSFileConfigUtilCompletenessTest.java b/flink-tsfile-connector/src/test/java/org/apache/iotdb/flink/util/TSFileConfigUtilCompletenessTest.java
index 156baa4e0c..5ed14b4051 100644
--- a/flink-tsfile-connector/src/test/java/org/apache/iotdb/flink/util/TSFileConfigUtilCompletenessTest.java
+++ b/flink-tsfile-connector/src/test/java/org/apache/iotdb/flink/util/TSFileConfigUtilCompletenessTest.java
@@ -72,7 +72,9 @@ public class TSFileConfigUtilCompletenessTest {
       "setUseKerberos",
       "setValueEncoder",
       "setFreqEncodingSNR",
-      "setFreqEncodingBlockSize"
+      "setFreqEncodingBlockSize",
+      "setMaxTsBlockLineNumber",
+      "setMaxTsBlockSizeInBytes"
     };
     Set<String> newSetters =
         Arrays.stream(TSFileConfig.class.getMethods())
diff --git a/integration-test/src/main/java/org/apache/iotdb/it/env/MppConfig.java b/integration-test/src/main/java/org/apache/iotdb/it/env/MppConfig.java
index 18b5dd942e..89a2611fcc 100644
--- a/integration-test/src/main/java/org/apache/iotdb/it/env/MppConfig.java
+++ b/integration-test/src/main/java/org/apache/iotdb/it/env/MppConfig.java
@@ -198,4 +198,10 @@ public class MppConfig implements BaseConfig {
         "avg_series_point_number_threshold", String.valueOf(avgSeriesPointNumberThreshold));
     return this;
   }
+
+  @Override
+  public BaseConfig setMaxTsBlockLineNumber(int maxTsBlockLineNumber) {
+    engineProperties.setProperty("max_tsblock_line_number", String.valueOf(maxTsBlockLineNumber));
+    return this;
+  }
 }
diff --git a/integration-test/src/main/java/org/apache/iotdb/itbase/env/BaseConfig.java b/integration-test/src/main/java/org/apache/iotdb/itbase/env/BaseConfig.java
index e538c319a4..8066de123d 100644
--- a/integration-test/src/main/java/org/apache/iotdb/itbase/env/BaseConfig.java
+++ b/integration-test/src/main/java/org/apache/iotdb/itbase/env/BaseConfig.java
@@ -185,4 +185,12 @@ public interface BaseConfig {
   default BaseConfig setAvgSeriesPointNumberThreshold(int avgSeriesPointNumberThreshold) {
     return this;
   }
+
+  default int getMaxTsBlockLineNumber() {
+    return 1000;
+  }
+
+  default BaseConfig setMaxTsBlockLineNumber(int maxTsBlockLineNumber) {
+    return this;
+  }
 }
diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBAggregationWithDeletion2IT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBAggregationWithDeletion2IT.java
index 3decbd8bd4..23b894fae7 100644
--- a/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBAggregationWithDeletion2IT.java
+++ b/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBAggregationWithDeletion2IT.java
@@ -47,11 +47,13 @@ public class IoTDBAggregationWithDeletion2IT extends IoTDBAggregationWithDeletio
     enableUnseqSpaceCompaction = ConfigFactory.getConfig().isEnableUnseqSpaceCompaction();
     enableCrossSpaceCompaction = ConfigFactory.getConfig().isEnableCrossSpaceCompaction();
     numOfPointsPerPage = ConfigFactory.getConfig().getMaxNumberOfPointsInPage();
+    maxTsBlockLineNumber = ConfigFactory.getConfig().getMaxTsBlockLineNumber();
 
     ConfigFactory.getConfig().setEnableSeqSpaceCompaction(false);
     ConfigFactory.getConfig().setEnableUnseqSpaceCompaction(false);
     ConfigFactory.getConfig().setEnableCrossSpaceCompaction(false);
     ConfigFactory.getConfig().setMaxNumberOfPointsInPage(3);
+    ConfigFactory.getConfig().setMaxTsBlockLineNumber(3);
     EnvFactory.getEnv().initBeforeClass();
     AlignedWriteUtil.insertData();
 
@@ -74,5 +76,6 @@ public class IoTDBAggregationWithDeletion2IT extends IoTDBAggregationWithDeletio
     ConfigFactory.getConfig().setEnableUnseqSpaceCompaction(enableUnseqSpaceCompaction);
     ConfigFactory.getConfig().setEnableCrossSpaceCompaction(enableCrossSpaceCompaction);
     ConfigFactory.getConfig().setMaxNumberOfPointsInPage(numOfPointsPerPage);
+    ConfigFactory.getConfig().setMaxTsBlockLineNumber(maxTsBlockLineNumber);
   }
 }
diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBAggregationWithDeletionIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBAggregationWithDeletionIT.java
index da4c147e69..aca4adc514 100644
--- a/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBAggregationWithDeletionIT.java
+++ b/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBAggregationWithDeletionIT.java
@@ -50,15 +50,18 @@ public class IoTDBAggregationWithDeletionIT {
   protected static boolean enableSeqSpaceCompaction;
   protected static boolean enableUnseqSpaceCompaction;
   protected static boolean enableCrossSpaceCompaction;
+  protected static int maxTsBlockLineNumber;
 
   @BeforeClass
   public static void setUp() throws Exception {
     enableSeqSpaceCompaction = ConfigFactory.getConfig().isEnableSeqSpaceCompaction();
     enableUnseqSpaceCompaction = ConfigFactory.getConfig().isEnableUnseqSpaceCompaction();
     enableCrossSpaceCompaction = ConfigFactory.getConfig().isEnableCrossSpaceCompaction();
+    maxTsBlockLineNumber = ConfigFactory.getConfig().getMaxTsBlockLineNumber();
     ConfigFactory.getConfig().setEnableSeqSpaceCompaction(false);
     ConfigFactory.getConfig().setEnableUnseqSpaceCompaction(false);
     ConfigFactory.getConfig().setEnableCrossSpaceCompaction(false);
+    ConfigFactory.getConfig().setMaxTsBlockLineNumber(3);
     EnvFactory.getEnv().initBeforeClass();
     AlignedWriteUtil.insertData();
 
@@ -80,6 +83,7 @@ public class IoTDBAggregationWithDeletionIT {
     ConfigFactory.getConfig().setEnableSeqSpaceCompaction(enableSeqSpaceCompaction);
     ConfigFactory.getConfig().setEnableUnseqSpaceCompaction(enableUnseqSpaceCompaction);
     ConfigFactory.getConfig().setEnableCrossSpaceCompaction(enableCrossSpaceCompaction);
+    ConfigFactory.getConfig().setMaxTsBlockLineNumber(maxTsBlockLineNumber);
   }
 
   @Test
diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBAlignedSeriesQuery2IT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBAlignedSeriesQuery2IT.java
index 9842ddfeb7..f0fadcbe96 100644
--- a/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBAlignedSeriesQuery2IT.java
+++ b/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBAlignedSeriesQuery2IT.java
@@ -41,11 +41,13 @@ public class IoTDBAlignedSeriesQuery2IT extends IoTDBAlignedSeriesQueryIT {
     enableUnseqSpaceCompaction = ConfigFactory.getConfig().isEnableUnseqSpaceCompaction();
     enableCrossSpaceCompaction = ConfigFactory.getConfig().isEnableCrossSpaceCompaction();
     numOfPointsPerPage = ConfigFactory.getConfig().getMaxNumberOfPointsInPage();
+    maxTsBlockLineNumber = ConfigFactory.getConfig().getMaxTsBlockLineNumber();
 
     ConfigFactory.getConfig().setEnableSeqSpaceCompaction(false);
     ConfigFactory.getConfig().setEnableUnseqSpaceCompaction(false);
     ConfigFactory.getConfig().setEnableCrossSpaceCompaction(false);
     ConfigFactory.getConfig().setMaxNumberOfPointsInPage(2);
+    ConfigFactory.getConfig().setMaxTsBlockLineNumber(3);
     EnvFactory.getEnv().initBeforeClass();
     AlignedWriteUtil.insertData();
   }
@@ -57,5 +59,6 @@ public class IoTDBAlignedSeriesQuery2IT extends IoTDBAlignedSeriesQueryIT {
     ConfigFactory.getConfig().setEnableUnseqSpaceCompaction(enableUnseqSpaceCompaction);
     ConfigFactory.getConfig().setEnableCrossSpaceCompaction(enableCrossSpaceCompaction);
     ConfigFactory.getConfig().setMaxNumberOfPointsInPage(numOfPointsPerPage);
+    ConfigFactory.getConfig().setMaxTsBlockLineNumber(maxTsBlockLineNumber);
   }
 }
diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBAlignedSeriesQuery3IT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBAlignedSeriesQuery3IT.java
index 697658379e..fb01fa9a83 100644
--- a/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBAlignedSeriesQuery3IT.java
+++ b/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBAlignedSeriesQuery3IT.java
@@ -41,11 +41,12 @@ public class IoTDBAlignedSeriesQuery3IT extends IoTDBAlignedSeriesQueryIT {
     enableUnseqSpaceCompaction = ConfigFactory.getConfig().isEnableUnseqSpaceCompaction();
     enableCrossSpaceCompaction = ConfigFactory.getConfig().isEnableCrossSpaceCompaction();
     numOfPointsPerPage = ConfigFactory.getConfig().getMaxNumberOfPointsInPage();
-
+    maxTsBlockLineNumber = ConfigFactory.getConfig().getMaxTsBlockLineNumber();
     ConfigFactory.getConfig().setEnableSeqSpaceCompaction(false);
     ConfigFactory.getConfig().setEnableUnseqSpaceCompaction(false);
     ConfigFactory.getConfig().setEnableCrossSpaceCompaction(false);
     ConfigFactory.getConfig().setMaxNumberOfPointsInPage(3);
+    ConfigFactory.getConfig().setMaxTsBlockLineNumber(3);
     EnvFactory.getEnv().initBeforeClass();
     AlignedWriteUtil.insertData();
   }
@@ -57,5 +58,6 @@ public class IoTDBAlignedSeriesQuery3IT extends IoTDBAlignedSeriesQueryIT {
     ConfigFactory.getConfig().setEnableUnseqSpaceCompaction(enableUnseqSpaceCompaction);
     ConfigFactory.getConfig().setEnableCrossSpaceCompaction(enableCrossSpaceCompaction);
     ConfigFactory.getConfig().setMaxNumberOfPointsInPage(numOfPointsPerPage);
+    ConfigFactory.getConfig().setMaxTsBlockLineNumber(maxTsBlockLineNumber);
   }
 }
diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBAlignedSeriesQueryIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBAlignedSeriesQueryIT.java
index 93eaf497d7..e13639c32d 100644
--- a/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBAlignedSeriesQueryIT.java
+++ b/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBAlignedSeriesQueryIT.java
@@ -70,15 +70,18 @@ public class IoTDBAlignedSeriesQueryIT {
   protected static boolean enableSeqSpaceCompaction;
   protected static boolean enableUnseqSpaceCompaction;
   protected static boolean enableCrossSpaceCompaction;
+  protected static int maxTsBlockLineNumber;
 
   @BeforeClass
   public static void setUp() throws Exception {
     enableSeqSpaceCompaction = ConfigFactory.getConfig().isEnableSeqSpaceCompaction();
     enableUnseqSpaceCompaction = ConfigFactory.getConfig().isEnableUnseqSpaceCompaction();
     enableCrossSpaceCompaction = ConfigFactory.getConfig().isEnableCrossSpaceCompaction();
+    maxTsBlockLineNumber = ConfigFactory.getConfig().getMaxTsBlockLineNumber();
     ConfigFactory.getConfig().setEnableSeqSpaceCompaction(false);
     ConfigFactory.getConfig().setEnableUnseqSpaceCompaction(false);
     ConfigFactory.getConfig().setEnableCrossSpaceCompaction(false);
+    ConfigFactory.getConfig().setMaxTsBlockLineNumber(3);
     EnvFactory.getEnv().initBeforeClass();
     AlignedWriteUtil.insertData();
   }
@@ -89,6 +92,7 @@ public class IoTDBAlignedSeriesQueryIT {
     ConfigFactory.getConfig().setEnableSeqSpaceCompaction(enableSeqSpaceCompaction);
     ConfigFactory.getConfig().setEnableUnseqSpaceCompaction(enableUnseqSpaceCompaction);
     ConfigFactory.getConfig().setEnableCrossSpaceCompaction(enableCrossSpaceCompaction);
+    ConfigFactory.getConfig().setMaxTsBlockLineNumber(maxTsBlockLineNumber);
   }
 
   // ----------------------------------------Last Query-----------------------------------------
diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBGroupByLevelQueryIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBGroupByLevelQueryIT.java
index 5909f6d3c6..65300100e1 100644
--- a/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBGroupByLevelQueryIT.java
+++ b/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBGroupByLevelQueryIT.java
@@ -43,15 +43,19 @@ public class IoTDBGroupByLevelQueryIT {
   protected static boolean enableSeqSpaceCompaction;
   protected static boolean enableUnseqSpaceCompaction;
   protected static boolean enableCrossSpaceCompaction;
+  protected static int maxTsBlockLineNumber;
 
   @BeforeClass
   public static void setUp() throws Exception {
     enableSeqSpaceCompaction = ConfigFactory.getConfig().isEnableSeqSpaceCompaction();
     enableUnseqSpaceCompaction = ConfigFactory.getConfig().isEnableUnseqSpaceCompaction();
     enableCrossSpaceCompaction = ConfigFactory.getConfig().isEnableCrossSpaceCompaction();
+    maxTsBlockLineNumber = ConfigFactory.getConfig().getMaxTsBlockLineNumber();
+
     ConfigFactory.getConfig().setEnableSeqSpaceCompaction(false);
     ConfigFactory.getConfig().setEnableUnseqSpaceCompaction(false);
     ConfigFactory.getConfig().setEnableCrossSpaceCompaction(false);
+    ConfigFactory.getConfig().setMaxTsBlockLineNumber(3);
     EnvFactory.getEnv().initBeforeClass();
 
     AlignedWriteUtil.insertData();
@@ -90,6 +94,7 @@ public class IoTDBGroupByLevelQueryIT {
     ConfigFactory.getConfig().setEnableSeqSpaceCompaction(enableSeqSpaceCompaction);
     ConfigFactory.getConfig().setEnableUnseqSpaceCompaction(enableUnseqSpaceCompaction);
     ConfigFactory.getConfig().setEnableCrossSpaceCompaction(enableCrossSpaceCompaction);
+    ConfigFactory.getConfig().setMaxTsBlockLineNumber(maxTsBlockLineNumber);
   }
 
   @Test
diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBGroupByQueryWithValueFilterWithDeletion2IT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBGroupByQueryWithValueFilterWithDeletion2IT.java
index 2c029ed5ac..8888370399 100644
--- a/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBGroupByQueryWithValueFilterWithDeletion2IT.java
+++ b/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBGroupByQueryWithValueFilterWithDeletion2IT.java
@@ -45,10 +45,12 @@ public class IoTDBGroupByQueryWithValueFilterWithDeletion2IT
     enableUnseqSpaceCompaction = ConfigFactory.getConfig().isEnableUnseqSpaceCompaction();
     enableCrossSpaceCompaction = ConfigFactory.getConfig().isEnableCrossSpaceCompaction();
     numOfPointsPerPage = ConfigFactory.getConfig().getMaxNumberOfPointsInPage();
+    maxTsBlockLineNumber = ConfigFactory.getConfig().getMaxTsBlockLineNumber();
     ConfigFactory.getConfig().setEnableSeqSpaceCompaction(false);
     ConfigFactory.getConfig().setEnableUnseqSpaceCompaction(false);
     ConfigFactory.getConfig().setEnableCrossSpaceCompaction(false);
     ConfigFactory.getConfig().setMaxNumberOfPointsInPage(3);
+    ConfigFactory.getConfig().setMaxTsBlockLineNumber(3);
 
     EnvFactory.getEnv().initBeforeClass();
 
@@ -70,6 +72,7 @@ public class IoTDBGroupByQueryWithValueFilterWithDeletion2IT
     ConfigFactory.getConfig().setEnableUnseqSpaceCompaction(enableUnseqSpaceCompaction);
     ConfigFactory.getConfig().setEnableCrossSpaceCompaction(enableCrossSpaceCompaction);
     ConfigFactory.getConfig().setMaxNumberOfPointsInPage(numOfPointsPerPage);
+    ConfigFactory.getConfig().setMaxTsBlockLineNumber(maxTsBlockLineNumber);
     EnvFactory.getEnv().cleanAfterClass();
   }
 }
diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBGroupByQueryWithValueFilterWithDeletionIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBGroupByQueryWithValueFilterWithDeletionIT.java
index d7053ff7a9..a1112d33fd 100644
--- a/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBGroupByQueryWithValueFilterWithDeletionIT.java
+++ b/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBGroupByQueryWithValueFilterWithDeletionIT.java
@@ -52,15 +52,18 @@ public class IoTDBGroupByQueryWithValueFilterWithDeletionIT {
   protected static boolean enableSeqSpaceCompaction;
   protected static boolean enableUnseqSpaceCompaction;
   protected static boolean enableCrossSpaceCompaction;
+  protected static int maxTsBlockLineNumber;
 
   @BeforeClass
   public static void setUp() throws Exception {
     enableSeqSpaceCompaction = ConfigFactory.getConfig().isEnableSeqSpaceCompaction();
     enableUnseqSpaceCompaction = ConfigFactory.getConfig().isEnableUnseqSpaceCompaction();
     enableCrossSpaceCompaction = ConfigFactory.getConfig().isEnableCrossSpaceCompaction();
+    maxTsBlockLineNumber = ConfigFactory.getConfig().getMaxTsBlockLineNumber();
     ConfigFactory.getConfig().setEnableSeqSpaceCompaction(false);
     ConfigFactory.getConfig().setEnableUnseqSpaceCompaction(false);
     ConfigFactory.getConfig().setEnableCrossSpaceCompaction(false);
+    ConfigFactory.getConfig().setMaxTsBlockLineNumber(3);
 
     EnvFactory.getEnv().initBeforeClass();
 
@@ -81,6 +84,7 @@ public class IoTDBGroupByQueryWithValueFilterWithDeletionIT {
     ConfigFactory.getConfig().setEnableSeqSpaceCompaction(enableSeqSpaceCompaction);
     ConfigFactory.getConfig().setEnableUnseqSpaceCompaction(enableUnseqSpaceCompaction);
     ConfigFactory.getConfig().setEnableCrossSpaceCompaction(enableCrossSpaceCompaction);
+    ConfigFactory.getConfig().setMaxTsBlockLineNumber(maxTsBlockLineNumber);
     EnvFactory.getEnv().cleanAfterClass();
   }
 
diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBGroupByQueryWithoutValueFilterWithDeletion2IT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBGroupByQueryWithoutValueFilterWithDeletion2IT.java
index 528ec2ac24..f4e8ea19c0 100644
--- a/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBGroupByQueryWithoutValueFilterWithDeletion2IT.java
+++ b/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBGroupByQueryWithoutValueFilterWithDeletion2IT.java
@@ -45,11 +45,13 @@ public class IoTDBGroupByQueryWithoutValueFilterWithDeletion2IT
     enableUnseqSpaceCompaction = ConfigFactory.getConfig().isEnableUnseqSpaceCompaction();
     enableCrossSpaceCompaction = ConfigFactory.getConfig().isEnableCrossSpaceCompaction();
     numOfPointsPerPage = ConfigFactory.getConfig().getMaxNumberOfPointsInPage();
+    maxTsBlockLineNumber = ConfigFactory.getConfig().getMaxTsBlockLineNumber();
 
     ConfigFactory.getConfig().setEnableSeqSpaceCompaction(false);
     ConfigFactory.getConfig().setEnableUnseqSpaceCompaction(false);
     ConfigFactory.getConfig().setEnableCrossSpaceCompaction(false);
     ConfigFactory.getConfig().setMaxNumberOfPointsInPage(3);
+    ConfigFactory.getConfig().setMaxTsBlockLineNumber(3);
     EnvFactory.getEnv().initBeforeClass();
     AlignedWriteUtil.insertData();
 
@@ -72,5 +74,6 @@ public class IoTDBGroupByQueryWithoutValueFilterWithDeletion2IT
     ConfigFactory.getConfig().setEnableUnseqSpaceCompaction(enableUnseqSpaceCompaction);
     ConfigFactory.getConfig().setEnableCrossSpaceCompaction(enableCrossSpaceCompaction);
     ConfigFactory.getConfig().setMaxNumberOfPointsInPage(numOfPointsPerPage);
+    ConfigFactory.getConfig().setMaxTsBlockLineNumber(maxTsBlockLineNumber);
   }
 }
diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBGroupByQueryWithoutValueFilterWithDeletionIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBGroupByQueryWithoutValueFilterWithDeletionIT.java
index d6b76d3a39..bb60bd1c25 100644
--- a/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBGroupByQueryWithoutValueFilterWithDeletionIT.java
+++ b/integration-test/src/test/java/org/apache/iotdb/db/it/aligned/IoTDBGroupByQueryWithoutValueFilterWithDeletionIT.java
@@ -52,6 +52,7 @@ public class IoTDBGroupByQueryWithoutValueFilterWithDeletionIT {
   protected static boolean enableUnseqSpaceCompaction;
   protected static boolean enableCrossSpaceCompaction;
   protected static long prevPartitionInterval;
+  protected static int maxTsBlockLineNumber;
 
   private static final String TIMESTAMP_STR = "Time";
 
@@ -60,9 +61,11 @@ public class IoTDBGroupByQueryWithoutValueFilterWithDeletionIT {
     enableSeqSpaceCompaction = ConfigFactory.getConfig().isEnableSeqSpaceCompaction();
     enableUnseqSpaceCompaction = ConfigFactory.getConfig().isEnableUnseqSpaceCompaction();
     enableCrossSpaceCompaction = ConfigFactory.getConfig().isEnableCrossSpaceCompaction();
+    maxTsBlockLineNumber = ConfigFactory.getConfig().getMaxTsBlockLineNumber();
     ConfigFactory.getConfig().setEnableSeqSpaceCompaction(false);
     ConfigFactory.getConfig().setEnableUnseqSpaceCompaction(false);
     ConfigFactory.getConfig().setEnableCrossSpaceCompaction(false);
+    ConfigFactory.getConfig().setMaxTsBlockLineNumber(3);
     EnvFactory.getEnv().initBeforeClass();
     AlignedWriteUtil.insertData();
 
@@ -84,6 +87,7 @@ public class IoTDBGroupByQueryWithoutValueFilterWithDeletionIT {
     ConfigFactory.getConfig().setEnableUnseqSpaceCompaction(enableUnseqSpaceCompaction);
     ConfigFactory.getConfig().setEnableCrossSpaceCompaction(enableCrossSpaceCompaction);
     ConfigFactory.getConfig().setPartitionInterval(prevPartitionInterval);
+    ConfigFactory.getConfig().setMaxTsBlockLineNumber(maxTsBlockLineNumber);
     EnvFactory.getEnv().cleanAfterClass();
   }
 
diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/env/StandaloneEnvConfig.java b/integration-test/src/test/java/org/apache/iotdb/db/it/env/StandaloneEnvConfig.java
index 55e65be26d..30d21973a5 100644
--- a/integration-test/src/test/java/org/apache/iotdb/db/it/env/StandaloneEnvConfig.java
+++ b/integration-test/src/test/java/org/apache/iotdb/db/it/env/StandaloneEnvConfig.java
@@ -231,4 +231,15 @@ public class StandaloneEnvConfig implements BaseConfig {
         .setAvgSeriesPointNumberThreshold(avgSeriesPointNumberThreshold);
     return this;
   }
+
+  @Override
+  public int getMaxTsBlockLineNumber() {
+    return TSFileDescriptor.getInstance().getConfig().getMaxTsBlockLineNumber();
+  }
+
+  @Override
+  public BaseConfig setMaxTsBlockLineNumber(int maxTsBlockLineNumber) {
+    TSFileDescriptor.getInstance().getConfig().setMaxTsBlockLineNumber(maxTsBlockLineNumber);
+    return this;
+  }
 }
diff --git a/server/src/assembly/resources/conf/iotdb-datanode.properties b/server/src/assembly/resources/conf/iotdb-datanode.properties
index 0bbdb3c64e..66c8460c5c 100644
--- a/server/src/assembly/resources/conf/iotdb-datanode.properties
+++ b/server/src/assembly/resources/conf/iotdb-datanode.properties
@@ -493,6 +493,18 @@ timestamp_precision=ms
 # Datatype: int, Unit: ms
 # default_fill_interval=-1
 
+# the max execution time of a DriverTask
+# Datatype: int, Unit: ms
+# driver_task_execution_time_slice_in_ms=100
+
+# the max capacity of a TsBlock
+# Datatype: int, Unit: byte
+# max_tsblock_size_in_bytes=1048576
+
+# the max number of lines in a single TsBlock
+# Datatype: int
+# max_tsblock_line_numbers=1000
+
 ####################
 ### Compaction Configurations
 ####################
diff --git a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java
index 36560c79e3..66d48dfa21 100644
--- a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java
+++ b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java
@@ -950,6 +950,10 @@ public class IoTDBConfig {
 
   private String readConsistencyLevel = "strong";
 
+  /** Maximum execution time of a DriverTask */
+  /** Maximum execution time of a DriverTask */
+  private int driverTaskExecutionTimeSliceInMs = 100;
+
   IoTDBConfig() {}
 
   public float getUdfMemoryBudgetInMB() {
@@ -3019,4 +3023,12 @@ public class IoTDBConfig {
   public void setReadConsistencyLevel(String readConsistencyLevel) {
     this.readConsistencyLevel = readConsistencyLevel;
   }
+
+  public int getDriverTaskExecutionTimeSliceInMs() {
+    return driverTaskExecutionTimeSliceInMs;
+  }
+
+  public void setDriverTaskExecutionTimeSliceInMs(int driverTaskExecutionTimeSliceInMs) {
+    this.driverTaskExecutionTimeSliceInMs = driverTaskExecutionTimeSliceInMs;
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java
index a25d6674d7..2c3a2a6ed0 100644
--- a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java
+++ b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java
@@ -1198,6 +1198,22 @@ public class IoTDBDescriptor {
                     "max_degree_of_index_node",
                     Integer.toString(
                         TSFileDescriptor.getInstance().getConfig().getMaxDegreeOfIndexNode()))));
+    TSFileDescriptor.getInstance()
+        .getConfig()
+        .setMaxTsBlockSizeInBytes(
+            Integer.parseInt(
+                properties.getProperty(
+                    "max_tsblock_size_in_bytes",
+                    Integer.toString(
+                        TSFileDescriptor.getInstance().getConfig().getMaxTsBlockSizeInBytes()))));
+    TSFileDescriptor.getInstance()
+        .getConfig()
+        .setMaxTsBlockLineNumber(
+            Integer.parseInt(
+                properties.getProperty(
+                    "max_tsblock_line_number",
+                    Integer.toString(
+                        TSFileDescriptor.getInstance().getConfig().getMaxTsBlockLineNumber()))));
   }
 
   // Mqtt related
@@ -1723,6 +1739,12 @@ public class IoTDBDescriptor {
         Integer.parseInt(
             properties.getProperty(
                 "partition_cache_size", Integer.toString(conf.getPartitionCacheSize()))));
+
+    conf.setDriverTaskExecutionTimeSliceInMs(
+        Integer.parseInt(
+            properties.getProperty(
+                "driver_task_execution_time_slice_in_ms",
+                Integer.toString(conf.getDriverTaskExecutionTimeSliceInMs()))));
   }
 
   /** Get default encode algorithm by data type */
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/Aggregator.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/Aggregator.java
index b1bd9bfb63..a1f73ef627 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/Aggregator.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/Aggregator.java
@@ -28,6 +28,7 @@ import org.apache.iotdb.tsfile.read.common.block.TsBlock;
 import org.apache.iotdb.tsfile.read.common.block.column.Column;
 import org.apache.iotdb.tsfile.read.common.block.column.ColumnBuilder;
 
+import java.util.Collections;
 import java.util.List;
 
 import static com.google.common.base.Preconditions.checkArgument;
@@ -45,6 +46,8 @@ public class Aggregator {
   public Aggregator(Accumulator accumulator, AggregationStep step) {
     this.accumulator = accumulator;
     this.step = step;
+    this.inputLocationList =
+        Collections.singletonList(new InputLocation[] {new InputLocation(0, 0)});
   }
 
   // Used for aggregateOperator
@@ -108,16 +111,9 @@ public class Aggregator {
     }
   }
 
-  public void processStatistics(Statistics statistics) {
-    accumulator.addStatistics(statistics);
-  }
-
-  /** Used for AlignedSeriesAggregateScanOperator. */
+  /** Used for SeriesAggregateScanOperator. */
   public void processStatistics(Statistics[] statistics) {
     for (InputLocation[] inputLocations : inputLocationList) {
-      checkArgument(
-          inputLocations[0].getTsBlockIndex() == 0,
-          "AlignedSeriesAggregateScanOperator can only process one tsBlock input.");
       int valueIndex = inputLocations[0].getValueColumnIndex();
       accumulator.addStatistics(statistics[valueIndex]);
     }
@@ -141,6 +137,7 @@ public class Aggregator {
   }
 
   public void updateTimeRange(TimeRange curTimeRange) {
+    reset();
     this.curTimeRange = curTimeRange;
   }
 
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/slidingwindow/EmptyQueueSlidingWindowAggregator.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/slidingwindow/EmptyQueueSlidingWindowAggregator.java
index 52b009e922..a3a0a1b735 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/slidingwindow/EmptyQueueSlidingWindowAggregator.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/slidingwindow/EmptyQueueSlidingWindowAggregator.java
@@ -50,7 +50,6 @@ public class EmptyQueueSlidingWindowAggregator extends SlidingWindowAggregator {
   @Override
   public void processPartialResult(PartialAggregationResult partialResult) {
     if (!partialResult.isNull()) {
-      this.accumulator.reset();
       this.accumulator.addIntermediate(partialResult.getPartialResult());
       lastTime = partialResult.getTime();
     }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/slidingwindow/MonotonicQueueSlidingWindowAggregator.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/slidingwindow/MonotonicQueueSlidingWindowAggregator.java
index 7706edfb2c..35eeb8f2e1 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/slidingwindow/MonotonicQueueSlidingWindowAggregator.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/slidingwindow/MonotonicQueueSlidingWindowAggregator.java
@@ -68,9 +68,6 @@ public class MonotonicQueueSlidingWindowAggregator extends SlidingWindowAggregat
       deque.removeLast();
     }
     deque.addLast(partialResult);
-    this.accumulator.reset();
-    if (!deque.isEmpty()) {
-      this.accumulator.addIntermediate(deque.getFirst().getPartialResult());
-    }
+    this.accumulator.addIntermediate(deque.getFirst().getPartialResult());
   }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/slidingwindow/NormalQueueSlidingWindowAggregator.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/slidingwindow/NormalQueueSlidingWindowAggregator.java
index eecaa8cffe..d89981a831 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/slidingwindow/NormalQueueSlidingWindowAggregator.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/slidingwindow/NormalQueueSlidingWindowAggregator.java
@@ -53,7 +53,6 @@ public class NormalQueueSlidingWindowAggregator extends SlidingWindowAggregator
     if (!partialResult.isNull()) {
       deque.addLast(partialResult);
     }
-    this.accumulator.reset();
     if (!deque.isEmpty()) {
       this.accumulator.addIntermediate(deque.getFirst().getPartialResult());
     }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/slidingwindow/SlidingWindowAggregator.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/slidingwindow/SlidingWindowAggregator.java
index aed468c8d8..ec848b354d 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/slidingwindow/SlidingWindowAggregator.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/slidingwindow/SlidingWindowAggregator.java
@@ -66,7 +66,7 @@ public abstract class SlidingWindowAggregator extends Aggregator {
       valueColumn[i] = tsBlock.getColumn(inputLocation.getValueColumnIndex());
     }
     processPartialResult(new PartialAggregationResult(timeColumn, valueColumn));
-    return tsBlock.getPositionCount();
+    return 1;
   }
 
   @Override
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/AggregationUtil.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/AggregationUtil.java
new file mode 100644
index 0000000000..5465800553
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/AggregationUtil.java
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.mpp.execution.operator;
+
+import org.apache.iotdb.db.mpp.aggregation.Aggregator;
+import org.apache.iotdb.db.mpp.aggregation.timerangeiterator.ITimeRangeIterator;
+import org.apache.iotdb.db.mpp.aggregation.timerangeiterator.SingleTimeWindowIterator;
+import org.apache.iotdb.db.mpp.aggregation.timerangeiterator.TimeRangeIteratorFactory;
+import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.GroupByTimeParameter;
+import org.apache.iotdb.tsfile.read.common.TimeRange;
+import org.apache.iotdb.tsfile.read.common.block.TsBlock;
+import org.apache.iotdb.tsfile.read.common.block.TsBlockBuilder;
+import org.apache.iotdb.tsfile.read.common.block.column.ColumnBuilder;
+import org.apache.iotdb.tsfile.read.common.block.column.TimeColumnBuilder;
+import org.apache.iotdb.tsfile.utils.Pair;
+
+import java.util.List;
+
+import static org.apache.iotdb.tsfile.read.common.block.TsBlockUtil.skipPointsOutOfTimeRange;
+
+public class AggregationUtil {
+
+  private AggregationUtil() {
+    // forbidding instantiation
+  }
+
+  /**
+   * If groupByTimeParameter is null, which means it's an aggregation query without down sampling.
+   * Aggregation query has only one time window and the result set of it does not contain a
+   * timestamp, so it doesn't matter what the time range returns.
+   */
+  public static ITimeRangeIterator initTimeRangeIterator(
+      GroupByTimeParameter groupByTimeParameter,
+      boolean ascending,
+      boolean outputPartialTimeWindow) {
+    if (groupByTimeParameter == null) {
+      return new SingleTimeWindowIterator(0, Long.MAX_VALUE);
+    } else {
+      return TimeRangeIteratorFactory.getTimeRangeIterator(
+          groupByTimeParameter.getStartTime(),
+          groupByTimeParameter.getEndTime(),
+          groupByTimeParameter.getInterval(),
+          groupByTimeParameter.getSlidingStep(),
+          ascending,
+          groupByTimeParameter.isIntervalByMonth(),
+          groupByTimeParameter.isSlidingStepByMonth(),
+          groupByTimeParameter.isLeftCRightO(),
+          outputPartialTimeWindow);
+    }
+  }
+
+  /**
+   * Calculate aggregation value on the time range from the tsBlock containing raw data.
+   *
+   * @return left - whether the aggregation calculation of the current time range has done; right -
+   *     remaining tsBlock
+   */
+  public static Pair<Boolean, TsBlock> calculateAggregationFromRawData(
+      TsBlock inputTsBlock,
+      List<Aggregator> aggregators,
+      TimeRange curTimeRange,
+      boolean ascending) {
+    if (inputTsBlock == null || inputTsBlock.isEmpty()) {
+      return new Pair<>(false, inputTsBlock);
+    }
+
+    // check if the tsBlock does not contain points in current interval
+    if (satisfiedTimeRange(inputTsBlock, curTimeRange, ascending)) {
+      // skip points that cannot be calculated
+      if ((ascending && inputTsBlock.getStartTime() < curTimeRange.getMin())
+          || (!ascending && inputTsBlock.getStartTime() > curTimeRange.getMax())) {
+        inputTsBlock = skipPointsOutOfTimeRange(inputTsBlock, curTimeRange, ascending);
+      }
+
+      int lastReadRowIndex = 0;
+      for (Aggregator aggregator : aggregators) {
+        // current agg method has been calculated
+        if (aggregator.hasFinalResult()) {
+          continue;
+        }
+
+        lastReadRowIndex = Math.max(lastReadRowIndex, aggregator.processTsBlock(inputTsBlock));
+      }
+      if (lastReadRowIndex >= inputTsBlock.getPositionCount()) {
+        inputTsBlock = null;
+      } else {
+        inputTsBlock = inputTsBlock.subTsBlock(lastReadRowIndex);
+      }
+    }
+
+    // judge whether the calculation finished
+    boolean isTsBlockOutOfBound =
+        inputTsBlock != null
+            && (ascending
+                ? inputTsBlock.getEndTime() > curTimeRange.getMax()
+                : inputTsBlock.getEndTime() < curTimeRange.getMin());
+    return new Pair<>(
+        isAllAggregatorsHasFinalResult(aggregators) || isTsBlockOutOfBound, inputTsBlock);
+  }
+
+  /** Append a row of aggregation results to the result tsBlock. */
+  public static void appendAggregationResult(
+      TsBlockBuilder tsBlockBuilder,
+      List<? extends Aggregator> aggregators,
+      ITimeRangeIterator timeRangeIterator) {
+    TimeColumnBuilder timeColumnBuilder = tsBlockBuilder.getTimeColumnBuilder();
+    // Use start time of current time range as time column
+    timeColumnBuilder.writeLong(timeRangeIterator.currentOutputTime());
+    ColumnBuilder[] columnBuilders = tsBlockBuilder.getValueColumnBuilders();
+    int columnIndex = 0;
+    for (Aggregator aggregator : aggregators) {
+      ColumnBuilder[] columnBuilder = new ColumnBuilder[aggregator.getOutputType().length];
+      columnBuilder[0] = columnBuilders[columnIndex++];
+      if (columnBuilder.length > 1) {
+        columnBuilder[1] = columnBuilders[columnIndex++];
+      }
+      aggregator.outputResult(columnBuilder);
+    }
+    tsBlockBuilder.declarePosition();
+  }
+
+  /** @return whether the tsBlock contains the data of the current time window */
+  public static boolean satisfiedTimeRange(
+      TsBlock tsBlock, TimeRange curTimeRange, boolean ascending) {
+    if (tsBlock == null || tsBlock.isEmpty()) {
+      return false;
+    }
+
+    return ascending
+        ? (tsBlock.getEndTime() >= curTimeRange.getMin()
+            && tsBlock.getStartTime() <= curTimeRange.getMax())
+        : (tsBlock.getEndTime() <= curTimeRange.getMax()
+            && tsBlock.getStartTime() >= curTimeRange.getMin());
+  }
+
+  public static boolean isAllAggregatorsHasFinalResult(List<Aggregator> aggregators) {
+    for (Aggregator aggregator : aggregators) {
+      if (!aggregator.hasFinalResult()) {
+        return false;
+      }
+    }
+    return true;
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/OperatorContext.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/OperatorContext.java
index 0fb63897a3..356e0c4eed 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/OperatorContext.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/OperatorContext.java
@@ -21,6 +21,10 @@ package org.apache.iotdb.db.mpp.execution.operator;
 import org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceContext;
 import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId;
 
+import io.airlift.units.Duration;
+
+import java.util.Objects;
+
 /**
  * Contains information about {@link Operator} execution.
  *
@@ -33,6 +37,8 @@ public class OperatorContext {
   private final String operatorType;
   private final FragmentInstanceContext instanceContext;
 
+  private Duration maxRunTime;
+
   public OperatorContext(
       int operatorId,
       PlanNodeId planNodeId,
@@ -48,7 +54,36 @@ public class OperatorContext {
     return operatorId;
   }
 
+  public String getOperatorType() {
+    return operatorType;
+  }
+
   public FragmentInstanceContext getInstanceContext() {
     return instanceContext;
   }
+
+  public Duration getMaxRunTime() {
+    return maxRunTime;
+  }
+
+  public void setMaxRunTime(Duration maxRunTime) {
+    this.maxRunTime = maxRunTime;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    OperatorContext that = (OperatorContext) o;
+    return operatorId == that.operatorId;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(operatorId);
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/AggregationOperator.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/AggregationOperator.java
index e4de130719..76b81c2873 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/AggregationOperator.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/AggregationOperator.java
@@ -27,36 +27,40 @@ import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
 import org.apache.iotdb.tsfile.read.common.TimeRange;
 import org.apache.iotdb.tsfile.read.common.block.TsBlock;
 import org.apache.iotdb.tsfile.read.common.block.TsBlockBuilder;
-import org.apache.iotdb.tsfile.read.common.block.column.ColumnBuilder;
-import org.apache.iotdb.tsfile.read.common.block.column.TimeColumnBuilder;
 
 import com.google.common.util.concurrent.ListenableFuture;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
-import static org.apache.iotdb.db.mpp.execution.operator.source.SeriesAggregationScanOperator.initTimeRangeIterator;
+import static org.apache.iotdb.db.mpp.execution.operator.AggregationUtil.appendAggregationResult;
+import static org.apache.iotdb.db.mpp.execution.operator.AggregationUtil.initTimeRangeIterator;
 
 /**
  * AggregationOperator can process the situation: aggregation of intermediate aggregate result, it
- * will output one result based on time interval. One intermediate tsBlock input will only contain
- * the result of one time interval exactly.
+ * will output one tsBlock contain many results on aggregation time intervals. One intermediate
+ * tsBlock input will contain the result of many time intervals.
  */
 public class AggregationOperator implements ProcessOperator {
 
   private final OperatorContext operatorContext;
-  private final List<Aggregator> aggregators;
-  private final List<Operator> children;
 
+  private final List<Operator> children;
   private final int inputOperatorsCount;
   private final TsBlock[] inputTsBlocks;
-  private final TsBlockBuilder tsBlockBuilder;
+  private final boolean[] canCallNext;
 
   private final ITimeRangeIterator timeRangeIterator;
   // current interval of aggregation window [curStartTime, curEndTime)
   private TimeRange curTimeRange;
 
+  private final List<Aggregator> aggregators;
+
+  // using for building result tsBlock
+  private final TsBlockBuilder resultTsBlockBuilder;
+
   public AggregationOperator(
       OperatorContext operatorContext,
       List<Aggregator> aggregators,
@@ -65,18 +69,21 @@ public class AggregationOperator implements ProcessOperator {
       GroupByTimeParameter groupByTimeParameter,
       boolean outputPartialTimeWindow) {
     this.operatorContext = operatorContext;
-    this.aggregators = aggregators;
     this.children = children;
+    this.aggregators = aggregators;
 
     this.inputOperatorsCount = children.size();
     this.inputTsBlocks = new TsBlock[inputOperatorsCount];
+    this.canCallNext = new boolean[inputOperatorsCount];
+
+    this.timeRangeIterator =
+        initTimeRangeIterator(groupByTimeParameter, ascending, outputPartialTimeWindow);
+
     List<TSDataType> dataTypes = new ArrayList<>();
     for (Aggregator aggregator : aggregators) {
       dataTypes.addAll(Arrays.asList(aggregator.getOutputType()));
     }
-    tsBlockBuilder = new TsBlockBuilder(dataTypes);
-    this.timeRangeIterator =
-        initTimeRangeIterator(groupByTimeParameter, ascending, outputPartialTimeWindow);
+    this.resultTsBlockBuilder = new TsBlockBuilder(dataTypes);
   }
 
   @Override
@@ -95,37 +102,55 @@ public class AggregationOperator implements ProcessOperator {
     return NOT_BLOCKED;
   }
 
+  @Override
+  public boolean hasNext() {
+    return curTimeRange != null || timeRangeIterator.hasNextTimeRange();
+  }
+
   @Override
   public TsBlock next() {
-    // update input tsBlock
-    if (curTimeRange == null && timeRangeIterator.hasNextTimeRange()) {
-      curTimeRange = timeRangeIterator.nextTimeRange();
-    }
+    // start stopwatch
+    long maxRuntime = operatorContext.getMaxRunTime().roundTo(TimeUnit.NANOSECONDS);
+    long start = System.nanoTime();
+
+    // reset operator state
+    resultTsBlockBuilder.reset();
     for (int i = 0; i < inputOperatorsCount; i++) {
-      if (inputTsBlocks[i] != null) {
-        continue;
+      canCallNext[i] = true;
+    }
+
+    while (System.nanoTime() - start < maxRuntime
+        && (curTimeRange != null || timeRangeIterator.hasNextTimeRange())
+        && !resultTsBlockBuilder.isFull()) {
+      boolean hasCachedData = prepareInput();
+      if (!hasCachedData) {
+        break;
       }
-      inputTsBlocks[i] = children.get(i).next();
-      if (inputTsBlocks[i] == null) {
-        return null;
+
+      if (curTimeRange == null && timeRangeIterator.hasNextTimeRange()) {
+        // move to next time window
+        curTimeRange = timeRangeIterator.nextTimeRange();
+
+        // clear previous aggregation result
+        for (Aggregator aggregator : aggregators) {
+          aggregator.updateTimeRange(curTimeRange);
+        }
       }
+
+      // calculate aggregation result on current time window
+      calculateNextAggregationResult();
     }
-    // consume current input tsBlocks
-    for (Aggregator aggregator : aggregators) {
-      aggregator.reset();
-      aggregator.processTsBlocks(inputTsBlocks);
-    }
-    // output result from aggregator
-    curTimeRange = null;
-    for (int i = 0; i < inputOperatorsCount; i++) {
-      inputTsBlocks[i] = null;
+
+    if (resultTsBlockBuilder.getPositionCount() > 0) {
+      return resultTsBlockBuilder.build();
+    } else {
+      return null;
     }
-    return updateResultTsBlockFromAggregators(tsBlockBuilder, aggregators, timeRangeIterator);
   }
 
   @Override
-  public boolean hasNext() {
-    return curTimeRange != null || timeRangeIterator.hasNextTimeRange();
+  public boolean isFinished() {
+    return !this.hasNext();
   }
 
   @Override
@@ -135,30 +160,43 @@ public class AggregationOperator implements ProcessOperator {
     }
   }
 
-  @Override
-  public boolean isFinished() {
-    return !this.hasNext();
+  private boolean prepareInput() {
+    for (int i = 0; i < inputOperatorsCount; i++) {
+      if (inputTsBlocks[i] != null) {
+        continue;
+      }
+      if (!canCallNext[i]) {
+        return false;
+      }
+
+      inputTsBlocks[i] = children.get(i).next();
+      canCallNext[i] = false;
+      if (inputTsBlocks[i] == null) {
+        return false;
+      }
+    }
+    return true;
   }
 
-  public static TsBlock updateResultTsBlockFromAggregators(
-      TsBlockBuilder tsBlockBuilder,
-      List<? extends Aggregator> aggregators,
-      ITimeRangeIterator timeRangeIterator) {
-    tsBlockBuilder.reset();
-    TimeColumnBuilder timeColumnBuilder = tsBlockBuilder.getTimeColumnBuilder();
-    // Use start time of current time range as time column
-    timeColumnBuilder.writeLong(timeRangeIterator.currentOutputTime());
-    ColumnBuilder[] columnBuilders = tsBlockBuilder.getValueColumnBuilders();
-    int columnIndex = 0;
+  private void calculateNextAggregationResult() {
+    // consume current input tsBlocks
     for (Aggregator aggregator : aggregators) {
-      ColumnBuilder[] columnBuilder = new ColumnBuilder[aggregator.getOutputType().length];
-      columnBuilder[0] = columnBuilders[columnIndex++];
-      if (columnBuilder.length > 1) {
-        columnBuilder[1] = columnBuilders[columnIndex++];
+      aggregator.processTsBlocks(inputTsBlocks);
+    }
+
+    for (int i = 0; i < inputOperatorsCount; i++) {
+      inputTsBlocks[i] = inputTsBlocks[i].skipFirst();
+      if (inputTsBlocks[i].isEmpty()) {
+        inputTsBlocks[i] = null;
       }
-      aggregator.outputResult(columnBuilder);
     }
-    tsBlockBuilder.declarePosition();
-    return tsBlockBuilder.build();
+
+    // update result using aggregators
+    updateResultTsBlock();
+  }
+
+  private void updateResultTsBlock() {
+    curTimeRange = null;
+    appendAggregationResult(resultTsBlockBuilder, aggregators, timeRangeIterator);
   }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/RawDataAggregationOperator.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/RawDataAggregationOperator.java
index 01b0625c60..88fb5fe20a 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/RawDataAggregationOperator.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/RawDataAggregationOperator.java
@@ -20,24 +20,15 @@
 package org.apache.iotdb.db.mpp.execution.operator.process;
 
 import org.apache.iotdb.db.mpp.aggregation.Aggregator;
-import org.apache.iotdb.db.mpp.aggregation.timerangeiterator.ITimeRangeIterator;
 import org.apache.iotdb.db.mpp.execution.operator.Operator;
 import org.apache.iotdb.db.mpp.execution.operator.OperatorContext;
 import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.GroupByTimeParameter;
-import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
-import org.apache.iotdb.tsfile.read.common.TimeRange;
 import org.apache.iotdb.tsfile.read.common.block.TsBlock;
-import org.apache.iotdb.tsfile.read.common.block.TsBlock.TsBlockSingleColumnIterator;
-import org.apache.iotdb.tsfile.read.common.block.TsBlockBuilder;
-import org.apache.iotdb.tsfile.read.common.block.column.TimeColumn;
+import org.apache.iotdb.tsfile.utils.Pair;
 
-import com.google.common.util.concurrent.ListenableFuture;
-
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
-import static org.apache.iotdb.db.mpp.execution.operator.source.SeriesAggregationScanOperator.initTimeRangeIterator;
+import static org.apache.iotdb.db.mpp.execution.operator.AggregationUtil.calculateAggregationFromRawData;
 
 /**
  * RawDataAggregationOperator is used to process raw data tsBlock input calculating using value
@@ -47,22 +38,9 @@ import static org.apache.iotdb.db.mpp.execution.operator.source.SeriesAggregatio
  * <p>Since raw data query with value filter is processed by FilterOperator above TimeJoinOperator,
  * there we can see RawDataAggregateOperator as a one-to-one(one input, ont output) operator.
  *
- * <p>Return aggregation result in one time interval once.
+ * <p>Return aggregation result in many time intervals once.
  */
-public class RawDataAggregationOperator implements ProcessOperator {
-
-  private final OperatorContext operatorContext;
-  private final List<Aggregator> aggregators;
-  private final Operator child;
-  private final boolean ascending;
-  private ITimeRangeIterator timeRangeIterator;
-  // current interval of aggregation window [curStartTime, curEndTime)
-  private TimeRange curTimeRange;
-
-  private TsBlock preCachedData;
-
-  // Using for building result tsBlock
-  private final TsBlockBuilder tsBlockBuilder;
+public class RawDataAggregationOperator extends SingleInputAggregationOperator {
 
   public RawDataAggregationOperator(
       OperatorContext operatorContext,
@@ -70,161 +48,36 @@ public class RawDataAggregationOperator implements ProcessOperator {
       Operator child,
       boolean ascending,
       GroupByTimeParameter groupByTimeParameter) {
-    this.operatorContext = operatorContext;
-    this.aggregators = aggregators;
-    this.child = child;
-    this.ascending = ascending;
-
-    List<TSDataType> dataTypes = new ArrayList<>();
-    for (Aggregator aggregator : aggregators) {
-      dataTypes.addAll(Arrays.asList(aggregator.getOutputType()));
-    }
-    tsBlockBuilder = new TsBlockBuilder(dataTypes);
-    this.timeRangeIterator = initTimeRangeIterator(groupByTimeParameter, ascending, true);
-  }
-
-  @Override
-  public OperatorContext getOperatorContext() {
-    return operatorContext;
-  }
-
-  @Override
-  public ListenableFuture<?> isBlocked() {
-    return child.isBlocked();
+    super(operatorContext, aggregators, child, ascending, groupByTimeParameter, true);
   }
 
   @Override
-  public TsBlock next() {
-    // Move to next timeRange
-    if (curTimeRange == null && timeRangeIterator.hasNextTimeRange()) {
-      curTimeRange = timeRangeIterator.nextTimeRange();
-      for (Aggregator aggregator : aggregators) {
-        aggregator.reset();
-        aggregator.updateTimeRange(curTimeRange);
-      }
-    }
+  protected boolean calculateNextAggregationResult() {
+    while (!calcFromRawData()) {
+      inputTsBlock = null;
 
-    // 1. Calculate aggregation result based on current time window
-    boolean canCallNext = true;
-    while (!calcFromCacheData(curTimeRange)) {
-      preCachedData = null;
-      // child.next can only be invoked once
+      // NOTE: child.next() can only be invoked once
       if (child.hasNext() && canCallNext) {
-        preCachedData = child.next();
+        inputTsBlock = child.next();
         canCallNext = false;
-        // if child still has next but can't be invoked now
       } else if (child.hasNext()) {
-        return null;
+        // if child still has next but can't be invoked now
+        return false;
       } else {
         break;
       }
     }
 
-    // 2. Update result using aggregators
-    curTimeRange = null;
-    return AggregationOperator.updateResultTsBlockFromAggregators(
-        tsBlockBuilder, aggregators, timeRangeIterator);
-  }
-
-  @Override
-  public boolean hasNext() {
-    return curTimeRange != null || timeRangeIterator.hasNextTimeRange();
-  }
-
-  @Override
-  public void close() throws Exception {
-    child.close();
-  }
-
-  @Override
-  public boolean isFinished() {
-    return !this.hasNext();
-  }
-
-  /** @return if already get the result */
-  private boolean calcFromCacheData(TimeRange curTimeRange) {
-    if (preCachedData == null || preCachedData.isEmpty()) {
-      return false;
-    }
-    // check if the batchData does not contain points in current interval
-    if (satisfied(preCachedData, curTimeRange, ascending)) {
-      // skip points that cannot be calculated
-      if ((ascending && preCachedData.getStartTime() < curTimeRange.getMin())
-          || (!ascending && preCachedData.getStartTime() > curTimeRange.getMax())) {
-        preCachedData = skipOutOfTimeRangePoints(preCachedData, curTimeRange, ascending);
-      }
-
-      int lastReadRowIndex = 0;
-      for (Aggregator aggregator : aggregators) {
-        // current agg method has been calculated
-        if (aggregator.hasFinalResult()) {
-          continue;
-        }
-
-        lastReadRowIndex = Math.max(lastReadRowIndex, aggregator.processTsBlock(preCachedData));
-      }
-      if (lastReadRowIndex >= preCachedData.getPositionCount()) {
-        preCachedData = null;
-      } else {
-        preCachedData = preCachedData.subTsBlock(lastReadRowIndex);
-      }
-    }
-    // The result is calculated from the cache
-    return (preCachedData != null
-            && (ascending
-                ? preCachedData.getEndTime() > curTimeRange.getMax()
-                : preCachedData.getEndTime() < curTimeRange.getMin()))
-        || isEndCalc(aggregators);
-  }
-
-  // skip points that cannot be calculated
-  public static TsBlock skipOutOfTimeRangePoints(
-      TsBlock tsBlock, TimeRange curTimeRange, boolean ascending) {
-    TimeColumn timeColumn = tsBlock.getTimeColumn();
-    long targetTime = ascending ? curTimeRange.getMin() : curTimeRange.getMax();
-    int left = 0, right = timeColumn.getPositionCount() - 1, mid;
-    // if ascending, find the first greater than or equal to targetTime
-    // else, find the first less than or equal to targetTime
-    while (left < right) {
-      mid = (left + right) >> 1;
-      if (timeColumn.getLongWithoutCheck(mid) < targetTime) {
-        if (ascending) {
-          left = mid + 1;
-        } else {
-          right = mid;
-        }
-      } else if (timeColumn.getLongWithoutCheck(mid) > targetTime) {
-        if (ascending) {
-          right = mid;
-        } else {
-          left = mid + 1;
-        }
-      } else if (timeColumn.getLongWithoutCheck(mid) == targetTime) {
-        return tsBlock.subTsBlock(mid);
-      }
-    }
-    return tsBlock.subTsBlock(left);
-  }
-
-  public static boolean satisfied(TsBlock tsBlock, TimeRange timeRange, boolean ascending) {
-    TsBlockSingleColumnIterator tsBlockIterator = tsBlock.getTsBlockSingleColumnIterator();
-    if (tsBlockIterator == null || !tsBlockIterator.hasNext()) {
-      return false;
-    }
+    // update result using aggregators
+    updateResultTsBlock();
 
-    return ascending
-        ? (tsBlockIterator.getEndTime() >= timeRange.getMin()
-            && tsBlockIterator.currentTime() <= timeRange.getMax())
-        : (tsBlockIterator.getEndTime() <= timeRange.getMax()
-            && tsBlockIterator.currentTime() >= timeRange.getMin());
+    return true;
   }
 
-  public static boolean isEndCalc(List<Aggregator> aggregators) {
-    for (Aggregator aggregator : aggregators) {
-      if (!aggregator.hasFinalResult()) {
-        return false;
-      }
-    }
-    return true;
+  private boolean calcFromRawData() {
+    Pair<Boolean, TsBlock> calcResult =
+        calculateAggregationFromRawData(inputTsBlock, aggregators, curTimeRange, ascending);
+    inputTsBlock = calcResult.getRight();
+    return calcResult.getLeft();
   }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/SingleInputAggregationOperator.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/SingleInputAggregationOperator.java
new file mode 100644
index 0000000000..c59de13988
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/SingleInputAggregationOperator.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.mpp.execution.operator.process;
+
+import org.apache.iotdb.db.mpp.aggregation.Aggregator;
+import org.apache.iotdb.db.mpp.aggregation.timerangeiterator.ITimeRangeIterator;
+import org.apache.iotdb.db.mpp.execution.operator.Operator;
+import org.apache.iotdb.db.mpp.execution.operator.OperatorContext;
+import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.GroupByTimeParameter;
+import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
+import org.apache.iotdb.tsfile.read.common.TimeRange;
+import org.apache.iotdb.tsfile.read.common.block.TsBlock;
+import org.apache.iotdb.tsfile.read.common.block.TsBlockBuilder;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import static org.apache.iotdb.db.mpp.execution.operator.AggregationUtil.appendAggregationResult;
+import static org.apache.iotdb.db.mpp.execution.operator.AggregationUtil.initTimeRangeIterator;
+
+public abstract class SingleInputAggregationOperator implements ProcessOperator {
+
+  protected final OperatorContext operatorContext;
+  protected final boolean ascending;
+
+  protected final Operator child;
+  protected TsBlock inputTsBlock;
+  protected boolean canCallNext;
+
+  protected final ITimeRangeIterator timeRangeIterator;
+  // current interval of aggregation window [curStartTime, curEndTime)
+  protected TimeRange curTimeRange;
+
+  protected final List<Aggregator> aggregators;
+
+  // using for building result tsBlock
+  protected final TsBlockBuilder resultTsBlockBuilder;
+
+  public SingleInputAggregationOperator(
+      OperatorContext operatorContext,
+      List<Aggregator> aggregators,
+      Operator child,
+      boolean ascending,
+      GroupByTimeParameter groupByTimeParameter,
+      boolean outputPartialTimeWindow) {
+    this.operatorContext = operatorContext;
+    this.ascending = ascending;
+    this.child = child;
+    this.aggregators = aggregators;
+
+    this.timeRangeIterator =
+        initTimeRangeIterator(groupByTimeParameter, ascending, outputPartialTimeWindow);
+
+    List<TSDataType> dataTypes = new ArrayList<>();
+    for (Aggregator aggregator : aggregators) {
+      dataTypes.addAll(Arrays.asList(aggregator.getOutputType()));
+    }
+    this.resultTsBlockBuilder = new TsBlockBuilder(dataTypes);
+  }
+
+  @Override
+  public OperatorContext getOperatorContext() {
+    return operatorContext;
+  }
+
+  @Override
+  public ListenableFuture<?> isBlocked() {
+    return child.isBlocked();
+  }
+
+  @Override
+  public boolean hasNext() {
+    return curTimeRange != null || timeRangeIterator.hasNextTimeRange();
+  }
+
+  @Override
+  public TsBlock next() {
+    // start stopwatch
+    long maxRuntime = operatorContext.getMaxRunTime().roundTo(TimeUnit.NANOSECONDS);
+    long start = System.nanoTime();
+
+    // reset operator state
+    resultTsBlockBuilder.reset();
+    canCallNext = true;
+
+    while (System.nanoTime() - start < maxRuntime
+        && (curTimeRange != null || timeRangeIterator.hasNextTimeRange())
+        && !resultTsBlockBuilder.isFull()) {
+      if (curTimeRange == null && timeRangeIterator.hasNextTimeRange()) {
+        // move to next time window
+        curTimeRange = timeRangeIterator.nextTimeRange();
+
+        // clear previous aggregation result
+        for (Aggregator aggregator : aggregators) {
+          aggregator.updateTimeRange(curTimeRange);
+        }
+      }
+
+      // calculate aggregation result on current time window
+      if (!calculateNextAggregationResult()) {
+        break;
+      }
+    }
+
+    if (resultTsBlockBuilder.getPositionCount() > 0) {
+      return resultTsBlockBuilder.build();
+    } else {
+      return null;
+    }
+  }
+
+  @Override
+  public boolean isFinished() {
+    return !this.hasNext();
+  }
+
+  @Override
+  public void close() throws Exception {
+    child.close();
+  }
+
+  protected abstract boolean calculateNextAggregationResult();
+
+  protected void updateResultTsBlock() {
+    curTimeRange = null;
+    appendAggregationResult(resultTsBlockBuilder, aggregators, timeRangeIterator);
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/SlidingWindowAggregationOperator.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/SlidingWindowAggregationOperator.java
index 2685e3e465..10303016c5 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/SlidingWindowAggregationOperator.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/process/SlidingWindowAggregationOperator.java
@@ -20,149 +20,88 @@
 package org.apache.iotdb.db.mpp.execution.operator.process;
 
 import org.apache.iotdb.db.mpp.aggregation.Aggregator;
-import org.apache.iotdb.db.mpp.aggregation.slidingwindow.SlidingWindowAggregator;
 import org.apache.iotdb.db.mpp.aggregation.timerangeiterator.ITimeRangeIterator;
 import org.apache.iotdb.db.mpp.execution.operator.Operator;
 import org.apache.iotdb.db.mpp.execution.operator.OperatorContext;
 import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.GroupByTimeParameter;
-import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
 import org.apache.iotdb.tsfile.read.common.TimeRange;
-import org.apache.iotdb.tsfile.read.common.block.TsBlock;
-import org.apache.iotdb.tsfile.read.common.block.TsBlockBuilder;
 
-import com.google.common.util.concurrent.ListenableFuture;
-
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
 import static com.google.common.base.Preconditions.checkArgument;
-import static org.apache.iotdb.db.mpp.execution.operator.process.AggregationOperator.updateResultTsBlockFromAggregators;
-import static org.apache.iotdb.db.mpp.execution.operator.process.RawDataAggregationOperator.satisfied;
-import static org.apache.iotdb.db.mpp.execution.operator.process.RawDataAggregationOperator.skipOutOfTimeRangePoints;
-import static org.apache.iotdb.db.mpp.execution.operator.source.SeriesAggregationScanOperator.initTimeRangeIterator;
-
-public class SlidingWindowAggregationOperator implements ProcessOperator {
-
-  private final OperatorContext operatorContext;
-  private final Operator child;
-
-  private TsBlock cachedTsBlock;
+import static org.apache.iotdb.db.mpp.execution.operator.AggregationUtil.initTimeRangeIterator;
 
-  private final List<SlidingWindowAggregator> aggregators;
+public class SlidingWindowAggregationOperator extends SingleInputAggregationOperator {
 
-  private final ITimeRangeIterator timeRangeIterator;
-  // current interval of aggregation window [curStartTime, curEndTime)
-  private TimeRange curTimeRange;
-
-  private final boolean ascending;
-
-  private final TsBlockBuilder tsBlockBuilder;
+  private final ITimeRangeIterator subTimeRangeIterator;
+  // current interval of pre-aggregation window [curStartTime, curEndTime)
+  private TimeRange curSubTimeRange;
 
   public SlidingWindowAggregationOperator(
       OperatorContext operatorContext,
-      List<SlidingWindowAggregator> aggregators,
+      List<Aggregator> aggregators,
       Operator child,
       boolean ascending,
       GroupByTimeParameter groupByTimeParameter) {
+    super(operatorContext, aggregators, child, ascending, groupByTimeParameter, false);
     checkArgument(
         groupByTimeParameter != null,
         "GroupByTimeParameter cannot be null in SlidingWindowAggregationOperator");
-
-    this.operatorContext = operatorContext;
-    this.aggregators = aggregators;
-    this.child = child;
-    List<TSDataType> outputDataTypes = new ArrayList<>();
-    for (Aggregator aggregator : aggregators) {
-      outputDataTypes.addAll(Arrays.asList(aggregator.getOutputType()));
-    }
-    this.tsBlockBuilder = new TsBlockBuilder(outputDataTypes);
-    this.timeRangeIterator = initTimeRangeIterator(groupByTimeParameter, ascending, false);
-    this.ascending = ascending;
-  }
-
-  @Override
-  public boolean hasNext() {
-    return curTimeRange != null || timeRangeIterator.hasNextTimeRange();
+    this.subTimeRangeIterator = initTimeRangeIterator(groupByTimeParameter, ascending, true);
   }
 
   @Override
-  public TsBlock next() {
-    // Move to next timeRange
-    if (curTimeRange == null && timeRangeIterator.hasNextTimeRange()) {
-      curTimeRange = timeRangeIterator.nextTimeRange();
-      for (Aggregator aggregator : aggregators) {
-        aggregator.updateTimeRange(curTimeRange);
+  protected boolean calculateNextAggregationResult() {
+    while (!isCalculationDone()) {
+      if (inputTsBlock == null) {
+        // NOTE: child.next() can only be invoked once
+        if (child.hasNext() && canCallNext) {
+          inputTsBlock = child.next();
+          canCallNext = false;
+        } else if (child.hasNext()) {
+          // if child still has next but can't be invoked now
+          return false;
+        } else {
+          break;
+        }
       }
-    }
 
-    // 1. Calculate aggregation result based on current time window
-    boolean canCallNext = true;
-    while (!calcFromTsBlock(cachedTsBlock, curTimeRange)) {
-      cachedTsBlock = null;
-      // child.next can only be invoked once
-      if (child.hasNext() && canCallNext) {
-        cachedTsBlock = child.next();
-        canCallNext = false;
-        // if child still has next but can't be invoked now
-      } else if (child.hasNext()) {
-        return null;
-      } else {
-        break;
-      }
+      calculateFromCachedData();
     }
 
-    // 2. Update result using aggregators
-    curTimeRange = null;
-    return updateResultTsBlockFromAggregators(tsBlockBuilder, aggregators, timeRangeIterator);
+    // update result using aggregators
+    updateResultTsBlock();
+
+    return true;
   }
 
-  private boolean calcFromTsBlock(TsBlock inputTsBlock, TimeRange timeRange) {
-    if (inputTsBlock == null || inputTsBlock.isEmpty()) {
-      return false;
+  /** @return if already get the result */
+  private boolean isCalculationDone() {
+    if (curSubTimeRange == null && !subTimeRangeIterator.hasNextTimeRange()) {
+      return true;
     }
-    // check if the batchData does not contain points in current interval
-    if (satisfied(inputTsBlock, timeRange, ascending)) {
-      // skip points that cannot be calculated
-      if ((ascending && inputTsBlock.getStartTime() < timeRange.getMin())
-          || (!ascending && inputTsBlock.getStartTime() > timeRange.getMax())) {
-        inputTsBlock = skipOutOfTimeRangePoints(inputTsBlock, timeRange, ascending);
-      }
 
-      int lastReadRowIndex = 0;
-      for (SlidingWindowAggregator aggregator : aggregators) {
-        lastReadRowIndex = Math.max(lastReadRowIndex, aggregator.processTsBlock(inputTsBlock));
-      }
-      if (lastReadRowIndex >= inputTsBlock.getPositionCount()) {
-        inputTsBlock = null;
-      } else {
-        inputTsBlock = inputTsBlock.subTsBlock(lastReadRowIndex);
-      }
+    if (curSubTimeRange == null && subTimeRangeIterator.hasNextTimeRange()) {
+      curSubTimeRange = subTimeRangeIterator.nextTimeRange();
     }
-    // The result is calculated from the cache
-    return inputTsBlock != null
-        && (ascending
-            ? inputTsBlock.getEndTime() > timeRange.getMax()
-            : inputTsBlock.getEndTime() < timeRange.getMin());
-  }
-
-  @Override
-  public OperatorContext getOperatorContext() {
-    return operatorContext;
+    return ascending
+        ? curSubTimeRange.getMin() > curTimeRange.getMax()
+        : curSubTimeRange.getMax() < curTimeRange.getMin();
   }
 
-  @Override
-  public ListenableFuture<?> isBlocked() {
-    return child.isBlocked();
-  }
+  private void calculateFromCachedData() {
+    if (inputTsBlock == null || inputTsBlock.isEmpty()) {
+      return;
+    }
 
-  @Override
-  public boolean isFinished() {
-    return !this.hasNext();
-  }
+    for (Aggregator aggregator : aggregators) {
+      aggregator.processTsBlock(inputTsBlock);
+    }
 
-  @Override
-  public void close() throws Exception {
-    child.close();
+    inputTsBlock = inputTsBlock.skipFirst();
+    if (inputTsBlock.isEmpty()) {
+      inputTsBlock = null;
+    }
+    curSubTimeRange = null;
   }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/AbstractSeriesAggregationScanOperator.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/AbstractSeriesAggregationScanOperator.java
new file mode 100644
index 0000000000..2d7d671be9
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/AbstractSeriesAggregationScanOperator.java
@@ -0,0 +1,350 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.mpp.execution.operator.source;
+
+import org.apache.iotdb.db.engine.querycontext.QueryDataSource;
+import org.apache.iotdb.db.mpp.aggregation.Aggregator;
+import org.apache.iotdb.db.mpp.aggregation.timerangeiterator.ITimeRangeIterator;
+import org.apache.iotdb.db.mpp.execution.operator.OperatorContext;
+import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId;
+import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.GroupByTimeParameter;
+import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
+import org.apache.iotdb.tsfile.file.metadata.statistics.Statistics;
+import org.apache.iotdb.tsfile.read.common.TimeRange;
+import org.apache.iotdb.tsfile.read.common.block.TsBlock;
+import org.apache.iotdb.tsfile.read.common.block.TsBlockBuilder;
+import org.apache.iotdb.tsfile.utils.Pair;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import static org.apache.iotdb.db.mpp.execution.operator.AggregationUtil.appendAggregationResult;
+import static org.apache.iotdb.db.mpp.execution.operator.AggregationUtil.calculateAggregationFromRawData;
+import static org.apache.iotdb.db.mpp.execution.operator.AggregationUtil.initTimeRangeIterator;
+import static org.apache.iotdb.db.mpp.execution.operator.AggregationUtil.isAllAggregatorsHasFinalResult;
+
+public abstract class AbstractSeriesAggregationScanOperator implements DataSourceOperator {
+
+  protected final PlanNodeId sourceId;
+  protected final OperatorContext operatorContext;
+  protected final boolean ascending;
+  protected final boolean isGroupByQuery;
+
+  protected SeriesScanUtil seriesScanUtil;
+  protected int subSensorSize;
+
+  protected TsBlock inputTsBlock;
+
+  protected final ITimeRangeIterator timeRangeIterator;
+  // current interval of aggregation window [curStartTime, curEndTime)
+  protected TimeRange curTimeRange;
+
+  // We still think aggregator in SeriesAggregateScanOperator is a inputRaw step.
+  // But in facing of statistics, it will invoke another method processStatistics()
+  protected final List<Aggregator> aggregators;
+
+  // using for building result tsBlock
+  protected final TsBlockBuilder resultTsBlockBuilder;
+
+  protected boolean finished = false;
+
+  public AbstractSeriesAggregationScanOperator(
+      PlanNodeId sourceId,
+      OperatorContext context,
+      SeriesScanUtil seriesScanUtil,
+      int subSensorSize,
+      List<Aggregator> aggregators,
+      boolean ascending,
+      GroupByTimeParameter groupByTimeParameter) {
+    this.sourceId = sourceId;
+    this.operatorContext = context;
+    this.ascending = ascending;
+    this.isGroupByQuery = groupByTimeParameter != null;
+    this.seriesScanUtil = seriesScanUtil;
+    this.subSensorSize = subSensorSize;
+    this.aggregators = aggregators;
+
+    this.timeRangeIterator = initTimeRangeIterator(groupByTimeParameter, ascending, true);
+
+    List<TSDataType> dataTypes = new ArrayList<>();
+    for (Aggregator aggregator : aggregators) {
+      dataTypes.addAll(Arrays.asList(aggregator.getOutputType()));
+    }
+    this.resultTsBlockBuilder = new TsBlockBuilder(dataTypes);
+  }
+
+  @Override
+  public PlanNodeId getSourceId() {
+    return sourceId;
+  }
+
+  @Override
+  public OperatorContext getOperatorContext() {
+    return operatorContext;
+  }
+
+  @Override
+  public void initQueryDataSource(QueryDataSource dataSource) {
+    seriesScanUtil.initQueryDataSource(dataSource);
+  }
+
+  @Override
+  public boolean hasNext() {
+    return timeRangeIterator.hasNextTimeRange();
+  }
+
+  @Override
+  public TsBlock next() {
+    // start stopwatch
+    long maxRuntime = operatorContext.getMaxRunTime().roundTo(TimeUnit.NANOSECONDS);
+    long start = System.nanoTime();
+
+    // reset operator state
+    resultTsBlockBuilder.reset();
+
+    while (System.nanoTime() - start < maxRuntime
+        && timeRangeIterator.hasNextTimeRange()
+        && !resultTsBlockBuilder.isFull()) {
+      // move to next time window
+      curTimeRange = timeRangeIterator.nextTimeRange();
+
+      // clear previous aggregation result
+      for (Aggregator aggregator : aggregators) {
+        aggregator.updateTimeRange(curTimeRange);
+      }
+
+      // calculate aggregation result on current time window
+      calculateNextAggregationResult();
+    }
+
+    if (resultTsBlockBuilder.getPositionCount() > 0) {
+      return resultTsBlockBuilder.build();
+    } else {
+      return null;
+    }
+  }
+
+  @Override
+  public boolean isFinished() {
+    return finished || (finished = !hasNext());
+  }
+
+  protected void calculateNextAggregationResult() {
+    try {
+      if (calcFromCachedData()) {
+        updateResultTsBlock();
+        return;
+      }
+
+      // read page data firstly
+      if (readAndCalcFromPage()) {
+        updateResultTsBlock();
+        return;
+      }
+
+      // read chunk data secondly
+      if (readAndCalcFromChunk()) {
+        updateResultTsBlock();
+        return;
+      }
+
+      // read from file
+      if (readAndCalcFromFile()) {
+        updateResultTsBlock();
+        return;
+      }
+
+      updateResultTsBlock();
+    } catch (IOException e) {
+      throw new RuntimeException("Error while scanning the file", e);
+    }
+  }
+
+  protected void updateResultTsBlock() {
+    appendAggregationResult(resultTsBlockBuilder, aggregators, timeRangeIterator);
+  }
+
+  protected boolean calcFromCachedData() {
+    return calcFromRawData(inputTsBlock);
+  }
+
+  private boolean calcFromRawData(TsBlock tsBlock) {
+    Pair<Boolean, TsBlock> calcResult =
+        calculateAggregationFromRawData(tsBlock, aggregators, curTimeRange, ascending);
+    inputTsBlock = calcResult.getRight();
+    return calcResult.getLeft();
+  }
+
+  protected void calcFromStatistics(Statistics[] statistics) {
+    for (Aggregator aggregator : aggregators) {
+      if (aggregator.hasFinalResult()) {
+        continue;
+      }
+      aggregator.processStatistics(statistics);
+    }
+  }
+
+  protected boolean readAndCalcFromFile() throws IOException {
+    while (seriesScanUtil.hasNextFile()) {
+      if (canUseCurrentFileStatistics()) {
+        Statistics fileTimeStatistics = seriesScanUtil.currentFileTimeStatistics();
+        if (fileTimeStatistics.getStartTime() > curTimeRange.getMax()) {
+          if (ascending) {
+            return true;
+          } else {
+            seriesScanUtil.skipCurrentFile();
+            continue;
+          }
+        }
+        // calc from fileMetaData
+        if (curTimeRange.contains(
+            fileTimeStatistics.getStartTime(), fileTimeStatistics.getEndTime())) {
+          Statistics[] statisticsList = new Statistics[subSensorSize];
+          for (int i = 0; i < subSensorSize; i++) {
+            statisticsList[i] = seriesScanUtil.currentFileStatistics(i);
+          }
+          calcFromStatistics(statisticsList);
+          seriesScanUtil.skipCurrentFile();
+          if (isAllAggregatorsHasFinalResult(aggregators) && !isGroupByQuery) {
+            return true;
+          } else {
+            continue;
+          }
+        }
+      }
+
+      // read chunk
+      if (readAndCalcFromChunk()) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  protected boolean readAndCalcFromChunk() throws IOException {
+    while (seriesScanUtil.hasNextChunk()) {
+      if (canUseCurrentChunkStatistics()) {
+        Statistics chunkTimeStatistics = seriesScanUtil.currentChunkTimeStatistics();
+        if (chunkTimeStatistics.getStartTime() > curTimeRange.getMax()) {
+          if (ascending) {
+            return true;
+          } else {
+            seriesScanUtil.skipCurrentChunk();
+            continue;
+          }
+        }
+        // calc from chunkMetaData
+        if (curTimeRange.contains(
+            chunkTimeStatistics.getStartTime(), chunkTimeStatistics.getEndTime())) {
+          // calc from chunkMetaData
+          Statistics[] statisticsList = new Statistics[subSensorSize];
+          for (int i = 0; i < subSensorSize; i++) {
+            statisticsList[i] = seriesScanUtil.currentChunkStatistics(i);
+          }
+          calcFromStatistics(statisticsList);
+          seriesScanUtil.skipCurrentChunk();
+          if (isAllAggregatorsHasFinalResult(aggregators) && !isGroupByQuery) {
+            return true;
+          } else {
+            continue;
+          }
+        }
+      }
+
+      // read page
+      if (readAndCalcFromPage()) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  protected boolean readAndCalcFromPage() throws IOException {
+    while (seriesScanUtil.hasNextPage()) {
+      if (canUseCurrentPageStatistics()) {
+        Statistics pageTimeStatistics = seriesScanUtil.currentPageTimeStatistics();
+        // There is no more eligible points in current time range
+        if (pageTimeStatistics.getStartTime() > curTimeRange.getMax()) {
+          if (ascending) {
+            return true;
+          } else {
+            seriesScanUtil.skipCurrentPage();
+            continue;
+          }
+        }
+        // can use pageHeader
+        if (curTimeRange.contains(
+            pageTimeStatistics.getStartTime(), pageTimeStatistics.getEndTime())) {
+          Statistics[] statisticsList = new Statistics[subSensorSize];
+          for (int i = 0; i < subSensorSize; i++) {
+            statisticsList[i] = seriesScanUtil.currentPageStatistics(i);
+          }
+          calcFromStatistics(statisticsList);
+          seriesScanUtil.skipCurrentPage();
+          if (isAllAggregatorsHasFinalResult(aggregators) && !isGroupByQuery) {
+            return true;
+          } else {
+            continue;
+          }
+        }
+      }
+
+      // calc from page data
+      TsBlock tsBlock = seriesScanUtil.nextPage();
+      if (tsBlock == null || tsBlock.isEmpty()) {
+        continue;
+      }
+
+      // calc from raw data
+      if (calcFromRawData(tsBlock)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  protected boolean canUseCurrentFileStatistics() throws IOException {
+    Statistics fileStatistics = seriesScanUtil.currentFileTimeStatistics();
+    return !seriesScanUtil.isFileOverlapped()
+        && fileStatistics.containedByTimeFilter(seriesScanUtil.getTimeFilter())
+        && !seriesScanUtil.currentFileModified();
+  }
+
+  protected boolean canUseCurrentChunkStatistics() throws IOException {
+    Statistics chunkStatistics = seriesScanUtil.currentChunkTimeStatistics();
+    return !seriesScanUtil.isChunkOverlapped()
+        && chunkStatistics.containedByTimeFilter(seriesScanUtil.getTimeFilter())
+        && !seriesScanUtil.currentChunkModified();
+  }
+
+  protected boolean canUseCurrentPageStatistics() throws IOException {
+    Statistics currentPageStatistics = seriesScanUtil.currentPageTimeStatistics();
+    if (currentPageStatistics == null) {
+      return false;
+    }
+    return !seriesScanUtil.isPageOverlapped()
+        && currentPageStatistics.containedByTimeFilter(seriesScanUtil.getTimeFilter())
+        && !seriesScanUtil.currentPageModified();
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/AlignedSeriesAggregationScanOperator.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/AlignedSeriesAggregationScanOperator.java
index 375b203976..f9fa1f8d52 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/AlignedSeriesAggregationScanOperator.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/AlignedSeriesAggregationScanOperator.java
@@ -19,55 +19,18 @@
 
 package org.apache.iotdb.db.mpp.execution.operator.source;
 
-import org.apache.iotdb.db.engine.querycontext.QueryDataSource;
 import org.apache.iotdb.db.metadata.path.AlignedPath;
 import org.apache.iotdb.db.mpp.aggregation.Aggregator;
-import org.apache.iotdb.db.mpp.aggregation.timerangeiterator.ITimeRangeIterator;
 import org.apache.iotdb.db.mpp.execution.operator.OperatorContext;
-import org.apache.iotdb.db.mpp.execution.operator.process.AggregationOperator;
 import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId;
 import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.GroupByTimeParameter;
-import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
-import org.apache.iotdb.tsfile.file.metadata.statistics.Statistics;
-import org.apache.iotdb.tsfile.read.common.TimeRange;
-import org.apache.iotdb.tsfile.read.common.block.TsBlock;
-import org.apache.iotdb.tsfile.read.common.block.TsBlock.TsBlockSingleColumnIterator;
-import org.apache.iotdb.tsfile.read.common.block.TsBlockBuilder;
 import org.apache.iotdb.tsfile.read.filter.basic.Filter;
 
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 
-import static org.apache.iotdb.db.mpp.execution.operator.process.RawDataAggregationOperator.isEndCalc;
-import static org.apache.iotdb.db.mpp.execution.operator.process.RawDataAggregationOperator.skipOutOfTimeRangePoints;
-import static org.apache.iotdb.db.mpp.execution.operator.source.SeriesAggregationScanOperator.initTimeRangeIterator;
-
 /** This operator is responsible to do the aggregation calculation especially for aligned series. */
-public class AlignedSeriesAggregationScanOperator implements DataSourceOperator {
-
-  private final OperatorContext operatorContext;
-  private final PlanNodeId sourceId;
-  private final AlignedSeriesScanUtil alignedSeriesScanUtil;
-  private final int subSensorSize;
-  private final boolean ascending;
-  // We still think aggregator in AlignedSeriesAggregateScanOperator is a inputRaw step.
-  // But in facing of statistics, it will invoke another method processStatistics()
-  private List<Aggregator> aggregators;
-
-  private ITimeRangeIterator timeRangeIterator;
-  // current interval of aggregation window [curStartTime, curEndTime)
-  private TimeRange curTimeRange;
-  private boolean isGroupByQuery;
-
-  private TsBlock preCachedData;
-
-  private TsBlockBuilder tsBlockBuilder;
-  private TsBlock resultTsBlock;
-  private boolean hasCachedTsBlock = false;
-  private boolean finished = false;
+public class AlignedSeriesAggregationScanOperator extends AbstractSeriesAggregationScanOperator {
 
   public AlignedSeriesAggregationScanOperator(
       PlanNodeId sourceId,
@@ -77,341 +40,19 @@ public class AlignedSeriesAggregationScanOperator implements DataSourceOperator
       Filter timeFilter,
       boolean ascending,
       GroupByTimeParameter groupByTimeParameter) {
-    this.sourceId = sourceId;
-    this.operatorContext = context;
-    this.ascending = ascending;
-    this.alignedSeriesScanUtil =
+    super(
+        sourceId,
+        context,
         new AlignedSeriesScanUtil(
             seriesPath,
             new HashSet<>(seriesPath.getMeasurementList()),
             context.getInstanceContext(),
             timeFilter,
             null,
-            ascending);
-    this.subSensorSize = seriesPath.getMeasurementList().size();
-    this.aggregators = aggregators;
-    List<TSDataType> dataTypes = new ArrayList<>();
-    for (Aggregator aggregator : aggregators) {
-      dataTypes.addAll(Arrays.asList(aggregator.getOutputType()));
-    }
-    tsBlockBuilder = new TsBlockBuilder(dataTypes);
-    this.timeRangeIterator = initTimeRangeIterator(groupByTimeParameter, ascending, true);
-    this.isGroupByQuery = groupByTimeParameter != null;
-  }
-
-  @Override
-  public OperatorContext getOperatorContext() {
-    return operatorContext;
-  }
-
-  @Override
-  public TsBlock next() {
-    if (hasCachedTsBlock || hasNext()) {
-      hasCachedTsBlock = false;
-      return resultTsBlock;
-    }
-    return null;
-  }
-
-  @Override
-  public boolean hasNext() {
-    if (hasCachedTsBlock) {
-      return true;
-    }
-    try {
-      if (!timeRangeIterator.hasNextTimeRange()) {
-        return false;
-      }
-      curTimeRange = timeRangeIterator.nextTimeRange();
-
-      // 1. Clear previous aggregation result
-      for (Aggregator aggregator : aggregators) {
-        aggregator.reset();
-        aggregator.updateTimeRange(curTimeRange);
-      }
-
-      // 2. Calculate aggregation result based on current time window
-      if (calcFromCacheData(curTimeRange)) {
-        updateResultTsBlockFromAggregators();
-        return true;
-      }
-
-      // read page data firstly
-      if (readAndCalcFromPage(curTimeRange)) {
-        updateResultTsBlockFromAggregators();
-        return true;
-      }
-
-      // read chunk data secondly
-      if (readAndCalcFromChunk(curTimeRange)) {
-        updateResultTsBlockFromAggregators();
-        return true;
-      }
-
-      // read from file first
-      while (alignedSeriesScanUtil.hasNextFile()) {
-        if (canUseCurrentFileStatistics()) {
-          Statistics fileTimeStatistics = alignedSeriesScanUtil.currentFileTimeStatistics();
-          if (fileTimeStatistics.getStartTime() > curTimeRange.getMax()) {
-            if (ascending) {
-              updateResultTsBlockFromAggregators();
-              return true;
-            } else {
-              alignedSeriesScanUtil.skipCurrentFile();
-              continue;
-            }
-          }
-          // calc from fileMetaData
-          if (curTimeRange.contains(
-              fileTimeStatistics.getStartTime(), fileTimeStatistics.getEndTime())) {
-            Statistics[] statisticsList = new Statistics[subSensorSize];
-            for (int i = 0; i < subSensorSize; i++) {
-              statisticsList[i] = alignedSeriesScanUtil.currentFileStatistics(i);
-            }
-            calcFromStatistics(statisticsList);
-            alignedSeriesScanUtil.skipCurrentFile();
-            if (isEndCalc(aggregators) && !isGroupByQuery) {
-              break;
-            } else {
-              continue;
-            }
-          }
-        }
-
-        // read chunk
-        if (readAndCalcFromChunk(curTimeRange)) {
-          updateResultTsBlockFromAggregators();
-          return true;
-        }
-      }
-
-      updateResultTsBlockFromAggregators();
-      return true;
-    } catch (IOException e) {
-      throw new RuntimeException("Error while scanning the file", e);
-    }
-  }
-
-  @Override
-  public boolean isFinished() {
-    return finished || (finished = !hasNext());
-  }
-
-  @Override
-  public void initQueryDataSource(QueryDataSource dataSource) {
-    alignedSeriesScanUtil.initQueryDataSource(dataSource);
-  }
-
-  @Override
-  public PlanNodeId getSourceId() {
-    return sourceId;
-  }
-
-  private void updateResultTsBlockFromAggregators() {
-    resultTsBlock =
-        AggregationOperator.updateResultTsBlockFromAggregators(
-            tsBlockBuilder, aggregators, timeRangeIterator);
-    hasCachedTsBlock = true;
-  }
-
-  /** @return if already get the result */
-  private boolean calcFromCacheData(TimeRange curTimeRange) throws IOException {
-    calcFromBatch(preCachedData, curTimeRange);
-    // The result is calculated from the cache
-    return (preCachedData != null
-            && (ascending
-                ? preCachedData.getEndTime() > curTimeRange.getMax()
-                : preCachedData.getEndTime() < curTimeRange.getMin()))
-        || isEndCalc(aggregators);
-  }
-
-  @SuppressWarnings("squid:S3776")
-  private void calcFromBatch(TsBlock tsBlock, TimeRange curTimeRange) {
-    // check if the batchData does not contain points in current interval
-    if (tsBlock != null && satisfied(tsBlock, curTimeRange, ascending)) {
-      // skip points that cannot be calculated
-      if ((ascending && tsBlock.getStartTime() < curTimeRange.getMin())
-          || (!ascending && tsBlock.getStartTime() > curTimeRange.getMax())) {
-        tsBlock = skipOutOfTimeRangePoints(tsBlock, curTimeRange, ascending);
-      }
-
-      int lastReadRowIndex = 0;
-      for (Aggregator aggregator : aggregators) {
-        // current agg method has been calculated
-        if (aggregator.hasFinalResult()) {
-          continue;
-        }
-
-        lastReadRowIndex = Math.max(lastReadRowIndex, aggregator.processTsBlock(tsBlock));
-      }
-      if (lastReadRowIndex >= tsBlock.getPositionCount()) {
-        tsBlock = null;
-      } else {
-        tsBlock = tsBlock.subTsBlock(lastReadRowIndex);
-      }
-
-      // can calc for next interval
-      if (tsBlock != null && tsBlock.getTsBlockSingleColumnIterator().hasNext()) {
-        preCachedData = tsBlock;
-      }
-    }
-  }
-
-  private boolean satisfied(TsBlock tsBlock, TimeRange timeRange, boolean ascending) {
-    TsBlockSingleColumnIterator tsBlockIterator = tsBlock.getTsBlockSingleColumnIterator();
-    if (tsBlockIterator == null || !tsBlockIterator.hasNext()) {
-      return false;
-    }
-
-    if (ascending
-        && (tsBlockIterator.getEndTime() < timeRange.getMin()
-            || tsBlockIterator.currentTime() > timeRange.getMax())) {
-      return false;
-    }
-    if (!ascending
-        && (tsBlockIterator.getEndTime() > timeRange.getMax()
-            || tsBlockIterator.currentTime() < timeRange.getMin())) {
-      preCachedData = tsBlock;
-      return false;
-    }
-    return true;
-  }
-
-  @SuppressWarnings("squid:S3776") // Suppress high Cognitive Complexity warning
-  private boolean readAndCalcFromPage(TimeRange curTimeRange) throws IOException {
-    while (alignedSeriesScanUtil.hasNextPage()) {
-      if (canUseCurrentPageStatistics()) {
-        Statistics pageTimeStatistics = alignedSeriesScanUtil.currentPageTimeStatistics();
-        // There is no more eligible points in current time range
-        if (pageTimeStatistics.getStartTime() > curTimeRange.getMax()) {
-          if (ascending) {
-            return true;
-          } else {
-            alignedSeriesScanUtil.skipCurrentPage();
-            continue;
-          }
-        }
-        // can use pageHeader
-        if (canUseCurrentPageStatistics()
-            && curTimeRange.contains(
-                pageTimeStatistics.getStartTime(), pageTimeStatistics.getEndTime())) {
-          Statistics[] statisticsList = new Statistics[subSensorSize];
-          for (int i = 0; i < subSensorSize; i++) {
-            statisticsList[i] = alignedSeriesScanUtil.currentPageStatistics(i);
-          }
-          calcFromStatistics(statisticsList);
-          alignedSeriesScanUtil.skipCurrentPage();
-          if (isEndCalc(aggregators) && !isGroupByQuery) {
-            return true;
-          } else {
-            continue;
-          }
-        }
-      }
-
-      // calc from page data
-      TsBlock tsBlock = alignedSeriesScanUtil.nextPage();
-      TsBlockSingleColumnIterator tsBlockIterator = tsBlock.getTsBlockSingleColumnIterator();
-      if (tsBlockIterator == null || !tsBlockIterator.hasNext()) {
-        continue;
-      }
-
-      // stop calc and cached current batchData
-      if (ascending && tsBlockIterator.currentTime() > curTimeRange.getMax()) {
-        preCachedData = tsBlock;
-        return true;
-      }
-
-      // calc from batch data
-      calcFromBatch(tsBlock, curTimeRange);
-
-      // judge whether the calculation finished
-      boolean isTsBlockOutOfBound =
-          ascending
-              ? tsBlock.getEndTime() > curTimeRange.getMax()
-              : tsBlock.getEndTime() < curTimeRange.getMin();
-      if (isEndCalc(aggregators) || isTsBlockOutOfBound) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  private boolean readAndCalcFromChunk(TimeRange curTimeRange) throws IOException {
-    while (alignedSeriesScanUtil.hasNextChunk()) {
-      if (canUseCurrentChunkStatistics()) {
-        Statistics chunkTimeStatistics = alignedSeriesScanUtil.currentChunkTimeStatistics();
-        if (chunkTimeStatistics.getStartTime() > curTimeRange.getMax()) {
-          if (ascending) {
-            return true;
-          } else {
-            alignedSeriesScanUtil.skipCurrentChunk();
-            continue;
-          }
-        }
-        // calc from chunkMetaData
-        if (curTimeRange.contains(
-            chunkTimeStatistics.getStartTime(), chunkTimeStatistics.getEndTime())) {
-          // calc from chunkMetaData
-          Statistics[] statisticsList = new Statistics[subSensorSize];
-          for (int i = 0; i < subSensorSize; i++) {
-            statisticsList[i] = alignedSeriesScanUtil.currentChunkStatistics(i);
-          }
-          calcFromStatistics(statisticsList);
-          alignedSeriesScanUtil.skipCurrentChunk();
-          if (isEndCalc(aggregators) && !isGroupByQuery) {
-            return true;
-          } else {
-            continue;
-          }
-        }
-      }
-
-      // read page
-      if (readAndCalcFromPage(curTimeRange)) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  private void calcFromStatistics(Statistics[] statistics) {
-    for (int i = 0; i < aggregators.size(); i++) {
-      Aggregator aggregator = aggregators.get(i);
-      if (aggregator.hasFinalResult()) {
-        continue;
-      }
-      aggregator.processStatistics(statistics);
-    }
-  }
-
-  public boolean canUseCurrentFileStatistics() throws IOException {
-    Statistics fileStatistics = alignedSeriesScanUtil.currentFileTimeStatistics();
-    return !alignedSeriesScanUtil.isFileOverlapped()
-        && containedByTimeFilter(fileStatistics)
-        && !alignedSeriesScanUtil.currentFileModified();
-  }
-
-  public boolean canUseCurrentChunkStatistics() throws IOException {
-    Statistics chunkStatistics = alignedSeriesScanUtil.currentChunkTimeStatistics();
-    return !alignedSeriesScanUtil.isChunkOverlapped()
-        && containedByTimeFilter(chunkStatistics)
-        && !alignedSeriesScanUtil.currentChunkModified();
-  }
-
-  public boolean canUseCurrentPageStatistics() throws IOException {
-    Statistics currentPageStatistics = alignedSeriesScanUtil.currentPageTimeStatistics();
-    if (currentPageStatistics == null) {
-      return false;
-    }
-    return !alignedSeriesScanUtil.isPageOverlapped()
-        && containedByTimeFilter(currentPageStatistics)
-        && !alignedSeriesScanUtil.currentPageModified();
-  }
-
-  private boolean containedByTimeFilter(Statistics statistics) {
-    Filter timeFilter = alignedSeriesScanUtil.getTimeFilter();
-    return timeFilter == null
-        || timeFilter.containStartEndTime(statistics.getStartTime(), statistics.getEndTime());
+            ascending),
+        seriesPath.getMeasurementList().size(),
+        aggregators,
+        ascending,
+        groupByTimeParameter);
   }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/AlignedSeriesScanUtil.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/AlignedSeriesScanUtil.java
index 5b9b3079e4..27d8ab3d9c 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/AlignedSeriesScanUtil.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/AlignedSeriesScanUtil.java
@@ -28,8 +28,10 @@ import org.apache.iotdb.db.query.reader.universal.AlignedPriorityMergeReader;
 import org.apache.iotdb.db.query.reader.universal.DescPriorityMergeReader;
 import org.apache.iotdb.db.query.reader.universal.PriorityMergeReader;
 import org.apache.iotdb.db.utils.FileLoaderUtils;
+import org.apache.iotdb.tsfile.file.metadata.AlignedChunkMetadata;
 import org.apache.iotdb.tsfile.file.metadata.AlignedTimeSeriesMetadata;
 import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
+import org.apache.iotdb.tsfile.file.metadata.statistics.Statistics;
 import org.apache.iotdb.tsfile.read.common.block.TsBlock;
 import org.apache.iotdb.tsfile.read.filter.basic.Filter;
 import org.apache.iotdb.tsfile.read.reader.IPointReader;
@@ -57,6 +59,42 @@ public class AlignedSeriesScanUtil extends SeriesScanUtil {
             .getSchemaList().stream().map(IMeasurementSchema::getType).collect(Collectors.toList());
   }
 
+  @Override
+  protected Statistics currentFileStatistics(int index) throws IOException {
+    return ((AlignedTimeSeriesMetadata) firstTimeSeriesMetadata).getStatistics(index);
+  }
+
+  @Override
+  protected Statistics currentFileTimeStatistics() throws IOException {
+    return ((AlignedTimeSeriesMetadata) firstTimeSeriesMetadata).getTimeStatistics();
+  }
+
+  @Override
+  protected Statistics currentChunkStatistics(int index) throws IOException {
+    return ((AlignedChunkMetadata) firstChunkMetadata).getStatistics(index);
+  }
+
+  @Override
+  protected Statistics currentChunkTimeStatistics() {
+    return ((AlignedChunkMetadata) firstChunkMetadata).getTimeStatistics();
+  }
+
+  @Override
+  protected Statistics currentPageStatistics(int index) throws IOException {
+    if (firstPageReader == null) {
+      return null;
+    }
+    return firstPageReader.getStatistics(index);
+  }
+
+  @Override
+  protected Statistics currentPageTimeStatistics() throws IOException {
+    if (firstPageReader == null) {
+      return null;
+    }
+    return firstPageReader.getTimeStatistics();
+  }
+
   @Override
   protected PriorityMergeReader getPriorityMergeReader() {
     return new AlignedPriorityMergeReader();
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/SeriesAggregationScanOperator.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/SeriesAggregationScanOperator.java
index 56b1a33b4a..157f51f2fa 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/SeriesAggregationScanOperator.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/SeriesAggregationScanOperator.java
@@ -19,61 +19,24 @@
 package org.apache.iotdb.db.mpp.execution.operator.source;
 
 import org.apache.iotdb.commons.path.PartialPath;
-import org.apache.iotdb.db.engine.querycontext.QueryDataSource;
 import org.apache.iotdb.db.mpp.aggregation.Aggregator;
-import org.apache.iotdb.db.mpp.aggregation.timerangeiterator.ITimeRangeIterator;
-import org.apache.iotdb.db.mpp.aggregation.timerangeiterator.SingleTimeWindowIterator;
-import org.apache.iotdb.db.mpp.aggregation.timerangeiterator.TimeRangeIteratorFactory;
 import org.apache.iotdb.db.mpp.execution.operator.OperatorContext;
-import org.apache.iotdb.db.mpp.execution.operator.process.AggregationOperator;
 import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId;
 import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.GroupByTimeParameter;
-import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
-import org.apache.iotdb.tsfile.file.metadata.statistics.Statistics;
-import org.apache.iotdb.tsfile.read.common.TimeRange;
-import org.apache.iotdb.tsfile.read.common.block.TsBlock;
-import org.apache.iotdb.tsfile.read.common.block.TsBlock.TsBlockSingleColumnIterator;
-import org.apache.iotdb.tsfile.read.common.block.TsBlockBuilder;
 import org.apache.iotdb.tsfile.read.filter.basic.Filter;
 
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.Set;
 
-import static org.apache.iotdb.db.mpp.execution.operator.process.RawDataAggregationOperator.isEndCalc;
-import static org.apache.iotdb.db.mpp.execution.operator.process.RawDataAggregationOperator.skipOutOfTimeRangePoints;
-
 /**
  * This operator is responsible to do the aggregation calculation for one series based on global
  * time range and time split parameter.
  *
- * <p>Every time next() is invoked, one tsBlock which contains current time window will be returned.
+ * <p>Every time next() is invoked, one tsBlock which contains many time windows will be returned.
  * In sliding window situation, current time window is a pre-aggregation window. If there is no time
  * split parameter, i.e. aggregation without groupBy, just one tsBlock will be returned.
  */
-public class SeriesAggregationScanOperator implements DataSourceOperator {
-
-  private final OperatorContext operatorContext;
-  private final PlanNodeId sourceId;
-  private final SeriesScanUtil seriesScanUtil;
-  private final boolean ascending;
-  // We still think aggregator in SeriesAggregateScanOperator is a inputRaw step.
-  // But in facing of statistics, it will invoke another method processStatistics()
-  private List<Aggregator> aggregators;
-
-  private ITimeRangeIterator timeRangeIterator;
-  // current interval of aggregation window [curStartTime, curEndTime)
-  private TimeRange curTimeRange;
-  private boolean isGroupByQuery;
-
-  private TsBlock preCachedData;
-
-  private TsBlockBuilder tsBlockBuilder;
-  private TsBlock resultTsBlock;
-  private boolean hasCachedTsBlock = false;
-  private boolean finished = false;
+public class SeriesAggregationScanOperator extends AbstractSeriesAggregationScanOperator {
 
   public SeriesAggregationScanOperator(
       PlanNodeId sourceId,
@@ -84,10 +47,9 @@ public class SeriesAggregationScanOperator implements DataSourceOperator {
       Filter timeFilter,
       boolean ascending,
       GroupByTimeParameter groupByTimeParameter) {
-    this.sourceId = sourceId;
-    this.operatorContext = context;
-    this.ascending = ascending;
-    this.seriesScanUtil =
+    super(
+        sourceId,
+        context,
         new SeriesScanUtil(
             seriesPath,
             allSensors,
@@ -95,338 +57,10 @@ public class SeriesAggregationScanOperator implements DataSourceOperator {
             context.getInstanceContext(),
             timeFilter,
             null,
-            ascending);
-    this.aggregators = aggregators;
-    List<TSDataType> dataTypes = new ArrayList<>();
-    for (Aggregator aggregator : aggregators) {
-      dataTypes.addAll(Arrays.asList(aggregator.getOutputType()));
-    }
-    tsBlockBuilder = new TsBlockBuilder(dataTypes);
-    this.timeRangeIterator = initTimeRangeIterator(groupByTimeParameter, ascending, true);
-    this.isGroupByQuery = groupByTimeParameter != null;
-  }
-
-  /**
-   * If groupByTimeParameter is null, which means it's an aggregation query without down sampling.
-   * Aggregation query has only one time window and the result set of it does not contain a
-   * timestamp, so it doesn't matter what the time range returns.
-   */
-  public static ITimeRangeIterator initTimeRangeIterator(
-      GroupByTimeParameter groupByTimeParameter,
-      boolean ascending,
-      boolean outputPartialTimeWindow) {
-    if (groupByTimeParameter == null) {
-      return new SingleTimeWindowIterator(0, Long.MAX_VALUE);
-    } else {
-      return TimeRangeIteratorFactory.getTimeRangeIterator(
-          groupByTimeParameter.getStartTime(),
-          groupByTimeParameter.getEndTime(),
-          groupByTimeParameter.getInterval(),
-          groupByTimeParameter.getSlidingStep(),
-          ascending,
-          groupByTimeParameter.isIntervalByMonth(),
-          groupByTimeParameter.isSlidingStepByMonth(),
-          groupByTimeParameter.isLeftCRightO(),
-          outputPartialTimeWindow);
-    }
-  }
-
-  @Override
-  public OperatorContext getOperatorContext() {
-    return operatorContext;
-  }
-
-  @Override
-  public TsBlock next() {
-    if (hasCachedTsBlock || hasNext()) {
-      hasCachedTsBlock = false;
-      return resultTsBlock;
-    }
-    return null;
-  }
-
-  @Override
-  public boolean hasNext() {
-    if (hasCachedTsBlock) {
-      return true;
-    }
-    try {
-      if (!timeRangeIterator.hasNextTimeRange()) {
-        return false;
-      }
-      curTimeRange = timeRangeIterator.nextTimeRange();
-
-      // 1. Clear previous aggregation result
-      for (Aggregator aggregator : aggregators) {
-        aggregator.reset();
-        aggregator.updateTimeRange(curTimeRange);
-      }
-
-      // 2. Calculate aggregation result based on current time window
-      if (calcFromCacheData(curTimeRange)) {
-        updateResultTsBlockFromAggregators();
-        return true;
-      }
-
-      // read page data firstly
-      if (readAndCalcFromPage(curTimeRange)) {
-        updateResultTsBlockFromAggregators();
-        return true;
-      }
-
-      // read chunk data secondly
-      if (readAndCalcFromChunk(curTimeRange)) {
-        updateResultTsBlockFromAggregators();
-        return true;
-      }
-
-      // read from file first
-      while (seriesScanUtil.hasNextFile()) {
-        if (canUseCurrentFileStatistics()) {
-          Statistics fileStatistics = seriesScanUtil.currentFileStatistics();
-          if (fileStatistics.getStartTime() > curTimeRange.getMax()) {
-            if (ascending) {
-              updateResultTsBlockFromAggregators();
-              return true;
-            } else {
-              seriesScanUtil.skipCurrentFile();
-              continue;
-            }
-          }
-          // calc from fileMetaData
-          if (curTimeRange.contains(fileStatistics.getStartTime(), fileStatistics.getEndTime())) {
-            calcFromStatistics(fileStatistics);
-            seriesScanUtil.skipCurrentFile();
-            if (isEndCalc(aggregators) && !isGroupByQuery) {
-              break;
-            } else {
-              continue;
-            }
-          }
-        }
-
-        // read chunk
-        if (readAndCalcFromChunk(curTimeRange)) {
-          updateResultTsBlockFromAggregators();
-          return true;
-        }
-      }
-
-      updateResultTsBlockFromAggregators();
-      return true;
-    } catch (IOException e) {
-      throw new RuntimeException("Error while scanning the file", e);
-    }
-  }
-
-  private void updateResultTsBlockFromAggregators() {
-    resultTsBlock =
-        AggregationOperator.updateResultTsBlockFromAggregators(
-            tsBlockBuilder, aggregators, timeRangeIterator);
-    hasCachedTsBlock = true;
-  }
-
-  @Override
-  public boolean isFinished() {
-    return finished || (finished = !hasNext());
-  }
-
-  @Override
-  public PlanNodeId getSourceId() {
-    return sourceId;
-  }
-
-  @Override
-  public void initQueryDataSource(QueryDataSource dataSource) {
-    seriesScanUtil.initQueryDataSource(dataSource);
-  }
-
-  /** @return if already get the result */
-  private boolean calcFromCacheData(TimeRange curTimeRange) throws IOException {
-    calcFromBatch(preCachedData, curTimeRange);
-    // The result is calculated from the cache
-    return (preCachedData != null
-            && (ascending
-                ? preCachedData.getEndTime() > curTimeRange.getMax()
-                : preCachedData.getEndTime() < curTimeRange.getMin()))
-        || isEndCalc(aggregators);
-  }
-
-  @SuppressWarnings("squid:S3776")
-  private void calcFromBatch(TsBlock tsBlock, TimeRange curTimeRange) {
-    // check if the batchData does not contain points in current interval
-    if (tsBlock != null && satisfied(tsBlock, curTimeRange, ascending)) {
-      // skip points that cannot be calculated
-      if ((ascending && tsBlock.getStartTime() < curTimeRange.getMin())
-          || (!ascending && tsBlock.getStartTime() > curTimeRange.getMax())) {
-        tsBlock = skipOutOfTimeRangePoints(tsBlock, curTimeRange, ascending);
-      }
-
-      int lastReadRowIndex = 0;
-      for (Aggregator aggregator : aggregators) {
-        // current agg method has been calculated
-        if (aggregator.hasFinalResult()) {
-          continue;
-        }
-
-        lastReadRowIndex = Math.max(lastReadRowIndex, aggregator.processTsBlock(tsBlock));
-      }
-      if (lastReadRowIndex >= tsBlock.getPositionCount()) {
-        tsBlock = null;
-      } else {
-        tsBlock = tsBlock.subTsBlock(lastReadRowIndex);
-      }
-
-      // can calc for next interval
-      if (tsBlock != null && tsBlock.getTsBlockSingleColumnIterator().hasNext()) {
-        preCachedData = tsBlock;
-      }
-    }
-  }
-
-  private boolean satisfied(TsBlock tsBlock, TimeRange timeRange, boolean ascending) {
-    TsBlockSingleColumnIterator tsBlockIterator = tsBlock.getTsBlockSingleColumnIterator();
-    if (tsBlockIterator == null || !tsBlockIterator.hasNext()) {
-      return false;
-    }
-
-    if (ascending
-        && (tsBlockIterator.getEndTime() < timeRange.getMin()
-            || tsBlockIterator.currentTime() > timeRange.getMax())) {
-      return false;
-    }
-    if (!ascending
-        && (tsBlockIterator.getEndTime() > timeRange.getMax()
-            || tsBlockIterator.currentTime() < timeRange.getMin())) {
-      preCachedData = tsBlock;
-      return false;
-    }
-    return true;
-  }
-
-  @SuppressWarnings("squid:S3776") // Suppress high Cognitive Complexity warning
-  private boolean readAndCalcFromPage(TimeRange curTimeRange) throws IOException {
-    while (seriesScanUtil.hasNextPage()) {
-      if (canUseCurrentPageStatistics()) {
-        Statistics pageStatistics = seriesScanUtil.currentPageStatistics();
-        // There is no more eligible points in current time range
-        if (pageStatistics.getStartTime() > curTimeRange.getMax()) {
-          if (ascending) {
-            return true;
-          } else {
-            seriesScanUtil.skipCurrentPage();
-            continue;
-          }
-        }
-        // can use pageHeader
-        if (curTimeRange.contains(pageStatistics.getStartTime(), pageStatistics.getEndTime())) {
-          calcFromStatistics(pageStatistics);
-          seriesScanUtil.skipCurrentPage();
-          if (isEndCalc(aggregators) && !isGroupByQuery) {
-            return true;
-          } else {
-            continue;
-          }
-        }
-      }
-
-      // calc from page data
-      TsBlock tsBlock = seriesScanUtil.nextPage();
-      TsBlockSingleColumnIterator tsBlockIterator = tsBlock.getTsBlockSingleColumnIterator();
-      if (tsBlockIterator == null || !tsBlockIterator.hasNext()) {
-        continue;
-      }
-
-      // stop calc and cached current batchData
-      if (ascending && tsBlockIterator.currentTime() > curTimeRange.getMax()) {
-        preCachedData = tsBlock;
-        return true;
-      }
-
-      // calc from batch data
-      calcFromBatch(tsBlock, curTimeRange);
-
-      // judge whether the calculation finished
-      boolean isTsBlockOutOfBound =
-          ascending
-              ? tsBlock.getEndTime() > curTimeRange.getMax()
-              : tsBlock.getEndTime() < curTimeRange.getMin();
-      if (isEndCalc(aggregators) || isTsBlockOutOfBound) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  private boolean readAndCalcFromChunk(TimeRange curTimeRange) throws IOException {
-    while (seriesScanUtil.hasNextChunk()) {
-      if (canUseCurrentChunkStatistics()) {
-        Statistics chunkStatistics = seriesScanUtil.currentChunkStatistics();
-        if (chunkStatistics.getStartTime() > curTimeRange.getMax()) {
-          if (ascending) {
-            return true;
-          } else {
-            seriesScanUtil.skipCurrentChunk();
-            continue;
-          }
-        }
-        // calc from chunkMetaData
-        if (curTimeRange.contains(chunkStatistics.getStartTime(), chunkStatistics.getEndTime())) {
-          calcFromStatistics(chunkStatistics);
-          seriesScanUtil.skipCurrentChunk();
-          if (isEndCalc(aggregators) && !isGroupByQuery) {
-            return true;
-          } else {
-            continue;
-          }
-        }
-      }
-
-      // read page
-      if (readAndCalcFromPage(curTimeRange)) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  private void calcFromStatistics(Statistics statistics) {
-    for (int i = 0; i < aggregators.size(); i++) {
-      Aggregator aggregator = aggregators.get(i);
-      if (aggregator.hasFinalResult()) {
-        continue;
-      }
-      aggregator.processStatistics(statistics);
-    }
-  }
-
-  public boolean canUseCurrentFileStatistics() throws IOException {
-    Statistics fileStatistics = seriesScanUtil.currentFileStatistics();
-    return !seriesScanUtil.isFileOverlapped()
-        && containedByTimeFilter(fileStatistics)
-        && !seriesScanUtil.currentFileModified();
-  }
-
-  public boolean canUseCurrentChunkStatistics() throws IOException {
-    Statistics chunkStatistics = seriesScanUtil.currentChunkStatistics();
-    return !seriesScanUtil.isChunkOverlapped()
-        && containedByTimeFilter(chunkStatistics)
-        && !seriesScanUtil.currentChunkModified();
-  }
-
-  public boolean canUseCurrentPageStatistics() throws IOException {
-    Statistics currentPageStatistics = seriesScanUtil.currentPageStatistics();
-    if (currentPageStatistics == null) {
-      return false;
-    }
-    return !seriesScanUtil.isPageOverlapped()
-        && containedByTimeFilter(currentPageStatistics)
-        && !seriesScanUtil.currentPageModified();
-  }
-
-  private boolean containedByTimeFilter(Statistics statistics) {
-    Filter timeFilter = seriesScanUtil.getTimeFilter();
-    return timeFilter == null
-        || timeFilter.containStartEndTime(statistics.getStartTime(), statistics.getEndTime());
+            ascending),
+        1,
+        aggregators,
+        ascending,
+        groupByTimeParameter);
   }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/SeriesScanUtil.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/SeriesScanUtil.java
index 279f6be932..483ef03afa 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/SeriesScanUtil.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/operator/source/SeriesScanUtil.java
@@ -29,8 +29,6 @@ import org.apache.iotdb.db.query.reader.universal.PriorityMergeReader;
 import org.apache.iotdb.db.utils.FileLoaderUtils;
 import org.apache.iotdb.db.utils.QueryUtils;
 import org.apache.iotdb.tsfile.exception.write.UnSupportedDataTypeException;
-import org.apache.iotdb.tsfile.file.metadata.AlignedChunkMetadata;
-import org.apache.iotdb.tsfile.file.metadata.AlignedTimeSeriesMetadata;
 import org.apache.iotdb.tsfile.file.metadata.IChunkMetadata;
 import org.apache.iotdb.tsfile.file.metadata.ITimeSeriesMetadata;
 import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
@@ -58,6 +56,8 @@ import java.util.Set;
 import java.util.function.ToLongFunction;
 import java.util.stream.Collectors;
 
+import static com.google.common.base.Preconditions.checkArgument;
+
 public class SeriesScanUtil {
   private final FragmentInstanceContext context;
 
@@ -226,18 +226,13 @@ public class SeriesScanUtil {
     return firstTimeSeriesMetadata.getStatistics();
   }
 
-  Statistics currentFileStatistics(int index) throws IOException {
-    if (!(firstTimeSeriesMetadata instanceof AlignedTimeSeriesMetadata)) {
-      throw new IOException("Can only get statistics by index from alignedTimeSeriesMetaData");
-    }
-    return ((AlignedTimeSeriesMetadata) firstTimeSeriesMetadata).getStatistics(index);
+  protected Statistics currentFileStatistics(int index) throws IOException {
+    checkArgument(index == 0, "Only one sensor in non-aligned SeriesScanUtil.");
+    return currentFileStatistics();
   }
 
-  public Statistics currentFileTimeStatistics() throws IOException {
-    if (!(firstTimeSeriesMetadata instanceof AlignedTimeSeriesMetadata)) {
-      throw new IOException("Can only get statistics of time column from alignedChunkMetaData");
-    }
-    return ((AlignedTimeSeriesMetadata) firstTimeSeriesMetadata).getTimeStatistics();
+  protected Statistics currentFileTimeStatistics() throws IOException {
+    return currentFileStatistics();
   }
 
   boolean currentFileModified() throws IOException {
@@ -362,18 +357,13 @@ public class SeriesScanUtil {
     return firstChunkMetadata.getStatistics();
   }
 
-  Statistics currentChunkStatistics(int index) throws IOException {
-    if (!(firstChunkMetadata instanceof AlignedChunkMetadata)) {
-      throw new IOException("Can only get statistics by index from vectorChunkMetaData");
-    }
-    return ((AlignedChunkMetadata) firstChunkMetadata).getStatistics(index);
+  protected Statistics currentChunkStatistics(int index) throws IOException {
+    checkArgument(index == 0, "Only one sensor in non-aligned SeriesScanUtil.");
+    return currentChunkStatistics();
   }
 
-  Statistics currentChunkTimeStatistics() throws IOException {
-    if (!(firstChunkMetadata instanceof AlignedChunkMetadata)) {
-      throw new IOException("Can only get statistics of time column from alignedChunkMetaData");
-    }
-    return ((AlignedChunkMetadata) firstChunkMetadata).getTimeStatistics();
+  protected Statistics currentChunkTimeStatistics() throws IOException {
+    return currentChunkStatistics();
   }
 
   boolean currentChunkModified() throws IOException {
@@ -590,24 +580,13 @@ public class SeriesScanUtil {
     return firstPageReader.getStatistics();
   }
 
-  Statistics currentPageStatistics(int index) throws IOException {
-    if (firstPageReader == null) {
-      return null;
-    }
-    if (!(firstPageReader.isAlignedPageReader())) {
-      throw new IOException("Can only get statistics by index from AlignedPageReader");
-    }
-    return firstPageReader.getStatistics(index);
+  protected Statistics currentPageStatistics(int index) throws IOException {
+    checkArgument(index == 0, "Only one sensor in non-aligned SeriesScanUtil.");
+    return currentPageStatistics();
   }
 
-  Statistics currentPageTimeStatistics() throws IOException {
-    if (firstPageReader == null) {
-      return null;
-    }
-    if (!(firstPageReader.isAlignedPageReader())) {
-      throw new IOException("Can only get statistics of time column from AlignedPageReader");
-    }
-    return firstPageReader.getTimeStatistics();
+  protected Statistics currentPageTimeStatistics() throws IOException {
+    return currentPageStatistics();
   }
 
   boolean currentPageModified() throws IOException {
@@ -1094,7 +1073,7 @@ public class SeriesScanUtil {
     return orderUtils;
   }
 
-  private class VersionPageReader {
+  protected class VersionPageReader {
 
     protected PriorityMergeReader.MergeReaderPriority version;
     protected IPageReader data;
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/schedule/DriverTaskThread.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/schedule/DriverTaskThread.java
index 02a795cff3..f49806ac31 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/execution/schedule/DriverTaskThread.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/schedule/DriverTaskThread.java
@@ -18,6 +18,7 @@
  */
 package org.apache.iotdb.db.mpp.execution.schedule;
 
+import org.apache.iotdb.db.conf.IoTDBDescriptor;
 import org.apache.iotdb.db.mpp.execution.driver.IDriver;
 import org.apache.iotdb.db.mpp.execution.schedule.queue.IndexedBlockingQueue;
 import org.apache.iotdb.db.mpp.execution.schedule.task.DriverTask;
@@ -34,7 +35,10 @@ import java.util.concurrent.TimeUnit;
 /** the worker thread of {@link DriverTask} */
 public class DriverTaskThread extends AbstractDriverThread {
 
-  public static final Duration EXECUTION_TIME_SLICE = new Duration(100, TimeUnit.MILLISECONDS);
+  public static final Duration EXECUTION_TIME_SLICE =
+      new Duration(
+          IoTDBDescriptor.getInstance().getConfig().getDriverTaskExecutionTimeSliceInMs(),
+          TimeUnit.MILLISECONDS);
 
   // As the callback is lightweight enough, there's no need to use another one thread to execute.
   private static final Executor listeningExecutor = MoreExecutors.directExecutor();
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/timer/ITimeSliceAllocator.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/timer/ITimeSliceAllocator.java
new file mode 100644
index 0000000000..d23da3fbc7
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/timer/ITimeSliceAllocator.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.mpp.execution.timer;
+
+import org.apache.iotdb.db.mpp.execution.operator.OperatorContext;
+
+import io.airlift.units.Duration;
+
+public interface ITimeSliceAllocator {
+
+  Duration getMaxRunTime(OperatorContext operatorContext);
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/timer/RuleBasedTimeSliceAllocator.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/timer/RuleBasedTimeSliceAllocator.java
new file mode 100644
index 0000000000..910b2295bc
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/timer/RuleBasedTimeSliceAllocator.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.mpp.execution.timer;
+
+import org.apache.iotdb.db.mpp.execution.operator.OperatorContext;
+import org.apache.iotdb.db.mpp.execution.schedule.DriverTaskThread;
+
+import io.airlift.units.Duration;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import static com.google.common.base.Preconditions.checkState;
+
+public class RuleBasedTimeSliceAllocator implements ITimeSliceAllocator {
+
+  private final long EXECUTION_TIME_SLICE_IN_MS =
+      DriverTaskThread.EXECUTION_TIME_SLICE.roundTo(TimeUnit.MILLISECONDS);
+
+  private final Map<OperatorContext, Integer> operatorToWeightMap;
+
+  private int totalWeight;
+
+  public RuleBasedTimeSliceAllocator() {
+    this.operatorToWeightMap = new HashMap<>();
+    this.totalWeight = 0;
+  }
+
+  public void recordExecutionWeight(OperatorContext operatorContext, int weight) {
+    checkState(
+        !operatorToWeightMap.containsKey(operatorContext), "Same operator has been weighted");
+    operatorToWeightMap.put(operatorContext, weight);
+    totalWeight += weight;
+  }
+
+  private int getWeight(OperatorContext operatorContext) {
+    checkState(
+        operatorToWeightMap.containsKey(operatorContext), "This operator has not been weighted");
+    return operatorToWeightMap.get(operatorContext);
+  }
+
+  @Override
+  public Duration getMaxRunTime(OperatorContext operatorContext) {
+    return new Duration(
+        (double) EXECUTION_TIME_SLICE_IN_MS * getWeight(operatorContext) / totalWeight,
+        TimeUnit.MILLISECONDS);
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java
index 343c328eea..fd2aebef52 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LocalExecutionPlanner.java
@@ -28,7 +28,6 @@ import org.apache.iotdb.db.metadata.path.MeasurementPath;
 import org.apache.iotdb.db.metadata.schemaregion.ISchemaRegion;
 import org.apache.iotdb.db.mpp.aggregation.AccumulatorFactory;
 import org.apache.iotdb.db.mpp.aggregation.Aggregator;
-import org.apache.iotdb.db.mpp.aggregation.slidingwindow.SlidingWindowAggregator;
 import org.apache.iotdb.db.mpp.aggregation.slidingwindow.SlidingWindowAggregatorFactory;
 import org.apache.iotdb.db.mpp.common.FragmentInstanceId;
 import org.apache.iotdb.db.mpp.execution.driver.DataDriver;
@@ -106,6 +105,8 @@ import org.apache.iotdb.db.mpp.execution.operator.source.ExchangeOperator;
 import org.apache.iotdb.db.mpp.execution.operator.source.LastCacheScanOperator;
 import org.apache.iotdb.db.mpp.execution.operator.source.SeriesAggregationScanOperator;
 import org.apache.iotdb.db.mpp.execution.operator.source.SeriesScanOperator;
+import org.apache.iotdb.db.mpp.execution.timer.ITimeSliceAllocator;
+import org.apache.iotdb.db.mpp.execution.timer.RuleBasedTimeSliceAllocator;
 import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider;
 import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand;
 import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNode;
@@ -182,7 +183,6 @@ import static com.google.common.base.Preconditions.checkArgument;
 import static java.util.Objects.requireNonNull;
 import static org.apache.iotdb.db.mpp.execution.operator.LastQueryUtil.satisfyFilter;
 import static org.apache.iotdb.db.mpp.plan.constant.DataNodeEndPoints.isSameNode;
-import static org.apache.iotdb.tsfile.file.metadata.enums.TSDataType.BOOLEAN;
 
 /**
  * Used to plan a fragment instance. Currently, we simply change it from PlanNode to executable
@@ -218,6 +218,13 @@ public class LocalExecutionPlanner {
 
     Operator root = plan.accept(new Visitor(), context);
 
+    ITimeSliceAllocator timeSliceAllocator = context.getTimeSliceAllocator();
+    instanceContext
+        .getOperatorContexts()
+        .forEach(
+            operatorContext ->
+                operatorContext.setMaxRunTime(timeSliceAllocator.getMaxRunTime(operatorContext)));
+
     DataDriverContext dataDriverContext =
         new DataDriverContext(
             instanceContext,
@@ -240,6 +247,13 @@ public class LocalExecutionPlanner {
 
     Operator root = plan.accept(new Visitor(), context);
 
+    ITimeSliceAllocator timeSliceAllocator = context.getTimeSliceAllocator();
+    instanceContext
+        .getOperatorContexts()
+        .forEach(
+            operatorContext ->
+                operatorContext.setMaxRunTime(timeSliceAllocator.getMaxRunTime(operatorContext)));
+
     return new SchemaDriver(root, context.getSinkHandle(), schemaDriverContext);
   }
 
@@ -274,7 +288,7 @@ public class LocalExecutionPlanner {
 
       context.addSourceOperator(seriesScanOperator);
       context.addPath(seriesPath);
-
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, 1);
       return seriesScanOperator;
     }
 
@@ -300,7 +314,9 @@ public class LocalExecutionPlanner {
 
       context.addSourceOperator(seriesScanOperator);
       context.addPath(seriesPath);
-
+      context
+          .getTimeSliceAllocator()
+          .recordExecutionWeight(operatorContext, seriesPath.getColumnNum());
       return seriesScanOperator;
     }
 
@@ -350,7 +366,7 @@ public class LocalExecutionPlanner {
 
       context.addSourceOperator(seriesAggregationScanOperator);
       context.addPath(seriesPath);
-
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, aggregators.size());
       return seriesAggregationScanOperator;
     }
 
@@ -368,6 +384,7 @@ public class LocalExecutionPlanner {
               node.getPlanNodeId(),
               SchemaQueryOrderByHeatOperator.class.getSimpleName());
 
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, 1);
       return new SchemaQueryOrderByHeatOperator(operatorContext, children);
     }
 
@@ -398,6 +415,7 @@ public class LocalExecutionPlanner {
               context.getNextOperatorId(),
               node.getPlanNodeId(),
               TimeSeriesSchemaScanOperator.class.getSimpleName());
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, 1);
       return new TimeSeriesSchemaScanOperator(
           node.getPlanNodeId(),
           operatorContext,
@@ -419,6 +437,7 @@ public class LocalExecutionPlanner {
               context.getNextOperatorId(),
               node.getPlanNodeId(),
               DevicesSchemaScanOperator.class.getSimpleName());
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, 1);
       return new DevicesSchemaScanOperator(
           node.getPlanNodeId(),
           operatorContext,
@@ -441,6 +460,7 @@ public class LocalExecutionPlanner {
               context.getNextOperatorId(),
               node.getPlanNodeId(),
               SchemaQueryMergeOperator.class.getSimpleName());
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, 1);
       return new SchemaQueryMergeOperator(node.getPlanNodeId(), operatorContext, children);
     }
 
@@ -454,7 +474,8 @@ public class LocalExecutionPlanner {
           context.instanceContext.addOperatorContext(
               context.getNextOperatorId(),
               node.getPlanNodeId(),
-              CountSchemaMergeNode.class.getSimpleName());
+              CountMergeOperator.class.getSimpleName());
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, 1);
       return new CountMergeOperator(node.getPlanNodeId(), operatorContext, children);
     }
 
@@ -464,7 +485,8 @@ public class LocalExecutionPlanner {
           context.instanceContext.addOperatorContext(
               context.getNextOperatorId(),
               node.getPlanNodeId(),
-              CountSchemaMergeNode.class.getSimpleName());
+              DevicesCountOperator.class.getSimpleName());
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, 1);
       return new DevicesCountOperator(
           node.getPlanNodeId(), operatorContext, node.getPath(), node.isPrefixPath());
     }
@@ -476,7 +498,8 @@ public class LocalExecutionPlanner {
           context.instanceContext.addOperatorContext(
               context.getNextOperatorId(),
               node.getPlanNodeId(),
-              TimeSeriesCountNode.class.getSimpleName());
+              TimeSeriesCountOperator.class.getSimpleName());
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, 1);
       return new TimeSeriesCountOperator(
           node.getPlanNodeId(), operatorContext, node.getPath(), node.isPrefixPath());
     }
@@ -488,7 +511,8 @@ public class LocalExecutionPlanner {
           context.instanceContext.addOperatorContext(
               context.getNextOperatorId(),
               node.getPlanNodeId(),
-              LevelTimeSeriesCountNode.class.getSimpleName());
+              LevelTimeSeriesCountOperator.class.getSimpleName());
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, 1);
       return new LevelTimeSeriesCountOperator(
           node.getPlanNodeId(),
           operatorContext,
@@ -504,7 +528,8 @@ public class LocalExecutionPlanner {
           context.instanceContext.addOperatorContext(
               context.getNextOperatorId(),
               node.getPlanNodeId(),
-              NodePathsSchemaScanNode.class.getSimpleName());
+              NodePathsSchemaScanOperator.class.getSimpleName());
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, 1);
       return new NodePathsSchemaScanOperator(
           node.getPlanNodeId(), operatorContext, node.getPrefixPath(), node.getLevel());
     }
@@ -513,37 +538,39 @@ public class LocalExecutionPlanner {
     public Operator visitNodeManagementMemoryMerge(
         NodeManagementMemoryMergeNode node, LocalExecutionPlanContext context) {
       Operator child = node.getChild().accept(this, context);
-      return new NodeManageMemoryMergeOperator(
+      OperatorContext operatorContext =
           context.instanceContext.addOperatorContext(
               context.getNextOperatorId(),
               node.getPlanNodeId(),
-              NodeManageMemoryMergeOperator.class.getSimpleName()),
-          node.getData(),
-          child);
+              NodeManageMemoryMergeOperator.class.getSimpleName());
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, 1);
+      return new NodeManageMemoryMergeOperator(operatorContext, node.getData(), child);
     }
 
     @Override
     public Operator visitNodePathConvert(
         NodePathsConvertNode node, LocalExecutionPlanContext context) {
       Operator child = node.getChild().accept(this, context);
-      return new NodePathsConvertOperator(
+      OperatorContext operatorContext =
           context.instanceContext.addOperatorContext(
               context.getNextOperatorId(),
               node.getPlanNodeId(),
-              NodeManageMemoryMergeOperator.class.getSimpleName()),
-          child);
+              NodePathsConvertOperator.class.getSimpleName());
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, 1);
+      return new NodePathsConvertOperator(operatorContext, child);
     }
 
     @Override
     public Operator visitNodePathsCount(
         NodePathsCountNode node, LocalExecutionPlanContext context) {
       Operator child = node.getChild().accept(this, context);
-      return new NodePathsCountOperator(
+      OperatorContext operatorContext =
           context.instanceContext.addOperatorContext(
               context.getNextOperatorId(),
               node.getPlanNodeId(),
-              NodeManageMemoryMergeOperator.class.getSimpleName()),
-          child);
+              NodePathsCountOperator.class.getSimpleName());
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, 1);
+      return new NodePathsCountOperator(operatorContext, child);
     }
 
     @Override
@@ -581,7 +608,7 @@ public class LocalExecutionPlanner {
 
       context.addSourceOperator(aggregateScanOperator);
       context.addPath(seriesPath);
-
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, aggregators.size());
       return aggregateScanOperator;
     }
 
@@ -601,6 +628,8 @@ public class LocalExecutionPlanner {
               .map(deviceName -> node.getDeviceToMeasurementIndexesMap().get(deviceName))
               .collect(Collectors.toList());
       List<TSDataType> outputColumnTypes = getOutputColumnTypes(node, context.getTypeProvider());
+
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, 1);
       return new DeviceViewOperator(
           operatorContext, node.getDevices(), children, deviceColumnIndex, outputColumnTypes);
     }
@@ -631,6 +660,8 @@ public class LocalExecutionPlanner {
             break;
         }
       }
+
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, 1);
       return new DeviceMergeOperator(
           operatorContext, node.getDevices(), children, dataTypes, selector, timeComparator);
     }
@@ -647,32 +678,25 @@ public class LocalExecutionPlanner {
       List<TSDataType> inputDataTypes = getOutputColumnTypes(node.getChild(), context.typeProvider);
       int inputColumns = inputDataTypes.size();
       FillPolicy fillPolicy = descriptor.getFillPolicy();
+      OperatorContext operatorContext =
+          context.instanceContext.addOperatorContext(
+              context.getNextOperatorId(),
+              node.getPlanNodeId(),
+              FillOperator.class.getSimpleName());
       switch (fillPolicy) {
         case VALUE:
           Literal literal = descriptor.getFillValue();
+          context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, 1);
           return new FillOperator(
-              context.instanceContext.addOperatorContext(
-                  context.getNextOperatorId(),
-                  node.getPlanNodeId(),
-                  FillOperator.class.getSimpleName()),
-              getConstantFill(inputColumns, inputDataTypes, literal),
-              child);
+              operatorContext, getConstantFill(inputColumns, inputDataTypes, literal), child);
         case PREVIOUS:
+          context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, 1);
           return new FillOperator(
-              context.instanceContext.addOperatorContext(
-                  context.getNextOperatorId(),
-                  node.getPlanNodeId(),
-                  FillOperator.class.getSimpleName()),
-              getPreviousFill(inputColumns, inputDataTypes),
-              child);
+              operatorContext, getPreviousFill(inputColumns, inputDataTypes), child);
         case LINEAR:
+          context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, 1);
           return new LinearFillOperator(
-              context.instanceContext.addOperatorContext(
-                  context.getNextOperatorId(),
-                  node.getPlanNodeId(),
-                  LinearFillOperator.class.getSimpleName()),
-              getLinearFill(inputColumns, inputDataTypes),
-              child);
+              operatorContext, getLinearFill(inputColumns, inputDataTypes), child);
         default:
           throw new IllegalArgumentException("Unknown fill policy: " + fillPolicy);
       }
@@ -779,6 +803,8 @@ public class LocalExecutionPlanner {
       final List<TSDataType> inputDataTypes = getInputColumnTypes(node, context.getTypeProvider());
       final Map<String, List<InputLocation>> inputLocations = makeLayout(node);
 
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, 1);
+
       try {
         return new TransformOperator(
             operatorContext,
@@ -806,6 +832,8 @@ public class LocalExecutionPlanner {
       final List<TSDataType> inputDataTypes = getInputColumnTypes(node, context.getTypeProvider());
       final Map<String, List<InputLocation>> inputLocations = makeLayout(node);
 
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, 1);
+
       try {
         return new FilterOperator(
             operatorContext,
@@ -859,6 +887,8 @@ public class LocalExecutionPlanner {
               context.getNextOperatorId(),
               node.getPlanNodeId(),
               AggregationOperator.class.getSimpleName());
+
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, aggregators.size());
       return new AggregationOperator(
           operatorContext, aggregators, children, ascending, node.getGroupByTimeParameter(), false);
     }
@@ -876,7 +906,7 @@ public class LocalExecutionPlanner {
               SlidingWindowAggregationOperator.class.getSimpleName());
       Operator child = node.getChild().accept(this, context);
       boolean ascending = node.getScanOrder() == OrderBy.TIMESTAMP_ASC;
-      List<SlidingWindowAggregator> aggregators = new ArrayList<>();
+      List<Aggregator> aggregators = new ArrayList<>();
       Map<String, List<InputLocation>> layout = makeLayout(node);
       for (AggregationDescriptor descriptor : node.getAggregationDescriptorList()) {
         List<InputLocation[]> inputLocationList = calcInputLocationList(descriptor, layout);
@@ -891,6 +921,8 @@ public class LocalExecutionPlanner {
                 inputLocationList,
                 descriptor.getStep()));
       }
+
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, aggregators.size());
       return new SlidingWindowAggregationOperator(
           operatorContext, aggregators, child, ascending, node.getGroupByTimeParameter());
     }
@@ -898,25 +930,27 @@ public class LocalExecutionPlanner {
     @Override
     public Operator visitLimit(LimitNode node, LocalExecutionPlanContext context) {
       Operator child = node.getChild().accept(this, context);
-      return new LimitOperator(
+      OperatorContext operatorContext =
           context.instanceContext.addOperatorContext(
               context.getNextOperatorId(),
               node.getPlanNodeId(),
-              LimitOperator.class.getSimpleName()),
-          node.getLimit(),
-          child);
+              LimitOperator.class.getSimpleName());
+
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, 1);
+      return new LimitOperator(operatorContext, node.getLimit(), child);
     }
 
     @Override
     public Operator visitOffset(OffsetNode node, LocalExecutionPlanContext context) {
       Operator child = node.getChild().accept(this, context);
-      return new OffsetOperator(
+      OperatorContext operatorContext =
           context.instanceContext.addOperatorContext(
               context.getNextOperatorId(),
               node.getPlanNodeId(),
-              OffsetOperator.class.getSimpleName()),
-          node.getOffset(),
-          child);
+              OffsetOperator.class.getSimpleName());
+
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, 1);
+      return new OffsetOperator(operatorContext, node.getOffset(), child);
     }
 
     @Override
@@ -953,6 +987,7 @@ public class LocalExecutionPlanner {
                 context.getNextOperatorId(),
                 node.getPlanNodeId(),
                 RawDataAggregationOperator.class.getSimpleName());
+        context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, aggregators.size());
         return new RawDataAggregationOperator(
             operatorContext,
             aggregators,
@@ -965,6 +1000,7 @@ public class LocalExecutionPlanner {
                 context.getNextOperatorId(),
                 node.getPlanNodeId(),
                 AggregationOperator.class.getSimpleName());
+        context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, aggregators.size());
         return new AggregationOperator(
             operatorContext,
             aggregators,
@@ -1021,6 +1057,8 @@ public class LocalExecutionPlanner {
       List<OutputColumn> outputColumns = generateOutputColumns(node);
       List<ColumnMerger> mergers = createColumnMergers(outputColumns, timeComparator);
       List<TSDataType> outputColumnTypes = getOutputColumnTypes(node, context.getTypeProvider());
+
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, 1);
       return new TimeJoinOperator(
           operatorContext,
           children,
@@ -1065,6 +1103,8 @@ public class LocalExecutionPlanner {
               context.getNextOperatorId(),
               node.getPlanNodeId(),
               ExchangeOperator.class.getSimpleName());
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, 0);
+
       FragmentInstanceId localInstanceId = context.instanceContext.getId();
       FragmentInstanceId remoteInstanceId = node.getUpstreamInstanceId();
 
@@ -1125,6 +1165,7 @@ public class LocalExecutionPlanner {
               context.getNextOperatorId(),
               node.getPlanNodeId(),
               SchemaFetchMergeOperator.class.getSimpleName());
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, 1);
       return new SchemaFetchMergeOperator(operatorContext, children);
     }
 
@@ -1136,6 +1177,7 @@ public class LocalExecutionPlanner {
               context.getNextOperatorId(),
               node.getPlanNodeId(),
               SchemaFetchScanOperator.class.getSimpleName());
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, 1);
       return new SchemaFetchScanOperator(
           node.getPlanNodeId(),
           operatorContext,
@@ -1171,11 +1213,14 @@ public class LocalExecutionPlanner {
         LastQueryScanNode node, LocalExecutionPlanContext context, PartialPath fullPath) {
       SeriesAggregationScanOperator lastQueryScan = createLastQueryScanOperator(node, context);
 
-      return new UpdateLastCacheOperator(
+      OperatorContext operatorContext =
           context.instanceContext.addOperatorContext(
               context.getNextOperatorId(),
               node.getPlanNodeId(),
-              UpdateLastCacheOperator.class.getSimpleName()),
+              UpdateLastCacheOperator.class.getSimpleName());
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, 1);
+      return new UpdateLastCacheOperator(
+          operatorContext,
           lastQueryScan,
           fullPath,
           node.getSeriesPath().getSeriesType(),
@@ -1207,6 +1252,7 @@ public class LocalExecutionPlanner {
               null);
       context.addSourceOperator(seriesAggregationScanOperator);
       context.addPath(seriesPath);
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, aggregators.size());
       return seriesAggregationScanOperator;
     }
 
@@ -1240,11 +1286,14 @@ public class LocalExecutionPlanner {
       AlignedSeriesAggregationScanOperator lastQueryScan =
           createLastQueryScanOperator(node, context);
 
-      return new UpdateLastCacheOperator(
+      OperatorContext operatorContext =
           context.instanceContext.addOperatorContext(
               context.getNextOperatorId(),
               node.getPlanNodeId(),
-              UpdateLastCacheOperator.class.getSimpleName()),
+              UpdateLastCacheOperator.class.getSimpleName());
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, 1);
+      return new UpdateLastCacheOperator(
+          operatorContext,
           lastQueryScan,
           fullPath,
           node.getSeriesPath().getSchemaList().get(0).getType(),
@@ -1275,6 +1324,7 @@ public class LocalExecutionPlanner {
               null);
       context.addSourceOperator(seriesAggregationScanOperator);
       context.addPath(seriesPath);
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, aggregators.size());
       return seriesAggregationScanOperator;
     }
 
@@ -1306,23 +1356,25 @@ public class LocalExecutionPlanner {
               timeValuePair.getValue().getDataType().name());
         }
 
+        OperatorContext operatorContext =
+            context.instanceContext.addOperatorContext(
+                context.getNextOperatorId(),
+                context.firstCachedPlanNodeId,
+                LastCacheScanOperator.class.getSimpleName());
+        context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, 1);
         LastCacheScanOperator operator =
             new LastCacheScanOperator(
-                context.instanceContext.addOperatorContext(
-                    context.getNextOperatorId(),
-                    context.firstCachedPlanNodeId,
-                    LastCacheScanOperator.class.getSimpleName()),
-                context.firstCachedPlanNodeId,
-                builder.build());
+                operatorContext, context.firstCachedPlanNodeId, builder.build());
         operatorList.add(operator);
       }
 
-      return new LastQueryMergeOperator(
+      OperatorContext operatorContext =
           context.instanceContext.addOperatorContext(
               context.getNextOperatorId(),
               node.getPlanNodeId(),
-              LastQueryMergeOperator.class.getSimpleName()),
-          operatorList);
+              LastQueryMergeOperator.class.getSimpleName());
+      context.getTimeSliceAllocator().recordExecutionWeight(operatorContext, 1);
+      return new LastQueryMergeOperator(operatorContext, operatorList);
     }
 
     private Map<String, List<InputLocation>> makeLayout(PlanNode node) {
@@ -1397,6 +1449,8 @@ public class LocalExecutionPlanner {
     // whether we need to update last cache
     private boolean needUpdateLastCache;
 
+    private final RuleBasedTimeSliceAllocator timeSliceAllocator;
+
     public LocalExecutionPlanContext(
         TypeProvider typeProvider, FragmentInstanceContext instanceContext) {
       this.typeProvider = typeProvider;
@@ -1404,6 +1458,7 @@ public class LocalExecutionPlanner {
       this.paths = new ArrayList<>();
       this.allSensorsMap = new HashMap<>();
       this.sourceOperators = new ArrayList<>();
+      this.timeSliceAllocator = new RuleBasedTimeSliceAllocator();
     }
 
     public LocalExecutionPlanContext(FragmentInstanceContext instanceContext) {
@@ -1411,6 +1466,9 @@ public class LocalExecutionPlanner {
       this.paths = new ArrayList<>();
       this.allSensorsMap = new HashMap<>();
       this.sourceOperators = new ArrayList<>();
+
+      // only used in `order by heat`
+      this.timeSliceAllocator = new RuleBasedTimeSliceAllocator();
     }
 
     private int getNextOperatorId() {
@@ -1476,5 +1534,9 @@ public class LocalExecutionPlanner {
     public TypeProvider getTypeProvider() {
       return typeProvider;
     }
+
+    public RuleBasedTimeSliceAllocator getTimeSliceAllocator() {
+      return timeSliceAllocator;
+    }
   }
 }
diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/AggregationOperatorTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/AggregationOperatorTest.java
index 3b940a78a8..a8380c27f1 100644
--- a/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/AggregationOperatorTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/AggregationOperatorTest.java
@@ -46,6 +46,7 @@ import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
 import org.apache.iotdb.tsfile.read.common.block.TsBlock;
 import org.apache.iotdb.tsfile.write.schema.MeasurementSchema;
 
+import io.airlift.units.Duration;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -55,12 +56,15 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
 
 import static org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceContext.createFragmentInstanceContext;
 import static org.junit.Assert.assertEquals;
 
 public class AggregationOperatorTest {
 
+  public static Duration TEST_TIME_SLICE = new Duration(500, TimeUnit.MILLISECONDS);
+
   private static final String AGGREGATION_OPERATOR_TEST_SG = "root.AggregationOperatorTest";
   private final List<String> deviceIds = new ArrayList<>();
   private final List<MeasurementSchema> measurementSchemas = new ArrayList<>();
@@ -177,14 +181,17 @@ public class AggregationOperatorTest {
     int count = 0;
     while (aggregationOperator.hasNext()) {
       TsBlock resultTsBlock = aggregationOperator.next();
-      assertEquals(100 * count, resultTsBlock.getTimeColumn().getLong(0));
-      assertEquals(result[0][count], resultTsBlock.getColumn(0).getLong(0));
-      assertEquals(result[1][count], resultTsBlock.getColumn(1).getDouble(0), 0.0001);
-      assertEquals(result[2][count], resultTsBlock.getColumn(2).getLong(0));
-      assertEquals(result[3][count], resultTsBlock.getColumn(3).getLong(0));
-      assertEquals(result[4][count], resultTsBlock.getColumn(4).getInt(0));
-      assertEquals(result[5][count], resultTsBlock.getColumn(5).getInt(0));
-      count++;
+      int positionCount = resultTsBlock.getPositionCount();
+      for (int pos = 0; pos < positionCount; pos++) {
+        assertEquals(100 * count, resultTsBlock.getTimeColumn().getLong(pos));
+        assertEquals(result[0][count], resultTsBlock.getColumn(0).getLong(pos));
+        assertEquals(result[1][count], resultTsBlock.getColumn(1).getDouble(pos), 0.0001);
+        assertEquals(result[2][count], resultTsBlock.getColumn(2).getLong(pos));
+        assertEquals(result[3][count], resultTsBlock.getColumn(3).getLong(pos));
+        assertEquals(result[4][count], resultTsBlock.getColumn(4).getInt(pos));
+        assertEquals(result[5][count], resultTsBlock.getColumn(5).getInt(pos));
+        count++;
+      }
     }
     assertEquals(4, count);
   }
@@ -216,11 +223,14 @@ public class AggregationOperatorTest {
     int count = 0;
     while (aggregationOperator.hasNext()) {
       TsBlock resultTsBlock = aggregationOperator.next();
-      assertEquals(100 * count, resultTsBlock.getTimeColumn().getLong(0));
-      assertEquals(result[0][count], resultTsBlock.getColumn(0).getDouble(0), 0.001);
-      assertEquals((int) result[1][count], resultTsBlock.getColumn(1).getInt(0));
-      assertEquals((int) result[2][count], resultTsBlock.getColumn(2).getInt(0));
-      count++;
+      int positionCount = resultTsBlock.getPositionCount();
+      for (int pos = 0; pos < positionCount; pos++) {
+        assertEquals(100 * count, resultTsBlock.getTimeColumn().getLong(pos));
+        assertEquals(result[0][count], resultTsBlock.getColumn(0).getDouble(pos), 0.001);
+        assertEquals((int) result[1][count], resultTsBlock.getColumn(1).getInt(pos));
+        assertEquals((int) result[2][count], resultTsBlock.getColumn(2).getInt(pos));
+        count++;
+      }
     }
     assertEquals(4, count);
   }
@@ -252,6 +262,12 @@ public class AggregationOperatorTest {
     PlanNodeId planNodeId3 = new PlanNodeId("3");
     fragmentInstanceContext.addOperatorContext(
         3, planNodeId3, AggregationOperator.class.getSimpleName());
+    fragmentInstanceContext
+        .getOperatorContexts()
+        .forEach(
+            operatorContext -> {
+              operatorContext.setMaxRunTime(TEST_TIME_SLICE);
+            });
 
     MeasurementPath measurementPath1 =
         new MeasurementPath(AGGREGATION_OPERATOR_TEST_SG + ".device0.sensor0", TSDataType.INT32);
diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/AlignedSeriesAggregationScanOperatorTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/AlignedSeriesAggregationScanOperatorTest.java
index fbd76ab819..2277299102 100644
--- a/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/AlignedSeriesAggregationScanOperatorTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/AlignedSeriesAggregationScanOperatorTest.java
@@ -62,6 +62,7 @@ import java.util.concurrent.ExecutorService;
 import java.util.stream.Collectors;
 
 import static org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceContext.createFragmentInstanceContext;
+import static org.apache.iotdb.db.mpp.execution.operator.AggregationOperatorTest.TEST_TIME_SLICE;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
@@ -380,11 +381,14 @@ public class AlignedSeriesAggregationScanOperatorTest {
     int count = 0;
     while (seriesAggregationScanOperator.hasNext()) {
       TsBlock resultTsBlock = seriesAggregationScanOperator.next();
-      assertEquals(100 * count, resultTsBlock.getTimeColumn().getLong(0));
-      for (int i = 0; i < measurementSchemas.size(); i++) {
-        assertEquals(result[count], resultTsBlock.getColumn(i).getLong(0));
+      int positionCount = resultTsBlock.getPositionCount();
+      for (int pos = 0; pos < positionCount; pos++) {
+        assertEquals(100 * count, resultTsBlock.getTimeColumn().getLong(pos));
+        for (int i = 0; i < measurementSchemas.size(); i++) {
+          assertEquals(result[count], resultTsBlock.getColumn(i).getLong(pos));
+        }
+        count++;
       }
-      count++;
     }
     assertEquals(4, count);
   }
@@ -411,11 +415,14 @@ public class AlignedSeriesAggregationScanOperatorTest {
     int count = 0;
     while (seriesAggregationScanOperator.hasNext()) {
       TsBlock resultTsBlock = seriesAggregationScanOperator.next();
-      assertEquals(100 * count, resultTsBlock.getTimeColumn().getLong(0));
-      for (int i = 0; i < measurementSchemas.size(); i++) {
-        assertEquals(result[count], resultTsBlock.getColumn(i).getLong(0));
+      int positionCount = resultTsBlock.getPositionCount();
+      for (int pos = 0; pos < positionCount; pos++) {
+        assertEquals(100 * count, resultTsBlock.getTimeColumn().getLong(pos));
+        for (int i = 0; i < measurementSchemas.size(); i++) {
+          assertEquals(result[count], resultTsBlock.getColumn(i).getLong(pos));
+        }
+        count++;
       }
-      count++;
     }
     assertEquals(4, count);
   }
@@ -445,12 +452,15 @@ public class AlignedSeriesAggregationScanOperatorTest {
     int count = 0;
     while (seriesAggregationScanOperator.hasNext()) {
       TsBlock resultTsBlock = seriesAggregationScanOperator.next();
-      assertEquals(100 * count, resultTsBlock.getTimeColumn().getLong(0));
-      assertEquals(result[0][count], resultTsBlock.getColumn(0).getInt(0));
-      assertEquals(result[1][count], resultTsBlock.getColumn(1).getInt(0));
-      assertEquals(result[2][count], resultTsBlock.getColumn(2).getInt(0));
-      assertEquals(result[3][count], resultTsBlock.getColumn(3).getInt(0));
-      count++;
+      int positionCount = resultTsBlock.getPositionCount();
+      for (int pos = 0; pos < positionCount; pos++) {
+        assertEquals(100 * count, resultTsBlock.getTimeColumn().getLong(pos));
+        assertEquals(result[0][count], resultTsBlock.getColumn(0).getInt(pos));
+        assertEquals(result[1][count], resultTsBlock.getColumn(1).getInt(pos));
+        assertEquals(result[2][count], resultTsBlock.getColumn(2).getInt(pos));
+        assertEquals(result[3][count], resultTsBlock.getColumn(3).getInt(pos));
+        count++;
+      }
     }
     assertEquals(4, count);
   }
@@ -480,12 +490,15 @@ public class AlignedSeriesAggregationScanOperatorTest {
     int count = 0;
     while (seriesAggregationScanOperator.hasNext()) {
       TsBlock resultTsBlock = seriesAggregationScanOperator.next();
-      assertEquals(100 * (3 - count), resultTsBlock.getTimeColumn().getLong(0));
-      assertEquals(result[0][3 - count], resultTsBlock.getColumn(0).getInt(0));
-      assertEquals(result[1][3 - count], resultTsBlock.getColumn(1).getInt(0));
-      assertEquals(result[2][3 - count], resultTsBlock.getColumn(2).getInt(0));
-      assertEquals(result[3][3 - count], resultTsBlock.getColumn(3).getInt(0));
-      count++;
+      int positionCount = resultTsBlock.getPositionCount();
+      for (int pos = 0; pos < positionCount; pos++) {
+        assertEquals(100 * (3 - count), resultTsBlock.getTimeColumn().getLong(pos));
+        assertEquals(result[0][3 - count], resultTsBlock.getColumn(0).getInt(pos));
+        assertEquals(result[1][3 - count], resultTsBlock.getColumn(1).getInt(pos));
+        assertEquals(result[2][3 - count], resultTsBlock.getColumn(2).getInt(pos));
+        assertEquals(result[3][3 - count], resultTsBlock.getColumn(3).getInt(pos));
+        count++;
+      }
     }
     assertEquals(4, count);
   }
@@ -505,9 +518,12 @@ public class AlignedSeriesAggregationScanOperatorTest {
     int count = 0;
     while (seriesAggregationScanOperator.hasNext()) {
       TsBlock resultTsBlock = seriesAggregationScanOperator.next();
-      assertEquals(50 * count, resultTsBlock.getTimeColumn().getLong(0));
-      assertEquals(result[count], resultTsBlock.getColumn(0).getLong(0));
-      count++;
+      int positionCount = resultTsBlock.getPositionCount();
+      for (int pos = 0; pos < positionCount; pos++) {
+        assertEquals(50 * count, resultTsBlock.getTimeColumn().getLong(pos));
+        assertEquals(result[count], resultTsBlock.getColumn(0).getLong(pos));
+        count++;
+      }
     }
     assertEquals(result.length, count);
   }
@@ -528,9 +544,12 @@ public class AlignedSeriesAggregationScanOperatorTest {
     int count = 0;
     while (seriesAggregationScanOperator.hasNext()) {
       TsBlock resultTsBlock = seriesAggregationScanOperator.next();
-      assertEquals(timeColumn[count], resultTsBlock.getTimeColumn().getLong(0));
-      assertEquals(result[count], resultTsBlock.getColumn(0).getLong(0));
-      count++;
+      int positionCount = resultTsBlock.getPositionCount();
+      for (int pos = 0; pos < positionCount; pos++) {
+        assertEquals(timeColumn[count], resultTsBlock.getTimeColumn().getLong(pos));
+        assertEquals(result[count], resultTsBlock.getColumn(0).getLong(pos));
+        count++;
+      }
     }
     assertEquals(timeColumn.length, count);
   }
@@ -561,12 +580,15 @@ public class AlignedSeriesAggregationScanOperatorTest {
     int count = 0;
     while (seriesAggregationScanOperator.hasNext()) {
       TsBlock resultTsBlock = seriesAggregationScanOperator.next();
-      assertEquals(timeColumn[count], resultTsBlock.getTimeColumn().getLong(0));
-      assertEquals(result[0][count], resultTsBlock.getColumn(0).getInt(0));
-      assertEquals(result[1][count], resultTsBlock.getColumn(1).getInt(0));
-      assertEquals(result[2][count], resultTsBlock.getColumn(2).getInt(0));
-      assertEquals(result[3][count], resultTsBlock.getColumn(3).getInt(0));
-      count++;
+      int positionCount = resultTsBlock.getPositionCount();
+      for (int pos = 0; pos < positionCount; pos++) {
+        assertEquals(timeColumn[count], resultTsBlock.getTimeColumn().getLong(pos));
+        assertEquals(result[0][count], resultTsBlock.getColumn(0).getInt(pos));
+        assertEquals(result[1][count], resultTsBlock.getColumn(1).getInt(pos));
+        assertEquals(result[2][count], resultTsBlock.getColumn(2).getInt(pos));
+        assertEquals(result[3][count], resultTsBlock.getColumn(3).getInt(pos));
+        count++;
+      }
     }
     assertEquals(timeColumn.length, count);
   }
@@ -597,6 +619,12 @@ public class AlignedSeriesAggregationScanOperatorTest {
     PlanNodeId planNodeId = new PlanNodeId("1");
     fragmentInstanceContext.addOperatorContext(
         1, planNodeId, SeriesScanOperator.class.getSimpleName());
+    fragmentInstanceContext
+        .getOperatorContexts()
+        .forEach(
+            operatorContext -> {
+              operatorContext.setMaxRunTime(TEST_TIME_SLICE);
+            });
 
     AlignedSeriesAggregationScanOperator seriesAggregationScanOperator =
         new AlignedSeriesAggregationScanOperator(
diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/LastQueryMergeOperatorTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/LastQueryMergeOperatorTest.java
index 03fa1a1584..c0ced72d91 100644
--- a/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/LastQueryMergeOperatorTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/LastQueryMergeOperatorTest.java
@@ -55,6 +55,7 @@ import java.util.Set;
 import java.util.concurrent.ExecutorService;
 
 import static org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceContext.createFragmentInstanceContext;
+import static org.apache.iotdb.db.mpp.execution.operator.AggregationOperatorTest.TEST_TIME_SLICE;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -118,6 +119,13 @@ public class LastQueryMergeOperatorTest {
       fragmentInstanceContext.addOperatorContext(
           5, planNodeId5, LastQueryMergeOperator.class.getSimpleName());
 
+      fragmentInstanceContext
+          .getOperatorContexts()
+          .forEach(
+              operatorContext -> {
+                operatorContext.setMaxRunTime(TEST_TIME_SLICE);
+              });
+
       SeriesAggregationScanOperator seriesAggregationScanOperator1 =
           new SeriesAggregationScanOperator(
               planNodeId1,
@@ -233,6 +241,13 @@ public class LastQueryMergeOperatorTest {
       fragmentInstanceContext.addOperatorContext(
           6, planNodeId6, LastQueryMergeOperator.class.getSimpleName());
 
+      fragmentInstanceContext
+          .getOperatorContexts()
+          .forEach(
+              operatorContext -> {
+                operatorContext.setMaxRunTime(TEST_TIME_SLICE);
+              });
+
       SeriesAggregationScanOperator seriesAggregationScanOperator1 =
           new SeriesAggregationScanOperator(
               planNodeId1,
diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/RawDataAggregationOperatorTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/RawDataAggregationOperatorTest.java
index 46b85b85f7..9cf7c6331f 100644
--- a/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/RawDataAggregationOperatorTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/RawDataAggregationOperatorTest.java
@@ -63,6 +63,7 @@ import java.util.Set;
 import java.util.concurrent.ExecutorService;
 
 import static org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceContext.createFragmentInstanceContext;
+import static org.apache.iotdb.db.mpp.execution.operator.AggregationOperatorTest.TEST_TIME_SLICE;
 import static org.junit.Assert.assertEquals;
 
 public class RawDataAggregationOperatorTest {
@@ -326,6 +327,13 @@ public class RawDataAggregationOperatorTest {
         3, new PlanNodeId("3"), TimeJoinOperator.class.getSimpleName());
     fragmentInstanceContext.addOperatorContext(
         4, new PlanNodeId("4"), RawDataAggregationOperatorTest.class.getSimpleName());
+    fragmentInstanceContext
+        .getOperatorContexts()
+        .forEach(
+            operatorContext -> {
+              operatorContext.setMaxRunTime(TEST_TIME_SLICE);
+            });
+
     SeriesScanOperator seriesScanOperator1 =
         new SeriesScanOperator(
             planNodeId1,
diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/SeriesAggregationScanOperatorTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/SeriesAggregationScanOperatorTest.java
index 7e95c4ac2c..71fd167404 100644
--- a/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/SeriesAggregationScanOperatorTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/SeriesAggregationScanOperatorTest.java
@@ -58,6 +58,7 @@ import java.util.Set;
 import java.util.concurrent.ExecutorService;
 
 import static org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceContext.createFragmentInstanceContext;
+import static org.apache.iotdb.db.mpp.execution.operator.AggregationOperatorTest.TEST_TIME_SLICE;
 import static org.junit.Assert.assertEquals;
 
 public class SeriesAggregationScanOperatorTest {
@@ -291,9 +292,12 @@ public class SeriesAggregationScanOperatorTest {
     int count = 0;
     while (seriesAggregationScanOperator.hasNext()) {
       TsBlock resultTsBlock = seriesAggregationScanOperator.next();
-      assertEquals(100 * count, resultTsBlock.getTimeColumn().getLong(0));
-      assertEquals(result[count], resultTsBlock.getColumn(0).getLong(0));
-      count++;
+      int positionCount = resultTsBlock.getPositionCount();
+      for (int pos = 0; pos < positionCount; pos++) {
+        assertEquals(100 * count, resultTsBlock.getTimeColumn().getLong(pos));
+        assertEquals(result[count], resultTsBlock.getColumn(0).getLong(pos));
+        count++;
+      }
     }
     assertEquals(4, count);
   }
@@ -312,9 +316,12 @@ public class SeriesAggregationScanOperatorTest {
     int count = 0;
     while (seriesAggregationScanOperator.hasNext()) {
       TsBlock resultTsBlock = seriesAggregationScanOperator.next();
-      assertEquals(100 * count, resultTsBlock.getTimeColumn().getLong(0));
-      assertEquals(result[count], resultTsBlock.getColumn(0).getLong(0));
-      count++;
+      int positionCount = resultTsBlock.getPositionCount();
+      for (int pos = 0; pos < positionCount; pos++) {
+        assertEquals(100 * count, resultTsBlock.getTimeColumn().getLong(pos));
+        assertEquals(result[count], resultTsBlock.getColumn(0).getLong(pos));
+        count++;
+      }
     }
     assertEquals(4, count);
   }
@@ -342,12 +349,15 @@ public class SeriesAggregationScanOperatorTest {
     int count = 0;
     while (seriesAggregationScanOperator.hasNext()) {
       TsBlock resultTsBlock = seriesAggregationScanOperator.next();
-      assertEquals(100 * count, resultTsBlock.getTimeColumn().getLong(0));
-      assertEquals(result[0][count], resultTsBlock.getColumn(0).getInt(0));
-      assertEquals(result[1][count], resultTsBlock.getColumn(1).getInt(0));
-      assertEquals(result[2][count], resultTsBlock.getColumn(2).getInt(0));
-      assertEquals(result[3][count], resultTsBlock.getColumn(3).getInt(0));
-      count++;
+      int positionCount = resultTsBlock.getPositionCount();
+      for (int pos = 0; pos < positionCount; pos++) {
+        assertEquals(100 * count, resultTsBlock.getTimeColumn().getLong(pos));
+        assertEquals(result[0][count], resultTsBlock.getColumn(0).getInt(pos));
+        assertEquals(result[1][count], resultTsBlock.getColumn(1).getInt(pos));
+        assertEquals(result[2][count], resultTsBlock.getColumn(2).getInt(pos));
+        assertEquals(result[3][count], resultTsBlock.getColumn(3).getInt(pos));
+        count++;
+      }
     }
     assertEquals(4, count);
   }
@@ -375,12 +385,15 @@ public class SeriesAggregationScanOperatorTest {
     int count = 0;
     while (seriesAggregationScanOperator.hasNext()) {
       TsBlock resultTsBlock = seriesAggregationScanOperator.next();
-      assertEquals(100 * (3 - count), resultTsBlock.getTimeColumn().getLong(0));
-      assertEquals(result[0][3 - count], resultTsBlock.getColumn(0).getInt(0));
-      assertEquals(result[1][3 - count], resultTsBlock.getColumn(1).getInt(0));
-      assertEquals(result[2][3 - count], resultTsBlock.getColumn(2).getInt(0));
-      assertEquals(result[3][3 - count], resultTsBlock.getColumn(3).getInt(0));
-      count++;
+      int positionCount = resultTsBlock.getPositionCount();
+      for (int pos = 0; pos < positionCount; pos++) {
+        assertEquals(100 * (3 - count), resultTsBlock.getTimeColumn().getLong(pos));
+        assertEquals(result[0][3 - count], resultTsBlock.getColumn(0).getInt(pos));
+        assertEquals(result[1][3 - count], resultTsBlock.getColumn(1).getInt(pos));
+        assertEquals(result[2][3 - count], resultTsBlock.getColumn(2).getInt(pos));
+        assertEquals(result[3][3 - count], resultTsBlock.getColumn(3).getInt(pos));
+        count++;
+      }
     }
     assertEquals(4, count);
   }
@@ -398,9 +411,12 @@ public class SeriesAggregationScanOperatorTest {
     int count = 0;
     while (seriesAggregationScanOperator.hasNext()) {
       TsBlock resultTsBlock = seriesAggregationScanOperator.next();
-      assertEquals(50 * count, resultTsBlock.getTimeColumn().getLong(0));
-      assertEquals(result[count], resultTsBlock.getColumn(0).getLong(0));
-      count++;
+      int positionCount = resultTsBlock.getPositionCount();
+      for (int pos = 0; pos < positionCount; pos++) {
+        assertEquals(50 * count, resultTsBlock.getTimeColumn().getLong(pos));
+        assertEquals(result[count], resultTsBlock.getColumn(0).getLong(pos));
+        count++;
+      }
     }
     assertEquals(result.length, count);
   }
@@ -419,9 +435,12 @@ public class SeriesAggregationScanOperatorTest {
     int count = 0;
     while (seriesAggregationScanOperator.hasNext()) {
       TsBlock resultTsBlock = seriesAggregationScanOperator.next();
-      assertEquals(timeColumn[count], resultTsBlock.getTimeColumn().getLong(0));
-      assertEquals(result[count], resultTsBlock.getColumn(0).getLong(0));
-      count++;
+      int positionCount = resultTsBlock.getPositionCount();
+      for (int pos = 0; pos < positionCount; pos++) {
+        assertEquals(timeColumn[count], resultTsBlock.getTimeColumn().getLong(pos));
+        assertEquals(result[count], resultTsBlock.getColumn(0).getLong(pos));
+        count++;
+      }
     }
     assertEquals(timeColumn.length, count);
   }
@@ -450,12 +469,15 @@ public class SeriesAggregationScanOperatorTest {
     int count = 0;
     while (seriesAggregationScanOperator.hasNext()) {
       TsBlock resultTsBlock = seriesAggregationScanOperator.next();
-      assertEquals(timeColumn[count], resultTsBlock.getTimeColumn().getLong(0));
-      assertEquals(result[0][count], resultTsBlock.getColumn(0).getInt(0));
-      assertEquals(result[1][count], resultTsBlock.getColumn(1).getInt(0));
-      assertEquals(result[2][count], resultTsBlock.getColumn(2).getInt(0));
-      assertEquals(result[3][count], resultTsBlock.getColumn(3).getInt(0));
-      count++;
+      int positionCount = resultTsBlock.getPositionCount();
+      for (int pos = 0; pos < positionCount; pos++) {
+        assertEquals(timeColumn[count], resultTsBlock.getTimeColumn().getLong(pos));
+        assertEquals(result[0][count], resultTsBlock.getColumn(0).getInt(pos));
+        assertEquals(result[1][count], resultTsBlock.getColumn(1).getInt(pos));
+        assertEquals(result[2][count], resultTsBlock.getColumn(2).getInt(pos));
+        assertEquals(result[3][count], resultTsBlock.getColumn(3).getInt(pos));
+        count++;
+      }
     }
     assertEquals(timeColumn.length, count);
   }
@@ -479,6 +501,12 @@ public class SeriesAggregationScanOperatorTest {
     PlanNodeId planNodeId = new PlanNodeId("1");
     fragmentInstanceContext.addOperatorContext(
         1, planNodeId, SeriesAggregationScanOperator.class.getSimpleName());
+    fragmentInstanceContext
+        .getOperatorContexts()
+        .forEach(
+            operatorContext -> {
+              operatorContext.setMaxRunTime(TEST_TIME_SLICE);
+            });
 
     SeriesAggregationScanOperator seriesAggregationScanOperator =
         new SeriesAggregationScanOperator(
diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/SlidingWindowAggregationOperatorTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/SlidingWindowAggregationOperatorTest.java
index b4f849c7b6..81ab99e1ff 100644
--- a/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/SlidingWindowAggregationOperatorTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/SlidingWindowAggregationOperatorTest.java
@@ -27,7 +27,6 @@ import org.apache.iotdb.db.engine.storagegroup.TsFileResource;
 import org.apache.iotdb.db.metadata.path.MeasurementPath;
 import org.apache.iotdb.db.mpp.aggregation.AccumulatorFactory;
 import org.apache.iotdb.db.mpp.aggregation.Aggregator;
-import org.apache.iotdb.db.mpp.aggregation.slidingwindow.SlidingWindowAggregator;
 import org.apache.iotdb.db.mpp.aggregation.slidingwindow.SlidingWindowAggregatorFactory;
 import org.apache.iotdb.db.mpp.common.FragmentInstanceId;
 import org.apache.iotdb.db.mpp.common.PlanFragmentId;
@@ -61,6 +60,7 @@ import java.util.concurrent.ExecutorService;
 import java.util.stream.Collectors;
 
 import static org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceContext.createFragmentInstanceContext;
+import static org.apache.iotdb.db.mpp.execution.operator.AggregationOperatorTest.TEST_TIME_SLICE;
 
 public class SlidingWindowAggregationOperatorTest {
 
@@ -148,8 +148,11 @@ public class SlidingWindowAggregationOperatorTest {
         continue;
       }
       Assert.assertEquals(rootAggregationTypes.size(), resultTsBlock.getValueColumnCount());
-      Assert.assertEquals(retArray[count], getResultString(resultTsBlock));
-      count++;
+      int positionCount = resultTsBlock.getPositionCount();
+      for (int pos = 0; pos < positionCount; pos++) {
+        Assert.assertEquals(retArray[count], getResultString(resultTsBlock, pos));
+        count++;
+      }
     }
     Assert.assertEquals(retArray.length, count);
 
@@ -161,32 +164,35 @@ public class SlidingWindowAggregationOperatorTest {
         continue;
       }
       Assert.assertEquals(rootAggregationTypes.size(), resultTsBlock.getValueColumnCount());
-      Assert.assertEquals(retArray[count - 1], getResultString(resultTsBlock));
-      count--;
+      int positionCount = resultTsBlock.getPositionCount();
+      for (int pos = 0; pos < positionCount; pos++) {
+        Assert.assertEquals(retArray[count - 1], getResultString(resultTsBlock, pos));
+        count--;
+      }
     }
     Assert.assertEquals(0, count);
   }
 
-  private String getResultString(TsBlock resultTsBlock) {
-    return resultTsBlock.getTimeColumn().getLong(0)
+  private String getResultString(TsBlock resultTsBlock, int pos) {
+    return resultTsBlock.getTimeColumn().getLong(pos)
         + ","
-        + resultTsBlock.getColumn(0).getLong(0)
+        + resultTsBlock.getColumn(0).getLong(pos)
         + ","
-        + resultTsBlock.getColumn(1).getDouble(0)
+        + resultTsBlock.getColumn(1).getDouble(pos)
         + ","
-        + resultTsBlock.getColumn(2).getDouble(0)
+        + resultTsBlock.getColumn(2).getDouble(pos)
         + ","
-        + resultTsBlock.getColumn(3).getInt(0)
+        + resultTsBlock.getColumn(3).getInt(pos)
         + ","
-        + resultTsBlock.getColumn(4).getLong(0)
+        + resultTsBlock.getColumn(4).getLong(pos)
         + ","
-        + resultTsBlock.getColumn(5).getLong(0)
+        + resultTsBlock.getColumn(5).getLong(pos)
         + ","
-        + resultTsBlock.getColumn(6).getInt(0)
+        + resultTsBlock.getColumn(6).getInt(pos)
         + ","
-        + resultTsBlock.getColumn(7).getInt(0)
+        + resultTsBlock.getColumn(7).getInt(pos)
         + ","
-        + resultTsBlock.getColumn(8).getInt(0);
+        + resultTsBlock.getColumn(8).getInt(pos);
   }
 
   private SlidingWindowAggregationOperator initSlidingWindowAggregationOperator(boolean ascending)
@@ -204,6 +210,12 @@ public class SlidingWindowAggregationOperatorTest {
         0, sourceId, SeriesAggregationScanOperator.class.getSimpleName());
     fragmentInstanceContext.addOperatorContext(
         1, queryId.genPlanNodeId(), SlidingWindowAggregationOperator.class.getSimpleName());
+    fragmentInstanceContext
+        .getOperatorContexts()
+        .forEach(
+            operatorContext -> {
+              operatorContext.setMaxRunTime(TEST_TIME_SLICE);
+            });
 
     MeasurementPath d0s0 =
         new MeasurementPath(AGGREGATION_OPERATOR_TEST_SG + ".device0.sensor0", TSDataType.INT32);
@@ -226,7 +238,7 @@ public class SlidingWindowAggregationOperatorTest {
     seriesAggregationScanOperator.initQueryDataSource(
         new QueryDataSource(seqResources, unSeqResources));
 
-    List<SlidingWindowAggregator> finalAggregators = new ArrayList<>();
+    List<Aggregator> finalAggregators = new ArrayList<>();
     for (int i = 0; i < rootAggregationTypes.size(); i++) {
       finalAggregators.add(
           SlidingWindowAggregatorFactory.createSlidingWindowAggregator(
diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/UpdateLastCacheOperatorTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/UpdateLastCacheOperatorTest.java
index b78cb7b1ac..581f1e703e 100644
--- a/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/UpdateLastCacheOperatorTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/mpp/execution/operator/UpdateLastCacheOperatorTest.java
@@ -54,6 +54,7 @@ import java.util.Set;
 import java.util.concurrent.ExecutorService;
 
 import static org.apache.iotdb.db.mpp.execution.fragment.FragmentInstanceContext.createFragmentInstanceContext;
+import static org.apache.iotdb.db.mpp.execution.operator.AggregationOperatorTest.TEST_TIME_SLICE;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -195,6 +196,13 @@ public class UpdateLastCacheOperatorTest {
     fragmentInstanceContext.addOperatorContext(
         2, planNodeId2, UpdateLastCacheOperator.class.getSimpleName());
 
+    fragmentInstanceContext
+        .getOperatorContexts()
+        .forEach(
+            operatorContext -> {
+              operatorContext.setMaxRunTime(TEST_TIME_SLICE);
+            });
+
     SeriesAggregationScanOperator seriesAggregationScanOperator =
         new SeriesAggregationScanOperator(
             planNodeId1,
diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/common/conf/TSFileConfig.java b/tsfile/src/main/java/org/apache/iotdb/tsfile/common/conf/TSFileConfig.java
index 5d36d6286b..a157d57058 100644
--- a/tsfile/src/main/java/org/apache/iotdb/tsfile/common/conf/TSFileConfig.java
+++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/common/conf/TSFileConfig.java
@@ -150,6 +150,12 @@ public class TSFileConfig implements Serializable {
   /** The amount of data iterate each time */
   private int batchSize = 1000;
 
+  /** Maximum capacity of a TsBlock */
+  private int maxTsBlockSizeInBytes = 1024 * 1024;
+
+  /** Maximum number of lines in a single TsBlock */
+  private int maxTsBlockLineNumber = 1000;
+
   public TSFileConfig() {}
 
   public int getGroupSizeInByte() {
@@ -431,4 +437,20 @@ public class TSFileConfig implements Serializable {
   public void setFreqEncodingBlockSize(int freqEncodingBlockSize) {
     this.freqEncodingBlockSize = freqEncodingBlockSize;
   }
+
+  public int getMaxTsBlockSizeInBytes() {
+    return maxTsBlockSizeInBytes;
+  }
+
+  public void setMaxTsBlockSizeInBytes(int maxTsBlockSizeInBytes) {
+    this.maxTsBlockSizeInBytes = maxTsBlockSizeInBytes;
+  }
+
+  public int getMaxTsBlockLineNumber() {
+    return maxTsBlockLineNumber;
+  }
+
+  public void setMaxTsBlockLineNumber(int maxTsBlockLineNumber) {
+    this.maxTsBlockLineNumber = maxTsBlockLineNumber;
+  }
 }
diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/file/metadata/statistics/Statistics.java b/tsfile/src/main/java/org/apache/iotdb/tsfile/file/metadata/statistics/Statistics.java
index 012fbe729d..197d9848f8 100644
--- a/tsfile/src/main/java/org/apache/iotdb/tsfile/file/metadata/statistics/Statistics.java
+++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/file/metadata/statistics/Statistics.java
@@ -21,6 +21,7 @@ package org.apache.iotdb.tsfile.file.metadata.statistics;
 import org.apache.iotdb.tsfile.exception.filter.StatisticsClassException;
 import org.apache.iotdb.tsfile.exception.write.UnknownColumnTypeException;
 import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
+import org.apache.iotdb.tsfile.read.filter.basic.Filter;
 import org.apache.iotdb.tsfile.utils.Binary;
 import org.apache.iotdb.tsfile.utils.ReadWriteForEncodingUtils;
 import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils;
@@ -373,6 +374,10 @@ public abstract class Statistics<T extends Serializable> {
 
   public abstract long calculateRamSize();
 
+  public boolean containedByTimeFilter(Filter timeFilter) {
+    return timeFilter == null || timeFilter.containStartEndTime(getStartTime(), getEndTime());
+  }
+
   @Override
   public String toString() {
     return "startTime: " + startTime + " endTime: " + endTime + " count: " + count;
diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/block/TsBlock.java b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/block/TsBlock.java
index 7e4d836bcd..c7206efb57 100644
--- a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/block/TsBlock.java
+++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/block/TsBlock.java
@@ -182,6 +182,10 @@ public class TsBlock {
     return new TsBlock(subTimeColumn, subValueColumns);
   }
 
+  public TsBlock skipFirst() {
+    return this.subTsBlock(1);
+  }
+
   public long getTimeByIndex(int index) {
     return timeColumn.getLong(index);
   }
diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/block/TsBlockBuilder.java b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/block/TsBlockBuilder.java
index b02e897235..b2b47bf43b 100644
--- a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/block/TsBlockBuilder.java
+++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/block/TsBlockBuilder.java
@@ -18,6 +18,7 @@
  */
 package org.apache.iotdb.tsfile.read.common.block;
 
+import org.apache.iotdb.tsfile.common.conf.TSFileDescriptor;
 import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
 import org.apache.iotdb.tsfile.read.common.block.column.BinaryColumnBuilder;
 import org.apache.iotdb.tsfile.read.common.block.column.BooleanColumnBuilder;
@@ -44,6 +45,9 @@ public class TsBlockBuilder {
   // This could be any other small number.
   private static final int DEFAULT_INITIAL_EXPECTED_ENTRIES = 8;
 
+  private static final int MAX_LINE_NUMBER =
+      TSFileDescriptor.getInstance().getConfig().getMaxTsBlockLineNumber();
+
   private TimeColumnBuilder timeColumnBuilder;
   private ColumnBuilder[] valueColumnBuilders;
   private List<TSDataType> types;
@@ -254,7 +258,7 @@ public class TsBlockBuilder {
   }
 
   public boolean isFull() {
-    return declaredPositions == Integer.MAX_VALUE || tsBlockBuilderStatus.isFull();
+    return declaredPositions == MAX_LINE_NUMBER || tsBlockBuilderStatus.isFull();
   }
 
   public boolean isEmpty() {
diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/block/TsBlockBuilderStatus.java b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/block/TsBlockBuilderStatus.java
index a48e9cd0ff..15d585800c 100644
--- a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/block/TsBlockBuilderStatus.java
+++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/block/TsBlockBuilderStatus.java
@@ -18,11 +18,13 @@
  */
 package org.apache.iotdb.tsfile.read.common.block;
 
+import org.apache.iotdb.tsfile.common.conf.TSFileDescriptor;
 import org.apache.iotdb.tsfile.read.common.block.column.ColumnBuilderStatus;
 
 public class TsBlockBuilderStatus {
 
-  public static final int DEFAULT_MAX_TSBLOCK_SIZE_IN_BYTES = 1024 * 1024;
+  public static final int DEFAULT_MAX_TSBLOCK_SIZE_IN_BYTES =
+      TSFileDescriptor.getInstance().getConfig().getMaxTsBlockSizeInBytes();
 
   private final int maxTsBlockSizeInBytes;
 
diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/block/TsBlockUtil.java b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/block/TsBlockUtil.java
new file mode 100644
index 0000000000..1aa79eb4a5
--- /dev/null
+++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/block/TsBlockUtil.java
@@ -0,0 +1,59 @@
+/*
+ * 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.tsfile.read.common.block;
+
+import org.apache.iotdb.tsfile.read.common.TimeRange;
+import org.apache.iotdb.tsfile.read.common.block.column.TimeColumn;
+
+public class TsBlockUtil {
+
+  private TsBlockUtil() {
+    // forbidding instantiation
+  }
+
+  /** Skip lines at the beginning of the tsBlock that are not in the time range. */
+  public static TsBlock skipPointsOutOfTimeRange(
+      TsBlock tsBlock, TimeRange targetTimeRange, boolean ascending) {
+    TimeColumn timeColumn = tsBlock.getTimeColumn();
+    long targetTime = ascending ? targetTimeRange.getMin() : targetTimeRange.getMax();
+    int left = 0, right = timeColumn.getPositionCount() - 1, mid;
+    // if ascending, find the first greater than or equal to targetTime
+    // else, find the first less than or equal to targetTime
+    while (left < right) {
+      mid = (left + right) >> 1;
+      if (timeColumn.getLongWithoutCheck(mid) < targetTime) {
+        if (ascending) {
+          left = mid + 1;
+        } else {
+          right = mid;
+        }
+      } else if (timeColumn.getLongWithoutCheck(mid) > targetTime) {
+        if (ascending) {
+          right = mid;
+        } else {
+          left = mid + 1;
+        }
+      } else if (timeColumn.getLongWithoutCheck(mid) == targetTime) {
+        return tsBlock.subTsBlock(mid);
+      }
+    }
+    return tsBlock.subTsBlock(left);
+  }
+}