You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@doris.apache.org by mo...@apache.org on 2023/10/13 04:56:38 UTC
[doris] branch branch-2.0 updated: Revert "[refactor][backport](stats) Migrate stats framework from master to branch 2.0 (#25119)" (#25403)
This is an automated email from the ASF dual-hosted git repository.
morningman pushed a commit to branch branch-2.0
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-2.0 by this push:
new 470d9ab03cc Revert "[refactor][backport](stats) Migrate stats framework from master to branch 2.0 (#25119)" (#25403)
470d9ab03cc is described below
commit 470d9ab03cce593d9a6a5096fa541abf55cff6db
Author: AKIRA <33...@users.noreply.github.com>
AuthorDate: Fri Oct 13 12:56:28 2023 +0800
Revert "[refactor][backport](stats) Migrate stats framework from master to branch 2.0 (#25119)" (#25403)
This reverts commit 4b6ff32ef079a1332aae7f62695e530ec3bc977d.
Co-authored-by: AKIHA <cy...@example.com>
---
.../main/java/org/apache/doris/common/Config.java | 7 -
fe/fe-core/src/main/cup/sql_parser.cup | 12 +-
.../apache/doris/analysis/AnalyzeProperties.java | 54 +-
.../org/apache/doris/analysis/AnalyzeStmt.java | 13 +-
.../org/apache/doris/analysis/AnalyzeTblStmt.java | 54 +-
.../org/apache/doris/analysis/PartitionNames.java | 38 +-
.../org/apache/doris/analysis/ShowAnalyzeStmt.java | 84 ++-
.../apache/doris/analysis/ShowTableStatsStmt.java | 39 +-
.../main/java/org/apache/doris/catalog/Env.java | 14 +-
.../doris/catalog/InternalSchemaInitializer.java | 40 +-
.../java/org/apache/doris/catalog/OlapTable.java | 53 --
.../main/java/org/apache/doris/catalog/Table.java | 11 -
.../java/org/apache/doris/catalog/TableIf.java | 13 -
.../doris/catalog/external/ExternalTable.java | 21 -
.../doris/catalog/external/HMSExternalTable.java | 59 +-
.../doris/catalog/external/JdbcExternalTable.java | 18 +-
.../org/apache/doris/common/ThreadPoolManager.java | 9 -
.../org/apache/doris/datasource/CatalogIf.java | 3 -
.../org/apache/doris/datasource/CatalogMgr.java | 6 -
.../apache/doris/datasource/ExternalCatalog.java | 16 -
.../apache/doris/datasource/InternalCatalog.java | 5 -
.../org/apache/doris/journal/JournalEntity.java | 17 -
.../org/apache/doris/nereids/cost/CostModelV1.java | 63 +-
.../java/org/apache/doris/nereids/cost/CostV1.java | 30 +-
.../processor/post/RuntimeFilterPruner.java | 21 +-
.../doris/nereids/stats/ExpressionEstimation.java | 54 +-
.../doris/nereids/stats/FilterEstimation.java | 379 ++++------
.../apache/doris/nereids/stats/JoinEstimation.java | 63 +-
.../doris/nereids/stats/StatsCalculator.java | 44 +-
.../nereids/trees/expressions/Properties.java | 96 ---
.../trees/expressions/functions/table/Hdfs.java | 8 +-
.../trees/expressions/functions/table/Local.java | 6 +-
.../trees/expressions/functions/table/Numbers.java | 10 +-
.../trees/expressions/functions/table/S3.java | 8 +-
.../functions/table/TableValuedFunction.java | 8 +-
.../expressions/visitor/ExpressionVisitor.java | 5 -
.../org/apache/doris/nereids/types/MapType.java | 2 -
.../doris/nereids/types/coercion/AnyDataType.java | 18 +-
.../java/org/apache/doris/persist/EditLog.java | 19 +-
.../doris/persist/TableStatsDeletionLog.java | 47 --
.../java/org/apache/doris/qe/AuditLogHelper.java | 120 ----
.../main/java/org/apache/doris/qe/DdlExecutor.java | 3 +
.../doris/qe/InternalQueryExecutionException.java | 24 -
.../java/org/apache/doris/qe/SessionVariable.java | 20 -
.../java/org/apache/doris/qe/ShowExecutor.java | 37 +-
.../java/org/apache/doris/qe/StmtExecutor.java | 35 +-
.../org/apache/doris/statistics/AnalysisInfo.java | 116 +--
.../doris/statistics/AnalysisInfoBuilder.java | 39 +-
.../apache/doris/statistics/AnalysisManager.java | 789 +++++++++------------
.../org/apache/doris/statistics/AnalysisState.java | 2 -
.../doris/statistics/AnalysisTaskExecutor.java | 53 +-
.../doris/statistics/AnalysisTaskScheduler.java | 108 +++
.../doris/statistics/AnalysisTaskWrapper.java | 12 -
.../apache/doris/statistics/BaseAnalysisTask.java | 61 +-
.../org/apache/doris/statistics/ColStatsData.java | 82 ---
.../org/apache/doris/statistics/ColStatsMeta.java | 58 --
.../apache/doris/statistics/ColumnStatistic.java | 144 ++--
.../doris/statistics/ColumnStatisticBuilder.java | 47 +-
.../statistics/ColumnStatisticsCacheLoader.java | 62 +-
.../apache/doris/statistics/HMSAnalysisTask.java | 254 +++----
.../java/org/apache/doris/statistics/HistData.java | 36 -
.../org/apache/doris/statistics/Histogram.java | 18 +-
.../doris/statistics/HistogramCacheLoader.java | 1 +
.../apache/doris/statistics/JdbcAnalysisTask.java | 9 +-
.../apache/doris/statistics/OlapAnalysisJob.java | 56 --
.../apache/doris/statistics/OlapAnalysisTask.java | 116 +--
.../org/apache/doris/statistics/ResultRow.java | 59 --
.../doris/statistics/StatisticConstants.java | 54 +-
.../apache/doris/statistics/StatisticRange.java | 96 +--
.../apache/doris/statistics/StatisticalType.java | 2 -
.../org/apache/doris/statistics/Statistics.java | 133 +++-
.../doris/statistics/StatisticsAutoAnalyzer.java | 215 ++++++
.../doris/statistics/StatisticsAutoCollector.java | 196 -----
.../apache/doris/statistics/StatisticsBuilder.java | 2 +-
.../apache/doris/statistics/StatisticsCache.java | 158 ++---
.../apache/doris/statistics/StatisticsCleaner.java | 21 +-
.../doris/statistics/StatisticsCollector.java | 86 ---
.../statistics/StatisticsPeriodCollector.java | 50 --
.../doris/statistics/StatisticsRepository.java | 144 +++-
.../apache/doris/statistics/StatsDeriveResult.java | 7 +
.../java/org/apache/doris/statistics/StatsId.java | 58 --
.../apache/doris/statistics/TableStatistic.java | 61 ++
.../doris/statistics/TableStatisticBuilder.java | 51 ++
.../statistics/TableStatisticsCacheLoader.java | 60 ++
.../apache/doris/statistics/TableStatsMeta.java | 137 ----
.../apache/doris/statistics/TaskStatusWrapper.java | 33 -
.../doris/statistics/util/InternalQuery.java | 21 +-
.../doris/statistics/util/InternalQueryResult.java | 242 +++++++
.../apache/doris/statistics/util/SimpleQueue.java | 65 --
.../doris/statistics/util/StatisticsUtil.java | 163 +----
.../jobs/joinorder/hypergraph/OtherJoinTest.java | 31 +-
.../doris/nereids/stats/FilterEstimationTest.java | 85 ++-
.../doris/nereids/util/HyperGraphBuilder.java | 51 +-
.../apache/doris/statistics/AnalysisJobTest.java | 33 +-
.../doris/statistics/AnalysisTaskExecutorTest.java | 37 +-
.../org/apache/doris/statistics/CacheTest.java | 192 ++---
.../apache/doris/statistics/HistogramTaskTest.java | 14 +-
.../doris/statistics/StatsDeriveResultTest.java | 5 +-
.../org/apache/doris/statistics/StatsMockUtil.java | 48 --
.../statistics/util/InternalQueryResultTest.java | 119 ++++
gensrc/thrift/FrontendService.thrift | 2 +-
.../suites/statistics/analyze_stats.groovy | 84 +--
102 files changed, 2724 insertions(+), 3860 deletions(-)
diff --git a/fe/fe-common/src/main/java/org/apache/doris/common/Config.java b/fe/fe-common/src/main/java/org/apache/doris/common/Config.java
index 72ecebca781..dae88cf40f4 100644
--- a/fe/fe-common/src/main/java/org/apache/doris/common/Config.java
+++ b/fe/fe-common/src/main/java/org/apache/doris/common/Config.java
@@ -2178,13 +2178,6 @@ public class Config extends ConfigBase {
})
public static int autobucket_min_buckets = 1;
- @ConfField
- public static int full_auto_analyze_simultaneously_running_task_num = 1;
-
- @ConfField
- public static final int period_analyze_simultaneously_running_task_num = 1;
-
-
@ConfField(mutable = true, description = {
"Doris 为了兼用 mysql 周边工具生态,会内置一个名为 mysql 的数据库,如果该数据库与用户自建数据库冲突,"
+ "请修改这个字段,为 doris 内置的 mysql database 更换一个名字",
diff --git a/fe/fe-core/src/main/cup/sql_parser.cup b/fe/fe-core/src/main/cup/sql_parser.cup
index 7f3aa2020e8..5950548c427 100644
--- a/fe/fe-core/src/main/cup/sql_parser.cup
+++ b/fe/fe-core/src/main/cup/sql_parser.cup
@@ -4134,17 +4134,13 @@ show_param ::=
RESULT = new ShowCreateMaterializedViewStmt(mvName, tableName);
:}
/* show analyze job */
- | KW_ANALYZE opt_table_name:tbl opt_wild_where
+ | KW_ANALYZE opt_table_name:tbl opt_wild_where order_by_clause:orderByClause limit_clause:limitClause
{:
- RESULT = new ShowAnalyzeStmt(tbl, parser.where, false);
+ RESULT = new ShowAnalyzeStmt(tbl, parser.where, orderByClause, limitClause);
:}
- | KW_ANALYZE INTEGER_LITERAL:jobId opt_wild_where
+ | KW_ANALYZE INTEGER_LITERAL:jobId opt_wild_where order_by_clause:orderByClause limit_clause:limitClause
{:
- RESULT = new ShowAnalyzeStmt(jobId, parser.where);
- :}
- | KW_AUTO KW_ANALYZE opt_table_name:tbl opt_wild_where
- {:
- RESULT = new ShowAnalyzeStmt(tbl, parser.where, true);
+ RESULT = new ShowAnalyzeStmt(jobId, parser.where, orderByClause, limitClause);
:}
| KW_ANALYZE KW_TASK KW_STATUS INTEGER_LITERAL:jobId
{:
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/AnalyzeProperties.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/AnalyzeProperties.java
index d7e639da3a5..ccb122bc269 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/AnalyzeProperties.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/AnalyzeProperties.java
@@ -22,18 +22,16 @@ import org.apache.doris.common.util.PrintableMap;
import org.apache.doris.statistics.AnalysisInfo.AnalysisType;
import com.google.common.collect.ImmutableSet;
-import com.google.gson.annotations.SerializedName;
import org.apache.commons.lang3.StringUtils;
-import org.apache.logging.log4j.core.util.CronExpression;
-import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
-// TODO: Remove map
public class AnalyzeProperties {
+ private final Map<String, String> properties;
+
public static final String PROPERTY_SYNC = "sync";
public static final String PROPERTY_INCREMENTAL = "incremental";
public static final String PROPERTY_AUTOMATIC = "automatic";
@@ -43,23 +41,6 @@ public class AnalyzeProperties {
public static final String PROPERTY_ANALYSIS_TYPE = "analysis.type";
public static final String PROPERTY_PERIOD_SECONDS = "period.seconds";
- public static final String PROPERTY_FORCE_FULL = "force.full";
-
- public static final AnalyzeProperties DEFAULT_PROP = new AnalyzeProperties(new HashMap<String, String>() {
- {
- put(AnalyzeProperties.PROPERTY_SYNC, "false");
- put(AnalyzeProperties.PROPERTY_AUTOMATIC, "false");
- put(AnalyzeProperties.PROPERTY_ANALYSIS_TYPE, AnalysisType.FUNDAMENTALS.toString());
- }
- });
-
- public static final String PROPERTY_PERIOD_CRON = "period.cron";
-
- private CronExpression cronExpression;
-
- @SerializedName("analyzeProperties")
- private final Map<String, String> properties;
-
private static final ImmutableSet<String> PROPERTIES_SET = new ImmutableSet.Builder<String>()
.add(PROPERTY_SYNC)
.add(PROPERTY_INCREMENTAL)
@@ -69,8 +50,6 @@ public class AnalyzeProperties {
.add(PROPERTY_NUM_BUCKETS)
.add(PROPERTY_ANALYSIS_TYPE)
.add(PROPERTY_PERIOD_SECONDS)
- .add(PROPERTY_PERIOD_CRON)
- .add(PROPERTY_FORCE_FULL)
.build();
public AnalyzeProperties(Map<String, String> properties) {
@@ -93,7 +72,6 @@ public class AnalyzeProperties {
checkAnalysisMode(msgTemplate);
checkAnalysisType(msgTemplate);
checkScheduleType(msgTemplate);
- checkPeriod();
}
public boolean isSync() {
@@ -137,10 +115,6 @@ public class AnalyzeProperties {
return TimeUnit.SECONDS.toMillis(minutes);
}
- public CronExpression getCron() {
- return cronExpression;
- }
-
private void checkPeriodSeconds() throws AnalysisException {
if (properties.containsKey(PROPERTY_PERIOD_SECONDS)) {
checkNumericProperty(PROPERTY_PERIOD_SECONDS, properties.get(PROPERTY_PERIOD_SECONDS),
@@ -233,22 +207,6 @@ public class AnalyzeProperties {
}
}
- private void checkPeriod() throws AnalysisException {
- if (properties.containsKey(PROPERTY_PERIOD_SECONDS)
- && properties.containsKey(PROPERTY_PERIOD_CRON)) {
- throw new AnalysisException(PROPERTY_PERIOD_SECONDS + " and " + PROPERTY_PERIOD_CRON
- + " couldn't be set simultaneously");
- }
- String cronExprStr = properties.get(PROPERTY_PERIOD_CRON);
- if (cronExprStr != null) {
- try {
- cronExpression = new CronExpression(cronExprStr);
- } catch (java.text.ParseException e) {
- throw new AnalysisException("Invalid cron expression: " + cronExprStr);
- }
- }
- }
-
private void checkNumericProperty(String key, String value, int lowerBound, int upperBound,
boolean includeBoundary, String errorMsg) throws AnalysisException {
if (!StringUtils.isNumeric(value)) {
@@ -268,14 +226,6 @@ public class AnalyzeProperties {
|| properties.containsKey(PROPERTY_SAMPLE_ROWS);
}
- public boolean forceFull() {
- return properties.containsKey(PROPERTY_FORCE_FULL);
- }
-
- public boolean isSampleRows() {
- return properties.containsKey(PROPERTY_SAMPLE_ROWS);
- }
-
public String toSQL() {
StringBuilder sb = new StringBuilder();
sb.append("PROPERTIES(");
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/AnalyzeStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/AnalyzeStmt.java
index ae2c6a7ff48..6f1f7c64d8f 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/AnalyzeStmt.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/AnalyzeStmt.java
@@ -23,8 +23,6 @@ import org.apache.doris.statistics.AnalysisInfo.AnalysisMode;
import org.apache.doris.statistics.AnalysisInfo.AnalysisType;
import org.apache.doris.statistics.AnalysisInfo.ScheduleType;
-import org.apache.logging.log4j.core.util.CronExpression;
-
import java.util.Map;
public class AnalyzeStmt extends StatementBase {
@@ -57,8 +55,7 @@ public class AnalyzeStmt extends StatementBase {
if (analyzeProperties.isAutomatic()) {
return ScheduleType.AUTOMATIC;
}
- return analyzeProperties.getPeriodTimeInMs() > 0 || analyzeProperties.getCron() != null
- ? ScheduleType.PERIOD : ScheduleType.ONCE;
+ return analyzeProperties.getPeriodTimeInMs() > 0 ? ScheduleType.PERIOD : ScheduleType.ONCE;
}
public boolean isSync() {
@@ -89,12 +86,4 @@ public class AnalyzeStmt extends StatementBase {
public RedirectStatus getRedirectStatus() {
return RedirectStatus.FORWARD_WITH_SYNC;
}
-
- public CronExpression getCron() {
- return analyzeProperties.getCron();
- }
-
- public boolean forceFull() {
- return analyzeProperties.forceFull();
- }
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/AnalyzeTblStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/AnalyzeTblStmt.java
index cbc66f367f2..920b60627a8 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/AnalyzeTblStmt.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/AnalyzeTblStmt.java
@@ -24,7 +24,6 @@ import org.apache.doris.catalog.Env;
import org.apache.doris.catalog.OlapTable;
import org.apache.doris.catalog.TableIf;
import org.apache.doris.catalog.View;
-import org.apache.doris.catalog.external.ExternalTable;
import org.apache.doris.catalog.external.HMSExternalTable;
import org.apache.doris.common.AnalysisException;
import org.apache.doris.common.Config;
@@ -42,7 +41,6 @@ import org.apache.doris.statistics.util.StatisticsUtil;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.StringUtils;
-import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
@@ -86,7 +84,7 @@ public class AnalyzeTblStmt extends AnalyzeStmt {
private final TableName tableName;
private List<String> columnNames;
- private PartitionNames partitionNames;
+ private List<String> partitionNames;
private boolean isAllColumns;
// after analyzed
@@ -99,7 +97,7 @@ public class AnalyzeTblStmt extends AnalyzeStmt {
AnalyzeProperties properties) {
super(properties);
this.tableName = tableName;
- this.partitionNames = partitionNames;
+ this.partitionNames = partitionNames == null ? null : partitionNames.getPartitionNames();
this.columnNames = columnNames;
this.analyzeProperties = properties;
this.isAllColumns = columnNames == null;
@@ -168,9 +166,11 @@ public class AnalyzeTblStmt extends AnalyzeStmt {
analyzeProperties.check();
// TODO support external table
- if (analyzeProperties.isSampleRows() && !(table instanceof OlapTable)) {
- throw new AnalysisException("Sampling statistics "
- + "collection of external tables is not supported with rows, use percent instead.");
+ if (analyzeProperties.isSample()) {
+ if (!(table instanceof OlapTable)) {
+ throw new AnalysisException("Sampling statistics "
+ + "collection of external tables is not supported");
+ }
}
if (analyzeProperties.isSync()
&& (analyzeProperties.isAutomatic() || analyzeProperties.getPeriodTimeInMs() != 0)) {
@@ -181,9 +181,6 @@ public class AnalyzeTblStmt extends AnalyzeStmt {
throw new AnalysisException("Automatic collection "
+ "and period statistics collection cannot be set at same time");
}
- if (analyzeProperties.isSample() && analyzeProperties.forceFull()) {
- throw new AnalysisException("Impossible to analyze with sample and full simultaneously");
- }
}
private void checkColumn() throws AnalysisException {
@@ -199,8 +196,7 @@ public class AnalyzeTblStmt extends AnalyzeStmt {
}
}
if (containsUnsupportedTytpe) {
- if (ConnectContext.get() == null
- || !ConnectContext.get().getSessionVariable().enableAnalyzeComplexTypeColumn) {
+ if (!ConnectContext.get().getSessionVariable().enableAnalyzeComplexTypeColumn) {
columnNames = columnNames.stream()
.filter(c -> !StatisticsUtil.isUnsupportedType(table.getColumn(c).getType()))
.collect(Collectors.toList());
@@ -240,33 +236,14 @@ public class AnalyzeTblStmt extends AnalyzeStmt {
}
public Set<String> getPartitionNames() {
- if (partitionNames == null || partitionNames.getPartitionNames() == null) {
- if (table instanceof ExternalTable) {
- // External table couldn't return all partitions when partitionNames is not set.
- // Because Analyze Table command for external table could specify partition names.
- return Collections.emptySet();
- }
- return table.getPartitionNames();
+ Set<String> partitions = partitionNames == null ? table.getPartitionNames() : Sets.newHashSet(partitionNames);
+ if (isSamplingPartition()) {
+ int partNum = ConnectContext.get().getSessionVariable().getExternalTableAnalyzePartNum();
+ partitions = partitions.stream().limit(partNum).collect(Collectors.toSet());
}
- Set<String> partitions = Sets.newHashSet();
- partitions.addAll(partitionNames.getPartitionNames());
return partitions;
}
- public boolean isAllPartitions() {
- if (partitionNames == null) {
- return false;
- }
- return partitionNames.isAllPartitions();
- }
-
- public long getPartitionCount() {
- if (partitionNames == null) {
- return 0;
- }
- return partitionNames.getCount();
- }
-
public boolean isPartitionOnly() {
return partitionNames != null;
}
@@ -283,13 +260,8 @@ public class AnalyzeTblStmt extends AnalyzeStmt {
}
private void checkAnalyzePriv(String dbName, String tblName) throws AnalysisException {
- ConnectContext ctx = ConnectContext.get();
- // means it a system analyze
- if (ctx == null) {
- return;
- }
if (!Env.getCurrentEnv().getAccessManager()
- .checkTblPriv(ctx, dbName, tblName, PrivPredicate.SELECT)) {
+ .checkTblPriv(ConnectContext.get(), dbName, tblName, PrivPredicate.SELECT)) {
ErrorReport.reportAnalysisException(
ErrorCode.ERR_TABLEACCESS_DENIED_ERROR,
"ANALYZE",
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/PartitionNames.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/PartitionNames.java
index ca26a2978e0..1140dfc6777 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/PartitionNames.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/PartitionNames.java
@@ -48,37 +48,15 @@ public class PartitionNames implements ParseNode, Writable {
// true if these partitions are temp partitions
@SerializedName(value = "isTemp")
private final boolean isTemp;
- private final boolean allPartitions;
- private final long count;
- // Default partition count to collect statistic for external table.
- private static final long DEFAULT_PARTITION_COUNT = 100;
public PartitionNames(boolean isTemp, List<String> partitionNames) {
this.partitionNames = partitionNames;
this.isTemp = isTemp;
- this.allPartitions = false;
- this.count = 0;
}
public PartitionNames(PartitionNames other) {
this.partitionNames = Lists.newArrayList(other.partitionNames);
this.isTemp = other.isTemp;
- this.allPartitions = other.allPartitions;
- this.count = 0;
- }
-
- public PartitionNames(boolean allPartitions) {
- this.partitionNames = null;
- this.isTemp = false;
- this.allPartitions = allPartitions;
- this.count = 0;
- }
-
- public PartitionNames(long partitionCount) {
- this.partitionNames = null;
- this.isTemp = false;
- this.allPartitions = false;
- this.count = partitionCount;
}
public List<String> getPartitionNames() {
@@ -89,23 +67,9 @@ public class PartitionNames implements ParseNode, Writable {
return isTemp;
}
- public boolean isAllPartitions() {
- return allPartitions;
- }
-
- public long getCount() {
- return count;
- }
-
@Override
public void analyze(Analyzer analyzer) throws AnalysisException {
- if (allPartitions && count > 0) {
- throw new AnalysisException("All partition and partition count couldn't be set at the same time.");
- }
- if (allPartitions || count > 0) {
- return;
- }
- if (partitionNames == null || partitionNames.isEmpty()) {
+ if (partitionNames.isEmpty()) {
throw new AnalysisException("No partition specified in partition lists");
}
// check if partition name is not empty string
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowAnalyzeStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowAnalyzeStmt.java
index fb19cb2fd5b..95035641a7e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowAnalyzeStmt.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowAnalyzeStmt.java
@@ -25,6 +25,7 @@ import org.apache.doris.common.Config;
import org.apache.doris.common.ErrorCode;
import org.apache.doris.common.ErrorReport;
import org.apache.doris.common.UserException;
+import org.apache.doris.common.util.OrderByPair;
import org.apache.doris.mysql.privilege.PrivPredicate;
import org.apache.doris.qe.ConnectContext;
import org.apache.doris.qe.ShowResultSetMetaData;
@@ -34,6 +35,10 @@ import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.IntStream;
+
/**
* ShowAnalyzeStmt is used to show statistics job info.
* syntax:
@@ -64,30 +69,36 @@ public class ShowAnalyzeStmt extends ShowStmt {
.build();
private long jobId;
- private final TableName dbTableName;
- private final Expr whereClause;
-
- // extract from predicate
+ private TableName dbTableName;
+ private Expr whereClause;
+ private LimitElement limitElement;
+ private List<OrderByElement> orderByElements;
private String stateValue;
+ private ArrayList<OrderByPair> orderByPairs;
- private final boolean auto;
-
+ public ShowAnalyzeStmt() {
+ }
public ShowAnalyzeStmt(TableName dbTableName,
- Expr whereClause, boolean auto) {
+ Expr whereClause,
+ List<OrderByElement> orderByElements,
+ LimitElement limitElement) {
this.dbTableName = dbTableName;
this.whereClause = whereClause;
- this.auto = auto;
-
+ this.orderByElements = orderByElements;
+ this.limitElement = limitElement;
}
public ShowAnalyzeStmt(long jobId,
- Expr whereClause) {
+ Expr whereClause,
+ List<OrderByElement> orderByElements,
+ LimitElement limitElement) {
Preconditions.checkArgument(jobId > 0, "JobId must greater than 0.");
this.jobId = jobId;
this.dbTableName = null;
this.whereClause = whereClause;
- this.auto = false;
+ this.orderByElements = orderByElements;
+ this.limitElement = limitElement;
}
public long getJobId() {
@@ -100,6 +111,12 @@ public class ShowAnalyzeStmt extends ShowStmt {
return stateValue;
}
+ public ArrayList<OrderByPair> getOrderByPairs() {
+ Preconditions.checkArgument(isAnalyzed(),
+ "The orderByPairs must be obtained after the parsing is complete");
+ return orderByPairs;
+ }
+
public Expr getWhereClause() {
Preconditions.checkArgument(isAnalyzed(),
"The whereClause must be obtained after the parsing is complete");
@@ -107,6 +124,13 @@ public class ShowAnalyzeStmt extends ShowStmt {
return whereClause;
}
+ public long getLimit() {
+ if (limitElement != null && limitElement.hasLimit()) {
+ return limitElement.getLimit();
+ }
+ return -1L;
+ }
+
@Override
public void analyze(Analyzer analyzer) throws UserException {
if (!Config.enable_stats) {
@@ -125,6 +149,21 @@ public class ShowAnalyzeStmt extends ShowStmt {
if (whereClause != null) {
analyzeSubPredicate(whereClause);
}
+
+ // analyze order by
+ if (orderByElements != null && !orderByElements.isEmpty()) {
+ orderByPairs = new ArrayList<>();
+ for (OrderByElement orderByElement : orderByElements) {
+ if (orderByElement.getExpr() instanceof SlotRef) {
+ SlotRef slotRef = (SlotRef) orderByElement.getExpr();
+ int index = analyzeColumn(slotRef.getColumnName());
+ OrderByPair orderByPair = new OrderByPair(index, !orderByElement.getIsAsc());
+ orderByPairs.add(orderByPair);
+ } else {
+ throw new AnalysisException("Should order by column");
+ }
+ }
+ }
}
@Override
@@ -240,6 +279,25 @@ public class ShowAnalyzeStmt extends ShowStmt {
sb.append(whereClause.toSql());
}
+ // Order By clause
+ if (orderByElements != null) {
+ sb.append(" ");
+ sb.append("ORDER BY");
+ sb.append(" ");
+ IntStream.range(0, orderByElements.size()).forEach(i -> {
+ sb.append(orderByElements.get(i).getExpr().toSql());
+ sb.append((orderByElements.get(i).getIsAsc()) ? " ASC" : " DESC");
+ sb.append((i + 1 != orderByElements.size()) ? ", " : "");
+ });
+ }
+
+ if (getLimit() != -1L) {
+ sb.append(" ");
+ sb.append("LIMIT");
+ sb.append(" ");
+ sb.append(getLimit());
+ }
+
return sb.toString();
}
@@ -251,8 +309,4 @@ public class ShowAnalyzeStmt extends ShowStmt {
public TableName getDbTableName() {
return dbTableName;
}
-
- public boolean isAuto() {
- return auto;
- }
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowTableStatsStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowTableStatsStmt.java
index fe499fa1b08..da10d5c492b 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowTableStatsStmt.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowTableStatsStmt.java
@@ -32,13 +32,12 @@ import org.apache.doris.mysql.privilege.PrivPredicate;
import org.apache.doris.qe.ConnectContext;
import org.apache.doris.qe.ShowResultSet;
import org.apache.doris.qe.ShowResultSetMetaData;
-import org.apache.doris.statistics.TableStatsMeta;
+import org.apache.doris.statistics.TableStatistic;
+import org.apache.doris.statistics.util.StatisticsUtil;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
-import java.sql.Date;
-import java.util.ArrayList;
import java.util.List;
public class ShowTableStatsStmt extends ShowStmt {
@@ -46,12 +45,9 @@ public class ShowTableStatsStmt extends ShowStmt {
// TODO add more columns
private static final ImmutableList<String> TITLE_NAMES =
new ImmutableList.Builder<String>()
- .add("updated_rows")
- .add("query_times")
.add("row_count")
- .add("updated_time")
- .add("columns")
- .add("trigger")
+ .add("update_time")
+ .add("last_analyze_time")
.build();
private final TableName tableName;
@@ -130,33 +126,12 @@ public class ShowTableStatsStmt extends ShowStmt {
return table.getPartition(partitionName).getId();
}
- public ShowResultSet constructResultSet(TableStatsMeta tableStatistic) {
- if (tableStatistic == null) {
- return new ShowResultSet(getMetaData(), new ArrayList<>());
- }
+ public ShowResultSet constructResultSet(TableStatistic tableStatistic) {
List<List<String>> result = Lists.newArrayList();
List<String> row = Lists.newArrayList();
- row.add(String.valueOf(tableStatistic.updatedRows));
- row.add(String.valueOf(tableStatistic.queriedTimes.get()));
row.add(String.valueOf(tableStatistic.rowCount));
- row.add(new Date(tableStatistic.updatedTime).toString());
- row.add(tableStatistic.analyzeColumns().toString());
- row.add(tableStatistic.jobType.toString());
- result.add(row);
- return new ShowResultSet(getMetaData(), result);
- }
-
- public ShowResultSet constructResultSet(long rowCount) {
- List<List<String>> result = Lists.newArrayList();
- List<String> row = Lists.newArrayList();
- row.add("");
- row.add("");
- row.add(String.valueOf(rowCount));
- row.add("");
- row.add("");
- row.add("");
- row.add("");
- row.add("");
+ row.add(String.valueOf(tableStatistic.updateTime));
+ row.add(StatisticsUtil.getReadableTime(tableStatistic.lastAnalyzeTimeInMs));
result.add(row);
return new ShowResultSet(getMetaData(), result);
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java
index 89b63455590..ea4410f60c6 100755
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java
@@ -211,7 +211,7 @@ import org.apache.doris.resource.Tag;
import org.apache.doris.resource.workloadgroup.WorkloadGroupMgr;
import org.apache.doris.service.FrontendOptions;
import org.apache.doris.statistics.AnalysisManager;
-import org.apache.doris.statistics.StatisticsAutoCollector;
+import org.apache.doris.statistics.StatisticsAutoAnalyzer;
import org.apache.doris.statistics.StatisticsCache;
import org.apache.doris.statistics.StatisticsCleaner;
import org.apache.doris.statistics.query.QueryStats;
@@ -457,7 +457,7 @@ public class Env {
*/
private final LoadManagerAdapter loadManagerAdapter;
- private StatisticsAutoCollector statisticsAutoCollector;
+ private StatisticsAutoAnalyzer statisticsAutoAnalyzer;
private HiveTransactionMgr hiveTransactionMgr;
@@ -663,7 +663,7 @@ public class Env {
this.extMetaCacheMgr = new ExternalMetaCacheMgr();
this.analysisManager = new AnalysisManager();
this.statisticsCleaner = new StatisticsCleaner();
- this.statisticsAutoCollector = new StatisticsAutoCollector();
+ this.statisticsAutoAnalyzer = new StatisticsAutoAnalyzer();
this.globalFunctionMgr = new GlobalFunctionMgr();
this.workloadGroupMgr = new WorkloadGroupMgr();
this.queryStats = new QueryStats();
@@ -907,8 +907,8 @@ public class Env {
if (statisticsCleaner != null) {
statisticsCleaner.start();
}
- if (statisticsAutoCollector != null) {
- statisticsAutoCollector.start();
+ if (statisticsAutoAnalyzer != null) {
+ statisticsAutoAnalyzer.start();
}
}
@@ -5421,8 +5421,8 @@ public class Env {
return loadManagerAdapter;
}
- public StatisticsAutoCollector getStatisticsAutoCollector() {
- return statisticsAutoCollector;
+ public StatisticsAutoAnalyzer getStatisticsAutoAnalyzer() {
+ return statisticsAutoAnalyzer;
}
public QueryStats getQueryStats() {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/InternalSchemaInitializer.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/InternalSchemaInitializer.java
index b7fdec73f02..fd42de00f78 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/InternalSchemaInitializer.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/InternalSchemaInitializer.java
@@ -83,6 +83,7 @@ public class InternalSchemaInitializer extends Thread {
return;
}
Database database = op.get();
+ modifyTblReplicaCount(database, StatisticConstants.ANALYSIS_TBL_NAME);
modifyTblReplicaCount(database, StatisticConstants.STATISTIC_TBL_NAME);
modifyTblReplicaCount(database, StatisticConstants.HISTOGRAM_TBL_NAME);
}
@@ -125,6 +126,7 @@ public class InternalSchemaInitializer extends Thread {
}
private void createTbl() throws UserException {
+ Env.getCurrentEnv().getInternalCatalog().createTable(buildAnalysisTblStmt());
Env.getCurrentEnv().getInternalCatalog().createTable(buildStatisticsTblStmt());
Env.getCurrentEnv().getInternalCatalog().createTable(buildHistogramTblStmt());
}
@@ -143,6 +145,41 @@ public class InternalSchemaInitializer extends Thread {
}
}
+ @VisibleForTesting
+ public CreateTableStmt buildAnalysisTblStmt() throws UserException {
+ TableName tableName = new TableName("",
+ FeConstants.INTERNAL_DB_NAME, StatisticConstants.ANALYSIS_TBL_NAME);
+ List<ColumnDef> columnDefs = new ArrayList<>();
+ columnDefs.add(new ColumnDef("id", TypeDef.createVarchar(StatisticConstants.ID_LEN)));
+ columnDefs.add(new ColumnDef("catalog_id", TypeDef.createVarchar(StatisticConstants.MAX_NAME_LEN)));
+ columnDefs.add(new ColumnDef("db_id", TypeDef.createVarchar(StatisticConstants.MAX_NAME_LEN)));
+ columnDefs.add(new ColumnDef("tbl_id", TypeDef.createVarchar(StatisticConstants.MAX_NAME_LEN)));
+ columnDefs.add(new ColumnDef("idx_id", TypeDef.createVarchar(StatisticConstants.MAX_NAME_LEN)));
+ ColumnDef partId = new ColumnDef("part_id", TypeDef.createVarchar(StatisticConstants.MAX_NAME_LEN));
+ partId.setAllowNull(true);
+ columnDefs.add(partId);
+ columnDefs.add(new ColumnDef("count", TypeDef.create(PrimitiveType.BIGINT)));
+ columnDefs.add(new ColumnDef("last_analyze_time_in_ms", TypeDef.create(PrimitiveType.BIGINT)));
+ columnDefs.add(new ColumnDef("update_time", TypeDef.create(PrimitiveType.DATETIME)));
+ String engineName = "olap";
+ ArrayList<String> uniqueKeys = Lists.newArrayList("id", "catalog_id",
+ "db_id", "tbl_id", "idx_id", "part_id");
+ KeysDesc keysDesc = new KeysDesc(KeysType.UNIQUE_KEYS, uniqueKeys);
+ DistributionDesc distributionDesc = new HashDistributionDesc(
+ StatisticConstants.STATISTIC_TABLE_BUCKET_COUNT, uniqueKeys);
+ Map<String, String> properties = new HashMap<String, String>() {
+ {
+ put("replication_num", String.valueOf(
+ Math.max(1, Config.min_replication_num_per_tablet)));
+ }
+ };
+ CreateTableStmt createTableStmt = new CreateTableStmt(true, false,
+ tableName, columnDefs, engineName, keysDesc, null, distributionDesc,
+ properties, null, "Doris internal statistics table, DO NOT MODIFY IT", null);
+ StatisticsUtil.analyze(createTableStmt);
+ return createTableStmt;
+ }
+
@VisibleForTesting
public CreateTableStmt buildStatisticsTblStmt() throws UserException {
TableName tableName = new TableName("",
@@ -244,7 +281,8 @@ public class InternalSchemaInitializer extends Thread {
}
return false;
}
- return db.getTable(StatisticConstants.HISTOGRAM_TBL_NAME).isPresent();
+ return db.getTable(StatisticConstants.HISTOGRAM_TBL_NAME).isPresent()
+ && db.getTable(StatisticConstants.ANALYSIS_TBL_NAME).isPresent();
}
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java
index f59df2554d3..9975ba0230a 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java
@@ -55,8 +55,6 @@ import org.apache.doris.statistics.BaseAnalysisTask;
import org.apache.doris.statistics.HistogramTask;
import org.apache.doris.statistics.MVAnalysisTask;
import org.apache.doris.statistics.OlapAnalysisTask;
-import org.apache.doris.statistics.TableStatsMeta;
-import org.apache.doris.statistics.util.StatisticsUtil;
import org.apache.doris.system.Backend;
import org.apache.doris.system.SystemInfoService;
import org.apache.doris.thrift.TColumn;
@@ -2251,55 +2249,4 @@ public class OlapTable extends Table {
}
}
}
-
- @Override
- public Map<String, Set<String>> findReAnalyzeNeededPartitions() {
- TableIf table = this;
- TableStatsMeta tableStats = Env.getCurrentEnv().getAnalysisManager().findTableStatsStatus(table.getId());
- Set<String> allPartitions = table.getPartitionNames().stream().map(table::getPartition)
- .filter(Partition::hasData).map(Partition::getName).collect(Collectors.toSet());
- if (tableStats == null) {
- return table.getBaseSchema().stream().collect(Collectors.toMap(Column::getName, v -> allPartitions));
- }
- Map<String, Set<String>> colToPart = new HashMap<>();
- for (Column col : table.getBaseSchema()) {
- long lastUpdateTime = tableStats.findColumnLastUpdateTime(col.getName());
- Set<String> partitions = table.getPartitionNames().stream()
- .map(table::getPartition)
- .filter(Partition::hasData)
- .filter(partition ->
- partition.getVisibleVersionTime() >= lastUpdateTime).map(Partition::getName)
- .collect(Collectors.toSet());
- colToPart.put(col.getName(), partitions);
- }
- return colToPart;
- }
-
- public long getDataSize(boolean singleReplica) {
- long dataSize = 0;
- for (Partition partition : getAllPartitions()) {
- dataSize += partition.getDataSize(singleReplica);
- }
- return dataSize;
- }
-
- public boolean needReAnalyzeTable(TableStatsMeta tblStats) {
- if (tblStats == null) {
- return true;
- }
- long rowCount = getRowCount();
- // TODO: Do we need to analyze an empty table?
- if (rowCount == 0) {
- return false;
- }
- if (!tblStats.analyzeColumns().containsAll(getBaseSchema()
- .stream()
- .map(Column::getName)
- .collect(Collectors.toSet()))) {
- return true;
- }
- long updateRows = tblStats.updatedRows.get();
- int tblHealth = StatisticsUtil.getTableHealth(rowCount, updateRows);
- return tblHealth < Config.table_stats_health_threshold;
- }
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/Table.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/Table.java
index ba7e55c7d86..0c50fc42b4b 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/Table.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/Table.java
@@ -30,7 +30,6 @@ import org.apache.doris.external.hudi.HudiTable;
import org.apache.doris.statistics.AnalysisInfo;
import org.apache.doris.statistics.BaseAnalysisTask;
import org.apache.doris.statistics.ColumnStatistic;
-import org.apache.doris.statistics.TableStatsMeta;
import org.apache.doris.thrift.TTableDescriptor;
import com.google.common.base.Preconditions;
@@ -558,14 +557,4 @@ public abstract class Table extends MetaObject implements Writable, TableIf {
public void analyze(String dbName) {
}
-
- @Override
- public Map<String, Set<String>> findReAnalyzeNeededPartitions() {
- return Collections.emptyMap();
- }
-
- @Override
- public boolean needReAnalyzeTable(TableStatsMeta tblStats) {
- return true;
- }
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/TableIf.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/TableIf.java
index 108d227e591..78717f0eca7 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/TableIf.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/TableIf.java
@@ -23,7 +23,6 @@ import org.apache.doris.common.MetaNotFoundException;
import org.apache.doris.statistics.AnalysisInfo;
import org.apache.doris.statistics.BaseAnalysisTask;
import org.apache.doris.statistics.ColumnStatistic;
-import org.apache.doris.statistics.TableStatsMeta;
import org.apache.doris.thrift.TTableDescriptor;
import com.google.common.collect.Lists;
@@ -34,7 +33,6 @@ import java.io.DataOutput;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
-import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -238,16 +236,5 @@ public interface TableIf {
default long getLastUpdateTime() {
return -1L;
}
-
- Map<String, Set<String>> findReAnalyzeNeededPartitions();
-
- default long getDataSize(boolean singleReplica) {
- // TODO: Each tableIf should impl it by itself.
- return 0;
- }
-
- boolean needReAnalyzeTable(TableStatsMeta tblStats);
-
-
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/external/ExternalTable.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/external/ExternalTable.java
index 6a1630e297c..f4c76cda7a9 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/external/ExternalTable.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/external/ExternalTable.java
@@ -35,10 +35,8 @@ import org.apache.doris.persist.gson.GsonUtils;
import org.apache.doris.statistics.AnalysisInfo;
import org.apache.doris.statistics.BaseAnalysisTask;
import org.apache.doris.statistics.ColumnStatistic;
-import org.apache.doris.statistics.TableStatsMeta;
import org.apache.doris.thrift.TTableDescriptor;
-import com.google.common.collect.Sets;
import com.google.gson.annotations.SerializedName;
import lombok.Getter;
import org.apache.commons.lang3.NotImplementedException;
@@ -48,14 +46,10 @@ import org.apache.logging.log4j.Logger;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
-import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Optional;
-import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;
-import java.util.stream.Collectors;
/**
* External table represent tables that are not self-managed by Doris.
@@ -379,19 +373,4 @@ public class ExternalTable implements TableIf, Writable, GsonPostProcessable {
rwLock = new ReentrantReadWriteLock(true);
objectCreated = false;
}
-
- @Override
- public boolean needReAnalyzeTable(TableStatsMeta tblStats) {
- // TODO: Find a way to decide if this external table need to be reanalyzed.
- // For now, simply return true for all external tables.
- return true;
- }
-
- @Override
- public Map<String, Set<String>> findReAnalyzeNeededPartitions() {
- HashSet<String> partitions = Sets.newHashSet();
- // TODO: Find a way to collect external table partitions that need to be analyzed.
- partitions.add("Dummy Partition");
- return getBaseSchema().stream().collect(Collectors.toMap(Column::getName, k -> partitions));
- }
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/external/HMSExternalTable.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/external/HMSExternalTable.java
index 5852bf69aae..02242803666 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/external/HMSExternalTable.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/external/HMSExternalTable.java
@@ -32,7 +32,7 @@ import org.apache.doris.statistics.BaseAnalysisTask;
import org.apache.doris.statistics.ColumnStatistic;
import org.apache.doris.statistics.ColumnStatisticBuilder;
import org.apache.doris.statistics.HMSAnalysisTask;
-import org.apache.doris.statistics.TableStatsMeta;
+import org.apache.doris.statistics.TableStatistic;
import org.apache.doris.statistics.util.StatisticsUtil;
import org.apache.doris.thrift.THiveTable;
import org.apache.doris.thrift.TTableDescriptor;
@@ -57,7 +57,6 @@ import org.apache.iceberg.Table;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
-import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.LocalDate;
@@ -103,16 +102,13 @@ public class HMSExternalTable extends ExternalTable {
SUPPORTED_HUDI_FILE_FORMATS.add("com.uber.hoodie.hadoop.realtime.HoodieRealtimeInputFormat");
}
- protected volatile org.apache.hadoop.hive.metastore.api.Table remoteTable = null;
- protected List<Column> partitionColumns;
+ private volatile org.apache.hadoop.hive.metastore.api.Table remoteTable = null;
+ private List<Column> partitionColumns;
- protected DLAType dlaType = DLAType.UNKNOWN;
-
- // No as precise as row count in TableStats, but better than none.
- private long estimatedRowCount = -1;
+ private DLAType dlaType = DLAType.UNKNOWN;
public enum DLAType {
- UNKNOWN, HIVE, HUDI, ICEBERG, DELTALAKE
+ UNKNOWN, HIVE, HUDI, ICEBERG
}
/**
@@ -127,10 +123,6 @@ public class HMSExternalTable extends ExternalTable {
super(id, name, catalog, dbName, TableType.HMS_EXTERNAL_TABLE);
}
- public HMSExternalTable(long id, String name, String dbName, HMSExternalCatalog catalog, TableType type) {
- super(id, name, catalog, dbName, type);
- }
-
public boolean isSupportedHmsTable() {
makeSureInitialized();
return dlaType != DLAType.UNKNOWN;
@@ -154,7 +146,6 @@ public class HMSExternalTable extends ExternalTable {
}
}
objectCreated = true;
- estimatedRowCount = getRowCountFromExternalSource(true);
}
}
@@ -278,19 +269,10 @@ public class HMSExternalTable extends ExternalTable {
@Override
public long getRowCount() {
makeSureInitialized();
- long rowCount = getRowCountFromExternalSource(false);
- if (rowCount == -1) {
- LOG.debug("Will estimate row count from file list.");
- rowCount = StatisticsUtil.getRowCountFromFileList(this);
- }
- return rowCount;
- }
-
- private long getRowCountFromExternalSource(boolean isInit) {
long rowCount;
switch (dlaType) {
case HIVE:
- rowCount = StatisticsUtil.getHiveRowCount(this, isInit);
+ rowCount = StatisticsUtil.getHiveRowCount(this);
break;
case ICEBERG:
rowCount = StatisticsUtil.getIcebergRowCount(this);
@@ -299,6 +281,10 @@ public class HMSExternalTable extends ExternalTable {
LOG.warn("getRowCount for dlaType {} is not supported.", dlaType);
rowCount = -1;
}
+ if (rowCount == -1) {
+ LOG.debug("Will estimate row count from file list.");
+ rowCount = StatisticsUtil.getRowCountFromFileList(this);
+ }
return rowCount;
}
@@ -436,20 +422,13 @@ public class HMSExternalTable extends ExternalTable {
@Override
public long estimatedRowCount() {
try {
- TableStatsMeta tableStats = Env.getCurrentEnv().getAnalysisManager().findTableStatsStatus(id);
- if (tableStats != null) {
- long rowCount = tableStats.rowCount;
+ Optional<TableStatistic> tableStatistics = Env.getCurrentEnv().getStatisticsCache().getTableStatistics(
+ catalog.getId(), catalog.getDbOrAnalysisException(dbName).getId(), id);
+ if (tableStatistics.isPresent()) {
+ long rowCount = tableStatistics.get().rowCount;
LOG.debug("Estimated row count for db {} table {} is {}.", dbName, name, rowCount);
return rowCount;
}
-
- if (estimatedRowCount != -1) {
- return estimatedRowCount;
- }
- // Cache the estimated row count in this structure
- // though the table never get analyzed, since the row estimation might be expensive caused by RPC.
- estimatedRowCount = getRowCount();
- return estimatedRowCount;
} catch (Exception e) {
LOG.warn("Fail to get row count for table {}", name, e);
}
@@ -470,7 +449,7 @@ public class HMSExternalTable extends ExternalTable {
return tmpSchema;
}
- protected void initPartitionColumns(List<Column> schema) {
+ private void initPartitionColumns(List<Column> schema) {
List<String> partitionKeys = remoteTable.getPartitionKeys().stream().map(FieldSchema::getName)
.collect(Collectors.toList());
partitionColumns = Lists.newArrayListWithCapacity(partitionKeys.size());
@@ -501,7 +480,7 @@ public class HMSExternalTable extends ExternalTable {
return getHiveColumnStats(colName);
case ICEBERG:
return StatisticsUtil.getIcebergColumnStats(colName,
- Env.getCurrentEnv().getExtMetaCacheMgr().getIcebergMetadataCache().getIcebergTable(this));
+ Env.getCurrentEnv().getExtMetaCacheMgr().getIcebergMetadataCache().getIcebergTable(this));
default:
LOG.warn("get column stats for dlaType {} is not supported.", dlaType);
}
@@ -629,12 +608,6 @@ public class HMSExternalTable extends ExternalTable {
builder.setMaxValue(Double.MAX_VALUE);
}
}
-
- @Override
- public void gsonPostProcess() throws IOException {
- super.gsonPostProcess();
- estimatedRowCount = -1;
- }
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/external/JdbcExternalTable.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/external/JdbcExternalTable.java
index a02c59080fc..051bfa5e585 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/external/JdbcExternalTable.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/external/JdbcExternalTable.java
@@ -24,13 +24,14 @@ import org.apache.doris.datasource.jdbc.JdbcExternalCatalog;
import org.apache.doris.statistics.AnalysisInfo;
import org.apache.doris.statistics.BaseAnalysisTask;
import org.apache.doris.statistics.JdbcAnalysisTask;
-import org.apache.doris.statistics.TableStatsMeta;
+import org.apache.doris.statistics.TableStatistic;
import org.apache.doris.thrift.TTableDescriptor;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.List;
+import java.util.Optional;
/**
* Elasticsearch external table.
@@ -111,11 +112,16 @@ public class JdbcExternalTable extends ExternalTable {
@Override
public long getRowCount() {
makeSureInitialized();
- TableStatsMeta tableStats = Env.getCurrentEnv().getAnalysisManager().findTableStatsStatus(id);
- if (tableStats != null) {
- long rowCount = tableStats.rowCount;
- LOG.debug("Estimated row count for db {} table {} is {}.", dbName, name, rowCount);
- return rowCount;
+ try {
+ Optional<TableStatistic> tableStatistics = Env.getCurrentEnv().getStatisticsCache().getTableStatistics(
+ catalog.getId(), catalog.getDbOrAnalysisException(dbName).getId(), id);
+ if (tableStatistics.isPresent()) {
+ long rowCount = tableStatistics.get().rowCount;
+ LOG.debug("Estimated row count for db {} table {} is {}.", dbName, name, rowCount);
+ return rowCount;
+ }
+ } catch (Exception e) {
+ LOG.warn("Fail to get row count for table {}", name, e);
}
return 1;
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/common/ThreadPoolManager.java b/fe/fe-core/src/main/java/org/apache/doris/common/ThreadPoolManager.java
index 31d608b8a25..be8731b6b25 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/common/ThreadPoolManager.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/common/ThreadPoolManager.java
@@ -134,15 +134,6 @@ public class ThreadPoolManager {
poolName, needRegisterMetric);
}
- public static ThreadPoolExecutor newDaemonFixedThreadPool(int numThread, int queueSize,
- String poolName,
- boolean needRegisterMetric,
- RejectedExecutionHandler handler) {
- return newDaemonThreadPool(numThread, numThread, KEEP_ALIVE_TIME, TimeUnit.SECONDS,
- new LinkedBlockingQueue<>(queueSize), handler,
- poolName, needRegisterMetric);
- }
-
public static <T> ThreadPoolExecutor newDaemonFixedPriorityThreadPool(int numThread, int initQueueSize,
Comparator<T> comparator, Class<T> tClass,
String poolName, boolean needRegisterMetric) {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/datasource/CatalogIf.java b/fe/fe-core/src/main/java/org/apache/doris/datasource/CatalogIf.java
index cebc526d14b..2d8925b5d98 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/datasource/CatalogIf.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/CatalogIf.java
@@ -175,7 +175,4 @@ public interface CatalogIf<T extends DatabaseIf> {
public Collection<DatabaseIf> getAllDbs();
public ConcurrentHashMap<Long, DatabaseIf> getIdToDb();
-
- public boolean enableAutoAnalyze();
-
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/datasource/CatalogMgr.java b/fe/fe-core/src/main/java/org/apache/doris/datasource/CatalogMgr.java
index e13372254a1..02a14076855 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/datasource/CatalogMgr.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/CatalogMgr.java
@@ -63,10 +63,8 @@ import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Arrays;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Function;
@@ -1149,9 +1147,5 @@ public class CatalogMgr implements Writable, GsonPostProcessable {
public Map<Long, CatalogIf> getIdToCatalog() {
return idToCatalog;
}
-
- public Set<CatalogIf> getCopyOfCatalog() {
- return new HashSet<>(idToCatalog.values());
- }
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/datasource/ExternalCatalog.java b/fe/fe-core/src/main/java/org/apache/doris/datasource/ExternalCatalog.java
index 22a6816543e..499a1f9385f 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/datasource/ExternalCatalog.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/ExternalCatalog.java
@@ -74,8 +74,6 @@ public abstract class ExternalCatalog
implements CatalogIf<ExternalDatabase<? extends ExternalTable>>, Writable, GsonPostProcessable {
private static final Logger LOG = LogManager.getLogger(ExternalCatalog.class);
- public static final String ENABLE_AUTO_ANALYZE = "enable.auto.analyze";
-
// Unique id of this catalog, will be assigned after catalog is loaded.
@SerializedName(value = "id")
protected long id;
@@ -599,18 +597,4 @@ public abstract class ExternalCatalog
public ConcurrentHashMap<Long, DatabaseIf> getIdToDb() {
return new ConcurrentHashMap<>(idToDb);
}
-
- @Override
- public boolean enableAutoAnalyze() {
- // By default, external catalog disables auto analyze, uses could set catalog property to enable it:
- // "enable.auto.analyze" = true
- Map<String, String> properties = catalogProperty.getProperties();
- boolean ret = false;
- if (properties.containsKey(ENABLE_AUTO_ANALYZE)
- && properties.get(ENABLE_AUTO_ANALYZE).equalsIgnoreCase("true")) {
- ret = true;
- }
- return ret;
- }
-
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalCatalog.java b/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalCatalog.java
index aee9b5f67af..65c8f885604 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalCatalog.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalCatalog.java
@@ -3183,9 +3183,4 @@ public class InternalCatalog implements CatalogIf<Database> {
public Collection<DatabaseIf> getAllDbs() {
return new HashSet<>(idToDb.values());
}
-
- @Override
- public boolean enableAutoAnalyze() {
- return true;
- }
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/journal/JournalEntity.java b/fe/fe-core/src/main/java/org/apache/doris/journal/JournalEntity.java
index 62b4bcfc851..0e0b8ac7df6 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/journal/JournalEntity.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/journal/JournalEntity.java
@@ -111,7 +111,6 @@ import org.apache.doris.persist.TableAddOrDropInvertedIndicesInfo;
import org.apache.doris.persist.TableInfo;
import org.apache.doris.persist.TablePropertyInfo;
import org.apache.doris.persist.TableRenameColumnInfo;
-import org.apache.doris.persist.TableStatsDeletionLog;
import org.apache.doris.persist.TruncateTableInfo;
import org.apache.doris.plugin.PluginInfo;
import org.apache.doris.policy.DropPolicyLog;
@@ -119,7 +118,6 @@ import org.apache.doris.policy.Policy;
import org.apache.doris.policy.StoragePolicy;
import org.apache.doris.resource.workloadgroup.WorkloadGroup;
import org.apache.doris.statistics.AnalysisInfo;
-import org.apache.doris.statistics.TableStatsMeta;
import org.apache.doris.system.Backend;
import org.apache.doris.system.Frontend;
import org.apache.doris.transaction.TransactionState;
@@ -847,21 +845,6 @@ public class JournalEntity implements Writable {
isRead = true;
break;
}
- case OperationType.OP_UPDATE_TABLE_STATS: {
- data = TableStatsMeta.read(in);
- isRead = true;
- break;
- }
- case OperationType.OP_PERSIST_AUTO_JOB: {
- data = AnalysisInfo.read(in);
- isRead = true;
- break;
- }
- case OperationType.OP_DELETE_TABLE_STATS: {
- data = TableStatsDeletionLog.read(in);
- isRead = true;
- break;
- }
default: {
IOException e = new IOException();
LOG.error("UNKNOWN Operation Type {}", opCode, e);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/cost/CostModelV1.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/cost/CostModelV1.java
index aa8f4d6cc7c..1f7255b7990 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/cost/CostModelV1.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/cost/CostModelV1.java
@@ -86,7 +86,8 @@ class CostModelV1 extends PlanVisitor<Cost, PlanContext> {
CostV1 planCostV1 = (CostV1) planCost;
return new CostV1(childCostV1.getCpuCost() + planCostV1.getCpuCost(),
childCostV1.getMemoryCost() + planCostV1.getMemoryCost(),
- childCostV1.getNetworkCost() + planCostV1.getNetworkCost());
+ childCostV1.getNetworkCost() + planCostV1.getNetworkCost(),
+ childCostV1.getPenalty() + planCostV1.getPenalty());
}
@Override
@@ -117,7 +118,7 @@ class CostModelV1 extends PlanVisitor<Cost, PlanContext> {
CostV1 costValue = (CostV1) storageLayerAggregate.getRelation().accept(this, context);
// multiply a factor less than 1, so we can select PhysicalStorageLayerAggregate as far as possible
return new CostV1(costValue.getCpuCost() * 0.7, costValue.getMemoryCost(),
- costValue.getNetworkCost());
+ costValue.getNetworkCost(), costValue.getPenalty());
}
@Override
@@ -149,14 +150,14 @@ class CostModelV1 extends PlanVisitor<Cost, PlanContext> {
// TODO: consider two-phase sort and enforcer.
Statistics statistics = context.getStatisticsWithCheck();
Statistics childStatistics = context.getChildStatistics(0);
-
- double childRowCount = childStatistics.getRowCount();
- double rowCount = statistics.getRowCount();
if (physicalQuickSort.getSortPhase().isGather()) {
// Now we do more like two-phase sort, so penalise one-phase sort
- rowCount *= 100;
+ statistics = statistics.withRowCount(statistics.getRowCount() * 100);
}
- return CostV1.of(childRowCount, rowCount, childRowCount);
+ return CostV1.of(
+ childStatistics.getRowCount(),
+ statistics.getRowCount(),
+ childStatistics.getRowCount());
}
@Override
@@ -164,14 +165,14 @@ class CostModelV1 extends PlanVisitor<Cost, PlanContext> {
// TODO: consider two-phase sort and enforcer.
Statistics statistics = context.getStatisticsWithCheck();
Statistics childStatistics = context.getChildStatistics(0);
-
- double childRowCount = childStatistics.getRowCount();
- double rowCount = statistics.getRowCount();
if (topN.getSortPhase().isGather()) {
// Now we do more like two-phase sort, so penalise one-phase sort
- rowCount *= 100;
+ statistics = statistics.withRowCount(statistics.getRowCount() * 100);
}
- return CostV1.of(childRowCount, rowCount, childRowCount);
+ return CostV1.of(
+ childStatistics.getRowCount(),
+ statistics.getRowCount(),
+ childStatistics.getRowCount());
}
@Override
@@ -185,9 +186,9 @@ class CostModelV1 extends PlanVisitor<Cost, PlanContext> {
Statistics statistics = context.getStatisticsWithCheck();
Statistics childStatistics = context.getChildStatistics(0);
return CostV1.of(
- childStatistics.getRowCount(),
- statistics.getRowCount(),
- childStatistics.getRowCount());
+ childStatistics.getRowCount(),
+ statistics.getRowCount(),
+ childStatistics.getRowCount());
}
@Override
@@ -286,38 +287,30 @@ class CostModelV1 extends PlanVisitor<Cost, PlanContext> {
pattern2: (L join1 Agg1) join2 agg2
in pattern2, join1 and join2 takes more time, but Agg1 and agg2 can be processed in parallel.
*/
+ double penalty = HEAVY_OPERATOR_PUNISH_FACTOR
+ * Math.min(probeStats.getPenalty(), buildStats.getPenalty());
+ if (buildStats.getWidth() >= 2) {
+ //penalty for right deep tree
+ penalty += rightRowCount;
+ }
if (physicalHashJoin.getJoinType().isCrossJoin()) {
return CostV1.of(leftRowCount + rightRowCount + outputRowCount,
0,
- leftRowCount + rightRowCount
- );
+ leftRowCount + rightRowCount,
+ penalty);
}
if (context.isBroadcastJoin()) {
- // compared with shuffle join, bc join will be taken a penalty for both build and probe side;
- // currently we use the following factor as the penalty factor:
- // build side factor: totalInstanceNumber to the power of 2, standing for the additional effort for
- // bigger cost for building hash table, taken on rightRowCount
- // probe side factor: totalInstanceNumber to the power of 2, standing for the additional effort for
- // bigger cost for ProbeWhenBuildSideOutput effort and ProbeWhenSearchHashTableTime
- // on the output rows, taken on outputRowCount()
- double probeSideFactor = 1.0;
- double buildSideFactor = ConnectContext.get().getSessionVariable().getBroadcastRightTableScaleFactor();
- int parallelInstance = Math.max(1, ConnectContext.get().getSessionVariable().getParallelExecInstanceNum());
- int totalInstanceNumber = parallelInstance * beNumber;
- if (buildSideFactor <= 1.0) {
- // use totalInstanceNumber to the power of 2 as the default factor value
- buildSideFactor = Math.pow(totalInstanceNumber, 0.5);
- }
- // TODO: since the outputs rows may expand a lot, penalty on it will cause bc never be chosen.
- // will refine this in next generation cost model.
- return CostV1.of(leftRowCount + rightRowCount * buildSideFactor + outputRowCount * probeSideFactor,
+ double broadcastJoinPenalty = broadCastJoinBalancePenalty(probeStats, buildStats);
+ return CostV1.of(leftRowCount * broadcastJoinPenalty + rightRowCount + outputRowCount,
rightRowCount,
+ 0,
0
);
}
return CostV1.of(leftRowCount + rightRowCount + outputRowCount,
rightRowCount,
+ 0,
0
);
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/cost/CostV1.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/cost/CostV1.java
index bf1cc425999..b5c5b50bd2e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/cost/CostV1.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/cost/CostV1.java
@@ -19,19 +19,23 @@ package org.apache.doris.nereids.cost;
class CostV1 implements Cost {
private static final CostV1 INFINITE = new CostV1(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY,
- Double.POSITIVE_INFINITY);
- private static final CostV1 ZERO = new CostV1(0, 0, 0);
+ Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
+ private static final CostV1 ZERO = new CostV1(0, 0, 0, 0);
private final double cpuCost;
private final double memoryCost;
private final double networkCost;
+ //penalty for
+ // 1. right deep tree
+ // 2. right XXX join
+ private final double penalty;
private final double cost;
/**
* Constructor of CostEstimate.
*/
- public CostV1(double cpuCost, double memoryCost, double networkCost) {
+ public CostV1(double cpuCost, double memoryCost, double networkCost, double penaltiy) {
// TODO: fix stats
cpuCost = Double.max(0, cpuCost);
memoryCost = Double.max(0, memoryCost);
@@ -39,10 +43,11 @@ class CostV1 implements Cost {
this.cpuCost = cpuCost;
this.memoryCost = memoryCost;
this.networkCost = networkCost;
+ this.penalty = penaltiy;
CostWeight costWeight = CostWeight.get();
this.cost = costWeight.cpuWeight * cpuCost + costWeight.memoryWeight * memoryCost
- + costWeight.networkWeight * networkCost;
+ + costWeight.networkWeight * networkCost + costWeight.penaltyWeight * penalty;
}
public CostV1(double cost) {
@@ -50,6 +55,7 @@ class CostV1 implements Cost {
this.cpuCost = 0;
this.networkCost = 0;
this.memoryCost = 0;
+ this.penalty = 0;
}
public static CostV1 infinite() {
@@ -72,20 +78,28 @@ class CostV1 implements Cost {
return networkCost;
}
+ public double getPenalty() {
+ return penalty;
+ }
+
public double getValue() {
return cost;
}
+ public static CostV1 of(double cpuCost, double maxMemory, double networkCost, double rightDeepPenaltiy) {
+ return new CostV1(cpuCost, maxMemory, networkCost, rightDeepPenaltiy);
+ }
+
public static CostV1 of(double cpuCost, double maxMemory, double networkCost) {
- return new CostV1(cpuCost, maxMemory, networkCost);
+ return new CostV1(cpuCost, maxMemory, networkCost, 0);
}
public static CostV1 ofCpu(double cpuCost) {
- return new CostV1(cpuCost, 0, 0);
+ return new CostV1(cpuCost, 0, 0, 0);
}
public static CostV1 ofMemory(double memoryCost) {
- return new CostV1(0, memoryCost, 0);
+ return new CostV1(0, memoryCost, 0, 0);
}
@Override
@@ -93,7 +107,7 @@ class CostV1 implements Cost {
StringBuilder sb = new StringBuilder();
sb.append("[").append((long) cpuCost).append("/")
.append((long) memoryCost).append("/").append((long) networkCost)
- .append("/").append("]");
+ .append("/").append((long) penalty).append("]");
return sb.toString();
}
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/RuntimeFilterPruner.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/RuntimeFilterPruner.java
index c9cc43d0c29..aa1f10aa478 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/RuntimeFilterPruner.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/RuntimeFilterPruner.java
@@ -27,6 +27,7 @@ import org.apache.doris.nereids.trees.plans.Plan;
import org.apache.doris.nereids.trees.plans.physical.PhysicalAssertNumRows;
import org.apache.doris.nereids.trees.plans.physical.PhysicalDistribute;
import org.apache.doris.nereids.trees.plans.physical.PhysicalFilter;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalHashAggregate;
import org.apache.doris.nereids.trees.plans.physical.PhysicalHashJoin;
import org.apache.doris.nereids.trees.plans.physical.PhysicalLimit;
import org.apache.doris.nereids.trees.plans.physical.PhysicalProject;
@@ -53,6 +54,17 @@ import java.util.Set;
*/
public class RuntimeFilterPruner extends PlanPostProcessor {
+ // *******************************
+ // Physical plans
+ // *******************************
+ @Override
+ public PhysicalHashAggregate visitPhysicalHashAggregate(
+ PhysicalHashAggregate<? extends Plan> agg, CascadesContext context) {
+ agg.child().accept(this, context);
+ context.getRuntimeFilterContext().addEffectiveSrcNode(agg);
+ return agg;
+ }
+
@Override
public PhysicalQuickSort visitPhysicalQuickSort(PhysicalQuickSort<? extends Plan> sort, CascadesContext context) {
sort.child().accept(this, context);
@@ -153,9 +165,7 @@ public class RuntimeFilterPruner extends PlanPostProcessor {
/**
* consider L join R on L.a=R.b
* runtime-filter: L.a<-R.b is effective,
- * if rf could reduce tuples of L,
- * 1. some L.a distinctive value are not covered by R.b, or
- * 2. if there is a effective RF applied on R
+ * if R.b.selectivity<1 or b is partly covered by a
*
* TODO: min-max
* @param equalTo join condition
@@ -189,7 +199,8 @@ public class RuntimeFilterPruner extends PlanPostProcessor {
if (probeColumnStat.isUnKnown || buildColumnStat.isUnKnown) {
return true;
}
- double buildNdvInProbeRange = buildColumnStat.ndvIntersection(probeColumnStat);
- return probeColumnStat.ndv > buildNdvInProbeRange * (1 + ColumnStatistic.STATS_ERROR);
+ return buildColumnStat.selectivity < 1
+ || probeColumnStat.notEnclosed(buildColumnStat)
+ || buildColumnStat.ndv < probeColumnStat.ndv * 0.95;
}
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/ExpressionEstimation.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/ExpressionEstimation.java
index aa1903e7b37..b8a7975a087 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/ExpressionEstimation.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/ExpressionEstimation.java
@@ -151,7 +151,7 @@ public class ExpressionEstimation extends ExpressionVisitor<ColumnStatistic, Sta
return new ColumnStatisticBuilder()
.setNdv(2)
.setMinValue(0)
- .setMaxValue(Double.POSITIVE_INFINITY)
+ .setMaxValue(Double.MAX_VALUE)
.setAvgSizeByte(8)
.setNumNulls(0)
.build();
@@ -206,15 +206,13 @@ public class ExpressionEstimation extends ExpressionVisitor<ColumnStatistic, Sta
return ColumnStatistic.UNKNOWN;
}
double literalVal = literal.getDouble();
- return new ColumnStatisticBuilder()
- .setMaxValue(literalVal)
- .setMinValue(literalVal)
- .setNdv(1)
- .setNumNulls(1)
- .setAvgSizeByte(1)
- .setMinExpr(literal.toLegacyLiteral())
- .setMaxExpr(literal.toLegacyLiteral())
- .build();
+ ColumnStatisticBuilder columnStatBuilder = new ColumnStatisticBuilder();
+ columnStatBuilder.setMaxValue(literalVal);
+ columnStatBuilder.setMinValue(literalVal);
+ columnStatBuilder.setNdv(1);
+ columnStatBuilder.setNumNulls(1);
+ columnStatBuilder.setAvgSizeByte(1);
+ return columnStatBuilder.build();
}
@Override
@@ -243,13 +241,13 @@ public class ExpressionEstimation extends ExpressionVisitor<ColumnStatistic, Sta
if (binaryArithmetic instanceof Add) {
return new ColumnStatisticBuilder().setCount(rowCount).setNdv(ndv).setAvgSizeByte(leftColStats.avgSizeByte)
.setNumNulls(numNulls).setDataSize(dataSize).setMinValue(leftMin + rightMin)
- .setMaxValue(leftMax + rightMax)
+ .setMaxValue(leftMax + rightMax).setSelectivity(1.0)
.setMinExpr(null).setMaxExpr(null).build();
}
if (binaryArithmetic instanceof Subtract) {
return new ColumnStatisticBuilder().setCount(rowCount).setNdv(ndv).setAvgSizeByte(leftColStats.avgSizeByte)
.setNumNulls(numNulls).setDataSize(dataSize).setMinValue(leftMin - rightMax)
- .setMaxValue(leftMax - rightMin).setMinExpr(null)
+ .setMaxValue(leftMax - rightMin).setSelectivity(1.0).setMinExpr(null)
.setMaxExpr(null).build();
}
// TODO: stat for multiply and divide produced by below algorithm may have huge deviation with reality.
@@ -261,11 +259,11 @@ public class ExpressionEstimation extends ExpressionVisitor<ColumnStatistic, Sta
leftMax * rightMax);
double max = Math.max(
Math.max(
- Math.max(leftMin * rightMin, leftMin * rightMax),
- leftMax * rightMin),
+ Math.max(leftMin * rightMin, leftMin * rightMax),
+ leftMax * rightMin),
leftMax * rightMax);
return new ColumnStatisticBuilder().setCount(rowCount).setNdv(ndv).setAvgSizeByte(leftColStats.avgSizeByte)
- .setNumNulls(numNulls).setDataSize(dataSize).setMinValue(min).setMaxValue(max)
+ .setNumNulls(numNulls).setDataSize(dataSize).setMinValue(min).setMaxValue(max).setSelectivity(1.0)
.setMaxExpr(null).setMinExpr(null).build();
}
if (binaryArithmetic instanceof Divide || binaryArithmetic instanceof IntegralDivide) {
@@ -281,7 +279,7 @@ public class ExpressionEstimation extends ExpressionVisitor<ColumnStatistic, Sta
leftMax / noneZeroDivisor(rightMax));
return new ColumnStatisticBuilder().setCount(rowCount).setNdv(ndv).setAvgSizeByte(leftColStats.avgSizeByte)
.setNumNulls(numNulls).setDataSize(binaryArithmetic.getDataType().width()).setMinValue(min)
- .setMaxValue(max).build();
+ .setMaxValue(max).setSelectivity(1.0).build();
}
if (binaryArithmetic instanceof Mod) {
double min = -Math.max(Math.abs(rightMin), Math.abs(rightMax));
@@ -311,12 +309,13 @@ public class ExpressionEstimation extends ExpressionVisitor<ColumnStatistic, Sta
}
/*
we keep columnStat.min and columnStat.max, but set ndv=1.
- if there is group-by keys, we will update count when visiting group clause
+ if there is group-by keys, we will update ndv when visiting group clause
*/
double width = min.child().getDataType().width();
- return new ColumnStatisticBuilder().setCount(1).setNdv(1).setAvgSizeByte(width)
- .setMinValue(columnStat.minValue).setMinExpr(columnStat.minExpr)
- .setMaxValue(columnStat.maxValue).setMaxExpr(columnStat.maxExpr).build();
+ return new ColumnStatisticBuilder().setCount(1).setNdv(1).setAvgSizeByte(width).setNumNulls(width)
+ .setDataSize(child.getDataType().width()).setMinValue(columnStat.minValue)
+ .setMaxValue(columnStat.maxValue).setSelectivity(1.0)
+ .setMinExpr(null).build();
}
@Override
@@ -328,20 +327,19 @@ public class ExpressionEstimation extends ExpressionVisitor<ColumnStatistic, Sta
}
/*
we keep columnStat.min and columnStat.max, but set ndv=1.
- if there is group-by keys, we will update count when visiting group clause
+ if there is group-by keys, we will update ndv when visiting group clause
*/
int width = max.child().getDataType().width();
- return new ColumnStatisticBuilder().setCount(1D).setNdv(1D).setAvgSizeByte(width)
- .setMinValue(columnStat.minValue).setMinExpr(columnStat.minExpr)
- .setMaxValue(columnStat.maxValue).setMaxExpr(columnStat.maxExpr)
- .build();
+ return new ColumnStatisticBuilder().setCount(1D).setNdv(1D).setAvgSizeByte(width).setNumNulls(0)
+ .setDataSize(width).setMinValue(columnStat.minValue).setMaxValue(columnStat.maxValue)
+ .setSelectivity(1.0).setMaxExpr(null).setMinExpr(null).build();
}
@Override
public ColumnStatistic visitCount(Count count, Statistics context) {
double width = count.getDataType().width();
return new ColumnStatisticBuilder().setCount(1D).setAvgSizeByte(width).setNumNulls(0)
- .setDataSize(width).setMinValue(0).setMaxValue(context.getRowCount())
+ .setDataSize(width).setMinValue(0).setMaxValue(context.getRowCount()).setSelectivity(1.0)
.setMaxExpr(null).setMinExpr(null).build();
}
@@ -369,7 +367,7 @@ public class ExpressionEstimation extends ExpressionVisitor<ColumnStatistic, Sta
.setNumNulls(childStat.numNulls)
.setDataSize(4 * childStat.count)
.setMinValue(minYear)
- .setMaxValue(maxYear).setMinExpr(null).build();
+ .setMaxValue(maxYear).setSelectivity(1.0).setMinExpr(null).build();
}
@Override
@@ -380,7 +378,7 @@ public class ExpressionEstimation extends ExpressionVisitor<ColumnStatistic, Sta
.setNdv(54)
.setAvgSizeByte(width)
.setNumNulls(childStat.numNulls)
- .setDataSize(1).setMinValue(1).setMaxValue(53).setMinExpr(null)
+ .setDataSize(1).setMinValue(1).setMaxValue(53).setSelectivity(1.0).setMinExpr(null)
.build();
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/FilterEstimation.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/FilterEstimation.java
index f06c9d1cc4f..c5ddbd285b3 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/FilterEstimation.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/FilterEstimation.java
@@ -17,10 +17,10 @@
package org.apache.doris.nereids.stats;
-import org.apache.doris.analysis.LiteralExpr;
import org.apache.doris.nereids.stats.FilterEstimation.EstimationContext;
import org.apache.doris.nereids.trees.TreeNode;
import org.apache.doris.nereids.trees.expressions.And;
+import org.apache.doris.nereids.trees.expressions.Cast;
import org.apache.doris.nereids.trees.expressions.ComparisonPredicate;
import org.apache.doris.nereids.trees.expressions.CompoundPredicate;
import org.apache.doris.nereids.trees.expressions.EqualTo;
@@ -28,7 +28,6 @@ import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.GreaterThan;
import org.apache.doris.nereids.trees.expressions.GreaterThanEqual;
import org.apache.doris.nereids.trees.expressions.InPredicate;
-import org.apache.doris.nereids.trees.expressions.IsNull;
import org.apache.doris.nereids.trees.expressions.LessThan;
import org.apache.doris.nereids.trees.expressions.LessThanEqual;
import org.apache.doris.nereids.trees.expressions.Like;
@@ -38,7 +37,6 @@ import org.apache.doris.nereids.trees.expressions.Or;
import org.apache.doris.nereids.trees.expressions.Slot;
import org.apache.doris.nereids.trees.expressions.SlotReference;
import org.apache.doris.nereids.trees.expressions.functions.Function;
-import org.apache.doris.nereids.trees.expressions.literal.Literal;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.statistics.Bucket;
import org.apache.doris.statistics.ColumnStatistic;
@@ -49,11 +47,10 @@ import org.apache.doris.statistics.StatisticRange;
import org.apache.doris.statistics.Statistics;
import org.apache.doris.statistics.StatisticsBuilder;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Sets;
-
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Predicate;
@@ -84,9 +81,7 @@ public class FilterEstimation extends ExpressionVisitor<Statistics, EstimationCo
public Statistics estimate(Expression expression, Statistics statistics) {
// For a comparison predicate, only when it's left side is a slot and right side is a literal, we would
// consider is a valid predicate.
- Statistics stats = expression.accept(this, new EstimationContext(statistics));
- stats.enforceValid();
- return stats;
+ return expression.accept(this, new EstimationContext(statistics));
}
@Override
@@ -99,7 +94,7 @@ public class FilterEstimation extends ExpressionVisitor<Statistics, EstimationCo
Expression leftExpr = predicate.child(0);
Expression rightExpr = predicate.child(1);
Statistics leftStats = leftExpr.accept(this, context);
- Statistics andStats = rightExpr.accept(this,
+ Statistics andStats = rightExpr.accept(new FilterEstimation(),
new EstimationContext(leftStats));
if (predicate instanceof And) {
return andStats;
@@ -107,29 +102,27 @@ public class FilterEstimation extends ExpressionVisitor<Statistics, EstimationCo
Statistics rightStats = rightExpr.accept(this, context);
double rowCount = leftStats.getRowCount() + rightStats.getRowCount() - andStats.getRowCount();
Statistics orStats = context.statistics.withRowCount(rowCount);
- Set<Slot> leftInputSlots = leftExpr.getInputSlots();
- Set<Slot> rightInputSlots = rightExpr.getInputSlots();
- for (Slot slot : context.keyColumns) {
- if (leftInputSlots.contains(slot) && rightInputSlots.contains(slot)) {
- ColumnStatistic leftColStats = leftStats.findColumnStatistics(slot);
- ColumnStatistic rightColStats = rightStats.findColumnStatistics(slot);
- StatisticRange leftRange = StatisticRange.from(leftColStats, slot.getDataType());
- StatisticRange rightRange = StatisticRange.from(rightColStats, slot.getDataType());
- StatisticRange union = leftRange.union(rightRange);
- ColumnStatisticBuilder colBuilder = new ColumnStatisticBuilder(
- context.statistics.findColumnStatistics(slot));
- colBuilder.setMinValue(union.getLow()).setMinExpr(union.getLowExpr())
- .setMaxValue(union.getHigh()).setMaxExpr(union.getHighExpr())
- .setNdv(union.getDistinctValues());
- orStats.addColumnStats(slot, colBuilder.build());
+ for (Map.Entry<Expression, ColumnStatistic> entry : orStats.columnStatistics().entrySet()) {
+ ColumnStatistic leftColStats = leftStats.findColumnStatistics(entry.getKey());
+ ColumnStatistic rightColStats = rightStats.findColumnStatistics(entry.getKey());
+ ColumnStatisticBuilder estimatedColStatsBuilder = new ColumnStatisticBuilder(entry.getValue());
+ if (leftColStats.minValue <= rightColStats.minValue) {
+ estimatedColStatsBuilder.setMinValue(leftColStats.minValue);
+ estimatedColStatsBuilder.setMinExpr(leftColStats.minExpr);
+ } else {
+ estimatedColStatsBuilder.setMinValue(rightColStats.minValue);
+ estimatedColStatsBuilder.setMinExpr(rightColStats.minExpr);
+ }
+ if (leftColStats.maxValue >= rightColStats.maxValue) {
+ estimatedColStatsBuilder.setMaxValue(leftColStats.maxValue);
+ estimatedColStatsBuilder.setMaxExpr(leftColStats.maxExpr);
+ } else {
+ estimatedColStatsBuilder.setMaxValue(rightColStats.maxValue);
+ estimatedColStatsBuilder.setMaxExpr(rightColStats.maxExpr);
}
}
return orStats;
}
- // should not come here
- Preconditions.checkArgument(false,
- "unsupported compound operator: %s in %s",
- predicate.getClass().getName(), predicate.toSql());
return context.statistics;
}
@@ -179,27 +172,25 @@ public class FilterEstimation extends ExpressionVisitor<Statistics, EstimationCo
}
private Statistics updateLessThanLiteral(Expression leftExpr, ColumnStatistic statsForLeft,
- ColumnStatistic statsForRight, EstimationContext context, boolean contains) {
+ double val, EstimationContext context, boolean contains) {
if (statsForLeft.hasHistogram()) {
- return estimateLessThanLiteralWithHistogram(leftExpr, statsForLeft,
- statsForRight.maxValue, context, contains);
+ return estimateLessThanLiteralWithHistogram(leftExpr, statsForLeft, val, context, contains);
}
- StatisticRange rightRange = new StatisticRange(statsForLeft.minValue, statsForLeft.minExpr,
- statsForRight.maxValue, statsForRight.maxExpr,
- statsForLeft.ndv, leftExpr.getDataType());
+ //rightRange.distinctValues should not be used
+ StatisticRange rightRange = new StatisticRange(statsForLeft.minValue, val, statsForLeft.ndv,
+ leftExpr.getDataType());
return estimateBinaryComparisonFilter(leftExpr,
statsForLeft,
rightRange, context);
}
private Statistics updateGreaterThanLiteral(Expression leftExpr, ColumnStatistic statsForLeft,
- ColumnStatistic statsForRight, EstimationContext context, boolean contains) {
+ double val, EstimationContext context, boolean contains) {
if (statsForLeft.hasHistogram()) {
- return estimateGreaterThanLiteralWithHistogram(leftExpr, statsForLeft,
- statsForRight.minValue, context, contains);
+ return estimateGreaterThanLiteralWithHistogram(leftExpr, statsForLeft, val, context, contains);
}
- StatisticRange rightRange = new StatisticRange(statsForRight.minValue, statsForRight.minExpr,
- statsForLeft.maxValue, statsForLeft.maxExpr,
+ //rightRange.distinctValues should not be used
+ StatisticRange rightRange = new StatisticRange(val, statsForLeft.maxValue,
statsForLeft.ndv, leftExpr.getDataType());
return estimateBinaryComparisonFilter(leftExpr, statsForLeft, rightRange, context);
}
@@ -213,12 +204,12 @@ public class FilterEstimation extends ExpressionVisitor<Statistics, EstimationCo
if (cp instanceof EqualTo || cp instanceof NullSafeEqual) {
return estimateEqualTo(cp, statsForLeft, statsForRight, context);
} else {
+ double val = statsForRight.maxValue;
if (cp instanceof LessThan || cp instanceof LessThanEqual) {
- return updateLessThanLiteral(cp.left(), statsForLeft, statsForRight,
- context, cp instanceof LessThanEqual);
+ return updateLessThanLiteral(cp.left(), statsForLeft, val, context, cp instanceof LessThanEqual);
} else if (cp instanceof GreaterThan || cp instanceof GreaterThanEqual) {
- return updateGreaterThanLiteral(cp.left(), statsForLeft, statsForRight, context,
+ return updateGreaterThanLiteral(cp.left(), statsForLeft, val, context,
cp instanceof GreaterThanEqual);
} else {
throw new RuntimeException(String.format("Unexpected expression : %s", cp.toSql()));
@@ -243,10 +234,19 @@ public class FilterEstimation extends ExpressionVisitor<Statistics, EstimationCo
Statistics equalStats = context.statistics.withSel(selectivity);
Expression left = cp.left();
- equalStats.addColumnStats(left, statsForRight);
- context.addKeyIfSlot(left);
- if (!(left instanceof SlotReference)) {
- left.accept(new ColumnStatsAdjustVisitor(), equalStats);
+ if (left instanceof Cast) {
+ left = ((Cast) left).child();
+ }
+ if (left instanceof SlotReference) {
+ Slot leftSlot = (SlotReference) left;
+ //update min/max of cp.left
+ ColumnStatistic columnStats = equalStats.findColumnStatistics(leftSlot);
+ ColumnStatisticBuilder colStatsBuilder = new ColumnStatisticBuilder(columnStats);
+ colStatsBuilder.setMaxValue(val);
+ colStatsBuilder.setMinValue(val);
+ colStatsBuilder.setNdv(1);
+ colStatsBuilder.setNumNulls(0);
+ equalStats.addColumnStats(leftSlot, colStatsBuilder.build());
}
return equalStats;
}
@@ -275,12 +275,8 @@ public class FilterEstimation extends ExpressionVisitor<Statistics, EstimationCo
return context.statistics.withSel(DEFAULT_IN_COEFFICIENT);
}
List<Expression> options = inPredicate.getOptions();
- // init minOption and maxOption by compareExpr.max and compareExpr.min respectively,
- // and then adjust min/max by options
- double minOptionValue = compareExprStats.maxValue;
- double maxOptionValue = compareExprStats.minValue;
- LiteralExpr minOptionLiteral = compareExprStats.maxExpr;
- LiteralExpr maxOptionLiteral = compareExprStats.minExpr;
+ double maxOption = 0;
+ double minOption = Double.MAX_VALUE;
/* suppose A.(min, max) = (0, 10), A.ndv=10
A in ( 1, 2, 5, 100):
validInOptCount = 3, that is (1, 2, 5)
@@ -296,199 +292,86 @@ public class FilterEstimation extends ExpressionVisitor<Statistics, EstimationCo
A.(min, max) not changed
A.selectivity = 7/10
*/
- int validInOptCount = 0;
+ double validInOptCount = 0;
double selectivity = 1.0;
ColumnStatisticBuilder compareExprStatsBuilder = new ColumnStatisticBuilder(compareExprStats);
- int nonLiteralOptionCount = 0;
+
for (Expression option : options) {
ColumnStatistic optionStats = ExpressionEstimation.estimate(option, context.statistics);
- if (option instanceof Literal) {
- // remove the options which is out of compareExpr.range
- if (compareExprStats.minValue <= optionStats.maxValue
- && optionStats.maxValue <= compareExprStats.maxValue) {
- validInOptCount++;
- LiteralExpr optionLiteralExpr = ((Literal) option).toLegacyLiteral();
- if (maxOptionLiteral == null || optionLiteralExpr.compareTo(maxOptionLiteral) >= 0) {
- maxOptionLiteral = optionLiteralExpr;
- maxOptionValue = optionStats.maxValue;
- }
-
- if (minOptionLiteral == null || optionLiteralExpr.compareTo(minOptionLiteral) <= 0) {
- minOptionLiteral = optionLiteralExpr;
- minOptionValue = optionStats.minValue;
- }
- }
- } else {
- nonLiteralOptionCount++;
- }
- }
- if (nonLiteralOptionCount > 0) {
- // A in (x+1, ...)
- // "x+1" is not literal, and if const-fold can not handle it, it blocks estimation of min/max value.
- // and hence, we do not adjust compareExpr.stats.range.
- int newNdv = nonLiteralOptionCount + validInOptCount;
- if (newNdv < compareExprStats.ndv) {
- compareExprStatsBuilder.setNdv(newNdv);
- selectivity = StatsMathUtil.divide(newNdv, compareExprStats.ndv);
- } else {
- selectivity = 1.0;
- }
- } else {
- maxOptionValue = Math.min(maxOptionValue, compareExprStats.maxValue);
- minOptionValue = Math.max(minOptionValue, compareExprStats.minValue);
- compareExprStatsBuilder.setMaxValue(maxOptionValue);
- compareExprStatsBuilder.setMaxExpr(maxOptionLiteral);
- compareExprStatsBuilder.setMinValue(minOptionValue);
- compareExprStatsBuilder.setMinExpr(minOptionLiteral);
- if (validInOptCount < compareExprStats.ndv) {
- compareExprStatsBuilder.setNdv(validInOptCount);
- selectivity = StatsMathUtil.divide(validInOptCount, compareExprStats.ndv);
- } else {
- selectivity = 1.0;
+ double validOptionNdv = compareExprStats.ndvIntersection(optionStats);
+ if (validOptionNdv > 0.0) {
+ validInOptCount += validOptionNdv;
+ maxOption = Math.max(optionStats.maxValue, maxOption);
+ minOption = Math.min(optionStats.minValue, minOption);
}
}
+ maxOption = Math.min(maxOption, compareExprStats.maxValue);
+ minOption = Math.max(minOption, compareExprStats.minValue);
+ compareExprStatsBuilder.setMaxValue(maxOption);
+ compareExprStatsBuilder.setMinValue(minOption);
+
+ selectivity = StatsMathUtil.minNonNaN(1.0, validInOptCount / compareExprStats.ndv);
+ compareExprStatsBuilder.setNdv(validInOptCount);
Statistics estimated = new Statistics(context.statistics);
estimated = estimated.withSel(selectivity);
- estimated.addColumnStats(compareExpr,
- compareExprStatsBuilder.build());
- context.addKeyIfSlot(compareExpr);
+ if (compareExpr instanceof SlotReference) {
+ estimated.addColumnStats(compareExpr,
+ compareExprStatsBuilder.build());
+ }
return estimated;
}
- // Right Now, we just assume the selectivity is 1 when stats is Unknown
- private Statistics handleUnknownCase(EstimationContext context) {
- return context.statistics;
- }
-
@Override
public Statistics visitNot(Not not, EstimationContext context) {
- if (context.statistics.isInputSlotsUnknown(not.getInputSlots())) {
- return handleUnknownCase(context);
- }
- Expression child = not.child();
- Statistics childStats = child.accept(this, context);
+ Statistics childStats = new FilterEstimation().estimate(not.child(), context.statistics);
//if estimated rowCount is 0, adjust to 1 to make upper join reorder reasonable.
double rowCount = Math.max(context.statistics.getRowCount() - childStats.getRowCount(), 1);
StatisticsBuilder statisticsBuilder = new StatisticsBuilder(context.statistics).setRowCount(rowCount);
- // update key col stats
- for (Slot slot : not.child().getInputSlots()) {
- ColumnStatistic originColStats = context.statistics.findColumnStatistics(slot);
- ColumnStatistic childColStats = childStats.findColumnStatistics(slot);
- if (context.isKeySlot(slot)) {
- ColumnStatisticBuilder colBuilder = new ColumnStatisticBuilder(childColStats);
- // update column stats for
- // 1. not (A=B)
- // 2. not A in (...)
- // 3. not A is null
- // 4. not A like XXX
- colBuilder.setNumNulls(0);
- Preconditions.checkArgument(
- child instanceof EqualTo
- || child instanceof InPredicate
- || child instanceof IsNull
- || child instanceof Like,
- "Not-predicate meet unexpected child: %s", child.toSql());
- if (child instanceof Like) {
- rowCount = context.statistics.getRowCount() - childStats.getRowCount();
- colBuilder.setNdv(originColStats.ndv - childColStats.ndv);
- } else if (child instanceof InPredicate) {
- colBuilder.setNdv(originColStats.ndv - childColStats.ndv);
- colBuilder.setMinValue(originColStats.minValue)
- .setMinExpr(originColStats.minExpr)
- .setMaxValue(originColStats.maxValue)
- .setMaxExpr(originColStats.maxExpr);
- } else if (child instanceof IsNull) {
- colBuilder.setNdv(originColStats.ndv);
- colBuilder.setMinValue(originColStats.minValue)
- .setMinExpr(originColStats.minExpr)
- .setMaxValue(originColStats.maxValue)
- .setMaxExpr(originColStats.maxExpr);
- } else if (child instanceof EqualTo) {
- colBuilder.setNdv(originColStats.ndv - childColStats.ndv);
- colBuilder.setMinValue(originColStats.minValue)
- .setMinExpr(originColStats.minExpr)
- .setMaxValue(originColStats.maxValue)
- .setMaxExpr(originColStats.maxExpr);
- }
- statisticsBuilder.putColumnStatistics(slot, colBuilder.build());
+ for (Entry<Expression, ColumnStatistic> entry : context.statistics.columnStatistics().entrySet()) {
+ Expression expr = entry.getKey();
+ ColumnStatistic originColStats = entry.getValue();
+ ColumnStatistic childColStats = childStats.findColumnStatistics(expr);
+ double originNonNullCount = Math.max(originColStats.count - originColStats.numNulls, 0);
+ double childNonNullCount = Math.max(childColStats.count - childColStats.numNulls, 0);
+ double supersetValuesPerDistinctValue = StatsMathUtil.divide(originNonNullCount, originColStats.ndv);
+ double subsetValuesPerDistinctValue = StatsMathUtil.divide(childNonNullCount, childColStats.ndv);
+ double ndv;
+ if (supersetValuesPerDistinctValue <= subsetValuesPerDistinctValue) {
+ ndv = Math.max(originColStats.ndv - childColStats.ndv, 0);
+ } else {
+ ndv = originColStats.ndv;
}
+ double nullCount = Math.max(originColStats.numNulls - childColStats.numNulls, 0);
+ ColumnStatistic columnStatistic = new ColumnStatisticBuilder(originColStats)
+ .setNdv(ndv)
+ .setNumNulls(nullCount)
+ .build();
+ statisticsBuilder.putColumnStatistics(expr, columnStatistic);
}
-
return statisticsBuilder.build();
}
- @Override
- public Statistics visitIsNull(IsNull isNull, EstimationContext context) {
- ColumnStatistic childStats = ExpressionEstimation.estimate(isNull.child(), context.statistics);
- if (childStats.isUnKnown()) {
- return new StatisticsBuilder(context.statistics).build();
- }
- double outputRowCount = childStats.numNulls;
- ColumnStatisticBuilder colBuilder = new ColumnStatisticBuilder(childStats);
- colBuilder.setCount(outputRowCount).setNumNulls(outputRowCount)
- .setMaxValue(Double.POSITIVE_INFINITY)
- .setMinValue(Double.NEGATIVE_INFINITY)
- .setNdv(0);
- StatisticsBuilder builder = new StatisticsBuilder(context.statistics);
- builder.putColumnStatistics(isNull.child(), colBuilder.build());
- context.addKeyIfSlot(isNull.child());
- return builder.build();
- }
-
static class EstimationContext {
private final Statistics statistics;
- private final Set<Slot> keyColumns = Sets.newHashSet();
-
public EstimationContext(Statistics statistics) {
this.statistics = statistics;
}
-
- public void addKeyIfSlot(Expression expr) {
- if (expr instanceof Slot) {
- keyColumns.add((Slot) expr);
- }
- }
-
- public boolean isKeySlot(Expression expr) {
- if (expr instanceof Slot) {
- return keyColumns.contains((Slot) expr);
- }
- return false;
- }
}
private Statistics estimateBinaryComparisonFilter(Expression leftExpr, ColumnStatistic leftStats,
StatisticRange rightRange, EstimationContext context) {
StatisticRange leftRange =
- new StatisticRange(leftStats.minValue, leftStats.minExpr, leftStats.maxValue, leftStats.maxExpr,
- leftStats.ndv, leftExpr.getDataType());
+ new StatisticRange(leftStats.minValue, leftStats.maxValue, leftStats.ndv, leftExpr.getDataType());
StatisticRange intersectRange = leftRange.cover(rightRange);
-
- ColumnStatisticBuilder leftColumnStatisticBuilder;
- Statistics updatedStatistics;
- if (intersectRange.isEmpty()) {
- updatedStatistics = context.statistics.withRowCount(0);
- leftColumnStatisticBuilder = new ColumnStatisticBuilder(leftStats)
- .setMinValue(Double.NEGATIVE_INFINITY)
- .setMinExpr(null)
- .setMaxValue(Double.POSITIVE_INFINITY)
- .setMaxExpr(null)
- .setNdv(0)
- .setCount(0);
- } else {
- leftColumnStatisticBuilder = new ColumnStatisticBuilder(leftStats)
- .setMinValue(intersectRange.getLow())
- .setMinExpr(intersectRange.getLowExpr())
- .setMaxValue(intersectRange.getHigh())
- .setMaxExpr(intersectRange.getHighExpr())
- .setNdv(intersectRange.getDistinctValues());
- double sel = leftRange.overlapPercentWith(rightRange);
- updatedStatistics = context.statistics.withSel(sel);
- leftColumnStatisticBuilder.setCount(updatedStatistics.getRowCount());
- }
+ ColumnStatisticBuilder leftColumnStatisticBuilder = new ColumnStatisticBuilder(leftStats)
+ .setMinValue(intersectRange.getLow())
+ .setMaxValue(intersectRange.getHigh())
+ .setNdv(intersectRange.getDistinctValues());
+ double sel = leftRange.overlapPercentWith(rightRange);
+ Statistics updatedStatistics = context.statistics.withSel(sel);
+ leftColumnStatisticBuilder.setCount(updatedStatistics.getRowCount());
updatedStatistics.addColumnStats(leftExpr, leftColumnStatisticBuilder.build());
- context.addKeyIfSlot(leftExpr);
leftExpr.accept(new ColumnStatsAdjustVisitor(), updatedStatistics);
return updatedStatistics;
}
@@ -498,17 +381,36 @@ public class FilterEstimation extends ExpressionVisitor<Statistics, EstimationCo
StatisticRange leftRange = StatisticRange.from(leftStats, leftExpr.getDataType());
StatisticRange rightRange = StatisticRange.from(rightStats, rightExpr.getDataType());
StatisticRange leftIntersectRight = leftRange.intersect(rightRange);
- StatisticRange intersect = rightRange.intersect(leftIntersectRight);
- ColumnStatisticBuilder intersectBuilder = new ColumnStatisticBuilder(leftStats);
- intersectBuilder.setNdv(intersect.getDistinctValues());
- intersectBuilder.setMinValue(intersect.getLow());
- intersectBuilder.setMaxValue(intersect.getHigh());
- double sel = 1 / StatsMathUtil.nonZeroDivisor(Math.max(leftStats.ndv, rightStats.ndv));
+ StatisticRange rightIntersectLeft = rightRange.intersect(leftIntersectRight);
+ ColumnStatisticBuilder leftBuilder = new ColumnStatisticBuilder(leftStats);
+ leftBuilder.setNdv(leftIntersectRight.getDistinctValues());
+ leftBuilder.setMinValue(leftIntersectRight.getLow());
+ leftBuilder.setMaxValue(leftIntersectRight.getHigh());
+ ColumnStatisticBuilder rightBuilder = new ColumnStatisticBuilder(rightStats);
+ rightBuilder.setNdv(rightIntersectLeft.getDistinctValues());
+ rightBuilder.setMinValue(rightIntersectLeft.getLow());
+ rightBuilder.setMaxValue(rightIntersectLeft.getDistinctValues());
+ double sel;
+ double reduceRatio = 0.25;
+ double bothSideReducedRatio = 0.9;
+ if (!leftStats.rangeChanged() && !rightStats.rangeChanged()
+ && leftStats.ndv < leftStats.getOriginalNdv() * bothSideReducedRatio
+ && rightStats.ndv < rightStats.getOriginalNdv() * bothSideReducedRatio) {
+ double sel1;
+ if (leftStats.ndv > rightStats.ndv) {
+ sel1 = 1 / StatsMathUtil.nonZeroDivisor(leftStats.ndv);
+ } else {
+ sel1 = 1 / StatsMathUtil.nonZeroDivisor(rightStats.ndv);
+ }
+ double sel2 = Math.min(rightStats.ndv / rightStats.getOriginalNdv(),
+ leftStats.ndv / leftStats.getOriginalNdv());
+ sel = sel1 * Math.pow(sel2, reduceRatio);
+ } else {
+ sel = 1 / StatsMathUtil.nonZeroDivisor(Math.max(leftStats.ndv, rightStats.ndv));
+ }
Statistics updatedStatistics = context.statistics.withSel(sel);
- updatedStatistics.addColumnStats(leftExpr, intersectBuilder.build());
- updatedStatistics.addColumnStats(rightExpr, intersectBuilder.build());
- context.addKeyIfSlot(leftExpr);
- context.addKeyIfSlot(rightExpr);
+ updatedStatistics.addColumnStats(leftExpr, leftBuilder.build());
+ updatedStatistics.addColumnStats(rightExpr, rightBuilder.build());
return updatedStatistics;
}
@@ -524,8 +426,6 @@ public class FilterEstimation extends ExpressionVisitor<Statistics, EstimationCo
context.statistics.getRowCount() - rightStats.numNulls));
statistics.addColumnStats(leftExpr, new ColumnStatisticBuilder(leftStats).setNumNulls(0.0).build());
statistics.addColumnStats(rightExpr, new ColumnStatisticBuilder(rightStats).setNumNulls(0.0).build());
- context.addKeyIfSlot(leftExpr);
- context.addKeyIfSlot(rightExpr);
return statistics;
}
double leftOverlapPercent = leftRange.overlapPercentWith(rightRange);
@@ -533,8 +433,8 @@ public class FilterEstimation extends ExpressionVisitor<Statistics, EstimationCo
if (leftOverlapPercent == 0) {
return context.statistics.withRowCount(0.0);
}
- StatisticRange leftAlwaysLessThanRightRange = new StatisticRange(leftStats.minValue, leftStats.minExpr,
- rightStats.minValue, rightStats.minExpr, Double.NaN, leftExpr.getDataType());
+ StatisticRange leftAlwaysLessThanRightRange = new StatisticRange(leftStats.minValue,
+ rightStats.minValue, Double.NaN, leftExpr.getDataType());
double leftAlwaysLessThanRightPercent = 0;
if (leftRange.getLow() < rightRange.getLow()) {
leftAlwaysLessThanRightPercent = leftRange.overlapPercentWith(leftAlwaysLessThanRightRange);
@@ -548,10 +448,8 @@ public class FilterEstimation extends ExpressionVisitor<Statistics, EstimationCo
double rightOverlappingRangeFraction = rightRange.overlapPercentWith(leftRange);
double rightAlwaysGreaterRangeFraction = 0;
if (leftRange.getHigh() < rightRange.getHigh()) {
- rightAlwaysGreaterRangeFraction = rightRange.overlapPercentWith(new StatisticRange(
- leftRange.getHigh(), leftRange.getHighExpr(),
- rightRange.getHigh(), rightRange.getHighExpr(),
- Double.NaN, rightExpr.getDataType()));
+ rightAlwaysGreaterRangeFraction = rightRange.overlapPercentWith(new StatisticRange(leftRange.getHigh(),
+ rightRange.getHigh(), Double.NaN, rightExpr.getDataType()));
}
ColumnStatistic rightColumnStatistic = new ColumnStatisticBuilder(rightStats)
.setMinValue(Math.max(leftRange.getLow(), rightRange.getLow()))
@@ -562,8 +460,6 @@ public class FilterEstimation extends ExpressionVisitor<Statistics, EstimationCo
double sel = leftAlwaysLessThanRightPercent
+ leftOverlapPercent * rightOverlappingRangeFraction * DEFAULT_INEQUALITY_COEFFICIENT
+ leftOverlapPercent * rightAlwaysGreaterRangeFraction;
- context.addKeyIfSlot(leftExpr);
- context.addKeyIfSlot(rightExpr);
return context.statistics.withSel(sel)
.addColumnStats(leftExpr, leftColumnStatistic)
.addColumnStats(rightExpr, rightColumnStatistic);
@@ -597,7 +493,6 @@ public class FilterEstimation extends ExpressionVisitor<Statistics, EstimationCo
.setMaxValue(numVal)
.setHistogram(new HistogramBuilder(leftHist).setBuckets(updatedBucketList).build())
.build();
- context.addKeyIfSlot(leftExpr);
return context.statistics.withSel(sel).addColumnStats(leftExpr, columnStatistic);
}
}
@@ -625,7 +520,7 @@ public class FilterEstimation extends ExpressionVisitor<Statistics, EstimationCo
double overlapCountInBucket = overlapPercentInBucket * bucket.count;
double sel = StatsMathUtil.minNonNaN(1,
(leftHist.size() - bucket.preSum - (bucket.count - overlapCountInBucket))
- / context.statistics.getRowCount());
+ / context.statistics.getRowCount());
List<Bucket> updatedBucketList = new ArrayList<>();
updatedBucketList.add(new Bucket(numVal, bucket.upper, overlapPercentInBucket * bucket.count,
0, overlapPercentInBucket * bucket.ndv));
@@ -634,7 +529,6 @@ public class FilterEstimation extends ExpressionVisitor<Statistics, EstimationCo
.setMaxValue(numVal)
.setHistogram(new HistogramBuilder(leftHist).setBuckets(updatedBucketList).build())
.build();
- context.addKeyIfSlot(leftExpr);
return context.statistics.withSel(sel).addColumnStats(leftExpr, columnStatistic);
}
}
@@ -662,24 +556,11 @@ public class FilterEstimation extends ExpressionVisitor<Statistics, EstimationCo
.setMaxValue(numVal)
.setMinValue(numVal)
.build();
- context.addKeyIfSlot(leftExpr);
return context.statistics.withSel(sel).addColumnStats(leftExpr, columnStatistic);
}
@Override
public Statistics visitLike(Like like, EstimationContext context) {
- StatisticsBuilder statsBuilder = new StatisticsBuilder(context.statistics);
- statsBuilder.setRowCount(context.statistics.getRowCount() * DEFAULT_LIKE_COMPARISON_SELECTIVITY);
- if (like.left() instanceof Slot) {
- ColumnStatistic origin = context.statistics.findColumnStatistics(like.left());
- Preconditions.checkArgument(origin != null,
- "col stats not found. slot=%s in %s",
- like.left().toSql(), like.toSql());
- ColumnStatisticBuilder colBuilder = new ColumnStatisticBuilder(origin);
- colBuilder.setNdv(origin.ndv * DEFAULT_LIKE_COMPARISON_SELECTIVITY).setNumNulls(0);
- statsBuilder.putColumnStatistics(like.left(), colBuilder.build());
- context.addKeyIfSlot(like.left());
- }
- return statsBuilder.build();
+ return context.statistics.withSel(DEFAULT_LIKE_COMPARISON_SELECTIVITY);
}
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/JoinEstimation.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/JoinEstimation.java
index ef4575e3308..a40e409fae4 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/JoinEstimation.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/JoinEstimation.java
@@ -72,7 +72,16 @@ public class JoinEstimation {
return false;
}
- private static Statistics estimateHashJoin(Statistics leftStats, Statistics rightStats, Join join) {
+ private static Statistics estimateInnerJoin(Statistics leftStats, Statistics rightStats, Join join) {
+ if (hashJoinConditionContainsUnknownColumnStats(leftStats, rightStats, join)) {
+ double rowCount = Math.max(leftStats.getRowCount(), rightStats.getRowCount());
+ rowCount = Math.max(1, rowCount);
+ return new StatisticsBuilder()
+ .setRowCount(rowCount)
+ .putColumnStatistics(leftStats.columnStatistics())
+ .putColumnStatistics(rightStats.columnStatistics())
+ .build();
+ }
/*
* When we estimate filter A=B,
* if any side of equation, A or B, is almost unique, the confidence level of estimation is high.
@@ -120,7 +129,7 @@ public class JoinEstimation {
.putColumnStatistics(rightStats.columnStatistics())
.build();
- double outputRowCount;
+ double outputRowCount = 1;
if (!trustableConditions.isEmpty()) {
List<Double> joinConditionSels = trustableConditions.stream()
.map(expression -> estimateJoinConditionSel(crossJoinStats, expression))
@@ -129,47 +138,20 @@ public class JoinEstimation {
double sel = 1.0;
double denominator = 1.0;
- for (Double joinConditionSel : joinConditionSels) {
- sel *= Math.pow(joinConditionSel, 1 / denominator);
+ for (int i = 0; i < joinConditionSels.size(); i++) {
+ sel *= Math.pow(joinConditionSels.get(i), 1 / denominator);
denominator *= 2;
}
outputRowCount = Math.max(1, crossJoinStats.getRowCount() * sel);
outputRowCount = outputRowCount * Math.pow(0.9, unTrustableCondition.size());
+ innerJoinStats = crossJoinStats.updateRowCountOnly(outputRowCount);
} else {
outputRowCount = Math.max(leftStats.getRowCount(), rightStats.getRowCount());
Optional<Double> ratio = unTrustEqualRatio.stream().min(Double::compareTo);
if (ratio.isPresent()) {
outputRowCount = Math.max(1, outputRowCount * ratio.get());
}
- }
- innerJoinStats = crossJoinStats.withRowCountAndEnforceValid(outputRowCount);
- return innerJoinStats;
- }
-
- private static Statistics estimateNestLoopJoin(Statistics leftStats, Statistics rightStats, Join join) {
- return new StatisticsBuilder()
- .setRowCount(Math.max(1, leftStats.getRowCount() * rightStats.getRowCount()))
- .putColumnStatistics(leftStats.columnStatistics())
- .putColumnStatistics(rightStats.columnStatistics())
- .build();
- }
-
- private static Statistics estimateInnerJoin(Statistics leftStats, Statistics rightStats, Join join) {
- if (hashJoinConditionContainsUnknownColumnStats(leftStats, rightStats, join)) {
- double rowCount = Math.max(leftStats.getRowCount(), rightStats.getRowCount());
- rowCount = Math.max(1, rowCount);
- return new StatisticsBuilder()
- .setRowCount(rowCount)
- .putColumnStatistics(leftStats.columnStatistics())
- .putColumnStatistics(rightStats.columnStatistics())
- .build();
- }
-
- Statistics innerJoinStats;
- if (join.getHashJoinConjuncts().isEmpty()) {
- innerJoinStats = estimateNestLoopJoin(leftStats, rightStats, join);
- } else {
- innerJoinStats = estimateHashJoin(leftStats, rightStats, join);
+ innerJoinStats = crossJoinStats.updateRowCountOnly(outputRowCount);
}
if (!join.getOtherJoinConjuncts().isEmpty()) {
@@ -180,6 +162,9 @@ public class JoinEstimation {
innerJoinStats = new StatisticsBuilder(innerJoinStats).setRowCount(1).build();
}
}
+
+ innerJoinStats.setWidth(leftStats.getWidth() + rightStats.getWidth());
+ innerJoinStats.setPenalty(0);
return innerJoinStats;
}
@@ -257,9 +242,10 @@ public class JoinEstimation {
double baseRowCount =
join.getJoinType().isLeftSemiOrAntiJoin() ? leftStats.getRowCount() : rightStats.getRowCount();
rowCount = Math.min(innerJoinStats.getRowCount(), baseRowCount);
- return innerJoinStats.withRowCountAndEnforceValid(rowCount);
+ return innerJoinStats.withRowCount(rowCount);
} else {
StatisticsBuilder builder;
+ double originalRowCount = leftStats.getRowCount();
if (join.getJoinType().isLeftSemiOrAntiJoin()) {
builder = new StatisticsBuilder(leftStats);
builder.setRowCount(rowCount);
@@ -267,9 +253,10 @@ public class JoinEstimation {
//right semi or anti
builder = new StatisticsBuilder(rightStats);
builder.setRowCount(rowCount);
+ originalRowCount = rightStats.getRowCount();
}
Statistics outputStats = builder.build();
- outputStats.enforceValid();
+ outputStats.fix(rowCount, originalRowCount);
return outputStats;
}
}
@@ -289,15 +276,15 @@ public class JoinEstimation {
Statistics innerJoinStats = estimateInnerJoin(leftStats, rightStats, join);
double rowCount = Math.max(leftStats.getRowCount(), innerJoinStats.getRowCount());
rowCount = Math.max(leftStats.getRowCount(), rowCount);
- return innerJoinStats.withRowCountAndEnforceValid(rowCount);
+ return innerJoinStats.withRowCount(rowCount);
} else if (joinType == JoinType.RIGHT_OUTER_JOIN) {
Statistics innerJoinStats = estimateInnerJoin(leftStats, rightStats, join);
double rowCount = Math.max(rightStats.getRowCount(), innerJoinStats.getRowCount());
rowCount = Math.max(rowCount, rightStats.getRowCount());
- return innerJoinStats.withRowCountAndEnforceValid(rowCount);
+ return innerJoinStats.withRowCount(rowCount);
} else if (joinType == JoinType.FULL_OUTER_JOIN) {
Statistics innerJoinStats = estimateInnerJoin(leftStats, rightStats, join);
- return innerJoinStats.withRowCountAndEnforceValid(leftStats.getRowCount()
+ return innerJoinStats.withRowCount(leftStats.getRowCount()
+ rightStats.getRowCount() + innerJoinStats.getRowCount());
} else if (joinType == JoinType.CROSS_JOIN) {
return new StatisticsBuilder()
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/StatsCalculator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/StatsCalculator.java
index 48ea4c9800d..6910f5b4af3 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/StatsCalculator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/StatsCalculator.java
@@ -17,7 +17,10 @@
package org.apache.doris.nereids.stats;
+import org.apache.doris.catalog.Column;
import org.apache.doris.catalog.Env;
+import org.apache.doris.catalog.OlapTable;
+import org.apache.doris.catalog.SchemaTable;
import org.apache.doris.catalog.TableIf;
import org.apache.doris.common.Config;
import org.apache.doris.common.FeConstants;
@@ -617,7 +620,7 @@ public class StatsCalculator extends DefaultPlanVisitor<Statistics, Void> {
double rowCount = catalogRelation.getTable().estimatedRowCount();
for (SlotReference slotReference : slotSet) {
String colName = slotReference.getName();
- boolean shouldIgnoreThisCol = StatisticConstants.shouldIgnoreCol(table, slotReference.getColumn().get());
+ boolean shouldIgnoreThisCol = shouldIgnoreCol(table, slotReference.getColumn().get());
if (colName == null) {
throw new RuntimeException(String.format("Invalid slot: %s", slotReference.getExprId()));
@@ -655,20 +658,16 @@ public class StatsCalculator extends DefaultPlanVisitor<Statistics, Void> {
}
private Statistics computePartitionTopN(PartitionTopN partitionTopN) {
- Statistics childStats = groupExpression.childStatistics(0);
- double rowCount = childStats.getRowCount();
+ Statistics stats = groupExpression.childStatistics(0);
+ double rowCount = stats.getRowCount();
List<Expression> partitionKeys = partitionTopN.getPartitionKeys();
if (!partitionTopN.hasGlobalLimit() && !partitionKeys.isEmpty()) {
// If there is no global limit. So result for the cardinality estimation is:
// NDV(partition key) * partitionLimit
+ Map<Expression, ColumnStatistic> childSlotToColumnStats = stats.columnStatistics();
List<ColumnStatistic> partitionByKeyStats = partitionKeys.stream()
- .map(partitionKey -> {
- ColumnStatistic partitionKeyStats = childStats.findColumnStatistics(partitionKey);
- if (partitionKeyStats == null) {
- partitionKeyStats = new ExpressionEstimation().visit(partitionKey, childStats);
- }
- return partitionKeyStats;
- })
+ .filter(childSlotToColumnStats::containsKey)
+ .map(childSlotToColumnStats::get)
.filter(s -> !s.isUnKnown)
.collect(Collectors.toList());
if (partitionByKeyStats.isEmpty()) {
@@ -676,7 +675,7 @@ public class StatsCalculator extends DefaultPlanVisitor<Statistics, Void> {
rowCount = rowCount * DEFAULT_COLUMN_NDV_RATIO;
} else {
rowCount = Math.min(rowCount, partitionByKeyStats.stream().map(s -> s.ndv)
- .max(Double::compare).get() * partitionTopN.getPartitionLimit());
+ .max(Double::compare).get());
}
} else {
rowCount = Math.min(rowCount, partitionTopN.getPartitionLimit());
@@ -684,7 +683,7 @@ public class StatsCalculator extends DefaultPlanVisitor<Statistics, Void> {
// TODO: for the filter push down window situation, we will prune the row count twice
// because we keep the pushed down filter. And it will be calculated twice, one of them in 'PartitionTopN'
// and the other is in 'Filter'. It's hard to dismiss.
- return childStats.withRowCountAndEnforceValid(rowCount);
+ return stats.updateRowCountOnly(rowCount);
}
private Statistics computeLimit(Limit limit) {
@@ -753,7 +752,9 @@ public class StatsCalculator extends DefaultPlanVisitor<Statistics, Void> {
builder.setDataSize(rowCount * outputExpression.getDataType().width());
slotToColumnStats.put(outputExpression.toSlot(), columnStat);
}
- return new Statistics(rowCount, slotToColumnStats);
+ return new Statistics(rowCount, slotToColumnStats, childStats.getWidth(),
+ childStats.getPenalty() + childStats.getRowCount());
+ // TODO: Update ColumnStats properly, add new mapping from output slot to ColumnStats
}
private Statistics computeRepeat(Repeat<? extends Plan> repeat) {
@@ -771,7 +772,8 @@ public class StatsCalculator extends DefaultPlanVisitor<Statistics, Void> {
.setDataSize(stats.dataSize < 0 ? stats.dataSize : stats.dataSize * groupingSetNum);
return Pair.of(kv.getKey(), columnStatisticBuilder.build());
}).collect(Collectors.toMap(Pair::key, Pair::value));
- return new Statistics(rowCount < 0 ? rowCount : rowCount * groupingSetNum, columnStatisticMap);
+ return new Statistics(rowCount < 0 ? rowCount : rowCount * groupingSetNum, columnStatisticMap,
+ childStats.getWidth(), childStats.getPenalty());
}
private Statistics computeProject(Project project) {
@@ -781,7 +783,7 @@ public class StatsCalculator extends DefaultPlanVisitor<Statistics, Void> {
ColumnStatistic columnStatistic = ExpressionEstimation.estimate(projection, childStats);
return new SimpleEntry<>(projection.toSlot(), columnStatistic);
}).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (item1, item2) -> item1));
- return new Statistics(childStats.getRowCount(), columnsStats);
+ return new Statistics(childStats.getRowCount(), columnsStats, childStats.getWidth(), childStats.getPenalty());
}
private Statistics computeOneRowRelation(List<NamedExpression> projects) {
@@ -1065,4 +1067,16 @@ public class StatsCalculator extends DefaultPlanVisitor<Statistics, Void> {
return groupExpression.childStatistics(1);
}
+ private boolean shouldIgnoreCol(TableIf tableIf, Column c) {
+ if (tableIf instanceof SchemaTable) {
+ return true;
+ }
+ if (tableIf instanceof OlapTable) {
+ OlapTable olapTable = (OlapTable) tableIf;
+ if (StatisticConstants.STATISTICS_DB_BLACK_LIST.contains(olapTable.getQualifiedDbName())) {
+ return true;
+ }
+ }
+ return !c.isVisible();
+ }
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Properties.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Properties.java
deleted file mode 100644
index db0c78c1f78..00000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Properties.java
+++ /dev/null
@@ -1,96 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements. See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership. The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License. You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied. See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.nereids.trees.expressions;
-
-import org.apache.doris.nereids.exceptions.UnboundException;
-import org.apache.doris.nereids.trees.expressions.shape.LeafExpression;
-import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
-import org.apache.doris.nereids.types.DataType;
-import org.apache.doris.nereids.types.MapType;
-
-import com.google.common.collect.ImmutableList;
-
-import java.util.Map;
-import java.util.Objects;
-import java.util.stream.Collectors;
-
-/**
- * Properties
- */
-public class Properties extends Expression implements LeafExpression {
-
- private final Map<String, String> keyValues;
-
- public Properties(Map<String, String> properties) {
- super(ImmutableList.of());
- this.keyValues = Objects.requireNonNull(properties, "properties can not be null");
- }
-
- public Map<String, String> getMap() {
- return keyValues;
- }
-
- @Override
- public boolean nullable() {
- return false;
- }
-
- @Override
- public DataType getDataType() throws UnboundException {
- return MapType.SYSTEM_DEFAULT;
- }
-
- @Override
- public String toSql() {
- return getMap()
- .entrySet()
- .stream()
- .map(kv -> "'" + kv.getKey() + "' = '" + kv.getValue() + "'")
- .collect(Collectors.joining(", "));
- }
-
- @Override
- public String toString() {
- return "Properties(" + toSql() + ")";
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- if (!super.equals(o)) {
- return false;
- }
- Properties that = (Properties) o;
- return Objects.equals(keyValues, that.keyValues);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(super.hashCode(), keyValues);
- }
-
- @Override
- public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
- return visitor.visitProperties(this, context);
- }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/table/Hdfs.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/table/Hdfs.java
index c66684cd22c..d82878c4a48 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/table/Hdfs.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/table/Hdfs.java
@@ -19,7 +19,7 @@ package org.apache.doris.nereids.trees.expressions.functions.table;
import org.apache.doris.catalog.FunctionSignature;
import org.apache.doris.nereids.exceptions.AnalysisException;
-import org.apache.doris.nereids.trees.expressions.Properties;
+import org.apache.doris.nereids.trees.expressions.TVFProperties;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.coercion.AnyDataType;
import org.apache.doris.tablefunction.HdfsTableValuedFunction;
@@ -30,13 +30,13 @@ import java.util.Map;
/** hdfs */
public class Hdfs extends TableValuedFunction {
- public Hdfs(Properties properties) {
+ public Hdfs(TVFProperties properties) {
super("hdfs", properties);
}
@Override
public FunctionSignature customSignature() {
- return FunctionSignature.of(AnyDataType.INSTANCE_WITHOUT_INDEX, (List) getArgumentsTypes());
+ return FunctionSignature.of(AnyDataType.INSTANCE, (List) getArgumentsTypes());
}
@Override
@@ -46,7 +46,7 @@ public class Hdfs extends TableValuedFunction {
return new HdfsTableValuedFunction(arguments);
} catch (Throwable t) {
throw new AnalysisException("Can not build HdfsTableValuedFunction by "
- + this + ": " + t.getMessage(), t);
+ + this + ": " + t.getMessage(), t);
}
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/table/Local.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/table/Local.java
index d45a4c93943..cd4169c1c95 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/table/Local.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/table/Local.java
@@ -19,7 +19,7 @@ package org.apache.doris.nereids.trees.expressions.functions.table;
import org.apache.doris.catalog.FunctionSignature;
import org.apache.doris.nereids.exceptions.AnalysisException;
-import org.apache.doris.nereids.trees.expressions.Properties;
+import org.apache.doris.nereids.trees.expressions.TVFProperties;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.coercion.AnyDataType;
import org.apache.doris.tablefunction.LocalTableValuedFunction;
@@ -31,13 +31,13 @@ import java.util.Map;
* local
*/
public class Local extends TableValuedFunction {
- public Local(Properties properties) {
+ public Local(TVFProperties properties) {
super("local", properties);
}
@Override
public FunctionSignature customSignature() {
- return FunctionSignature.of(AnyDataType.INSTANCE_WITHOUT_INDEX, getArgumentsTypes());
+ return FunctionSignature.of(AnyDataType.INSTANCE, getArgumentsTypes());
}
@Override
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/table/Numbers.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/table/Numbers.java
index c5febcf9749..0043ab9c1f7 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/table/Numbers.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/table/Numbers.java
@@ -24,8 +24,8 @@ import org.apache.doris.common.NereidsException;
import org.apache.doris.nereids.exceptions.AnalysisException;
import org.apache.doris.nereids.properties.PhysicalProperties;
import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.expressions.Properties;
import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.TVFProperties;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.BigIntType;
import org.apache.doris.statistics.ColumnStatistic;
@@ -42,7 +42,7 @@ import java.util.Map;
/** Numbers */
public class Numbers extends TableValuedFunction {
- public Numbers(Properties properties) {
+ public Numbers(TVFProperties properties) {
super("numbers", properties);
}
@@ -72,7 +72,7 @@ public class Numbers extends TableValuedFunction {
Map<Expression, ColumnStatistic> columnToStatistics = Maps.newHashMap();
ColumnStatistic columnStat = new ColumnStatisticBuilder()
.setCount(rowNum).setNdv(rowNum).setAvgSizeByte(8).setNumNulls(0).setDataSize(8).setMinValue(0)
- .setMaxValue(rowNum - 1)
+ .setMaxValue(rowNum - 1).setSelectivity(1.0 / rowNum)
.setMinExpr(new IntLiteral(0, Type.BIGINT))
.setMaxExpr(new IntLiteral(rowNum - 1, Type.BIGINT))
.build();
@@ -101,7 +101,7 @@ public class Numbers extends TableValuedFunction {
@Override
public Numbers withChildren(List<Expression> children) {
Preconditions.checkArgument(children().size() == 1
- && children().get(0) instanceof Properties);
- return new Numbers((Properties) children.get(0));
+ && children().get(0) instanceof TVFProperties);
+ return new Numbers((TVFProperties) children.get(0));
}
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/table/S3.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/table/S3.java
index 29d8ad08218..d871219b7a8 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/table/S3.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/table/S3.java
@@ -19,7 +19,7 @@ package org.apache.doris.nereids.trees.expressions.functions.table;
import org.apache.doris.catalog.FunctionSignature;
import org.apache.doris.nereids.exceptions.AnalysisException;
-import org.apache.doris.nereids.trees.expressions.Properties;
+import org.apache.doris.nereids.trees.expressions.TVFProperties;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.coercion.AnyDataType;
import org.apache.doris.tablefunction.S3TableValuedFunction;
@@ -29,13 +29,13 @@ import java.util.Map;
/** s3 */
public class S3 extends TableValuedFunction {
- public S3(Properties properties) {
+ public S3(TVFProperties properties) {
super("s3", properties);
}
@Override
public FunctionSignature customSignature() {
- return FunctionSignature.of(AnyDataType.INSTANCE_WITHOUT_INDEX, getArgumentsTypes());
+ return FunctionSignature.of(AnyDataType.INSTANCE, getArgumentsTypes());
}
@Override
@@ -45,7 +45,7 @@ public class S3 extends TableValuedFunction {
return new S3TableValuedFunction(arguments);
} catch (Throwable t) {
throw new AnalysisException("Can not build S3TableValuedFunction by "
- + this + ": " + t.getMessage(), t);
+ + this + ": " + t.getMessage(), t);
}
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/table/TableValuedFunction.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/table/TableValuedFunction.java
index 5acc73eb75a..6443eab9728 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/table/TableValuedFunction.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/table/TableValuedFunction.java
@@ -23,8 +23,8 @@ import org.apache.doris.nereids.exceptions.AnalysisException;
import org.apache.doris.nereids.exceptions.UnboundException;
import org.apache.doris.nereids.properties.PhysicalProperties;
import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.expressions.Properties;
import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.TVFProperties;
import org.apache.doris.nereids.trees.expressions.functions.BoundFunction;
import org.apache.doris.nereids.trees.expressions.functions.CustomSignature;
import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression;
@@ -57,7 +57,7 @@ public abstract class TableValuedFunction extends BoundFunction implements Unary
}
});
- public TableValuedFunction(String functionName, Properties tvfProperties) {
+ public TableValuedFunction(String functionName, TVFProperties tvfProperties) {
super(functionName, tvfProperties);
}
@@ -78,8 +78,8 @@ public abstract class TableValuedFunction extends BoundFunction implements Unary
return new Statistics(0, columnToStatistics);
}
- public Properties getTVFProperties() {
- return (Properties) child(0);
+ public TVFProperties getTVFProperties() {
+ return (TVFProperties) child(0);
}
public final String getTableName() {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitor.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitor.java
index d95f35a6f69..4517f444e0b 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitor.java
@@ -63,7 +63,6 @@ import org.apache.doris.nereids.trees.expressions.Not;
import org.apache.doris.nereids.trees.expressions.NullSafeEqual;
import org.apache.doris.nereids.trees.expressions.Or;
import org.apache.doris.nereids.trees.expressions.OrderExpression;
-import org.apache.doris.nereids.trees.expressions.Properties;
import org.apache.doris.nereids.trees.expressions.ScalarSubquery;
import org.apache.doris.nereids.trees.expressions.Slot;
import org.apache.doris.nereids.trees.expressions.SlotReference;
@@ -118,10 +117,6 @@ public abstract class ExpressionVisitor<R, C>
public abstract R visit(Expression expr, C context);
- public R visitProperties(Properties properties, C context) {
- return visit(properties, context);
- }
-
@Override
public R visitAggregateFunction(AggregateFunction aggregateFunction, C context) {
return visitBoundFunction(aggregateFunction, context);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/MapType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/MapType.java
index c576dcb9933..40e0dae0fdb 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/MapType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/MapType.java
@@ -29,8 +29,6 @@ public class MapType extends DataType {
public static final MapType INSTANCE = new MapType();
- public static final MapType SYSTEM_DEFAULT = new MapType();
-
public static final int WIDTH = 24;
private MapType() {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/AnyDataType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/AnyDataType.java
index e1097df65f1..be07a7ee7b8 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/AnyDataType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/AnyDataType.java
@@ -26,18 +26,7 @@ import org.apache.doris.nereids.types.DataType;
*/
public class AnyDataType implements AbstractDataType {
- public static final AnyDataType INSTANCE_WITHOUT_INDEX = new AnyDataType(-1);
-
- public static final AnyDataType INSTANCE = new AnyDataType(-1);
-
- private final int index;
-
- public AnyDataType(int index) {
- if (index < 0) {
- index = -1;
- }
- this.index = index;
- }
+ public static final AnyDataType INSTANCE = new AnyDataType();
@Override
public DataType defaultConcreteType() {
@@ -58,9 +47,4 @@ public class AnyDataType implements AbstractDataType {
public String simpleString() {
return "any";
}
-
- public int getIndex() {
- return index;
- }
-
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/persist/EditLog.java b/fe/fe-core/src/main/java/org/apache/doris/persist/EditLog.java
index 2703165b428..97dd6719e9b 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/persist/EditLog.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/persist/EditLog.java
@@ -82,7 +82,6 @@ import org.apache.doris.policy.Policy;
import org.apache.doris.policy.StoragePolicy;
import org.apache.doris.resource.workloadgroup.WorkloadGroup;
import org.apache.doris.statistics.AnalysisInfo;
-import org.apache.doris.statistics.TableStatsMeta;
import org.apache.doris.system.Backend;
import org.apache.doris.system.Frontend;
import org.apache.doris.transaction.TransactionState;
@@ -1066,16 +1065,16 @@ public class EditLog {
env.getBinlogManager().addBarrierLog(log, logId);
break;
}
+ // For backward compatible with 2.0.3
case OperationType.OP_UPDATE_TABLE_STATS: {
- env.getAnalysisManager().replayUpdateTableStatsStatus((TableStatsMeta) journal.getData());
break;
}
+ // For backward compatible with 2.0.3
case OperationType.OP_PERSIST_AUTO_JOB: {
- env.getAnalysisManager().replayPersistSysJob((AnalysisInfo) journal.getData());
break;
}
+ // For backward compatible with 2.0.3
case OperationType.OP_DELETE_TABLE_STATS: {
- env.getAnalysisManager().replayTableStatsDeletion((TableStatsDeletionLog) journal.getData());
break;
}
default: {
@@ -1881,16 +1880,4 @@ public class EditLog {
LOG.info("logId {}, barrier {}", logId, log);
return logId;
}
-
- public void logCreateTableStats(TableStatsMeta tableStats) {
- logEdit(OperationType.OP_UPDATE_TABLE_STATS, tableStats);
- }
-
- public void logAutoJob(AnalysisInfo analysisInfo) {
- logEdit(OperationType.OP_PERSIST_AUTO_JOB, analysisInfo);
- }
-
- public void logDeleteTableStats(TableStatsDeletionLog log) {
- logEdit(OperationType.OP_DELETE_TABLE_STATS, log);
- }
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/persist/TableStatsDeletionLog.java b/fe/fe-core/src/main/java/org/apache/doris/persist/TableStatsDeletionLog.java
deleted file mode 100644
index 4016ff0139e..00000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/persist/TableStatsDeletionLog.java
+++ /dev/null
@@ -1,47 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements. See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership. The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License. You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied. See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.persist;
-
-import org.apache.doris.common.io.Text;
-import org.apache.doris.common.io.Writable;
-import org.apache.doris.persist.gson.GsonUtils;
-
-import com.google.gson.annotations.SerializedName;
-
-import java.io.DataInput;
-import java.io.DataOutput;
-import java.io.IOException;
-
-public class TableStatsDeletionLog implements Writable {
-
- @SerializedName("id")
- public final long id;
-
- public TableStatsDeletionLog(long id) {
- this.id = id;
- }
-
- @Override
- public void write(DataOutput out) throws IOException {
- Text.writeString(out, GsonUtils.GSON.toJson(this));
- }
-
- public static TableStatsDeletionLog read(DataInput dataInput) throws IOException {
- return GsonUtils.GSON.fromJson(Text.readString(dataInput), TableStatsDeletionLog.class);
- }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/AuditLogHelper.java b/fe/fe-core/src/main/java/org/apache/doris/qe/AuditLogHelper.java
deleted file mode 100644
index 40f870eee11..00000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/AuditLogHelper.java
+++ /dev/null
@@ -1,120 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements. See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership. The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License. You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied. See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.qe;
-
-import org.apache.doris.analysis.InsertStmt;
-import org.apache.doris.analysis.Queriable;
-import org.apache.doris.analysis.StatementBase;
-import org.apache.doris.catalog.Env;
-import org.apache.doris.cluster.ClusterNamespace;
-import org.apache.doris.common.Config;
-import org.apache.doris.common.util.DebugUtil;
-import org.apache.doris.metric.MetricRepo;
-import org.apache.doris.plugin.AuditEvent.EventType;
-import org.apache.doris.qe.QueryState.MysqlStateType;
-import org.apache.doris.service.FrontendOptions;
-
-import io.opentelemetry.api.trace.Span;
-import io.opentelemetry.api.trace.SpanContext;
-import io.opentelemetry.context.Context;
-import org.apache.commons.codec.digest.DigestUtils;
-
-public class AuditLogHelper {
-
- public static void logAuditLog(ConnectContext ctx, String origStmt, StatementBase parsedStmt,
- org.apache.doris.proto.Data.PQueryStatistics statistics, boolean printFuzzyVariables) {
- origStmt = origStmt.replace("\n", " ");
- // slow query
- long endTime = System.currentTimeMillis();
- long elapseMs = endTime - ctx.getStartTime();
- SpanContext spanContext = Span.fromContext(Context.current()).getSpanContext();
-
- ctx.getAuditEventBuilder().setEventType(EventType.AFTER_QUERY)
- .setDb(ClusterNamespace.getNameFromFullName(ctx.getDatabase()))
- .setState(ctx.getState().toString())
- .setErrorCode(ctx.getState().getErrorCode() == null ? 0 : ctx.getState().getErrorCode().getCode())
- .setErrorMessage((ctx.getState().getErrorMessage() == null ? "" :
- ctx.getState().getErrorMessage().replace("\n", " ").replace("\t", " ")))
- .setQueryTime(elapseMs)
- .setScanBytes(statistics == null ? 0 : statistics.getScanBytes())
- .setScanRows(statistics == null ? 0 : statistics.getScanRows())
- .setCpuTimeMs(statistics == null ? 0 : statistics.getCpuMs())
- .setPeakMemoryBytes(statistics == null ? 0 : statistics.getMaxPeakMemoryBytes())
- .setReturnRows(ctx.getReturnRows())
- .setStmtId(ctx.getStmtId())
- .setQueryId(ctx.queryId() == null ? "NaN" : DebugUtil.printId(ctx.queryId()))
- .setTraceId(spanContext.isValid() ? spanContext.getTraceId() : "")
- .setWorkloadGroup(ctx.getWorkloadGroupName())
- .setFuzzyVariables(!printFuzzyVariables ? "" : ctx.getSessionVariable().printFuzzyVariables());
-
- if (ctx.getState().isQuery()) {
- MetricRepo.COUNTER_QUERY_ALL.increase(1L);
- MetricRepo.USER_COUNTER_QUERY_ALL.getOrAdd(ctx.getQualifiedUser()).increase(1L);
- if (ctx.getState().getStateType() == MysqlStateType.ERR
- && ctx.getState().getErrType() != QueryState.ErrType.ANALYSIS_ERR) {
- // err query
- MetricRepo.COUNTER_QUERY_ERR.increase(1L);
- MetricRepo.USER_COUNTER_QUERY_ERR.getOrAdd(ctx.getQualifiedUser()).increase(1L);
- } else if (ctx.getState().getStateType() == MysqlStateType.OK
- || ctx.getState().getStateType() == MysqlStateType.EOF) {
- // ok query
- MetricRepo.HISTO_QUERY_LATENCY.update(elapseMs);
- MetricRepo.USER_HISTO_QUERY_LATENCY.getOrAdd(ctx.getQualifiedUser()).update(elapseMs);
-
- if (elapseMs > Config.qe_slow_log_ms) {
- String sqlDigest = DigestUtils.md5Hex(((Queriable) parsedStmt).toDigest());
- ctx.getAuditEventBuilder().setSqlDigest(sqlDigest);
- }
- }
- ctx.getAuditEventBuilder().setIsQuery(true);
- if (ctx.getQueryDetail() != null) {
- ctx.getQueryDetail().setEventTime(endTime);
- ctx.getQueryDetail().setEndTime(endTime);
- ctx.getQueryDetail().setLatency(elapseMs);
- ctx.getQueryDetail().setState(QueryDetail.QueryMemState.FINISHED);
- QueryDetailQueue.addOrUpdateQueryDetail(ctx.getQueryDetail());
- ctx.setQueryDetail(null);
- }
- } else {
- ctx.getAuditEventBuilder().setIsQuery(false);
- }
- ctx.getAuditEventBuilder().setIsNereids(ctx.getState().isNereids);
-
- ctx.getAuditEventBuilder().setFeIp(FrontendOptions.getLocalHostAddress());
-
- // We put origin query stmt at the end of audit log, for parsing the log more convenient.
- if (!ctx.getState().isQuery() && (parsedStmt != null && parsedStmt.needAuditEncryption())) {
- ctx.getAuditEventBuilder().setStmt(parsedStmt.toSql());
- } else {
- if (parsedStmt instanceof InsertStmt && !((InsertStmt) parsedStmt).needLoadManager()
- && ((InsertStmt) parsedStmt).isValuesOrConstantSelect()) {
- // INSERT INTO VALUES may be very long, so we only log at most 1K bytes.
- int length = Math.min(1024, origStmt.length());
- ctx.getAuditEventBuilder().setStmt(origStmt.substring(0, length));
- } else {
- ctx.getAuditEventBuilder().setStmt(origStmt);
- }
- }
- if (!Env.getCurrentEnv().isMaster()) {
- if (ctx.executor.isForwardToMaster()) {
- ctx.getAuditEventBuilder().setState(ctx.executor.getProxyStatus());
- }
- }
- Env.getCurrentAuditEventProcessor().handleAuditEvent(ctx.getAuditEventBuilder().build());
- }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/DdlExecutor.java b/fe/fe-core/src/main/java/org/apache/doris/qe/DdlExecutor.java
index c06ddde23ac..29e29a9a7dd 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/DdlExecutor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/DdlExecutor.java
@@ -39,6 +39,7 @@ import org.apache.doris.analysis.AlterResourceStmt;
import org.apache.doris.analysis.AlterRoutineLoadStmt;
import org.apache.doris.analysis.AlterSqlBlockRuleStmt;
import org.apache.doris.analysis.AlterSystemStmt;
+import org.apache.doris.analysis.AlterTableStatsStmt;
import org.apache.doris.analysis.AlterTableStmt;
import org.apache.doris.analysis.AlterUserStmt;
import org.apache.doris.analysis.AlterViewStmt;
@@ -160,6 +161,8 @@ public class DdlExecutor {
env.createMaterializedView((CreateMaterializedViewStmt) ddlStmt);
} else if (ddlStmt instanceof AlterTableStmt) {
env.alterTable((AlterTableStmt) ddlStmt);
+ } else if (ddlStmt instanceof AlterTableStatsStmt) {
+ StatisticsRepository.alterTableStatistics((AlterTableStatsStmt) ddlStmt);
} else if (ddlStmt instanceof AlterColumnStatsStmt) {
StatisticsRepository.alterColumnStatistics((AlterColumnStatsStmt) ddlStmt);
} else if (ddlStmt instanceof AlterViewStmt) {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/InternalQueryExecutionException.java b/fe/fe-core/src/main/java/org/apache/doris/qe/InternalQueryExecutionException.java
deleted file mode 100644
index c368533c53d..00000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/InternalQueryExecutionException.java
+++ /dev/null
@@ -1,24 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements. See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership. The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License. You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied. See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.qe;
-
-public class InternalQueryExecutionException extends RuntimeException {
- public InternalQueryExecutionException(String message, Throwable cause) {
- super(message, cause);
- }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java b/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
index 34518114453..d4247496548 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
@@ -403,8 +403,6 @@ public class SessionVariable implements Serializable, Writable {
public static final String TEST_QUERY_CACHE_HIT = "test_query_cache_hit";
- public static final String ENABLE_FULL_AUTO_ANALYZE = "enable_full_auto_analyze";
-
public static final List<String> DEBUG_VARIABLES = ImmutableList.of(
SKIP_DELETE_PREDICATE,
SKIP_DELETE_BITMAP,
@@ -1144,24 +1142,6 @@ public class SessionVariable implements Serializable, Writable {
options = {"none", "sql_cache", "partition_cache"})
public String testQueryCacheHit = "none";
- @VariableMgr.VarAttr(name = ENABLE_FULL_AUTO_ANALYZE,
- description = {"该参数控制是否开启自动收集", "Set false to disable auto analyze"},
- flag = VariableMgr.GLOBAL)
- public boolean enableFullAutoAnalyze = true;
-
- @VariableMgr.VarAttr(name = FULL_AUTO_ANALYZE_START_TIME, needForward = true, checker = "checkAnalyzeTimeFormat",
- description = {"该参数定义自动ANALYZE例程的开始时间",
- "This parameter defines the start time for the automatic ANALYZE routine."},
- flag = VariableMgr.GLOBAL)
- public String fullAutoAnalyzeStartTime = "00:00:00";
-
- @VariableMgr.VarAttr(name = FULL_AUTO_ANALYZE_END_TIME, needForward = true, checker = "checkAnalyzeTimeFormat",
- description = {"该参数定义自动ANALYZE例程的结束时间",
- "This parameter defines the end time for the automatic ANALYZE routine."},
- flag = VariableMgr.GLOBAL)
- public String fullAutoAnalyzeEndTime = "02:00:00";
-
-
// If this fe is in fuzzy mode, then will use initFuzzyModeVariables to generate some variables,
// not the default value set in the code.
public void initFuzzyModeVariables() {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java b/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java
index f4f40047b75..06e8f4d3929 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java
@@ -138,6 +138,7 @@ import org.apache.doris.catalog.Tablet;
import org.apache.doris.catalog.TabletInvertedIndex;
import org.apache.doris.catalog.TabletMeta;
import org.apache.doris.catalog.View;
+import org.apache.doris.catalog.external.ExternalTable;
import org.apache.doris.catalog.external.HMSExternalTable;
import org.apache.doris.clone.DynamicPartitionScheduler;
import org.apache.doris.cluster.ClusterNamespace;
@@ -195,7 +196,7 @@ import org.apache.doris.statistics.AnalysisInfo;
import org.apache.doris.statistics.ColumnStatistic;
import org.apache.doris.statistics.Histogram;
import org.apache.doris.statistics.StatisticsRepository;
-import org.apache.doris.statistics.TableStatsMeta;
+import org.apache.doris.statistics.TableStatistic;
import org.apache.doris.statistics.query.QueryStatsUtil;
import org.apache.doris.system.Backend;
import org.apache.doris.system.Diagnoser;
@@ -239,6 +240,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
@@ -2390,16 +2392,29 @@ public class ShowExecutor {
private void handleShowTableStats() {
ShowTableStatsStmt showTableStatsStmt = (ShowTableStatsStmt) stmt;
TableIf tableIf = showTableStatsStmt.getTable();
- TableStatsMeta tableStats = Env.getCurrentEnv().getAnalysisManager().findTableStatsStatus(tableIf.getId());
- /*
- HMSExternalTable table will fetch row count from HMS
- or estimate with file size and schema if it's not analyzed.
- tableStats == null means it's not analyzed, in this case show the estimated row count.
- */
- if (tableStats == null && tableIf instanceof HMSExternalTable) {
- resultSet = showTableStatsStmt.constructResultSet(tableIf.estimatedRowCount());
- } else {
- resultSet = showTableStatsStmt.constructResultSet(tableStats);
+ long partitionId = showTableStatsStmt.getPartitionId();
+ boolean showCache = showTableStatsStmt.isCached();
+ try {
+ if (tableIf instanceof ExternalTable && showCache) {
+ Optional<TableStatistic> tableStatistics = Env.getCurrentEnv().getStatisticsCache().getTableStatistics(
+ tableIf.getDatabase().getCatalog().getId(),
+ tableIf.getDatabase().getId(),
+ tableIf.getId());
+ if (tableStatistics.isPresent()) {
+ resultSet = showTableStatsStmt.constructResultSet(tableStatistics.get());
+ } else {
+ resultSet = showTableStatsStmt.constructResultSet(TableStatistic.UNKNOWN);
+ }
+ } else if (partitionId > 0) {
+ TableStatistic partStats = StatisticsRepository.fetchTableLevelOfPartStats(partitionId);
+ resultSet = showTableStatsStmt.constructResultSet(partStats);
+ } else {
+ TableStatistic tableStats = StatisticsRepository.fetchTableLevelStats(tableIf.getId());
+ resultSet = showTableStatsStmt.constructResultSet(tableStats);
+ }
+ } catch (DdlException e) {
+ LOG.warn("Table statistics do not exist: {}", tableIf.getName());
+ resultSet = showTableStatsStmt.constructResultSet(TableStatistic.UNKNOWN);
}
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java b/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
index 5d90008ff75..197c5fc9088 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
@@ -142,8 +142,8 @@ import org.apache.doris.rewrite.ExprRewriter;
import org.apache.doris.rewrite.mvrewrite.MVSelectFailedException;
import org.apache.doris.rpc.RpcException;
import org.apache.doris.service.FrontendOptions;
-import org.apache.doris.statistics.ResultRow;
import org.apache.doris.statistics.util.InternalQueryBuffer;
+import org.apache.doris.statistics.util.InternalQueryResult.ResultRow;
import org.apache.doris.task.LoadEtlTask;
import org.apache.doris.thrift.TFileFormatType;
import org.apache.doris.thrift.TFileType;
@@ -2490,8 +2490,7 @@ public class StmtExecutor {
planner = new NereidsPlanner(statementContext);
planner.plan(parsedStmt, context.getSessionVariable().toThrift());
} catch (Exception e) {
- LOG.warn("Arrow Flight SQL fall back to legacy planner, because: {}",
- e.getMessage(), e);
+ LOG.warn("fall back to legacy planner, because: {}", e.getMessage(), e);
parsedStmt = null;
planner = null;
context.getState().setNereids(false);
@@ -2503,9 +2502,10 @@ public class StmtExecutor {
analyze(context.getSessionVariable().toThrift());
}
} catch (Exception e) {
- LOG.warn("Failed to run internal SQL: {}", originStmt, e);
- throw new RuntimeException("Failed to execute internal SQL. " + Util.getRootCauseMessage(e), e);
+ throw new RuntimeException("Failed to execute internal SQL. "
+ + Util.getRootCauseMessage(e) + " " + originStmt.toString(), e);
}
+ planner.getFragments();
RowBatch batch;
coord = new Coordinator(context, analyzer, planner, context.getStatsErrorEstimator());
profile.addExecutionProfile(coord.getExecutionProfile());
@@ -2513,7 +2513,8 @@ public class StmtExecutor {
QeProcessorImpl.INSTANCE.registerQuery(context.queryId(),
new QeProcessorImpl.QueryInfo(context, originStmt.originStmt, coord));
} catch (UserException e) {
- throw new RuntimeException("Failed to execute internal SQL. " + Util.getRootCauseMessage(e), e);
+ throw new RuntimeException("Failed to execute internal SQL. "
+ + " " + Util.getRootCauseMessage(e) + originStmt.toString(), e);
}
Span queryScheduleSpan = context.getTracer()
@@ -2522,7 +2523,8 @@ public class StmtExecutor {
coord.exec();
} catch (Exception e) {
queryScheduleSpan.recordException(e);
- throw new InternalQueryExecutionException(e.getMessage() + Util.getRootCauseMessage(e), e);
+ throw new RuntimeException("Failed to execute internal SQL. "
+ + Util.getRootCauseMessage(e) + " " + originStmt.toString(), e);
} finally {
queryScheduleSpan.end();
}
@@ -2539,19 +2541,21 @@ public class StmtExecutor {
}
} catch (Exception e) {
fetchResultSpan.recordException(e);
- throw new RuntimeException("Failed to fetch internal SQL result. " + Util.getRootCauseMessage(e), e);
+ throw new RuntimeException("Failed to execute internal SQL. " + Util.getRootCauseMessage(e) + " "
+ + originStmt.toString(), e);
} finally {
fetchResultSpan.end();
}
} finally {
- AuditLogHelper.logAuditLog(context, originStmt.toString(), parsedStmt, getQueryStatisticsForAuditLog(),
- true);
QeProcessorImpl.INSTANCE.unregisterQuery(context.queryId());
}
}
private List<ResultRow> convertResultBatchToResultRows(TResultBatch batch) {
List<String> columns = parsedStmt.getColLabels();
+ List<PrimitiveType> types = parsedStmt.getResultExprs().stream()
+ .map(e -> e.getType().getPrimitiveType())
+ .collect(Collectors.toList());
List<ResultRow> resultRows = new ArrayList<>();
List<ByteBuffer> rows = batch.getRows();
for (ByteBuffer buffer : rows) {
@@ -2562,7 +2566,8 @@ public class StmtExecutor {
String value = queryBuffer.readStringWithLength();
values.add(value);
}
- ResultRow resultRow = new ResultRow(values);
+
+ ResultRow resultRow = new ResultRow(columns, types, values);
resultRows.add(resultRow);
}
return resultRows;
@@ -2585,14 +2590,6 @@ public class StmtExecutor {
this.proxyResultSet = proxyResultSet;
}
- public ConnectContext getContext() {
- return context;
- }
-
- public OriginStatement getOriginStmt() {
- return originStmt;
- }
-
public String getOriginStmtInString() {
if (originStmt != null && originStmt.originStmt != null) {
return originStmt.originStmt;
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisInfo.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisInfo.java
index e3d5c8a91b8..ee39582aac4 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisInfo.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisInfo.java
@@ -22,21 +22,19 @@ import org.apache.doris.common.FeMetaVersion;
import org.apache.doris.common.io.Text;
import org.apache.doris.common.io.Writable;
import org.apache.doris.persist.gson.GsonUtils;
+import org.apache.doris.statistics.util.InternalQueryResult.ResultRow;
import org.apache.doris.statistics.util.StatisticsUtil;
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;
-import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.core.util.CronExpression;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.lang.reflect.Type;
-import java.text.ParseException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -48,7 +46,6 @@ public class AnalysisInfo implements Writable {
private static final Logger LOG = LogManager.getLogger(AnalysisInfo.class);
- // TODO: useless, remove it later
public enum AnalysisMode {
INCREMENTAL,
FULL
@@ -69,12 +66,10 @@ public class AnalysisInfo implements Writable {
// submit by user directly
MANUAL,
// submit by system automatically
- SYSTEM;
+ SYSTEM
}
public enum ScheduleType {
- // Job created by AutoCollector is also `ONCE` type, this is because it runs once only and should be removed
- // when its information is expired
ONCE,
PERIOD,
AUTOMATIC
@@ -100,7 +95,6 @@ public class AnalysisInfo implements Writable {
@SerializedName("tblName")
public final String tblName;
- // TODO: Map here is wired, List is enough
@SerializedName("colToPartitions")
public final Map<String, Set<String>> colToPartitions;
@@ -157,39 +151,24 @@ public class AnalysisInfo implements Writable {
// True means this task is a table level task for external table.
// This kind of task is mainly to collect the number of rows of a table.
@SerializedName("externalTableLevelTask")
- public final boolean externalTableLevelTask;
+ public boolean externalTableLevelTask;
@SerializedName("partitionOnly")
- public final boolean partitionOnly;
+ public boolean partitionOnly;
@SerializedName("samplingPartition")
- public final boolean samplingPartition;
-
- @SerializedName("isAllPartition")
- public final boolean isAllPartition;
-
- @SerializedName("partitionCount")
- public final long partitionCount;
+ public boolean samplingPartition;
// For serialize
@SerializedName("cronExpr")
public String cronExprStr;
- @SerializedName("progress")
- public String progress;
-
- public CronExpression cronExpression;
-
- @SerializedName("forceFull")
- public final boolean forceFull;
-
public AnalysisInfo(long jobId, long taskId, List<Long> taskIds, String catalogName, String dbName, String tblName,
Map<String, Set<String>> colToPartitions, Set<String> partitionNames, String colName, Long indexId,
JobType jobType, AnalysisMode analysisMode, AnalysisMethod analysisMethod, AnalysisType analysisType,
int samplePercent, int sampleRows, int maxBucketNum, long periodTimeInMs, String message,
long lastExecTimeInMs, long timeCostInMs, AnalysisState state, ScheduleType scheduleType,
- boolean isExternalTableLevelTask, boolean partitionOnly, boolean samplingPartition,
- boolean isAllPartition, long partitionCount, CronExpression cronExpression, boolean forceFull) {
+ boolean isExternalTableLevelTask, boolean partitionOnly, boolean samplingPartition) {
this.jobId = jobId;
this.taskId = taskId;
this.taskIds = taskIds;
@@ -216,13 +195,6 @@ public class AnalysisInfo implements Writable {
this.externalTableLevelTask = isExternalTableLevelTask;
this.partitionOnly = partitionOnly;
this.samplingPartition = samplingPartition;
- this.isAllPartition = isAllPartition;
- this.partitionCount = partitionCount;
- this.cronExpression = cronExpression;
- if (cronExpression != null) {
- this.cronExprStr = cronExpression.getCronExpression();
- }
- this.forceFull = forceFull;
}
@Override
@@ -233,11 +205,11 @@ public class AnalysisInfo implements Writable {
sj.add("DBName: " + dbName);
sj.add("TableName: " + tblName);
sj.add("ColumnName: " + colName);
- sj.add("TaskType: " + analysisType);
- sj.add("TaskMode: " + analysisMode);
- sj.add("TaskMethod: " + analysisMethod);
+ sj.add("TaskType: " + analysisType.toString());
+ sj.add("TaskMode: " + analysisMode.toString());
+ sj.add("TaskMethod: " + analysisMethod.toString());
sj.add("Message: " + message);
- sj.add("CurrentState: " + state);
+ sj.add("CurrentState: " + state.toString());
if (samplePercent > 0) {
sj.add("SamplePercent: " + samplePercent);
}
@@ -259,10 +231,6 @@ public class AnalysisInfo implements Writable {
if (periodTimeInMs > 0) {
sj.add("periodTimeInMs: " + StatisticsUtil.getReadableTime(periodTimeInMs));
}
- if (StringUtils.isNotEmpty(cronExprStr)) {
- sj.add("cronExpr: " + cronExprStr);
- }
- sj.add("forceFull: " + forceFull);
return sj.toString();
}
@@ -278,6 +246,60 @@ public class AnalysisInfo implements Writable {
taskIds.add(taskId);
}
+ // TODO: use thrift
+ public static AnalysisInfo fromResultRow(ResultRow resultRow) {
+ try {
+ AnalysisInfoBuilder analysisInfoBuilder = new AnalysisInfoBuilder();
+ long jobId = Long.parseLong(resultRow.getColumnValue("job_id"));
+ analysisInfoBuilder.setJobId(jobId);
+ long taskId = Long.parseLong(resultRow.getColumnValue("task_id"));
+ analysisInfoBuilder.setTaskId(taskId);
+ String catalogName = resultRow.getColumnValue("catalog_name");
+ analysisInfoBuilder.setCatalogName(catalogName);
+ String dbName = resultRow.getColumnValue("db_name");
+ analysisInfoBuilder.setDbName(dbName);
+ String tblName = resultRow.getColumnValue("tbl_name");
+ analysisInfoBuilder.setTblName(tblName);
+ String colName = resultRow.getColumnValue("col_name");
+ analysisInfoBuilder.setColName(colName);
+ long indexId = Long.parseLong(resultRow.getColumnValue("index_id"));
+ analysisInfoBuilder.setIndexId(indexId);
+ String partitionNames = resultRow.getColumnValue("col_partitions");
+ Map<String, Set<String>> colToPartitions = getColToPartition(partitionNames);
+ analysisInfoBuilder.setColToPartitions(colToPartitions);
+ String jobType = resultRow.getColumnValue("job_type");
+ analysisInfoBuilder.setJobType(JobType.valueOf(jobType));
+ String analysisType = resultRow.getColumnValue("analysis_type");
+ analysisInfoBuilder.setAnalysisType(AnalysisType.valueOf(analysisType));
+ String analysisMode = resultRow.getColumnValue("analysis_mode");
+ analysisInfoBuilder.setAnalysisMode(AnalysisMode.valueOf(analysisMode));
+ String analysisMethod = resultRow.getColumnValue("analysis_method");
+ analysisInfoBuilder.setAnalysisMethod(AnalysisMethod.valueOf(analysisMethod));
+ String scheduleType = resultRow.getColumnValue("schedule_type");
+ analysisInfoBuilder.setScheduleType(ScheduleType.valueOf(scheduleType));
+ String state = resultRow.getColumnValue("state");
+ analysisInfoBuilder.setState(AnalysisState.valueOf(state));
+ String samplePercent = resultRow.getColumnValue("sample_percent");
+ analysisInfoBuilder.setSamplePercent(StatisticsUtil.convertStrToInt(samplePercent));
+ String sampleRows = resultRow.getColumnValue("sample_rows");
+ analysisInfoBuilder.setSampleRows(StatisticsUtil.convertStrToInt(sampleRows));
+ String maxBucketNum = resultRow.getColumnValue("max_bucket_num");
+ analysisInfoBuilder.setMaxBucketNum(StatisticsUtil.convertStrToInt(maxBucketNum));
+ String periodTimeInMs = resultRow.getColumnValue("period_time_in_ms");
+ analysisInfoBuilder.setPeriodTimeInMs(StatisticsUtil.convertStrToInt(periodTimeInMs));
+ String lastExecTimeInMs = resultRow.getColumnValue("last_exec_time_in_ms");
+ analysisInfoBuilder.setLastExecTimeInMs(StatisticsUtil.convertStrToLong(lastExecTimeInMs));
+ String timeCostInMs = resultRow.getColumnValue("time_cost_in_ms");
+ analysisInfoBuilder.setTimeCostInMs(StatisticsUtil.convertStrToLong(timeCostInMs));
+ String message = resultRow.getColumnValue("message");
+ analysisInfoBuilder.setMessage(message);
+ return analysisInfoBuilder.build();
+ } catch (Exception e) {
+ LOG.warn("Failed to deserialize analysis task info.", e);
+ return null;
+ }
+ }
+
public String getColToPartitionStr() {
if (colToPartitions == null || colToPartitions.isEmpty()) {
return "";
@@ -340,15 +362,7 @@ public class AnalysisInfo implements Writable {
return analysisInfoBuilder.build();
} else {
String json = Text.readString(dataInput);
- AnalysisInfo analysisInfo = GsonUtils.GSON.fromJson(json, AnalysisInfo.class);
- if (analysisInfo.cronExprStr != null) {
- try {
- analysisInfo.cronExpression = new CronExpression(analysisInfo.cronExprStr);
- } catch (ParseException e) {
- LOG.warn("Cron expression of job is invalid, there is a bug", e);
- }
- }
- return analysisInfo;
+ return GsonUtils.GSON.fromJson(json, AnalysisInfo.class);
}
}
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisInfoBuilder.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisInfoBuilder.java
index 7e97b4d951f..2fd0e25d727 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisInfoBuilder.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisInfoBuilder.java
@@ -23,8 +23,6 @@ import org.apache.doris.statistics.AnalysisInfo.AnalysisType;
import org.apache.doris.statistics.AnalysisInfo.JobType;
import org.apache.doris.statistics.AnalysisInfo.ScheduleType;
-import org.apache.logging.log4j.core.util.CronExpression;
-
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -56,12 +54,6 @@ public class AnalysisInfoBuilder {
private boolean externalTableLevelTask;
private boolean partitionOnly;
private boolean samplingPartition;
- private boolean isAllPartition;
- private long partitionCount;
-
- private CronExpression cronExpression;
-
- private boolean forceFull;
public AnalysisInfoBuilder() {
}
@@ -93,10 +85,6 @@ public class AnalysisInfoBuilder {
externalTableLevelTask = info.externalTableLevelTask;
partitionOnly = info.partitionOnly;
samplingPartition = info.samplingPartition;
- isAllPartition = info.isAllPartition;
- partitionCount = info.partitionCount;
- cronExpression = info.cronExpression;
- forceFull = info.forceFull;
}
public AnalysisInfoBuilder setJobId(long jobId) {
@@ -229,30 +217,11 @@ public class AnalysisInfoBuilder {
return this;
}
- public AnalysisInfoBuilder setAllPartition(boolean isAllPartition) {
- this.isAllPartition = isAllPartition;
- return this;
- }
-
- public AnalysisInfoBuilder setPartitionCount(long partitionCount) {
- this.partitionCount = partitionCount;
- return this;
- }
-
- public void setCronExpression(CronExpression cronExpression) {
- this.cronExpression = cronExpression;
- }
-
- public void setForceFull(boolean forceFull) {
- this.forceFull = forceFull;
- }
-
public AnalysisInfo build() {
return new AnalysisInfo(jobId, taskId, taskIds, catalogName, dbName, tblName, colToPartitions, partitionNames,
colName, indexId, jobType, analysisMode, analysisMethod, analysisType, samplePercent,
sampleRows, maxBucketNum, periodTimeInMs, message, lastExecTimeInMs, timeCostInMs, state, scheduleType,
- externalTableLevelTask, partitionOnly, samplingPartition, isAllPartition, partitionCount,
- cronExpression, forceFull);
+ externalTableLevelTask, partitionOnly, samplingPartition);
}
public AnalysisInfoBuilder copy() {
@@ -279,10 +248,6 @@ public class AnalysisInfoBuilder {
.setTimeCostInMs(timeCostInMs)
.setState(state)
.setScheduleType(scheduleType)
- .setExternalTableLevelTask(externalTableLevelTask)
- .setSamplingPartition(samplingPartition)
- .setPartitionOnly(partitionOnly)
- .setAllPartition(isAllPartition)
- .setPartitionCount(partitionCount);
+ .setExternalTableLevelTask(externalTableLevelTask);
}
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisManager.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisManager.java
index 3eebb1ec9aa..5ac0c0cd524 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisManager.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisManager.java
@@ -18,7 +18,6 @@
package org.apache.doris.statistics;
import org.apache.doris.analysis.AnalyzeDBStmt;
-import org.apache.doris.analysis.AnalyzeProperties;
import org.apache.doris.analysis.AnalyzeStmt;
import org.apache.doris.analysis.AnalyzeTblStmt;
import org.apache.doris.analysis.DropAnalyzeJobStmt;
@@ -29,9 +28,12 @@ import org.apache.doris.analysis.TableName;
import org.apache.doris.catalog.Column;
import org.apache.doris.catalog.DatabaseIf;
import org.apache.doris.catalog.Env;
+import org.apache.doris.catalog.MaterializedIndexMeta;
+import org.apache.doris.catalog.OlapTable;
import org.apache.doris.catalog.Partition;
import org.apache.doris.catalog.ScalarType;
import org.apache.doris.catalog.TableIf;
+import org.apache.doris.catalog.TableIf.TableType;
import org.apache.doris.catalog.View;
import org.apache.doris.catalog.external.ExternalTable;
import org.apache.doris.catalog.external.HMSExternalTable;
@@ -40,14 +42,12 @@ import org.apache.doris.common.Config;
import org.apache.doris.common.DdlException;
import org.apache.doris.common.FeConstants;
import org.apache.doris.common.ThreadPoolManager.BlockedPolicy;
-import org.apache.doris.common.io.Text;
import org.apache.doris.common.io.Writable;
import org.apache.doris.common.util.Daemon;
import org.apache.doris.common.util.Util;
+import org.apache.doris.datasource.CatalogIf;
import org.apache.doris.mysql.privilege.PrivPredicate;
import org.apache.doris.persist.AnalyzeDeletionLog;
-import org.apache.doris.persist.TableStatsDeletionLog;
-import org.apache.doris.persist.gson.GsonUtils;
import org.apache.doris.qe.ConnectContext;
import org.apache.doris.qe.ShowResultSet;
import org.apache.doris.qe.ShowResultSetMetaData;
@@ -56,31 +56,25 @@ import org.apache.doris.statistics.AnalysisInfo.AnalysisMode;
import org.apache.doris.statistics.AnalysisInfo.AnalysisType;
import org.apache.doris.statistics.AnalysisInfo.JobType;
import org.apache.doris.statistics.AnalysisInfo.ScheduleType;
-import org.apache.doris.statistics.util.SimpleQueue;
import org.apache.doris.statistics.util.StatisticsUtil;
-import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
-import com.google.common.reflect.TypeToken;
+import com.google.common.collect.Maps;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.core.util.CronExpression;
import org.jetbrains.annotations.Nullable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
-import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
-import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -99,141 +93,26 @@ import java.util.stream.Collectors;
public class AnalysisManager extends Daemon implements Writable {
+ public AnalysisTaskScheduler taskScheduler;
+
private static final Logger LOG = LogManager.getLogger(AnalysisManager.class);
- // Tracking running manually submitted async tasks, keep in mem only
- protected final ConcurrentMap<Long, Map<Long, BaseAnalysisTask>> analysisJobIdToTaskMap = new ConcurrentHashMap<>();
+ private ConcurrentMap<Long, Map<Long, BaseAnalysisTask>> analysisJobIdToTaskMap = new ConcurrentHashMap<>();
private StatisticsCache statisticsCache;
private AnalysisTaskExecutor taskExecutor;
- // Store task information in metadata.
private final Map<Long, AnalysisInfo> analysisTaskInfoMap = Collections.synchronizedMap(new TreeMap<>());
-
- // Store job information in metadata
private final Map<Long, AnalysisInfo> analysisJobInfoMap = Collections.synchronizedMap(new TreeMap<>());
- // Tracking system submitted job, keep in mem only
- protected final Map<Long, AnalysisInfo> systemJobInfoMap = new ConcurrentHashMap<>();
-
- // Tracking and control sync analyze tasks, keep in mem only
private final ConcurrentMap<ConnectContext, SyncTaskCollection> ctxToSyncTask = new ConcurrentHashMap<>();
- private final Map<Long, TableStatsMeta> idToTblStats = new ConcurrentHashMap<>();
-
- protected SimpleQueue<AnalysisInfo> autoJobs = createSimpleQueue(null, this);
-
- private final Function<TaskStatusWrapper, Void> userJobStatusUpdater = w -> {
- AnalysisInfo info = w.info;
- AnalysisState taskState = w.taskState;
- String message = w.message;
- long time = w.time;
- if (analysisJobIdToTaskMap.get(info.jobId) == null) {
- return null;
- }
- info.state = taskState;
- info.message = message;
- // Update the task cost time when task finished or failed. And only log the final state.
- if (taskState.equals(AnalysisState.FINISHED) || taskState.equals(AnalysisState.FAILED)) {
- info.timeCostInMs = time - info.lastExecTimeInMs;
- info.lastExecTimeInMs = time;
- logCreateAnalysisTask(info);
- }
- info.lastExecTimeInMs = time;
- AnalysisInfo job = analysisJobInfoMap.get(info.jobId);
- // Job may get deleted during execution.
- if (job == null) {
- return null;
- }
- // Synchronize the job state change in job level.
- synchronized (job) {
- job.lastExecTimeInMs = time;
- // Set the job state to RUNNING when its first task becomes RUNNING.
- if (info.state.equals(AnalysisState.RUNNING) && job.state.equals(AnalysisState.PENDING)) {
- job.state = AnalysisState.RUNNING;
- replayCreateAnalysisJob(job);
- }
- boolean allFinished = true;
- boolean hasFailure = false;
- for (BaseAnalysisTask task : analysisJobIdToTaskMap.get(info.jobId).values()) {
- AnalysisInfo taskInfo = task.info;
- if (taskInfo.state.equals(AnalysisState.RUNNING) || taskInfo.state.equals(AnalysisState.PENDING)) {
- allFinished = false;
- break;
- }
- if (taskInfo.state.equals(AnalysisState.FAILED)) {
- hasFailure = true;
- }
- }
- if (allFinished) {
- if (hasFailure) {
- job.state = AnalysisState.FAILED;
- } else {
- job.state = AnalysisState.FINISHED;
- try {
- updateTableStats(job);
- } catch (Throwable e) {
- LOG.warn("Failed to update Table statistics in job: {}", info.toString(), e);
- }
- }
- logCreateAnalysisJob(job);
- analysisJobIdToTaskMap.remove(job.jobId);
- }
- }
- return null;
- };
-
- private final String progressDisplayTemplate = "%d Finished | %d Failed | %d In Progress | %d Total";
-
- protected final Function<TaskStatusWrapper, Void> systemJobStatusUpdater = w -> {
- AnalysisInfo info = w.info;
- info.state = w.taskState;
- info.message = w.message;
- AnalysisInfo job = systemJobInfoMap.get(info.jobId);
- if (job == null) {
- return null;
- }
- int failedCount = 0;
- StringJoiner reason = new StringJoiner(", ");
- Map<Long, BaseAnalysisTask> taskMap = analysisJobIdToTaskMap.get(info.jobId);
- for (BaseAnalysisTask task : taskMap.values()) {
- if (task.info.state.equals(AnalysisState.RUNNING) || task.info.state.equals(AnalysisState.PENDING)) {
- return null;
- }
- if (task.info.state.equals(AnalysisState.FAILED)) {
- failedCount++;
- reason.add(task.info.message);
- }
- }
- try {
- updateTableStats(job);
- } catch (Throwable e) {
- LOG.warn("Failed to update Table statistics in job: {}", info.toString(), e);
- } finally {
- job.lastExecTimeInMs = System.currentTimeMillis();
- job.message = reason.toString();
- job.progress = String.format(progressDisplayTemplate,
- taskMap.size() - failedCount, failedCount, 0, taskMap.size());
- if (failedCount > 0) {
- job.message = reason.toString();
- job.state = AnalysisState.FAILED;
- } else {
- job.state = AnalysisState.FINISHED;
- }
- autoJobs.offer(job);
- systemJobInfoMap.remove(info.jobId);
- }
- return null;
- };
-
- private final Function<TaskStatusWrapper, Void>[] updaters =
- new Function[] {userJobStatusUpdater, systemJobStatusUpdater};
-
public AnalysisManager() {
super(TimeUnit.SECONDS.toMillis(StatisticConstants.ANALYZE_MANAGER_INTERVAL_IN_SECS));
if (!Env.isCheckpointThread()) {
- this.taskExecutor = new AnalysisTaskExecutor(Config.statistics_simultaneously_running_task_num);
+ this.taskScheduler = new AnalysisTaskScheduler();
+ this.taskExecutor = new AnalysisTaskExecutor(taskScheduler);
this.statisticsCache = new StatisticsCache();
taskExecutor.start();
}
@@ -245,7 +124,7 @@ public class AnalysisManager extends Daemon implements Writable {
}
private void clear() {
- clearExpiredAnalysisInfo(analysisJobInfoMap, (a) ->
+ clearMeta(analysisJobInfoMap, (a) ->
a.scheduleType.equals(ScheduleType.ONCE)
&& System.currentTimeMillis() - a.lastExecTimeInMs
> TimeUnit.DAYS.toMillis(StatisticConstants.ANALYSIS_JOB_INFO_EXPIRATION_TIME_IN_DAYS),
@@ -253,7 +132,7 @@ public class AnalysisManager extends Daemon implements Writable {
Env.getCurrentEnv().getEditLog().logDeleteAnalysisJob(new AnalyzeDeletionLog(id));
return null;
});
- clearExpiredAnalysisInfo(analysisTaskInfoMap, (a) -> System.currentTimeMillis() - a.lastExecTimeInMs
+ clearMeta(analysisTaskInfoMap, (a) -> System.currentTimeMillis() - a.lastExecTimeInMs
> TimeUnit.DAYS.toMillis(StatisticConstants.ANALYSIS_JOB_INFO_EXPIRATION_TIME_IN_DAYS),
(id) -> {
Env.getCurrentEnv().getEditLog().logDeleteAnalysisTask(new AnalyzeDeletionLog(id));
@@ -261,7 +140,7 @@ public class AnalysisManager extends Daemon implements Writable {
});
}
- private void clearExpiredAnalysisInfo(Map<Long, AnalysisInfo> infoMap, Predicate<AnalysisInfo> isExpired,
+ private void clearMeta(Map<Long, AnalysisInfo> infoMap, Predicate<AnalysisInfo> isExpired,
Function<Long, Void> writeLog) {
synchronized (infoMap) {
List<Long> expired = new ArrayList<>();
@@ -282,9 +161,6 @@ public class AnalysisManager extends Daemon implements Writable {
}
public void createAnalyze(AnalyzeStmt analyzeStmt, boolean proxy) throws DdlException {
- if (!StatisticsUtil.statsTblAvailable() && !FeConstants.runningUnitTest) {
- throw new DdlException("Stats table not available, please make sure your cluster status is normal");
- }
if (analyzeStmt instanceof AnalyzeDBStmt) {
createAnalysisJobs((AnalyzeDBStmt) analyzeStmt, proxy);
} else if (analyzeStmt instanceof AnalyzeTblStmt) {
@@ -294,53 +170,38 @@ public class AnalysisManager extends Daemon implements Writable {
public void createAnalysisJobs(AnalyzeDBStmt analyzeDBStmt, boolean proxy) throws DdlException {
DatabaseIf<TableIf> db = analyzeDBStmt.getDb();
- // Using auto analyzer if user specifies.
- if (analyzeDBStmt.getAnalyzeProperties().getProperties().containsKey("use.auto.analyzer")) {
- Env.getCurrentEnv().getStatisticsAutoCollector().analyzeDb(db);
- return;
- }
- List<AnalysisInfo> analysisInfos = buildAnalysisInfosForDB(db, analyzeDBStmt.getAnalyzeProperties());
- if (!analyzeDBStmt.isSync()) {
- sendJobId(analysisInfos, proxy);
- }
- }
-
- public List<AnalysisInfo> buildAnalysisInfosForDB(DatabaseIf<TableIf> db, AnalyzeProperties analyzeProperties) {
- db.readLock();
List<TableIf> tbls = db.getTables();
List<AnalysisInfo> analysisInfos = new ArrayList<>();
+ db.readLock();
try {
List<AnalyzeTblStmt> analyzeStmts = new ArrayList<>();
for (TableIf table : tbls) {
if (table instanceof View) {
continue;
}
- TableName tableName = new TableName(db.getCatalog().getName(), db.getFullName(),
+ TableName tableName = new TableName(analyzeDBStmt.getCtlIf().getName(), db.getFullName(),
table.getName());
// columnNames null means to add all visitable columns.
// Will get all the visible columns in analyzeTblStmt.check()
- AnalyzeTblStmt analyzeTblStmt = new AnalyzeTblStmt(analyzeProperties, tableName,
+ AnalyzeTblStmt analyzeTblStmt = new AnalyzeTblStmt(analyzeDBStmt.getAnalyzeProperties(), tableName,
null, db.getId(), table);
try {
analyzeTblStmt.check();
} catch (AnalysisException analysisException) {
- LOG.warn("Failed to build analyze job: {}",
- analysisException.getMessage(), analysisException);
+ throw new DdlException(analysisException.getMessage(), analysisException);
}
analyzeStmts.add(analyzeTblStmt);
}
for (AnalyzeTblStmt analyzeTblStmt : analyzeStmts) {
- try {
- analysisInfos.add(buildAndAssignJob(analyzeTblStmt));
- } catch (DdlException e) {
- LOG.warn("Failed to build analyze job: {}",
- e.getMessage(), e);
- }
+ analysisInfos.add(buildAndAssignJob(analyzeTblStmt));
+ }
+ if (!analyzeDBStmt.isSync()) {
+ sendJobId(analysisInfos, proxy);
}
} finally {
db.readUnlock();
}
- return analysisInfos;
+
}
// Each analyze stmt corresponding to an analysis job.
@@ -353,8 +214,11 @@ public class AnalysisManager extends Daemon implements Writable {
}
@Nullable
- @VisibleForTesting
- protected AnalysisInfo buildAndAssignJob(AnalyzeTblStmt stmt) throws DdlException {
+ private AnalysisInfo buildAndAssignJob(AnalyzeTblStmt stmt) throws DdlException {
+ if (!StatisticsUtil.statsTblAvailable() && !FeConstants.runningUnitTest) {
+ throw new DdlException("Stats table not available, please make sure your cluster status is normal");
+ }
+
AnalysisInfo jobInfo = buildAnalysisJobInfo(stmt);
if (jobInfo.colToPartitions.isEmpty()) {
// No statistics need to be collected or updated
@@ -364,44 +228,66 @@ public class AnalysisManager extends Daemon implements Writable {
boolean isSync = stmt.isSync();
Map<Long, BaseAnalysisTask> analysisTaskInfos = new HashMap<>();
createTaskForEachColumns(jobInfo, analysisTaskInfos, isSync);
- if (!jobInfo.partitionOnly && stmt.isAllColumns()
- && StatisticsUtil.isExternalTable(jobInfo.catalogName, jobInfo.dbName, jobInfo.tblName)) {
- createTableLevelTaskForExternalTable(jobInfo, analysisTaskInfos, isSync);
+ createTaskForMVIdx(jobInfo, analysisTaskInfos, isSync);
+ if (stmt.isAllColumns()) {
+ createTaskForExternalTable(jobInfo, analysisTaskInfos, isSync);
+ }
+ if (!isSync) {
+ persistAnalysisJob(jobInfo);
+ analysisJobIdToTaskMap.put(jobInfo.jobId, analysisTaskInfos);
}
+ if (!isSync) {
+ try {
+ updateTableStats(jobInfo);
+ } catch (Throwable e) {
+ throw new DdlException("Failed to update Table statistics");
+ }
+ }
+
if (isSync) {
syncExecute(analysisTaskInfos.values());
- updateTableStats(jobInfo);
return null;
}
- recordAnalysisJob(jobInfo);
- analysisJobIdToTaskMap.put(jobInfo.jobId, analysisTaskInfos);
- // TODO: maybe we should update table stats only when all task succeeded.
- updateTableStats(jobInfo);
- if (!jobInfo.scheduleType.equals(ScheduleType.PERIOD)) {
- analysisTaskInfos.values().forEach(taskExecutor::submitTask);
- }
+
+ analysisTaskInfos.values().forEach(taskScheduler::schedule);
return jobInfo;
}
+ // Analysis job created by the system
+ public void createAnalysisJob(AnalysisInfo info) throws DdlException {
+ AnalysisInfo jobInfo = buildAnalysisJobInfo(info);
+ if (jobInfo.colToPartitions.isEmpty()) {
+ // No statistics need to be collected or updated
+ return;
+ }
+
+ Map<Long, BaseAnalysisTask> analysisTaskInfos = new HashMap<>();
+ createTaskForEachColumns(jobInfo, analysisTaskInfos, false);
+ createTaskForMVIdx(jobInfo, analysisTaskInfos, false);
+ if (!jobInfo.jobType.equals(JobType.SYSTEM)) {
+ persistAnalysisJob(jobInfo);
+ analysisJobIdToTaskMap.put(jobInfo.jobId, analysisTaskInfos);
+ }
+
+ analysisTaskInfos.values().forEach(taskScheduler::schedule);
+ }
+
private void sendJobId(List<AnalysisInfo> analysisInfos, boolean proxy) {
List<Column> columns = new ArrayList<>();
- columns.add(new Column("Job_Id", ScalarType.createVarchar(19)));
columns.add(new Column("Catalog_Name", ScalarType.createVarchar(1024)));
columns.add(new Column("DB_Name", ScalarType.createVarchar(1024)));
columns.add(new Column("Table_Name", ScalarType.createVarchar(1024)));
columns.add(new Column("Columns", ScalarType.createVarchar(1024)));
+ columns.add(new Column("Job_Id", ScalarType.createVarchar(19)));
ShowResultSetMetaData commonResultSetMetaData = new ShowResultSetMetaData(columns);
List<List<String>> resultRows = new ArrayList<>();
for (AnalysisInfo analysisInfo : analysisInfos) {
- if (analysisInfo == null) {
- continue;
- }
List<String> row = new ArrayList<>();
- row.add(String.valueOf(analysisInfo.jobId));
row.add(analysisInfo.catalogName);
row.add(analysisInfo.dbName);
row.add(analysisInfo.tblName);
row.add(analysisInfo.colName);
+ row.add(String.valueOf(analysisInfo.jobId));
resultRows.add(row);
}
ShowResultSet commonResultSet = new ShowResultSet(commonResultSetMetaData, resultRows);
@@ -431,13 +317,13 @@ public class AnalysisManager extends Daemon implements Writable {
* TODO Supports incremental collection of statistics from materialized views
*/
private Map<String, Set<String>> validateAndGetPartitions(TableIf table, Set<String> columnNames,
- Set<String> partitionNames, AnalysisType analysisType) throws DdlException {
+ Set<String> partitionNames, AnalysisType analysisType, AnalysisMode analysisMode) throws DdlException {
long tableId = table.getId();
Map<String, Set<String>> columnToPartitions = columnNames.stream()
.collect(Collectors.toMap(
columnName -> columnName,
- columnName -> new HashSet<>(partitionNames == null ? Collections.emptySet() : partitionNames)
+ columnName -> new HashSet<>(partitionNames)
));
if (analysisType == AnalysisType.HISTOGRAM) {
@@ -454,7 +340,7 @@ public class AnalysisManager extends Daemon implements Writable {
}
// Get the partition granularity statistics that have been collected
- Map<String, Set<String>> existColAndPartsForStats = StatisticsRepository
+ Map<String, Set<Long>> existColAndPartsForStats = StatisticsRepository
.fetchColAndPartsForStats(tableId);
if (existColAndPartsForStats.isEmpty()) {
@@ -462,30 +348,42 @@ public class AnalysisManager extends Daemon implements Writable {
return columnToPartitions;
}
- Set<String> existPartIdsForStats = new HashSet<>();
+ Set<Long> existPartIdsForStats = new HashSet<>();
existColAndPartsForStats.values().forEach(existPartIdsForStats::addAll);
- Set<String> idToPartition = StatisticsUtil.getPartitionIds(table);
+ Map<Long, String> idToPartition = StatisticsUtil.getPartitionIdToName(table);
// Get an invalid set of partitions (those partitions were deleted)
- Set<String> invalidPartIds = existPartIdsForStats.stream()
- .filter(id -> !idToPartition.contains(id)).collect(Collectors.toSet());
+ Set<Long> invalidPartIds = existPartIdsForStats.stream()
+ .filter(id -> !idToPartition.containsKey(id)).collect(Collectors.toSet());
if (!invalidPartIds.isEmpty()) {
// Delete invalid partition statistics to avoid affecting table statistics
StatisticsRepository.dropStatistics(invalidPartIds);
}
- if (analysisType == AnalysisType.FUNDAMENTALS) {
- return table.findReAnalyzeNeededPartitions();
+ if (analysisMode == AnalysisMode.INCREMENTAL && analysisType == AnalysisType.FUNDAMENTALS) {
+ existColAndPartsForStats.values().forEach(partIds -> partIds.removeAll(invalidPartIds));
+ // In incremental collection mode, just collect the uncollected partition statistics
+ existColAndPartsForStats.forEach((columnName, partitionIds) -> {
+ Set<String> existPartitions = partitionIds.stream()
+ .map(idToPartition::get)
+ .collect(Collectors.toSet());
+ columnToPartitions.computeIfPresent(columnName, (colName, partNames) -> {
+ partNames.removeAll(existPartitions);
+ return partNames;
+ });
+ });
+ if (invalidPartIds.isEmpty()) {
+ // There is no invalid statistics, so there is no need to update table statistics,
+ // remove columns that do not require re-collection of statistics
+ columnToPartitions.entrySet().removeIf(entry -> entry.getValue().isEmpty());
+ }
}
return columnToPartitions;
}
- // Make sure colName of job has all the column as this AnalyzeStmt specified, no matter whether it will be analyzed
- // or not.
- @VisibleForTesting
- public AnalysisInfo buildAnalysisJobInfo(AnalyzeTblStmt stmt) throws DdlException {
- AnalysisInfoBuilder infoBuilder = new AnalysisInfoBuilder();
+ private AnalysisInfo buildAnalysisJobInfo(AnalyzeTblStmt stmt) throws DdlException {
+ AnalysisInfoBuilder taskInfoBuilder = new AnalysisInfoBuilder();
long jobId = Env.getCurrentEnv().getNextId();
String catalogName = stmt.getCatalogName();
String db = stmt.getDBName();
@@ -497,75 +395,140 @@ public class AnalysisManager extends Daemon implements Writable {
Set<String> partitionNames = stmt.getPartitionNames();
boolean partitionOnly = stmt.isPartitionOnly();
boolean isSamplingPartition = stmt.isSamplingPartition();
- boolean isAllPartition = stmt.isAllPartitions();
- long partitionCount = stmt.getPartitionCount();
int samplePercent = stmt.getSamplePercent();
int sampleRows = stmt.getSampleRows();
AnalysisType analysisType = stmt.getAnalysisType();
AnalysisMode analysisMode = stmt.getAnalysisMode();
AnalysisMethod analysisMethod = stmt.getAnalysisMethod();
ScheduleType scheduleType = stmt.getScheduleType();
- CronExpression cronExpression = stmt.getCron();
- infoBuilder.setJobId(jobId);
- infoBuilder.setCatalogName(catalogName);
- infoBuilder.setDbName(db);
- infoBuilder.setTblName(tblName);
- // TODO: Refactor later, DON'T MODIFY IT RIGHT NOW
+ taskInfoBuilder.setJobId(jobId);
+ taskInfoBuilder.setCatalogName(catalogName);
+ taskInfoBuilder.setDbName(db);
+ taskInfoBuilder.setTblName(tblName);
StringJoiner stringJoiner = new StringJoiner(",", "[", "]");
for (String colName : columnNames) {
stringJoiner.add(colName);
}
- infoBuilder.setColName(stringJoiner.toString());
- infoBuilder.setPartitionNames(partitionNames);
- infoBuilder.setPartitionOnly(partitionOnly);
- infoBuilder.setSamplingPartition(isSamplingPartition);
- infoBuilder.setAllPartition(isAllPartition);
- infoBuilder.setPartitionCount(partitionCount);
- infoBuilder.setJobType(JobType.MANUAL);
- infoBuilder.setState(AnalysisState.PENDING);
- infoBuilder.setLastExecTimeInMs(System.currentTimeMillis());
- infoBuilder.setAnalysisType(analysisType);
- infoBuilder.setAnalysisMode(analysisMode);
- infoBuilder.setAnalysisMethod(analysisMethod);
- infoBuilder.setScheduleType(scheduleType);
- infoBuilder.setLastExecTimeInMs(0);
- infoBuilder.setCronExpression(cronExpression);
- infoBuilder.setForceFull(stmt.forceFull());
+ taskInfoBuilder.setColName(stringJoiner.toString());
+ taskInfoBuilder.setPartitionNames(partitionNames);
+ taskInfoBuilder.setPartitionOnly(partitionOnly);
+ taskInfoBuilder.setSamplingPartition(isSamplingPartition);
+ taskInfoBuilder.setJobType(JobType.MANUAL);
+ taskInfoBuilder.setState(AnalysisState.PENDING);
+ taskInfoBuilder.setLastExecTimeInMs(System.currentTimeMillis());
+ taskInfoBuilder.setAnalysisType(analysisType);
+ taskInfoBuilder.setAnalysisMode(analysisMode);
+ taskInfoBuilder.setAnalysisMethod(analysisMethod);
+ taskInfoBuilder.setScheduleType(scheduleType);
+ taskInfoBuilder.setLastExecTimeInMs(0);
+
if (analysisMethod == AnalysisMethod.SAMPLE) {
- infoBuilder.setSamplePercent(samplePercent);
- infoBuilder.setSampleRows(sampleRows);
+ taskInfoBuilder.setSamplePercent(samplePercent);
+ taskInfoBuilder.setSampleRows(sampleRows);
}
if (analysisType == AnalysisType.HISTOGRAM) {
int numBuckets = stmt.getNumBuckets();
int maxBucketNum = numBuckets > 0 ? numBuckets
: StatisticConstants.HISTOGRAM_MAX_BUCKET_NUM;
- infoBuilder.setMaxBucketNum(maxBucketNum);
+ taskInfoBuilder.setMaxBucketNum(maxBucketNum);
}
- long periodTimeInMs = stmt.getPeriodTimeInMs();
- infoBuilder.setPeriodTimeInMs(periodTimeInMs);
+ if (scheduleType == ScheduleType.PERIOD) {
+ long periodTimeInMs = stmt.getPeriodTimeInMs();
+ taskInfoBuilder.setPeriodTimeInMs(periodTimeInMs);
+ }
Map<String, Set<String>> colToPartitions = validateAndGetPartitions(table, columnNames,
- partitionNames, analysisType);
- infoBuilder.setColToPartitions(colToPartitions);
- infoBuilder.setTaskIds(Lists.newArrayList());
-
- return infoBuilder.build();
+ partitionNames, analysisType, analysisMode);
+ taskInfoBuilder.setColToPartitions(colToPartitions);
+ taskInfoBuilder.setTaskIds(Lists.newArrayList());
+
+ return taskInfoBuilder.build();
+ }
+
+ private AnalysisInfo buildAnalysisJobInfo(AnalysisInfo jobInfo) {
+ AnalysisInfoBuilder taskInfoBuilder = new AnalysisInfoBuilder();
+ taskInfoBuilder.setJobId(jobInfo.jobId);
+ taskInfoBuilder.setCatalogName(jobInfo.catalogName);
+ taskInfoBuilder.setDbName(jobInfo.dbName);
+ taskInfoBuilder.setTblName(jobInfo.tblName);
+ taskInfoBuilder.setJobType(JobType.SYSTEM);
+ taskInfoBuilder.setState(AnalysisState.PENDING);
+ taskInfoBuilder.setLastExecTimeInMs(System.currentTimeMillis());
+ taskInfoBuilder.setAnalysisType(jobInfo.analysisType);
+ taskInfoBuilder.setAnalysisMode(jobInfo.analysisMode);
+ taskInfoBuilder.setAnalysisMethod(jobInfo.analysisMethod);
+ taskInfoBuilder.setScheduleType(jobInfo.scheduleType);
+ taskInfoBuilder.setSamplePercent(jobInfo.samplePercent);
+ taskInfoBuilder.setSampleRows(jobInfo.sampleRows);
+ taskInfoBuilder.setMaxBucketNum(jobInfo.maxBucketNum);
+ taskInfoBuilder.setPeriodTimeInMs(jobInfo.periodTimeInMs);
+ taskInfoBuilder.setLastExecTimeInMs(jobInfo.lastExecTimeInMs);
+ try {
+ TableIf table = StatisticsUtil
+ .findTable(jobInfo.catalogName, jobInfo.dbName, jobInfo.tblName);
+ Map<String, Set<String>> colToPartitions = validateAndGetPartitions(table, jobInfo.colToPartitions.keySet(),
+ jobInfo.partitionNames, jobInfo.analysisType, jobInfo.analysisMode);
+ taskInfoBuilder.setColToPartitions(colToPartitions);
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ return taskInfoBuilder.build();
}
- @VisibleForTesting
- public void recordAnalysisJob(AnalysisInfo jobInfo) throws DdlException {
+ private void persistAnalysisJob(AnalysisInfo jobInfo) throws DdlException {
if (jobInfo.scheduleType == ScheduleType.PERIOD && jobInfo.lastExecTimeInMs > 0) {
return;
}
AnalysisInfoBuilder jobInfoBuilder = new AnalysisInfoBuilder(jobInfo);
AnalysisInfo analysisInfo = jobInfoBuilder.setTaskId(-1).build();
- replayCreateAnalysisJob(analysisInfo);
+ logCreateAnalysisJob(analysisInfo);
+ }
+
+ private void createTaskForMVIdx(AnalysisInfo jobInfo, Map<Long, BaseAnalysisTask> analysisTasks,
+ boolean isSync) throws DdlException {
+ TableIf table;
+ try {
+ table = StatisticsUtil.findTable(jobInfo.catalogName, jobInfo.dbName, jobInfo.tblName);
+ } catch (Throwable e) {
+ LOG.warn(e.getMessage());
+ return;
+ }
+
+ TableType type = table.getType();
+ if (jobInfo.analysisType != AnalysisType.INDEX || !type.equals(TableType.OLAP)) {
+ // not need to collect statistics for materialized view
+ return;
+ }
+
+ OlapTable olapTable = (OlapTable) table;
+
+ try {
+ olapTable.readLock();
+ for (MaterializedIndexMeta meta : olapTable.getIndexIdToMeta().values()) {
+ if (meta.getDefineStmt() == null) {
+ continue;
+ }
+ long indexId = meta.getIndexId();
+ long taskId = Env.getCurrentEnv().getNextId();
+ AnalysisInfoBuilder indexTaskInfoBuilder = new AnalysisInfoBuilder(jobInfo);
+ AnalysisInfo analysisInfo = indexTaskInfoBuilder.setIndexId(indexId)
+ .setTaskId(taskId).setLastExecTimeInMs(System.currentTimeMillis()).build();
+ jobInfo.addTaskId(taskId);
+ if (isSync) {
+ return;
+ }
+ analysisTasks.put(taskId, createTask(analysisInfo));
+ logCreateAnalysisTask(analysisInfo);
+ }
+ } finally {
+ olapTable.readUnlock();
+ }
}
- public void createTaskForEachColumns(AnalysisInfo jobInfo, Map<Long, BaseAnalysisTask> analysisTasks,
+ private void createTaskForEachColumns(AnalysisInfo jobInfo, Map<Long, BaseAnalysisTask> analysisTasks,
boolean isSync) throws DdlException {
Map<String, Set<String>> columnToPartitions = jobInfo.colToPartitions;
for (Entry<String, Set<String>> entry : columnToPartitions.entrySet()) {
@@ -586,7 +549,7 @@ public class AnalysisManager extends Daemon implements Writable {
}
try {
if (!jobInfo.jobType.equals(JobType.SYSTEM)) {
- replayCreateAnalysisTask(analysisInfo);
+ logCreateAnalysisTask(analysisInfo);
}
} catch (Exception e) {
throw new DdlException("Failed to create analysis task", e);
@@ -606,12 +569,17 @@ public class AnalysisManager extends Daemon implements Writable {
Env.getCurrentEnv().getEditLog().logCreateAnalysisJob(analysisJob);
}
- @VisibleForTesting
- public void createTableLevelTaskForExternalTable(AnalysisInfo jobInfo,
+ private void createTaskForExternalTable(AnalysisInfo jobInfo,
Map<Long, BaseAnalysisTask> analysisTasks,
boolean isSync) throws DdlException {
-
- if (jobInfo.analysisType == AnalysisType.HISTOGRAM) {
+ TableIf table;
+ try {
+ table = StatisticsUtil.findTable(jobInfo.catalogName, jobInfo.dbName, jobInfo.tblName);
+ } catch (Throwable e) {
+ LOG.warn(e.getMessage());
+ return;
+ }
+ if (jobInfo.analysisType == AnalysisType.HISTOGRAM || !(table instanceof ExternalTable)) {
return;
}
AnalysisInfoBuilder colTaskInfoBuilder = new AnalysisInfoBuilder(jobInfo);
@@ -625,49 +593,120 @@ public class AnalysisManager extends Daemon implements Writable {
return;
}
try {
- replayCreateAnalysisTask(analysisInfo);
+ logCreateAnalysisTask(analysisInfo);
} catch (Exception e) {
throw new DdlException("Failed to create analysis task", e);
}
}
public void updateTaskStatus(AnalysisInfo info, AnalysisState taskState, String message, long time) {
- TaskStatusWrapper taskStatusWrapper = new TaskStatusWrapper(info, taskState, message, time);
- updaters[info.jobType.ordinal()].apply(taskStatusWrapper);
+ if (analysisJobIdToTaskMap.get(info.jobId) == null) {
+ return;
+ }
+ info.state = taskState;
+ info.message = message;
+ // Update the task cost time when task finished or failed. And only log the final state.
+ if (taskState.equals(AnalysisState.FINISHED) || taskState.equals(AnalysisState.FAILED)) {
+ info.timeCostInMs = time - info.lastExecTimeInMs;
+ info.lastExecTimeInMs = time;
+ logCreateAnalysisTask(info);
+ }
+ info.lastExecTimeInMs = time;
+ AnalysisInfo job = analysisJobInfoMap.get(info.jobId);
+ // Synchronize the job state change in job level.
+ synchronized (job) {
+ job.lastExecTimeInMs = time;
+ // Set the job state to RUNNING when its first task becomes RUNNING.
+ if (info.state.equals(AnalysisState.RUNNING) && job.state.equals(AnalysisState.PENDING)) {
+ job.state = AnalysisState.RUNNING;
+ replayCreateAnalysisJob(job);
+ }
+ boolean allFinished = true;
+ boolean hasFailure = false;
+ for (BaseAnalysisTask task : analysisJobIdToTaskMap.get(info.jobId).values()) {
+ AnalysisInfo taskInfo = task.info;
+ if (taskInfo.state.equals(AnalysisState.RUNNING) || taskInfo.state.equals(AnalysisState.PENDING)) {
+ allFinished = false;
+ break;
+ }
+ if (taskInfo.state.equals(AnalysisState.FAILED)) {
+ hasFailure = true;
+ }
+ }
+ if (allFinished) {
+ if (hasFailure) {
+ job.state = AnalysisState.FAILED;
+ logCreateAnalysisJob(job);
+ } else {
+ job.state = AnalysisState.FINISHED;
+ if (job.jobType.equals(JobType.SYSTEM)) {
+ try {
+ updateTableStats(job);
+ } catch (Throwable e) {
+ LOG.warn("Failed to update Table statistics in job: {}", info.toString(), e);
+ }
+ }
+ logCreateAnalysisJob(job);
+ }
+ analysisJobIdToTaskMap.remove(job.jobId);
+ }
+ }
}
- @VisibleForTesting
- public void updateTableStats(AnalysisInfo jobInfo) {
+ private void updateTableStats(AnalysisInfo jobInfo) throws Throwable {
+ Map<String, String> params = buildTableStatsParams(jobInfo);
TableIf tbl = StatisticsUtil.findTable(jobInfo.catalogName,
jobInfo.dbName, jobInfo.tblName);
- // External Table update table stats after table level task finished.
- if (tbl instanceof ExternalTable) {
- return;
- }
- TableStatsMeta tableStats = findTableStatsStatus(tbl.getId());
- if (tableStats == null) {
- updateTableStatsStatus(new TableStatsMeta(tbl.getId(), tbl.estimatedRowCount(), jobInfo));
- } else {
- tableStats.updateByJob(jobInfo);
- logCreateTableStats(tableStats);
+
+ // update olap table stats
+ if (tbl.getType() == TableType.OLAP) {
+ OlapTable table = (OlapTable) tbl;
+ updateOlapTableStats(table, params);
}
+ // External Table doesn't collect table stats here.
+ // We create task for external table to collect table/partition level statistics.
}
- public List<AnalysisInfo> showAnalysisJob(ShowAnalyzeStmt stmt) {
- if (stmt.isAuto()) {
- // It's ok to sync on this field, it would only be assigned when instance init or do checkpoint
- synchronized (autoJobs) {
- return findShowAnalyzeResult(autoJobs, stmt);
- }
+ @SuppressWarnings("rawtypes")
+ private Map<String, String> buildTableStatsParams(AnalysisInfo jobInfo) throws Throwable {
+ CatalogIf catalog = StatisticsUtil.findCatalog(jobInfo.catalogName);
+ DatabaseIf db = StatisticsUtil.findDatabase(jobInfo.catalogName, jobInfo.dbName);
+ TableIf tbl = StatisticsUtil.findTable(jobInfo.catalogName, jobInfo.dbName, jobInfo.tblName);
+ String indexId = String.valueOf(jobInfo.indexId);
+ String id = StatisticsUtil.constructId(tbl.getId(), indexId);
+ Map<String, String> commonParams = new HashMap<>();
+ commonParams.put("id", id);
+ commonParams.put("catalogId", String.valueOf(catalog.getId()));
+ commonParams.put("dbId", String.valueOf(db.getId()));
+ commonParams.put("tblId", String.valueOf(tbl.getId()));
+ commonParams.put("indexId", indexId);
+ commonParams.put("lastAnalyzeTimeInMs", String.valueOf(System.currentTimeMillis()));
+ return commonParams;
+ }
+
+ private void updateOlapTableStats(OlapTable table, Map<String, String> params) throws Throwable {
+ for (Partition partition : table.getPartitions()) {
+ HashMap<String, String> partParams = Maps.newHashMap(params);
+ long rowCount = partition.getBaseIndex().getRowCount();
+ partParams.put("id", StatisticsUtil
+ .constructId(params.get("id"), partition.getId()));
+ partParams.put("partId", String.valueOf(partition.getId()));
+ partParams.put("rowCount", String.valueOf(rowCount));
+ StatisticsRepository.persistTableStats(partParams);
}
- return findShowAnalyzeResult(analysisJobInfoMap.values(), stmt);
+
+ HashMap<String, String> tblParams = Maps.newHashMap(params);
+ long rowCount = table.getRowCount();
+ tblParams.put("partId", "NULL");
+ tblParams.put("rowCount", String.valueOf(rowCount));
+ StatisticsRepository.persistTableStats(tblParams);
}
- protected List<AnalysisInfo> findShowAnalyzeResult(Collection<AnalysisInfo> analysisInfos, ShowAnalyzeStmt stmt) {
+ public List<AnalysisInfo> showAnalysisJob(ShowAnalyzeStmt stmt) {
String state = stmt.getStateValue();
TableName tblName = stmt.getDbTableName();
- return analysisInfos.stream()
+ return analysisJobInfoMap.values().stream()
.filter(a -> stmt.getJobId() == 0 || a.jobId == stmt.getJobId())
.filter(a -> state == null || a.state.equals(AnalysisState.valueOf(state)))
.filter(a -> tblName == null || a.catalogName.equals(tblName.getCtl())
@@ -698,11 +737,10 @@ public class AnalysisManager extends Daemon implements Writable {
break;
}
}
- return String.format(progressDisplayTemplate, finished, failed, inProgress, total);
+ return String.format("%d Finished/%d Failed/%d In Progress/%d Total", finished, failed, inProgress, total);
}
- @VisibleForTesting
- public void syncExecute(Collection<BaseAnalysisTask> tasks) {
+ private void syncExecute(Collection<BaseAnalysisTask> tasks) {
SyncTaskCollection syncTaskCollection = new SyncTaskCollection(tasks);
ConnectContext ctx = ConnectContext.get();
try {
@@ -716,8 +754,7 @@ public class AnalysisManager extends Daemon implements Writable {
private ThreadPoolExecutor createThreadPoolForSyncAnalyze() {
String poolName = "SYNC ANALYZE THREAD POOL";
- return new ThreadPoolExecutor(0,
- ConnectContext.get().getSessionVariable().parallelSyncAnalyzeTaskNum,
+ return new ThreadPoolExecutor(0, 64,
0, TimeUnit.SECONDS,
new SynchronousQueue(),
new ThreadFactoryBuilder().setDaemon(true).setNameFormat("SYNC ANALYZE" + "-%d")
@@ -730,23 +767,17 @@ public class AnalysisManager extends Daemon implements Writable {
Env.getCurrentEnv().getStatisticsCleaner().clear();
return;
}
-
Set<String> cols = dropStatsStmt.getColumnNames();
long tblId = dropStatsStmt.getTblId();
- TableStatsMeta tableStats = findTableStatsStatus(dropStatsStmt.getTblId());
- if (tableStats == null) {
- return;
+ StatisticsRepository.dropStatistics(tblId, cols);
+ for (String col : cols) {
+ Env.getCurrentEnv().getStatisticsCache().invalidate(tblId, -1L, col);
}
- if (cols == null) {
- tableStats.reset();
- } else {
- dropStatsStmt.getColumnNames().forEach(tableStats::removeColumn);
- for (String col : cols) {
- Env.getCurrentEnv().getStatisticsCache().invalidate(tblId, -1L, col);
- }
+ if (dropStatsStmt.dropTableRowCount()) {
+ StatisticsRepository.dropExternalTableStatistics(tblId);
+ // Table cache key doesn't care about catalog id and db id, because the table id is globally unique.
+ Env.getCurrentEnv().getStatisticsCache().invalidateTableStats(-1, -1, tblId);
}
- logCreateTableStats(tableStats);
- StatisticsRepository.dropStatistics(tblId, cols);
}
public void handleKillAnalyzeStmt(KillAnalysisJobStmt killAnalysisJobStmt) throws DdlException {
@@ -841,15 +872,15 @@ public class AnalysisManager extends Daemon implements Writable {
executor.submit(() -> {
try {
if (cancelled) {
- errorMessages.add("Query timeout or user cancelled."
- + "Could set analyze_timeout to a bigger value.");
return;
}
try {
task.execute();
+ updateSyncTaskStatus(task, AnalysisState.FINISHED);
} catch (Throwable t) {
colNames.add(task.info.colName);
errorMessages.add(Util.getRootCauseMessage(t));
+ updateSyncTaskStatus(task, AnalysisState.FAILED);
LOG.warn("Failed to analyze, info: {}", task, t);
}
} finally {
@@ -867,22 +898,32 @@ public class AnalysisManager extends Daemon implements Writable {
+ "] Reasons: " + String.join(",", errorMessages));
}
}
+
+ private void updateSyncTaskStatus(BaseAnalysisTask task, AnalysisState state) {
+ Env.getCurrentEnv().getAnalysisManager()
+ .updateTaskStatus(task.info, state, "", System.currentTimeMillis());
+ }
+ }
+
+ public List<AnalysisInfo> findAutomaticAnalysisJobs() {
+ synchronized (analysisJobInfoMap) {
+ return analysisJobInfoMap.values().stream()
+ .filter(a ->
+ a.scheduleType.equals(ScheduleType.AUTOMATIC)
+ && (!(a.state.equals(AnalysisState.RUNNING)
+ || a.state.equals(AnalysisState.PENDING)))
+ && System.currentTimeMillis() - a.lastExecTimeInMs
+ > TimeUnit.MINUTES.toMillis(Config.auto_check_statistics_in_minutes))
+ .collect(Collectors.toList());
+ }
}
public List<AnalysisInfo> findPeriodicJobs() {
synchronized (analysisJobInfoMap) {
- Predicate<AnalysisInfo> p = a -> {
- if (a.state.equals(AnalysisState.RUNNING)) {
- return false;
- }
- if (a.cronExpression == null) {
- return a.scheduleType.equals(ScheduleType.PERIOD)
- && System.currentTimeMillis() - a.lastExecTimeInMs > a.periodTimeInMs;
- }
- return a.cronExpression.getTimeAfter(new Date(a.lastExecTimeInMs)).before(new Date());
- };
return analysisJobInfoMap.values().stream()
- .filter(p)
+ .filter(a -> a.scheduleType.equals(ScheduleType.PERIOD)
+ && (a.state.equals(AnalysisState.FINISHED))
+ && System.currentTimeMillis() - a.lastExecTimeInMs > a.periodTimeInMs)
.collect(Collectors.toList());
}
}
@@ -896,8 +937,7 @@ public class AnalysisManager extends Daemon implements Writable {
public List<AnalysisInfo> findTasksByTaskIds(long jobId) {
AnalysisInfo jobInfo = analysisJobInfoMap.get(jobId);
if (jobInfo != null && jobInfo.taskIds != null) {
- return jobInfo.taskIds.stream().map(analysisTaskInfoMap::get).filter(i -> i != null)
- .collect(Collectors.toList());
+ return jobInfo.taskIds.stream().map(id -> analysisTaskInfoMap.get(id)).collect(Collectors.toList());
}
return null;
}
@@ -923,185 +963,34 @@ public class AnalysisManager extends Daemon implements Writable {
public static AnalysisManager readFields(DataInput in) throws IOException {
AnalysisManager analysisManager = new AnalysisManager();
- readAnalysisInfo(in, analysisManager.analysisJobInfoMap, true);
- readAnalysisInfo(in, analysisManager.analysisTaskInfoMap, false);
- readIdToTblStats(in, analysisManager.idToTblStats);
- readAutoJobs(in, analysisManager);
+ doRead(in, analysisManager.analysisJobInfoMap, true);
+ doRead(in, analysisManager.analysisTaskInfoMap, false);
return analysisManager;
}
- private static void readAnalysisInfo(DataInput in, Map<Long, AnalysisInfo> map, boolean job) throws IOException {
+ private static void doRead(DataInput in, Map<Long, AnalysisInfo> map, boolean job) throws IOException {
int size = in.readInt();
for (int i = 0; i < size; i++) {
AnalysisInfo analysisInfo = AnalysisInfo.read(in);
- // Unfinished manual once job/tasks doesn't need to keep in memory anymore.
- if (needAbandon(analysisInfo)) {
- continue;
- }
map.put(job ? analysisInfo.jobId : analysisInfo.taskId, analysisInfo);
}
}
- // Need to abandon the unfinished manual once jobs/tasks while loading image and replay journal.
- // Journal only store finished tasks and jobs.
- public static boolean needAbandon(AnalysisInfo analysisInfo) {
- if (analysisInfo == null) {
- return true;
- }
- if ((AnalysisState.PENDING.equals(analysisInfo.state) || AnalysisState.RUNNING.equals(analysisInfo.state))
- && ScheduleType.ONCE.equals(analysisInfo.scheduleType)
- && JobType.MANUAL.equals(analysisInfo.jobType)) {
- return true;
- }
- return false;
- }
-
- private static void readIdToTblStats(DataInput in, Map<Long, TableStatsMeta> map) throws IOException {
- int size = in.readInt();
- for (int i = 0; i < size; i++) {
- TableStatsMeta tableStats = TableStatsMeta.read(in);
- map.put(tableStats.tblId, tableStats);
- }
- }
-
- private static void readAutoJobs(DataInput in, AnalysisManager analysisManager) throws IOException {
- Type type = new TypeToken<LinkedList<AnalysisInfo>>() {}.getType();
- Collection<AnalysisInfo> autoJobs = GsonUtils.GSON.fromJson(Text.readString(in), type);
- analysisManager.autoJobs = analysisManager.createSimpleQueue(autoJobs, analysisManager);
- }
-
@Override
public void write(DataOutput out) throws IOException {
- writeJobInfo(out, analysisJobInfoMap);
- writeJobInfo(out, analysisTaskInfoMap);
- writeTableStats(out);
- writeAutoJobsStatus(out);
+ doWrite(out, analysisJobInfoMap);
+ doWrite(out, analysisTaskInfoMap);
}
- private void writeJobInfo(DataOutput out, Map<Long, AnalysisInfo> infoMap) throws IOException {
+ private void doWrite(DataOutput out, Map<Long, AnalysisInfo> infoMap) throws IOException {
out.writeInt(infoMap.size());
for (Entry<Long, AnalysisInfo> entry : infoMap.entrySet()) {
entry.getValue().write(out);
}
}
- private void writeTableStats(DataOutput out) throws IOException {
- out.writeInt(idToTblStats.size());
- for (Entry<Long, TableStatsMeta> entry : idToTblStats.entrySet()) {
- entry.getValue().write(out);
- }
- }
-
- private void writeAutoJobsStatus(DataOutput output) throws IOException {
- Type type = new TypeToken<LinkedList<AnalysisInfo>>() {}.getType();
- String autoJobs = GsonUtils.GSON.toJson(this.autoJobs, type);
- Text.writeString(output, autoJobs);
- }
-
// For unit test use only.
public void addToJobIdTasksMap(long jobId, Map<Long, BaseAnalysisTask> tasks) {
analysisJobIdToTaskMap.put(jobId, tasks);
}
-
- public TableStatsMeta findTableStatsStatus(long tblId) {
- return idToTblStats.get(tblId);
- }
-
- // Invoke this when load transaction finished.
- public void updateUpdatedRows(long tblId, long rows) {
- TableStatsMeta statsStatus = idToTblStats.get(tblId);
- if (statsStatus != null) {
- statsStatus.updatedRows.addAndGet(rows);
- logCreateTableStats(statsStatus);
- }
- }
-
- public void updateTableStatsStatus(TableStatsMeta tableStats) {
- replayUpdateTableStatsStatus(tableStats);
- logCreateTableStats(tableStats);
- }
-
- public void replayUpdateTableStatsStatus(TableStatsMeta tableStats) {
- idToTblStats.put(tableStats.tblId, tableStats);
- }
-
- public void logCreateTableStats(TableStatsMeta tableStats) {
- Env.getCurrentEnv().getEditLog().logCreateTableStats(tableStats);
- }
-
- public void registerSysJob(AnalysisInfo jobInfo, Map<Long, BaseAnalysisTask> taskInfos) {
- jobInfo.state = AnalysisState.RUNNING;
- systemJobInfoMap.put(jobInfo.jobId, jobInfo);
- analysisJobIdToTaskMap.put(jobInfo.jobId, taskInfos);
- }
-
- @VisibleForTesting
- protected Set<String> findReAnalyzeNeededPartitions(TableIf table) {
- TableStatsMeta tableStats = findTableStatsStatus(table.getId());
- if (tableStats == null) {
- return table.getPartitionNames().stream().map(table::getPartition)
- .filter(Partition::hasData).map(Partition::getName).collect(Collectors.toSet());
- }
- return table.getPartitionNames().stream()
- .map(table::getPartition)
- .filter(Partition::hasData)
- .filter(partition ->
- partition.getVisibleVersionTime() >= tableStats.updatedTime).map(Partition::getName)
- .collect(Collectors.toSet());
- }
-
- protected void logAutoJob(AnalysisInfo autoJob) {
- Env.getCurrentEnv().getEditLog().logAutoJob(autoJob);
- }
-
- public void replayPersistSysJob(AnalysisInfo analysisInfo) {
- autoJobs.offer(analysisInfo);
- }
-
- protected SimpleQueue<AnalysisInfo> createSimpleQueue(Collection<AnalysisInfo> collection,
- AnalysisManager analysisManager) {
- return new SimpleQueue<>(Config.auto_analyze_job_record_count,
- a -> {
- // FE is not ready when replaying log and operations triggered by replaying
- // shouldn't be logged again.
- if (Env.getCurrentEnv().isReady() && Env.getCurrentEnv().isMaster() && !Env.isCheckpointThread()) {
- analysisManager.logAutoJob(a);
- }
- return null;
- },
- a -> {
- // DO NOTHING
- return null;
- }, null);
- }
-
- // Remove col stats status from TableStats if failed load some col stats after analyze corresponding column so that
- // we could make sure it would be analyzed again soon if user or system submit job for that column again.
- public void removeColStatsStatus(long tblId, String colName) {
- TableStatsMeta tableStats = findTableStatsStatus(tblId);
- if (tableStats != null) {
- tableStats.removeColumn(colName);
- }
- }
-
- public void removeTableStats(long tblId) {
- if (!idToTblStats.containsKey(tblId)) {
- return;
- }
- TableStatsDeletionLog log = new TableStatsDeletionLog(tblId);
- Env.getCurrentEnv().getEditLog().logDeleteTableStats(log);
- replayTableStatsDeletion(log);
- }
-
- public void replayTableStatsDeletion(TableStatsDeletionLog log) {
- idToTblStats.remove(log.id);
- }
-
- public ColStatsMeta findColStatsMeta(long tblId, String colName) {
- TableStatsMeta tableStats = findTableStatsStatus(tblId);
- if (tableStats == null) {
- return null;
- }
- return tableStats.findColumnStatsMeta(colName);
- }
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisState.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisState.java
index 3abc4c224fa..bab8a462e8a 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisState.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisState.java
@@ -18,9 +18,7 @@
package org.apache.doris.statistics;
public enum AnalysisState {
- // When analyze job/task created, but never run
PENDING,
- // When analyze job/task is in running queue
RUNNING,
FINISHED,
FAILED;
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisTaskExecutor.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisTaskExecutor.java
index 4b133ce0ebf..b5ec7aeb876 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisTaskExecutor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisTaskExecutor.java
@@ -17,7 +17,6 @@
package org.apache.doris.statistics;
-import org.apache.doris.catalog.Env;
import org.apache.doris.common.Config;
import org.apache.doris.common.ThreadPoolManager;
import org.apache.doris.common.ThreadPoolManager.BlockedPolicy;
@@ -36,30 +35,26 @@ public class AnalysisTaskExecutor extends Thread {
private static final Logger LOG = LogManager.getLogger(AnalysisTaskExecutor.class);
- private final ThreadPoolExecutor executors;
+ private final ThreadPoolExecutor executors = ThreadPoolManager.newDaemonThreadPool(
+ Config.statistics_simultaneously_running_task_num,
+ Config.statistics_simultaneously_running_task_num, 0,
+ TimeUnit.DAYS, new LinkedBlockingQueue<>(),
+ new BlockedPolicy("Analysis Job Executor", Integer.MAX_VALUE),
+ "Analysis Job Executor", true);
+
+ private final AnalysisTaskScheduler taskScheduler;
private final BlockingQueue<AnalysisTaskWrapper> taskQueue =
new PriorityBlockingQueue<AnalysisTaskWrapper>(20,
Comparator.comparingLong(AnalysisTaskWrapper::getStartTime));
- public AnalysisTaskExecutor(int simultaneouslyRunningTaskNum) {
- if (!Env.isCheckpointThread()) {
- executors = ThreadPoolManager.newDaemonThreadPool(
- simultaneouslyRunningTaskNum,
- simultaneouslyRunningTaskNum, 0,
- TimeUnit.DAYS, new LinkedBlockingQueue<>(),
- new BlockedPolicy("Analysis Job Executor", Integer.MAX_VALUE),
- "Analysis Job Executor", true);
- } else {
- executors = null;
- }
+ public AnalysisTaskExecutor(AnalysisTaskScheduler jobExecutor) {
+ this.taskScheduler = jobExecutor;
}
@Override
public void run() {
- if (Env.isCheckpointThread()) {
- return;
- }
+ fetchAndExecute();
cancelExpiredTask();
}
@@ -87,7 +82,22 @@ public class AnalysisTaskExecutor extends Thread {
}
}
- public void submitTask(BaseAnalysisTask task) {
+ public void fetchAndExecute() {
+ Thread t = new Thread(() -> {
+ for (;;) {
+ try {
+ doFetchAndExecute();
+ } catch (Throwable throwable) {
+ LOG.warn(throwable);
+ }
+ }
+ }, "Analysis Task Submitter");
+ t.setDaemon(true);
+ t.start();
+ }
+
+ private void doFetchAndExecute() {
+ BaseAnalysisTask task = taskScheduler.getPendingTasks();
AnalysisTaskWrapper taskWrapper = new AnalysisTaskWrapper(this, task);
executors.submit(taskWrapper);
}
@@ -95,13 +105,4 @@ public class AnalysisTaskExecutor extends Thread {
public void putJob(AnalysisTaskWrapper wrapper) throws Exception {
taskQueue.put(wrapper);
}
-
- public boolean idle() {
- return executors.getQueue().isEmpty();
- }
-
- public void clear() {
- executors.getQueue().clear();
- taskQueue.clear();
- }
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisTaskScheduler.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisTaskScheduler.java
new file mode 100644
index 00000000000..5c9de2b58b2
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisTaskScheduler.java
@@ -0,0 +1,108 @@
+// 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.doris.statistics;
+
+import org.apache.doris.catalog.Env;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.PriorityQueue;
+import java.util.Queue;
+import java.util.Set;
+
+public class AnalysisTaskScheduler {
+
+ private static final Logger LOG = LogManager.getLogger(AnalysisTaskScheduler.class);
+
+ private final PriorityQueue<BaseAnalysisTask> systemJobQueue =
+ new PriorityQueue<>(Comparator.comparingLong(BaseAnalysisTask::getLastExecTime));
+
+ private final Queue<BaseAnalysisTask> manualJobQueue = new LinkedList<>();
+
+ private final Set<BaseAnalysisTask> systemJobSet = new HashSet<>();
+
+ private final Set<BaseAnalysisTask> manualJobSet = new HashSet<>();
+
+ public synchronized void schedule(BaseAnalysisTask analysisTask) {
+ try {
+
+ switch (analysisTask.info.jobType) {
+ case MANUAL:
+ addToManualJobQueue(analysisTask);
+ break;
+ case SYSTEM:
+ addToSystemQueue(analysisTask);
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown job type: " + analysisTask.info.jobType);
+ }
+ } catch (Throwable t) {
+ Env.getCurrentEnv().getAnalysisManager().updateTaskStatus(
+ analysisTask.info, AnalysisState.FAILED, t.getMessage(), System.currentTimeMillis());
+ }
+ }
+
+ // Make sure invoker of this method is synchronized on object.
+
+ private void addToSystemQueue(BaseAnalysisTask analysisJobInfo) {
+ if (systemJobSet.contains(analysisJobInfo)) {
+ return;
+ }
+ systemJobSet.add(analysisJobInfo);
+ systemJobQueue.add(analysisJobInfo);
+ notify();
+ }
+
+ // Make sure invoker of this method is synchronized on object.
+ private void addToManualJobQueue(BaseAnalysisTask analysisJobInfo) {
+ if (manualJobSet.contains(analysisJobInfo)) {
+ return;
+ }
+ manualJobSet.add(analysisJobInfo);
+ manualJobQueue.add(analysisJobInfo);
+ notify();
+ }
+
+ public synchronized BaseAnalysisTask getPendingTasks() {
+ while (true) {
+ if (!manualJobQueue.isEmpty()) {
+ return pollAndRemove(manualJobQueue, manualJobSet);
+ }
+ if (!systemJobQueue.isEmpty()) {
+ return pollAndRemove(systemJobQueue, systemJobSet);
+ }
+ try {
+ wait();
+ } catch (Exception e) {
+ LOG.warn("Thread get interrupted when waiting for pending jobs", e);
+ return null;
+ }
+ }
+ }
+
+ // Poll from queue, remove from set. Make sure invoker of this method is synchronized on object.
+ private BaseAnalysisTask pollAndRemove(Queue<BaseAnalysisTask> q, Set<BaseAnalysisTask> s) {
+ BaseAnalysisTask t = q.poll();
+ s.remove(t);
+ return t;
+ }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisTaskWrapper.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisTaskWrapper.java
index 9aa3d85992b..7f55469f533 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisTaskWrapper.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/AnalysisTaskWrapper.java
@@ -18,15 +18,11 @@
package org.apache.doris.statistics;
import org.apache.doris.catalog.Env;
-import org.apache.doris.common.util.TimeUtils;
import org.apache.doris.common.util.Util;
-import org.apache.doris.statistics.AnalysisInfo.ScheduleType;
-import org.apache.doris.statistics.util.StatisticsUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
-import java.time.LocalTime;
import java.util.concurrent.FutureTask;
public class AnalysisTaskWrapper extends FutureTask<Void> {
@@ -56,14 +52,6 @@ public class AnalysisTaskWrapper extends FutureTask<Void> {
if (task.killed) {
return;
}
- if (task.info.scheduleType.equals(ScheduleType.AUTOMATIC) && !StatisticsUtil.inAnalyzeTime(
- LocalTime.now(TimeUtils.getTimeZone().toZoneId()))) {
- // TODO: Do we need a separate AnalysisState here?
- Env.getCurrentEnv().getAnalysisManager()
- .updateTaskStatus(task.info, AnalysisState.FAILED, "Auto task"
- + "doesn't get executed within specified time range", System.currentTimeMillis());
- return;
- }
executor.putJob(this);
super.run();
Object result = get();
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/BaseAnalysisTask.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/BaseAnalysisTask.java
index fd99c97e83b..719df8769a4 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/BaseAnalysisTask.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/BaseAnalysisTask.java
@@ -21,11 +21,7 @@ import org.apache.doris.catalog.Column;
import org.apache.doris.catalog.DatabaseIf;
import org.apache.doris.catalog.Env;
import org.apache.doris.catalog.TableIf;
-import org.apache.doris.common.Config;
import org.apache.doris.datasource.CatalogIf;
-import org.apache.doris.qe.AuditLogHelper;
-import org.apache.doris.qe.QueryState;
-import org.apache.doris.qe.QueryState.MysqlStateType;
import org.apache.doris.qe.StmtExecutor;
import org.apache.doris.statistics.AnalysisInfo.AnalysisMethod;
import org.apache.doris.statistics.AnalysisInfo.AnalysisType;
@@ -68,7 +64,7 @@ public abstract class BaseAnalysisTask {
protected static final String INSERT_COL_STATISTICS = "INSERT INTO "
+ "${internalDB}.${columnStatTbl}"
+ " SELECT id, catalog_id, db_id, tbl_id, idx_id, col_id, part_id, row_count, "
- + " ndv, null_count, CAST(min AS string), CAST(max AS string), data_size, update_time\n"
+ + " ndv, null_count, min, max, data_size, update_time\n"
+ " FROM \n"
+ " (SELECT CONCAT(${tblId}, '-', ${idxId}, '-', '${colId}') AS id, "
+ " ${catalogId} AS catalog_id, "
@@ -115,7 +111,7 @@ public abstract class BaseAnalysisTask {
init(info);
}
- protected void init(AnalysisInfo info) {
+ private void init(AnalysisInfo info) {
catalog = Env.getCurrentEnv().getCatalogMgr().getCatalog(info.catalogName);
if (catalog == null) {
Env.getCurrentEnv().getAnalysisManager().updateTaskStatus(info, AnalysisState.FAILED,
@@ -170,9 +166,6 @@ public abstract class BaseAnalysisTask {
doExecute();
break;
} catch (Throwable t) {
- if (killed) {
- throw new RuntimeException(t);
- }
LOG.warn("Failed to execute analysis task, retried times: {}", retriedTimes++, t);
if (retriedTimes > StatisticConstants.ANALYZE_TASK_RETRY_TIMES) {
throw new RuntimeException(t);
@@ -188,16 +181,12 @@ public abstract class BaseAnalysisTask {
if (killed) {
return;
}
- long tblId = tbl.getId();
- String colName = col.getName();
- if (!Env.getCurrentEnv().getStatisticsCache().syncLoadColStats(tblId, -1, colName)) {
- Env.getCurrentEnv().getAnalysisManager().removeColStatsStatus(tblId, colName);
- }
+ Env.getCurrentEnv().getStatisticsCache().syncLoadColStats(tbl.getId(), -1, col.getName());
}
protected void setTaskStateToRunning() {
Env.getCurrentEnv().getAnalysisManager()
- .updateTaskStatus(info, AnalysisState.RUNNING, "", System.currentTimeMillis());
+ .updateTaskStatus(info, AnalysisState.RUNNING, "", System.currentTimeMillis());
}
public void cancel() {
@@ -210,6 +199,10 @@ public abstract class BaseAnalysisTask {
String.format("Job has been cancelled: %s", info.message), System.currentTimeMillis());
}
+ public long getLastExecTime() {
+ return info.lastExecTimeInMs;
+ }
+
public long getJobId() {
return info.jobId;
}
@@ -223,49 +216,21 @@ public abstract class BaseAnalysisTask {
}
protected String getSampleExpression() {
- if (info.forceFull) {
- return "";
- }
- int sampleRows = info.sampleRows;
if (info.analysisMethod == AnalysisMethod.FULL) {
- if (Config.enable_auto_sample
- && tbl.getDataSize(true) > Config.huge_table_lower_bound_size_in_bytes) {
- sampleRows = Config.huge_table_default_sample_rows;
- } else {
- return "";
- }
+ return "";
}
+ // TODO Add sampling methods for external tables
if (info.samplePercent > 0) {
return String.format("TABLESAMPLE(%d PERCENT)", info.samplePercent);
} else {
- return String.format("TABLESAMPLE(%d ROWS)", sampleRows);
+ return String.format("TABLESAMPLE(%d ROWS)", info.sampleRows);
}
}
@Override
public String toString() {
return String.format("Job id [%d], Task id [%d], catalog [%s], db [%s], table [%s], column [%s]",
- info.jobId, info.taskId, catalog.getName(), db.getFullName(), tbl.getName(),
- col == null ? "TableRowCount" : col.getName());
- }
-
- protected void executeWithExceptionOnFail(StmtExecutor stmtExecutor) throws Exception {
- if (killed) {
- return;
- }
- LOG.debug("execute internal sql: {}", stmtExecutor.getOriginStmt());
- try {
- stmtExecutor.execute();
- QueryState queryState = stmtExecutor.getContext().getState();
- if (queryState.getStateType().equals(MysqlStateType.ERR)) {
- throw new RuntimeException(String.format("Failed to analyze %s.%s.%s, error: %s sql: %s",
- info.catalogName, info.dbName, info.colName, stmtExecutor.getOriginStmt().toString(),
- queryState.getErrorMessage()));
- }
- } finally {
- AuditLogHelper.logAuditLog(stmtExecutor.getContext(), stmtExecutor.getOriginStmt().toString(),
- stmtExecutor.getParsedStmt(), stmtExecutor.getQueryStatisticsForAuditLog(),
- true);
- }
+ info.jobId, info.taskId, catalog.getName(), db.getFullName(), tbl.getName(),
+ col == null ? "TableRowCount" : col.getName());
}
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/ColStatsData.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/ColStatsData.java
deleted file mode 100644
index a14e32d726b..00000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/ColStatsData.java
+++ /dev/null
@@ -1,82 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements. See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership. The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License. You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied. See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.statistics;
-
-import org.apache.doris.statistics.util.StatisticsUtil;
-
-import java.util.StringJoiner;
-
-/**
- * Used to convert data from ResultRow.
- * 0: id
- * 1: catalog_id
- * 2: db_id
- * 3: tbl_id
- * 4: idx_id
- * 5: col_id
- * 6: part_id
- * 7: count
- * 8: ndv
- * 9: null_count
- * 10: min
- * 11: max
- * 12: data_size_in_bytes
- * 13: update_time
- */
-public class ColStatsData {
- public final StatsId statsId;
- public final long count;
- public final long ndv;
-
- public final long nullCount;
-
- public final String minLit;
- public final String maxLit;
-
- public final long dataSizeInBytes;
-
- public final String updateTime;
-
- public ColStatsData(ResultRow row) {
- this.statsId = new StatsId(row);
- this.count = Long.parseLong(row.get(7));
- this.ndv = Long.parseLong(row.getWithDefault(8, "0"));
- this.nullCount = Long.parseLong(row.getWithDefault(9, "0"));
- this.minLit = row.get(10);
- this.maxLit = row.get(11);
- this.dataSizeInBytes = Long.parseLong(row.getWithDefault(12, "0"));
- this.updateTime = row.get(13);
- }
-
- public String toSQL(boolean roundByParentheses) {
- StringJoiner sj = null;
- if (roundByParentheses) {
- sj = new StringJoiner(",", "(" + statsId.toSQL() + ",", ")");
- } else {
- sj = new StringJoiner(",", statsId.toSQL(), "");
- }
- sj.add(String.valueOf(count));
- sj.add(String.valueOf(ndv));
- sj.add(String.valueOf(nullCount));
- sj.add(StatisticsUtil.quote(minLit));
- sj.add(StatisticsUtil.quote(maxLit));
- sj.add(String.valueOf(dataSizeInBytes));
- sj.add(StatisticsUtil.quote(updateTime));
- return sj.toString();
- }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/ColStatsMeta.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/ColStatsMeta.java
deleted file mode 100644
index 445641b2505..00000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/ColStatsMeta.java
+++ /dev/null
@@ -1,58 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements. See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership. The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License. You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied. See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.statistics;
-
-import org.apache.doris.statistics.AnalysisInfo.AnalysisMethod;
-import org.apache.doris.statistics.AnalysisInfo.AnalysisType;
-import org.apache.doris.statistics.AnalysisInfo.JobType;
-
-import com.google.gson.annotations.SerializedName;
-
-import java.util.concurrent.atomic.AtomicLong;
-
-public class ColStatsMeta {
-
- @SerializedName("updateTime")
- public long updatedTime;
-
- @SerializedName("method")
- public AnalysisMethod analysisMethod;
-
- @SerializedName("type")
- public AnalysisType analysisType;
-
- @SerializedName("queriedTimes")
- public final AtomicLong queriedTimes = new AtomicLong();
-
- // TODO: For column that manually analyzed, we should use same analyze method as user specified.
- @SerializedName("trigger")
- public JobType jobType;
-
- public ColStatsMeta(long updatedTime, AnalysisMethod analysisMethod,
- AnalysisType analysisType, JobType jobType, long queriedTimes) {
- this.updatedTime = updatedTime;
- this.analysisMethod = analysisMethod;
- this.analysisType = analysisType;
- this.jobType = jobType;
- this.queriedTimes.addAndGet(queriedTimes);
- }
-
- public void clear() {
- updatedTime = 0;
- }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/ColumnStatistic.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/ColumnStatistic.java
index c6b019f669b..7986cb07a57 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/ColumnStatistic.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/ColumnStatistic.java
@@ -19,26 +19,26 @@ package org.apache.doris.statistics;
import org.apache.doris.analysis.LiteralExpr;
import org.apache.doris.catalog.Column;
-import org.apache.doris.catalog.PartitionInfo;
+import org.apache.doris.catalog.Env;
import org.apache.doris.catalog.Type;
import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.DdlException;
+import org.apache.doris.statistics.util.InternalQueryResult.ResultRow;
import org.apache.doris.statistics.util.StatisticsUtil;
-import com.google.common.collect.Sets;
import com.google.gson.annotations.SerializedName;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.json.JSONObject;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class ColumnStatistic {
- public static final double STATS_ERROR = 0.1D;
-
public static final StatsType NDV = StatsType.NDV;
public static final StatsType AVG_SIZE = StatsType.AVG_SIZE;
public static final StatsType MAX_SIZE = StatsType.MAX_SIZE;
@@ -50,17 +50,30 @@ public class ColumnStatistic {
public static ColumnStatistic UNKNOWN = new ColumnStatisticBuilder().setAvgSizeByte(1).setNdv(1)
.setNumNulls(1).setCount(1).setMaxValue(Double.POSITIVE_INFINITY).setMinValue(Double.NEGATIVE_INFINITY)
- .setIsUnknown(true).setUpdatedTime("")
+ .setSelectivity(1.0).setIsUnknown(true)
.build();
public static ColumnStatistic ZERO = new ColumnStatisticBuilder().setAvgSizeByte(0).setNdv(0)
.setNumNulls(0).setCount(0).setMaxValue(Double.NaN).setMinValue(Double.NaN)
+ .setSelectivity(0)
.build();
- public static final Set<Type> UNSUPPORTED_TYPE = Sets.newHashSet(
- Type.HLL, Type.BITMAP, Type.ARRAY, Type.STRUCT, Type.MAP, Type.QUANTILE_STATE, Type.AGG_STATE, Type.JSONB,
- Type.VARIANT, Type.TIME, Type.TIMEV2, Type.LAMBDA_FUNCTION
- );
+ public static final Set<Type> UNSUPPORTED_TYPE = new HashSet<>();
+
+ static {
+ UNSUPPORTED_TYPE.add(Type.HLL);
+ UNSUPPORTED_TYPE.add(Type.BITMAP);
+ UNSUPPORTED_TYPE.add(Type.ARRAY);
+ UNSUPPORTED_TYPE.add(Type.STRUCT);
+ UNSUPPORTED_TYPE.add(Type.MAP);
+ UNSUPPORTED_TYPE.add(Type.QUANTILE_STATE);
+ UNSUPPORTED_TYPE.add(Type.AGG_STATE);
+ UNSUPPORTED_TYPE.add(Type.JSONB);
+ UNSUPPORTED_TYPE.add(Type.VARIANT);
+ UNSUPPORTED_TYPE.add(Type.TIME);
+ UNSUPPORTED_TYPE.add(Type.TIMEV2);
+ UNSUPPORTED_TYPE.add(Type.LAMBDA_FUNCTION);
+ }
@SerializedName("count")
public final double count;
@@ -77,6 +90,19 @@ public class ColumnStatistic {
@SerializedName("maxValue")
public final double maxValue;
public final boolean isUnKnown;
+ /*
+ selectivity of Column T1.A:
+ if T1.A = T2.B is the inner join condition, for a given `b` in B, b in
+ intersection of range(A) and range(B), selectivity means the probability that
+ the equation can be satisfied.
+ We take tpch as example.
+ l_orderkey = o_orderkey and o_orderstatus='o'
+ there are 3 distinct o_orderstatus in orders table. filter o_orderstatus='o' reduces orders table by 1/3
+ because o_orderkey is primary key, thus the o_orderkey.selectivity = 1/3,
+ and after join(l_orderkey = o_orderkey), lineitem is reduced by 1/3.
+ But after filter, other columns' selectivity is still 1.0
+ */
+ public final double selectivity;
/*
originalNdv is the ndv in stats of ScanNode. ndv may be changed after filter or join,
@@ -85,6 +111,7 @@ public class ColumnStatistic {
*/
public final ColumnStatistic original;
+ // For display only.
public final LiteralExpr minExpr;
public final LiteralExpr maxExpr;
@@ -92,17 +119,14 @@ public class ColumnStatistic {
// assign value when do stats estimation.
public final Histogram histogram;
- @SerializedName("partitionIdToColStats")
- public final Map<String, ColumnStatistic> partitionIdToColStats = new HashMap<>();
+ public final Map<Long, ColumnStatistic> partitionIdToColStats = new HashMap<>();
public final String updatedTime;
- public final PartitionInfo partitionInfo;
-
public ColumnStatistic(double count, double ndv, ColumnStatistic original, double avgSizeByte,
double numNulls, double dataSize, double minValue, double maxValue,
- LiteralExpr minExpr, LiteralExpr maxExpr, boolean isUnKnown, Histogram histogram,
- String updatedTime, PartitionInfo partitionInfo) {
+ double selectivity, LiteralExpr minExpr, LiteralExpr maxExpr, boolean isUnKnown, Histogram histogram,
+ String updatedTime) {
this.count = count;
this.ndv = ndv;
this.original = original;
@@ -111,30 +135,31 @@ public class ColumnStatistic {
this.dataSize = dataSize;
this.minValue = minValue;
this.maxValue = maxValue;
+ this.selectivity = selectivity;
this.minExpr = minExpr;
this.maxExpr = maxExpr;
this.isUnKnown = isUnKnown;
this.histogram = histogram;
this.updatedTime = updatedTime;
- this.partitionInfo = partitionInfo;
}
public static ColumnStatistic fromResultRow(List<ResultRow> resultRows) {
- Map<String, ColumnStatistic> partitionIdToColStats = new HashMap<>();
+ Map<Long, ColumnStatistic> partitionIdToColStats = new HashMap<>();
ColumnStatistic columnStatistic = null;
try {
for (ResultRow resultRow : resultRows) {
- String partId = resultRow.get(6);
+ String partId = resultRow.getColumnValue("part_id");
if (partId == null) {
columnStatistic = fromResultRow(resultRow);
} else {
- partitionIdToColStats.put(partId, fromResultRow(resultRow));
+ partitionIdToColStats.put(Long.parseLong(partId), fromResultRow(resultRow));
}
}
} catch (Throwable t) {
LOG.debug("Failed to deserialize column stats", t);
return ColumnStatistic.UNKNOWN;
}
+ // Means last analyze failed or interrupted for some reason.
if (columnStatistic == null) {
return ColumnStatistic.UNKNOWN;
}
@@ -143,44 +168,47 @@ public class ColumnStatistic {
}
// TODO: use thrift
- public static ColumnStatistic fromResultRow(ResultRow row) {
+ public static ColumnStatistic fromResultRow(ResultRow resultRow) {
try {
ColumnStatisticBuilder columnStatisticBuilder = new ColumnStatisticBuilder();
- double count = Double.parseDouble(row.get(7));
+ double count = Double.parseDouble(resultRow.getColumnValueWithDefault("count", "0"));
columnStatisticBuilder.setCount(count);
- double ndv = Double.parseDouble(row.getWithDefault(8, "0"));
+ double ndv = Double.parseDouble(resultRow.getColumnValueWithDefault("ndv", "0"));
+ if (0.99 * count < ndv && ndv < 1.01 * count) {
+ ndv = count;
+ }
columnStatisticBuilder.setNdv(ndv);
- String nullCount = row.getWithDefault(9, "0");
+ String nullCount = resultRow.getColumnValueWithDefault("null_count", "0");
columnStatisticBuilder.setNumNulls(Double.parseDouble(nullCount));
columnStatisticBuilder.setDataSize(Double
- .parseDouble(row.getWithDefault(12, "0")));
+ .parseDouble(resultRow.getColumnValueWithDefault("data_size_in_bytes", "0")));
columnStatisticBuilder.setAvgSizeByte(columnStatisticBuilder.getCount() == 0
? 0 : columnStatisticBuilder.getDataSize()
/ columnStatisticBuilder.getCount());
- long catalogId = Long.parseLong(row.get(1));
- long idxId = Long.parseLong(row.get(4));
- long dbID = Long.parseLong(row.get(2));
- long tblId = Long.parseLong(row.get(3));
- String colName = row.get(5);
+ long catalogId = Long.parseLong(resultRow.getColumnValue("catalog_id"));
+ long idxId = Long.parseLong(resultRow.getColumnValue("idx_id"));
+ long dbID = Long.parseLong(resultRow.getColumnValue("db_id"));
+ long tblId = Long.parseLong(resultRow.getColumnValue("tbl_id"));
+ String colName = resultRow.getColumnValue("col_id");
Column col = StatisticsUtil.findColumn(catalogId, dbID, tblId, idxId, colName);
if (col == null) {
- LOG.debug("Failed to deserialize column statistics, ctlId: {} dbId: {}"
- + "tblId: {} column: {} not exists",
+ LOG.warn("Failed to deserialize column statistics, ctlId: {} dbId: {}"
+ + "tblId: {} column: {} not exists",
catalogId, dbID, tblId, colName);
return ColumnStatistic.UNKNOWN;
}
- String min = row.get(10);
- String max = row.get(11);
+ String min = resultRow.getColumnValue("min");
+ String max = resultRow.getColumnValue("max");
if (min != null && !min.equalsIgnoreCase("NULL")) {
try {
columnStatisticBuilder.setMinValue(StatisticsUtil.convertToDouble(col.getType(), min));
columnStatisticBuilder.setMinExpr(StatisticsUtil.readableValue(col.getType(), min));
} catch (AnalysisException e) {
LOG.warn("Failed to deserialize column {} min value {}.", col, min, e);
- columnStatisticBuilder.setMinValue(Double.NEGATIVE_INFINITY);
+ columnStatisticBuilder.setMinValue(Double.MIN_VALUE);
}
} else {
- columnStatisticBuilder.setMinValue(Double.NEGATIVE_INFINITY);
+ columnStatisticBuilder.setMinValue(Double.MIN_VALUE);
}
if (max != null && !max.equalsIgnoreCase("NULL")) {
try {
@@ -188,12 +216,16 @@ public class ColumnStatistic {
columnStatisticBuilder.setMaxExpr(StatisticsUtil.readableValue(col.getType(), max));
} catch (AnalysisException e) {
LOG.warn("Failed to deserialize column {} max value {}.", col, max, e);
- columnStatisticBuilder.setMaxValue(Double.POSITIVE_INFINITY);
+ columnStatisticBuilder.setMaxValue(Double.MAX_VALUE);
}
} else {
- columnStatisticBuilder.setMaxValue(Double.POSITIVE_INFINITY);
+ columnStatisticBuilder.setMaxValue(Double.MAX_VALUE);
}
- columnStatisticBuilder.setUpdatedTime(row.get(13));
+ columnStatisticBuilder.setSelectivity(1.0);
+ Histogram histogram = Env.getCurrentEnv().getStatisticsCache().getHistogram(tblId, idxId, colName)
+ .orElse(null);
+ columnStatisticBuilder.setHistogram(histogram);
+ columnStatisticBuilder.setUpdatedTime(resultRow.getColumnValue("update_time"));
return columnStatisticBuilder.build();
} catch (Exception e) {
LOG.warn("Failed to deserialize column statistics.", e);
@@ -205,12 +237,25 @@ public class ColumnStatistic {
return rowCount * 0.9 < ndv && ndv < rowCount * 1.1;
}
+ public ColumnStatistic copy() {
+ return new ColumnStatisticBuilder().setCount(count).setNdv(ndv).setAvgSizeByte(avgSizeByte)
+ .setNumNulls(numNulls).setDataSize(dataSize).setMinValue(minValue)
+ .setMaxValue(maxValue).setMinExpr(minExpr).setMaxExpr(maxExpr)
+ .setSelectivity(selectivity).setIsUnknown(isUnKnown).build();
+ }
+
public ColumnStatistic updateByLimit(long limit, double rowCount) {
double ratio = 0;
if (rowCount != 0) {
ratio = limit / rowCount;
}
double newNdv = Math.ceil(Math.min(ndv, limit));
+ double newSelectivity = selectivity;
+ if (newNdv != 0) {
+ newSelectivity = newSelectivity * newNdv / ndv;
+ } else {
+ newSelectivity = 0;
+ }
return new ColumnStatisticBuilder()
.setCount(Math.ceil(limit))
.setNdv(newNdv)
@@ -221,6 +266,7 @@ public class ColumnStatistic {
.setMaxValue(maxValue)
.setMinExpr(minExpr)
.setMaxExpr(maxExpr)
+ .setSelectivity(newSelectivity)
.setIsUnknown(isUnKnown)
.build();
}
@@ -236,11 +282,14 @@ public class ColumnStatistic {
ColumnStatisticBuilder builder = new ColumnStatisticBuilder(this);
Double rowsAfterFilter = rowCount * selectivity;
if (isAlmostUnique(ndv, rowCount)) {
+ builder.setSelectivity(this.selectivity * selectivity);
builder.setNdv(ndv * selectivity);
} else {
if (ndv > rowsAfterFilter) {
+ builder.setSelectivity(this.selectivity * rowsAfterFilter / ndv);
builder.setNdv(rowsAfterFilter);
} else {
+ builder.setSelectivity(this.selectivity);
builder.setNdv(this.ndv);
}
}
@@ -283,8 +332,8 @@ public class ColumnStatistic {
@Override
public String toString() {
- return isUnKnown ? "unknown" : String.format("ndv=%.4f, min=%f(%s), max=%f(%s), count=%.4f, avgSizeByte=%f",
- ndv, minValue, minExpr, maxValue, maxExpr, count, avgSizeByte);
+ return isUnKnown ? "unknown" : String.format("ndv=%.4f, min=%f(%s), max=%f(%s), count=%.4f",
+ ndv, minValue, minExpr, maxValue, maxExpr, count);
}
public JSONObject toJson() {
@@ -306,16 +355,17 @@ public class ColumnStatistic {
statistic.put("MaxValueType", "Normal");
statistic.put("MaxValue", maxValue);
}
+ statistic.put("Selectivity", selectivity);
statistic.put("Count", count);
statistic.put("AvgSizeByte", avgSizeByte);
statistic.put("NumNulls", numNulls);
statistic.put("DataSize", dataSize);
+ statistic.put("Selectivity", selectivity);
statistic.put("MinExpr", minExpr);
statistic.put("MaxExpr", maxExpr);
statistic.put("IsUnKnown", isUnKnown);
statistic.put("Histogram", Histogram.serializeToJson(histogram));
statistic.put("Original", original);
- statistic.put("LastUpdatedTime", updatedTime);
return statistic;
}
@@ -360,11 +410,12 @@ public class ColumnStatistic {
stat.getDouble("DataSize"),
minValue,
maxValue,
+ stat.getDouble("Selectivity"),
null,
null,
stat.getBoolean("IsUnKnown"),
Histogram.deserializeFromJson(stat.getString("Histogram")),
- stat.getString("LastUpdatedTime"), null
+ stat.getString("lastUpdatedTine")
);
}
@@ -392,7 +443,12 @@ public class ColumnStatistic {
return isUnKnown;
}
- public void putPartStats(String partId, ColumnStatistic columnStatistic) {
- this.partitionIdToColStats.put(partId, columnStatistic);
+ public void loadPartitionStats(long tableId, long idxId, String colName) throws DdlException {
+ List<ResultRow> resultRows = StatisticsRepository.loadPartStats(tableId, idxId, colName);
+ for (ResultRow resultRow : resultRows) {
+ String partId = resultRow.getColumnValue("part_id");
+ ColumnStatistic columnStatistic = ColumnStatistic.fromResultRow(resultRow);
+ partitionIdToColStats.put(Long.parseLong(partId), columnStatistic);
+ }
}
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/ColumnStatisticBuilder.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/ColumnStatisticBuilder.java
index fa4cf7ebc99..6ca2cc55b79 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/ColumnStatisticBuilder.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/ColumnStatisticBuilder.java
@@ -18,10 +18,6 @@
package org.apache.doris.statistics;
import org.apache.doris.analysis.LiteralExpr;
-import org.apache.doris.catalog.PartitionInfo;
-
-import java.util.HashMap;
-import java.util.Map;
public class ColumnStatisticBuilder {
private double count;
@@ -31,6 +27,7 @@ public class ColumnStatisticBuilder {
private double dataSize;
private double minValue;
private double maxValue;
+ private double selectivity = 1.0;
private LiteralExpr minExpr;
private LiteralExpr maxExpr;
@@ -40,24 +37,11 @@ public class ColumnStatisticBuilder {
private ColumnStatistic original;
- private Map<String, ColumnStatistic> partitionIdToColStats = new HashMap<>();
-
private String updatedTime;
- private PartitionInfo partitionInfo;
-
public ColumnStatisticBuilder() {
}
- public PartitionInfo getPartitionInfo() {
- return partitionInfo;
- }
-
- public ColumnStatisticBuilder setPartitionInfo(PartitionInfo partitionInfo) {
- this.partitionInfo = partitionInfo;
- return this;
- }
-
public ColumnStatisticBuilder(ColumnStatistic columnStatistic) {
this.count = columnStatistic.count;
this.ndv = columnStatistic.ndv;
@@ -66,14 +50,13 @@ public class ColumnStatisticBuilder {
this.dataSize = columnStatistic.dataSize;
this.minValue = columnStatistic.minValue;
this.maxValue = columnStatistic.maxValue;
+ this.selectivity = columnStatistic.selectivity;
this.minExpr = columnStatistic.minExpr;
this.maxExpr = columnStatistic.maxExpr;
this.isUnknown = columnStatistic.isUnKnown;
this.histogram = columnStatistic.histogram;
this.original = columnStatistic.original;
- this.partitionIdToColStats.putAll(columnStatistic.partitionIdToColStats);
this.updatedTime = columnStatistic.updatedTime;
- this.partitionInfo = columnStatistic.partitionInfo;
}
public ColumnStatisticBuilder setCount(double count) {
@@ -116,6 +99,11 @@ public class ColumnStatisticBuilder {
return this;
}
+ public ColumnStatisticBuilder setSelectivity(double selectivity) {
+ this.selectivity = selectivity;
+ return this;
+ }
+
public ColumnStatisticBuilder setMinExpr(LiteralExpr minExpr) {
this.minExpr = minExpr;
return this;
@@ -159,6 +147,10 @@ public class ColumnStatisticBuilder {
return maxValue;
}
+ public double getSelectivity() {
+ return selectivity;
+ }
+
public LiteralExpr getMinExpr() {
return minExpr;
}
@@ -184,23 +176,18 @@ public class ColumnStatisticBuilder {
return updatedTime;
}
- public ColumnStatisticBuilder setUpdatedTime(String updatedTime) {
+ public void setUpdatedTime(String updatedTime) {
this.updatedTime = updatedTime;
- return this;
}
public ColumnStatistic build() {
dataSize = Math.max((count - numNulls + 1) * avgSizeByte, 0);
- if (original == null && !isUnknown) {
+ if (original == null) {
original = new ColumnStatistic(count, ndv, null, avgSizeByte, numNulls,
- dataSize, minValue, maxValue, minExpr, maxExpr,
- isUnknown, histogram, updatedTime, partitionInfo);
- original.partitionIdToColStats.putAll(partitionIdToColStats);
+ dataSize, minValue, maxValue, selectivity, minExpr, maxExpr, false,
+ histogram, updatedTime);
}
- ColumnStatistic colStats = new ColumnStatistic(count, ndv, original, avgSizeByte, numNulls,
- dataSize, minValue, maxValue, minExpr, maxExpr,
- isUnknown, histogram, updatedTime, partitionInfo);
- colStats.partitionIdToColStats.putAll(partitionIdToColStats);
- return colStats;
+ return new ColumnStatistic(count, ndv, original, avgSizeByte, numNulls,
+ dataSize, minValue, maxValue, selectivity, minExpr, maxExpr, isUnknown, histogram, updatedTime);
}
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/ColumnStatisticsCacheLoader.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/ColumnStatisticsCacheLoader.java
index 281a0e82502..d94a90b75f0 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/ColumnStatisticsCacheLoader.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/ColumnStatisticsCacheLoader.java
@@ -19,8 +19,7 @@ package org.apache.doris.statistics;
import org.apache.doris.catalog.Env;
import org.apache.doris.catalog.TableIf;
-import org.apache.doris.common.ThreadPoolManager;
-import org.apache.doris.qe.InternalQueryExecutionException;
+import org.apache.doris.statistics.util.InternalQueryResult.ResultRow;
import org.apache.doris.statistics.util.StatisticsUtil;
import org.apache.logging.log4j.LogManager;
@@ -28,23 +27,16 @@ import org.apache.logging.log4j.Logger;
import java.util.List;
import java.util.Optional;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy;
public class ColumnStatisticsCacheLoader extends StatisticsCacheLoader<Optional<ColumnStatistic>> {
private static final Logger LOG = LogManager.getLogger(ColumnStatisticsCacheLoader.class);
- private static final ThreadPoolExecutor singleThreadPool = ThreadPoolManager.newDaemonFixedThreadPool(
- StatisticConstants.RETRY_LOAD_THREAD_POOL_SIZE,
- StatisticConstants.RETRY_LOAD_QUEUE_SIZE, "STATS_RELOAD",
- true,
- new DiscardOldestPolicy());
-
@Override
protected Optional<ColumnStatistic> doLoad(StatisticsCacheKey key) {
// Load from statistics table.
- Optional<ColumnStatistic> columnStatistic = loadFromStatsTable(key);
+ Optional<ColumnStatistic> columnStatistic = loadFromStatsTable(key.tableId,
+ key.idxId, key.colName);
if (columnStatistic.isPresent()) {
return columnStatistic;
}
@@ -60,14 +52,8 @@ public class ColumnStatisticsCacheLoader extends StatisticsCacheLoader<Optional<
return columnStatistic;
}
- private Optional<ColumnStatistic> loadFromStatsTable(StatisticsCacheKey key) {
- List<ResultRow> columnResults = null;
- try {
- columnResults = StatisticsRepository.loadColStats(key.tableId, key.idxId, key.colName);
- } catch (InternalQueryExecutionException e) {
- retryLoad(key);
- return Optional.empty();
- }
+ private Optional<ColumnStatistic> loadFromStatsTable(long tableId, long idxId, String colName) {
+ List<ResultRow> columnResults = StatisticsRepository.loadColStats(tableId, idxId, colName);
ColumnStatistic columnStatistics;
try {
columnStatistics = StatisticsUtil.deserializeToColumnStatistics(columnResults);
@@ -81,42 +67,4 @@ public class ColumnStatisticsCacheLoader extends StatisticsCacheLoader<Optional<
return Optional.of(columnStatistics);
}
}
-
- private void retryLoad(StatisticsCacheKey key) {
- singleThreadPool.submit(new RetryTask(key, 1));
- }
-
- private static class RetryTask implements Runnable {
- StatisticsCacheKey key;
- int retryTimes;
-
- public RetryTask(StatisticsCacheKey key, int retryTimes) {
- this.key = key;
- this.retryTimes = retryTimes;
- }
-
- @Override
- public void run() {
- List<ResultRow> columnResults = null;
- try {
- columnResults = StatisticsRepository.loadColStats(key.tableId, key.idxId, key.colName);
- } catch (InternalQueryExecutionException e) {
- if (this.retryTimes < StatisticConstants.LOAD_RETRY_TIMES) {
- retryTimes++;
- singleThreadPool.submit(this);
- }
- return;
- }
- ColumnStatistic columnStatistics;
- try {
- columnStatistics = StatisticsUtil.deserializeToColumnStatistics(columnResults);
- } catch (Exception e) {
- LOG.warn("Exception to deserialize column statistics", e);
- return;
- }
- if (columnStatistics != null) {
- Env.getCurrentEnv().getStatisticsCache().putCache(key, columnStatistics);
- }
- }
- }
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/HMSAnalysisTask.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/HMSAnalysisTask.java
index a446cc2610a..d569cd79bd4 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/HMSAnalysisTask.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/HMSAnalysisTask.java
@@ -17,28 +17,29 @@
package org.apache.doris.statistics;
-import org.apache.doris.catalog.Column;
import org.apache.doris.catalog.Env;
import org.apache.doris.catalog.external.HMSExternalTable;
import org.apache.doris.common.FeConstants;
+import org.apache.doris.common.util.TimeUtils;
import org.apache.doris.qe.AutoCloseConnectContext;
import org.apache.doris.qe.QueryState;
import org.apache.doris.qe.StmtExecutor;
+import org.apache.doris.statistics.util.InternalQueryResult;
import org.apache.doris.statistics.util.StatisticsUtil;
-import com.google.common.collect.Lists;
+import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringSubstitutor;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
import java.util.ArrayList;
-import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.StringJoiner;
-import java.util.stream.Collectors;
public class HMSAnalysisTask extends BaseAnalysisTask {
private static final Logger LOG = LogManager.getLogger(HMSAnalysisTask.class);
@@ -48,7 +49,7 @@ public class HMSAnalysisTask extends BaseAnalysisTask {
public static final String NUM_FILES = "numFiles";
public static final String TIMESTAMP = "transient_lastDdlTime";
- private static final String ANALYZE_TABLE_TEMPLATE = "INSERT INTO "
+ private static final String ANALYZE_SQL_TABLE_TEMPLATE = "INSERT INTO "
+ "${internalDB}.${columnStatTbl}"
+ " SELECT "
+ "CONCAT(${tblId}, '-', ${idxId}, '-', '${colId}') AS id, "
@@ -58,17 +59,19 @@ public class HMSAnalysisTask extends BaseAnalysisTask {
+ "${idxId} AS idx_id, "
+ "'${colId}' AS col_id, "
+ "NULL AS part_id, "
- + "${countExpr} AS row_count, "
+ + "COUNT(1) AS row_count, "
+ "NDV(`${colName}`) AS ndv, "
- + "${nullCountExpr} AS null_count, "
+ + "SUM(CASE WHEN `${colName}` IS NULL THEN 1 ELSE 0 END) AS null_count, "
+ "MIN(`${colName}`) AS min, "
+ "MAX(`${colName}`) AS max, "
+ "${dataSizeFunction} AS data_size, "
+ "NOW() "
- + "FROM `${catalogName}`.`${dbName}`.`${tblName}` ${sampleExpr}";
+ + "FROM `${catalogName}`.`${dbName}`.`${tblName}`";
- private static final String ANALYZE_PARTITION_TEMPLATE = " SELECT "
- + "CONCAT(${tblId}, '-', ${idxId}, '-', '${colId}', '-', ${partId}) AS id, "
+ private static final String ANALYZE_SQL_PARTITION_TEMPLATE = "INSERT INTO "
+ + "${internalDB}.${columnStatTbl}"
+ + " SELECT "
+ + "CONCAT(${tblId}, '-', ${idxId}, '-', '${colId}') AS id, "
+ "${catalogId} AS catalog_id, "
+ "${dbId} AS db_id, "
+ "${tblId} AS tbl_id, "
@@ -81,22 +84,22 @@ public class HMSAnalysisTask extends BaseAnalysisTask {
+ "MIN(`${colName}`) AS min, "
+ "MAX(`${colName}`) AS max, "
+ "${dataSizeFunction} AS data_size, "
- + "NOW() FROM `${catalogName}`.`${dbName}`.`${tblName}` where ";
-
- private static final String ANALYZE_TABLE_COUNT_TEMPLATE = "SELECT ${countExpr} as rowCount "
- + "FROM `${catalogName}`.`${dbName}`.`${tblName}` ${sampleExpr}";
+ + "NOW() "
+ + "FROM `${catalogName}`.`${dbName}`.`${tblName}`";
- // cache stats for each partition, it would be inserted into column_statistics in a batch.
- private final List<List<ColStatsData>> buf = new ArrayList<>();
+ private static final String ANALYZE_TABLE_COUNT_TEMPLATE = "SELECT COUNT(1) as rowCount "
+ + "FROM `${catalogName}`.`${dbName}`.`${tblName}`";
private final boolean isTableLevelTask;
+ private final boolean isSamplingPartition;
private final boolean isPartitionOnly;
- private Set<String> partitionNames;
+ private final Set<String> partitionNames;
private HMSExternalTable table;
public HMSAnalysisTask(AnalysisInfo info) {
super(info);
isTableLevelTask = info.externalTableLevelTask;
+ isSamplingPartition = info.samplingPartition;
isPartitionOnly = info.partitionOnly;
partitionNames = info.partitionNames;
table = (HMSExternalTable) tbl;
@@ -111,17 +114,42 @@ public class HMSAnalysisTask extends BaseAnalysisTask {
}
/**
- * Get table row count
+ * Get table row count and insert the result to __internal_schema.table_statistics
*/
private void getTableStats() throws Exception {
- Map<String, String> params = buildTableStatsParams(null);
- List<ResultRow> columnResult =
- StatisticsUtil.execStatisticQuery(new StringSubstitutor(params)
- .replace(ANALYZE_TABLE_COUNT_TEMPLATE));
- String rowCount = columnResult.get(0).get(0);
- Env.getCurrentEnv().getAnalysisManager()
- .updateTableStatsStatus(
- new TableStatsMeta(table.getId(), Long.parseLong(rowCount), info));
+ // Get table level information. An example sql for table stats:
+ // INSERT INTO __internal_schema.table_statistics VALUES
+ // ('13055', 13002, 13038, 13055, -1, 'NULL', 5, 1686111064658, NOW())
+ Map<String, String> parameters = table.getRemoteTable().getParameters();
+ if (isPartitionOnly) {
+ for (String partId : partitionNames) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(ANALYZE_SQL_PARTITION_TEMPLATE);
+ sb.append(" where ");
+ String[] splits = partId.split("/");
+ for (int i = 0; i < splits.length; i++) {
+ String value = splits[i].split("=")[1];
+ splits[i] = splits[i].replace(value, "\'" + value + "\'");
+ }
+ sb.append(StringUtils.join(splits, " and "));
+ Map<String, String> params = buildTableStatsParams(partId);
+ setParameterData(parameters, params);
+ List<InternalQueryResult.ResultRow> columnResult =
+ StatisticsUtil.execStatisticQuery(new StringSubstitutor(params)
+ .replace(sb.toString()));
+ String rowCount = columnResult.get(0).getColumnValue("rowCount");
+ params.put("rowCount", rowCount);
+ StatisticsRepository.persistTableStats(params);
+ }
+ } else {
+ Map<String, String> params = buildTableStatsParams(null);
+ List<InternalQueryResult.ResultRow> columnResult =
+ StatisticsUtil.execStatisticQuery(new StringSubstitutor(params)
+ .replace(ANALYZE_TABLE_COUNT_TEMPLATE));
+ String rowCount = columnResult.get(0).getColumnValue("rowCount");
+ params.put("rowCount", rowCount);
+ StatisticsRepository.persistTableStats(params);
+ }
}
/**
@@ -145,102 +173,67 @@ public class HMSAnalysisTask extends BaseAnalysisTask {
// 0 AS data_size,
// NOW() FROM `hive`.`tpch100`.`region`
if (isPartitionOnly) {
- getPartitionNames();
- List<String> partitionAnalysisSQLs = new ArrayList<>();
- for (String partId : this.partitionNames) {
- partitionAnalysisSQLs.add(generateSqlForPartition(partId));
+ for (String partId : partitionNames) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(ANALYZE_SQL_TABLE_TEMPLATE);
+ sb.append(" where ");
+ String[] splits = partId.split("/");
+ for (int i = 0; i < splits.length; i++) {
+ String value = splits[i].split("=")[1];
+ splits[i] = splits[i].replace(value, "\'" + value + "\'");
+ }
+ sb.append(StringUtils.join(splits, " and "));
+ Map<String, String> params = buildTableStatsParams(partId);
+ params.put("internalDB", FeConstants.INTERNAL_DB_NAME);
+ params.put("columnStatTbl", StatisticConstants.STATISTIC_TBL_NAME);
+ params.put("colName", col.getName());
+ params.put("colId", info.colName);
+ params.put("dataSizeFunction", getDataSizeFunction(col));
+ StringSubstitutor stringSubstitutor = new StringSubstitutor(params);
+ String sql = stringSubstitutor.replace(sb.toString());
+ executeInsertSql(sql);
}
- execSQLs(partitionAnalysisSQLs);
} else {
StringBuilder sb = new StringBuilder();
- sb.append(ANALYZE_TABLE_TEMPLATE);
+ sb.append(ANALYZE_SQL_TABLE_TEMPLATE);
+ if (isSamplingPartition) {
+ sb.append(" where 1=1 ");
+ String[] splitExample = partitionNames.stream().findFirst().get().split("/");
+ int parts = splitExample.length;
+ List<String> partNames = new ArrayList<>();
+ for (String split : splitExample) {
+ partNames.add(split.split("=")[0]);
+ }
+ List<List<String>> valueLists = new ArrayList<>();
+ for (int i = 0; i < parts; i++) {
+ valueLists.add(new ArrayList<>());
+ }
+ for (String partId : partitionNames) {
+ String[] partIds = partId.split("/");
+ for (int i = 0; i < partIds.length; i++) {
+ valueLists.get(i).add("\'" + partIds[i].split("=")[1] + "\'");
+ }
+ }
+ for (int i = 0; i < parts; i++) {
+ sb.append(" and ");
+ sb.append(partNames.get(i));
+ sb.append(" in (");
+ sb.append(StringUtils.join(valueLists.get(i), ","));
+ sb.append(") ");
+ }
+ }
Map<String, String> params = buildTableStatsParams("NULL");
params.put("internalDB", FeConstants.INTERNAL_DB_NAME);
params.put("columnStatTbl", StatisticConstants.STATISTIC_TBL_NAME);
params.put("colName", col.getName());
params.put("colId", info.colName);
params.put("dataSizeFunction", getDataSizeFunction(col));
- params.put("nullCountExpr", getNullCountExpression());
StringSubstitutor stringSubstitutor = new StringSubstitutor(params);
String sql = stringSubstitutor.replace(sb.toString());
executeInsertSql(sql);
}
}
- private void getPartitionNames() {
- if (partitionNames == null) {
- if (info.isAllPartition) {
- partitionNames = table.getPartitionNames();
- } else if (info.partitionCount > 0) {
- partitionNames = table.getPartitionNames().stream()
- .limit(info.partitionCount).collect(Collectors.toSet());
- }
- if (partitionNames == null || partitionNames.isEmpty()) {
- throw new RuntimeException("Not a partition table or no partition specified.");
- }
- }
- }
-
- private String generateSqlForPartition(String partId) {
- StringBuilder sb = new StringBuilder();
- sb.append(ANALYZE_PARTITION_TEMPLATE);
- String[] splits = partId.split("/");
- for (int i = 0; i < splits.length; i++) {
- String[] kv = splits[i].split("=");
- sb.append(kv[0]);
- sb.append("='");
- sb.append(kv[1]);
- sb.append("'");
- if (i < splits.length - 1) {
- sb.append(" and ");
- }
- }
- Map<String, String> params = buildTableStatsParams(partId);
- params.put("internalDB", FeConstants.INTERNAL_DB_NAME);
- params.put("columnStatTbl", StatisticConstants.STATISTIC_TBL_NAME);
- params.put("colName", col.getName());
- params.put("colId", info.colName);
- params.put("dataSizeFunction", getDataSizeFunction(col));
- return new StringSubstitutor(params).replace(sb.toString());
- }
-
- public void execSQLs(List<String> partitionAnalysisSQLs) throws Exception {
- long startTime = System.currentTimeMillis();
- LOG.debug("analyze task {} start at {}", info.toString(), new Date());
- try (AutoCloseConnectContext r = StatisticsUtil.buildConnectContext()) {
- List<List<String>> sqlGroups = Lists.partition(partitionAnalysisSQLs, StatisticConstants.UNION_ALL_LIMIT);
- for (List<String> group : sqlGroups) {
- if (killed) {
- return;
- }
- StringJoiner partitionCollectSQL = new StringJoiner("UNION ALL");
- group.forEach(partitionCollectSQL::add);
- stmtExecutor = new StmtExecutor(r.connectContext, partitionCollectSQL.toString());
- buf.add(stmtExecutor.executeInternalQuery()
- .stream().map(ColStatsData::new).collect(Collectors.toList()));
- QueryState queryState = r.connectContext.getState();
- if (queryState.getStateType().equals(QueryState.MysqlStateType.ERR)) {
- throw new RuntimeException(String.format("Failed to analyze %s.%s.%s, error: %s sql: %s",
- info.catalogName, info.dbName, info.colName, partitionCollectSQL,
- queryState.getErrorMessage()));
- }
- }
- for (List<ColStatsData> colStatsDataList : buf) {
- StringBuilder batchInsertSQL =
- new StringBuilder("INSERT INTO " + StatisticConstants.FULL_QUALIFIED_STATS_TBL_NAME
- + " VALUES ");
- StringJoiner sj = new StringJoiner(",");
- colStatsDataList.forEach(c -> sj.add(c.toSQL(true)));
- batchInsertSQL.append(sj);
- stmtExecutor = new StmtExecutor(r.connectContext, batchInsertSQL.toString());
- executeWithExceptionOnFail(stmtExecutor);
- }
- } finally {
- LOG.debug("analyze task {} end. cost {}ms", info, System.currentTimeMillis() - startTime);
- }
-
- }
-
private void executeInsertSql(String sql) throws Exception {
long startTime = System.currentTimeMillis();
try (AutoCloseConnectContext r = StatisticsUtil.buildConnectContext()) {
@@ -277,8 +270,6 @@ public class HMSAnalysisTask extends BaseAnalysisTask {
commonParams.put("catalogName", catalog.getName());
commonParams.put("dbName", db.getFullName());
commonParams.put("tblName", tbl.getName());
- commonParams.put("sampleExpr", getSampleExpression());
- commonParams.put("countExpr", getCountExpression());
if (col != null) {
commonParams.put("type", col.getType().toString());
}
@@ -286,39 +277,28 @@ public class HMSAnalysisTask extends BaseAnalysisTask {
return commonParams;
}
- protected String getCountExpression() {
- if (info.samplePercent > 0) {
- return String.format("ROUND(COUNT(1) * 100 / %d)", info.samplePercent);
- } else {
- return "COUNT(1)";
+ private void setParameterData(Map<String, String> parameters, Map<String, String> params) {
+ String numRows = "";
+ String timestamp = "";
+ if (parameters.containsKey(NUM_ROWS)) {
+ numRows = parameters.get(NUM_ROWS);
}
- }
-
- protected String getNullCountExpression() {
- if (info.samplePercent > 0) {
- return String.format("ROUND(SUM(CASE WHEN `${colName}` IS NULL THEN 1 ELSE 0 END) * 100 / %d)",
- info.samplePercent);
- } else {
- return "SUM(CASE WHEN `${colName}` IS NULL THEN 1 ELSE 0 END)";
- }
- }
-
- protected String getDataSizeFunction(Column column) {
- String originFunction = super.getDataSizeFunction(column);
- if (info.samplePercent > 0 && !isPartitionOnly) {
- return String.format("ROUND((%s) * 100 / %d)", originFunction, info.samplePercent);
- } else {
- return originFunction;
+ if (parameters.containsKey(TIMESTAMP)) {
+ timestamp = parameters.get(TIMESTAMP);
}
+ params.put("numRows", numRows);
+ params.put("rowCount", numRows);
+ params.put("update_time", TimeUtils.DATETIME_FORMAT.format(
+ LocalDateTime.ofInstant(Instant.ofEpochMilli(Long.parseLong(timestamp) * 1000),
+ ZoneId.systemDefault())));
}
@Override
protected void afterExecution() {
- // Table level task doesn't need to sync any value to sync stats, it stores the value in metadata.
- // Partition only task doesn't need to refresh cached.
- if (isTableLevelTask || isPartitionOnly) {
- return;
+ if (isTableLevelTask) {
+ Env.getCurrentEnv().getStatisticsCache().refreshTableStatsSync(catalog.getId(), db.getId(), tbl.getId());
+ } else {
+ Env.getCurrentEnv().getStatisticsCache().syncLoadColStats(tbl.getId(), -1, col.getName());
}
- Env.getCurrentEnv().getStatisticsCache().syncLoadColStats(tbl.getId(), -1, col.getName());
}
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/HistData.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/HistData.java
deleted file mode 100644
index 85f2fe45bd5..00000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/HistData.java
+++ /dev/null
@@ -1,36 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements. See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership. The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License. You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied. See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.statistics;
-
-public class HistData {
-
- public final StatsId statsId;
-
- public final double sampleRate;
-
- public final String buckets;
-
- public final String updateTime;
-
- public HistData(ResultRow row) {
- this.statsId = new StatsId(row);
- this.sampleRate = Double.parseDouble(row.get(7));
- this.buckets = row.get(8);
- this.updateTime = row.get(9);
- }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/Histogram.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/Histogram.java
index 2068c368c40..05e2c199ed9 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/Histogram.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/Histogram.java
@@ -20,6 +20,7 @@ package org.apache.doris.statistics;
import org.apache.doris.catalog.Column;
import org.apache.doris.catalog.PrimitiveType;
import org.apache.doris.catalog.Type;
+import org.apache.doris.statistics.util.InternalQueryResult.ResultRow;
import org.apache.doris.statistics.util.StatisticsUtil;
import com.google.common.base.Strings;
@@ -61,12 +62,13 @@ public class Histogram {
public static Histogram fromResultRow(ResultRow resultRow) {
try {
HistogramBuilder histogramBuilder = new HistogramBuilder();
- HistData histData = new HistData(resultRow);
- long catalogId = histData.statsId.catalogId;
- long idxId = histData.statsId.idxId;
- long dbId = histData.statsId.dbId;
- long tblId = histData.statsId.tblId;
- String colName = histData.statsId.colId;
+
+ long catalogId = Long.parseLong(resultRow.getColumnValue("catalog_id"));
+ long idxId = Long.parseLong(resultRow.getColumnValue("idx_id"));
+ long dbId = Long.parseLong(resultRow.getColumnValue("db_id"));
+ long tblId = Long.parseLong(resultRow.getColumnValue("tbl_id"));
+
+ String colName = resultRow.getColumnValue("col_id");
Column col = StatisticsUtil.findColumn(catalogId, dbId, tblId, idxId, colName);
if (col == null) {
LOG.warn("Failed to deserialize histogram statistics, ctlId: {} dbId: {}"
@@ -77,10 +79,10 @@ public class Histogram {
Type dataType = col.getType();
histogramBuilder.setDataType(dataType);
- double sampleRate = histData.sampleRate;
+ double sampleRate = Double.parseDouble(resultRow.getColumnValue("sample_rate"));
histogramBuilder.setSampleRate(sampleRate);
- String json = histData.buckets;
+ String json = resultRow.getColumnValue("buckets");
JsonObject jsonObj = JsonParser.parseString(json).getAsJsonObject();
int bucketNum = jsonObj.get("num_buckets").getAsInt();
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/HistogramCacheLoader.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/HistogramCacheLoader.java
index d9928f2a639..0e075240923 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/HistogramCacheLoader.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/HistogramCacheLoader.java
@@ -18,6 +18,7 @@
package org.apache.doris.statistics;
import org.apache.doris.common.FeConstants;
+import org.apache.doris.statistics.util.InternalQueryResult.ResultRow;
import org.apache.doris.statistics.util.StatisticsUtil;
import org.apache.commons.collections.CollectionUtils;
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/JdbcAnalysisTask.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/JdbcAnalysisTask.java
index 58be1510b44..25b9db0b2c7 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/JdbcAnalysisTask.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/JdbcAnalysisTask.java
@@ -23,6 +23,7 @@ import org.apache.doris.common.FeConstants;
import org.apache.doris.qe.AutoCloseConnectContext;
import org.apache.doris.qe.QueryState;
import org.apache.doris.qe.StmtExecutor;
+import org.apache.doris.statistics.util.InternalQueryResult;
import org.apache.doris.statistics.util.StatisticsUtil;
import org.apache.commons.text.StringSubstitutor;
@@ -80,11 +81,11 @@ public class JdbcAnalysisTask extends BaseAnalysisTask {
*/
private void getTableStats() throws Exception {
Map<String, String> params = buildTableStatsParams(null);
- List<ResultRow> columnResult =
+ List<InternalQueryResult.ResultRow> columnResult =
StatisticsUtil.execStatisticQuery(new StringSubstitutor(params).replace(ANALYZE_TABLE_COUNT_TEMPLATE));
- String rowCount = columnResult.get(0).get(0);
- Env.getCurrentEnv().getAnalysisManager()
- .updateTableStatsStatus(new TableStatsMeta(table.getId(), Long.parseLong(rowCount), info));
+ String rowCount = columnResult.get(0).getColumnValue("rowCount");
+ params.put("rowCount", rowCount);
+ StatisticsRepository.persistTableStats(params);
}
/**
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/OlapAnalysisJob.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/OlapAnalysisJob.java
deleted file mode 100644
index 877a4f5bd09..00000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/OlapAnalysisJob.java
+++ /dev/null
@@ -1,56 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements. See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership. The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License. You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied. See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.statistics;
-
-import java.util.List;
-
-public class OlapAnalysisJob {
-
-
-
- private List<String> columns;
-
- private static String collectPartionStatsSQLTemplate =
- " SELECT "
- + "CONCAT(${tblId}, '-', ${idxId}, '-', '${colId}', '-', ${partId}) AS id, "
- + "${catalogId} AS catalog_id, "
- + "${dbId} AS db_id, "
- + "${tblId} AS tbl_id, "
- + "${idxId} AS idx_id, "
- + "'${colId}' AS col_id, "
- + "${partId} AS part_id, "
- + "COUNT(1) AS row_count, "
- + "NDV(`${colName}`) AS ndv, "
- + "SUM(CASE WHEN `${colName}` IS NULL THEN 1 ELSE 0 END) AS null_count, "
- + "MIN(`${colName}`) AS min, "
- + "MAX(`${colName}`) AS max, "
- + "${dataSizeFunction} AS data_size, "
- + "NOW() ";
-
-
- protected void beforeExecution() {
- }
-
- public void execute() {
- }
-
- protected void afterExecution() {
-
- }
-
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/OlapAnalysisTask.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/OlapAnalysisTask.java
index 180ac9d9839..257708de54f 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/OlapAnalysisTask.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/OlapAnalysisTask.java
@@ -23,66 +23,37 @@ import org.apache.doris.qe.AutoCloseConnectContext;
import org.apache.doris.qe.QueryState;
import org.apache.doris.qe.QueryState.MysqlStateType;
import org.apache.doris.qe.StmtExecutor;
-import org.apache.doris.statistics.AnalysisInfo.JobType;
import org.apache.doris.statistics.util.StatisticsUtil;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.Lists;
import org.apache.commons.text.StringSubstitutor;
import java.util.ArrayList;
-import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.StringJoiner;
-import java.util.stream.Collectors;
/**
* Each task analyze one column.
*/
public class OlapAnalysisTask extends BaseAnalysisTask {
+ private static final String ANALYZE_PARTITION_SQL_TEMPLATE = INSERT_PART_STATISTICS
+ + "FROM `${dbName}`.`${tblName}` "
+ + "PARTITION ${partName} ${sampleExpr}";
+
// TODO Currently, NDV is computed for the full table; in fact,
// NDV should only be computed for the relevant partition.
private static final String ANALYZE_COLUMN_SQL_TEMPLATE = INSERT_COL_STATISTICS
+ " (SELECT NDV(`${colName}`) AS ndv "
+ " FROM `${dbName}`.`${tblName}` ${sampleExpr}) t2\n";
- private static final String collectPartitionStatsSQLTemplate =
- " SELECT "
- + "CONCAT(${tblId}, '-', ${idxId}, '-', '${colId}', '-', ${partId}) AS id, "
- + "${catalogId} AS catalog_id, "
- + "${dbId} AS db_id, "
- + "${tblId} AS tbl_id, "
- + "${idxId} AS idx_id, "
- + "'${colId}' AS col_id, "
- + "${partId} AS part_id, "
- + "COUNT(1) AS row_count, "
- + "NDV(`${colName}`) AS ndv, "
- + "SUM(CASE WHEN `${colName}` IS NULL THEN 1 ELSE 0 END) AS null_count, "
- + "MIN(`${colName}`) AS min, "
- + "MAX(`${colName}`) AS max, "
- + "${dataSizeFunction} AS data_size, "
- + "NOW() FROM `${dbName}`.`${tblName}` PARTITION ${partitionName} ${sampleExpr}";
-
- // cache stats for each partition, it would be inserted into column_statistics in a batch.
- private final List<List<ColStatsData>> buf = new ArrayList<>();
-
- @VisibleForTesting
- public OlapAnalysisTask() {
- }
-
public OlapAnalysisTask(AnalysisInfo info) {
super(info);
}
public void doExecute() throws Exception {
- Set<String> partitionNames = info.colToPartitions.get(info.colName);
- if (partitionNames.isEmpty()) {
- return;
- }
Map<String, String> params = new HashMap<>();
params.put("internalDB", FeConstants.INTERNAL_DB_NAME);
params.put("columnStatTbl", StatisticConstants.STATISTIC_TBL_NAME);
@@ -99,65 +70,56 @@ public class OlapAnalysisTask extends BaseAnalysisTask {
List<String> partitionAnalysisSQLs = new ArrayList<>();
try {
tbl.readLock();
-
- for (String partitionName : partitionNames) {
- Partition part = tbl.getPartition(partitionName);
+ Set<String> partNames = info.colToPartitions.get(info.colName);
+ for (String partName : partNames) {
+ Partition part = tbl.getPartition(partName);
if (part == null) {
continue;
}
- params.put("partId", String.valueOf(tbl.getPartition(partitionName).getId()));
+ params.put("partId", String.valueOf(tbl.getPartition(partName).getId()));
// Avoid error when get the default partition
- params.put("partitionName", "`" + partitionName + "`");
+ params.put("partName", "`" + partName + "`");
StringSubstitutor stringSubstitutor = new StringSubstitutor(params);
- partitionAnalysisSQLs.add(stringSubstitutor.replace(collectPartitionStatsSQLTemplate));
+ partitionAnalysisSQLs.add(stringSubstitutor.replace(ANALYZE_PARTITION_SQL_TEMPLATE));
}
} finally {
tbl.readUnlock();
}
- execSQLs(partitionAnalysisSQLs, params);
+ execSQLs(partitionAnalysisSQLs);
+ params.remove("partId");
+ params.put("type", col.getType().toString());
+ StringSubstitutor stringSubstitutor = new StringSubstitutor(params);
+ String sql = stringSubstitutor.replace(ANALYZE_COLUMN_SQL_TEMPLATE);
+ execSQL(sql);
+ }
+
+ @VisibleForTesting
+ public void execSQLs(List<String> partitionAnalysisSQLs) throws Exception {
+ for (String sql : partitionAnalysisSQLs) {
+ execSQL(sql);
+ }
}
@VisibleForTesting
- public void execSQLs(List<String> partitionAnalysisSQLs, Map<String, String> params) throws Exception {
+ public void execSQL(String sql) throws Exception {
+ if (killed) {
+ return;
+ }
long startTime = System.currentTimeMillis();
- LOG.debug("analyze task {} start at {}", info.toString(), new Date());
- try (AutoCloseConnectContext r = StatisticsUtil.buildConnectContext(info.jobType.equals(JobType.SYSTEM))) {
- List<List<String>> sqlGroups = Lists.partition(partitionAnalysisSQLs, StatisticConstants.UNION_ALL_LIMIT);
- for (List<String> group : sqlGroups) {
- if (killed) {
- return;
- }
- StringJoiner partitionCollectSQL = new StringJoiner("UNION ALL");
- group.forEach(partitionCollectSQL::add);
- stmtExecutor = new StmtExecutor(r.connectContext, partitionCollectSQL.toString());
- buf.add(stmtExecutor.executeInternalQuery()
- .stream().map(ColStatsData::new).collect(Collectors.toList()));
- QueryState queryState = r.connectContext.getState();
- if (queryState.getStateType().equals(MysqlStateType.ERR)) {
- throw new RuntimeException(String.format("Failed to analyze %s.%s.%s, error: %s sql: %s",
- info.catalogName, info.dbName, info.colName, partitionCollectSQL,
- queryState.getErrorMessage()));
- }
- }
- for (List<ColStatsData> colStatsDataList : buf) {
- StringBuilder batchInsertSQL =
- new StringBuilder("INSERT INTO " + StatisticConstants.FULL_QUALIFIED_STATS_TBL_NAME
- + " VALUES ");
- StringJoiner sj = new StringJoiner(",");
- colStatsDataList.forEach(c -> sj.add(c.toSQL(true)));
- batchInsertSQL.append(sj.toString());
- stmtExecutor = new StmtExecutor(r.connectContext, batchInsertSQL.toString());
- executeWithExceptionOnFail(stmtExecutor);
- }
- params.put("type", col.getType().toString());
- StringSubstitutor stringSubstitutor = new StringSubstitutor(params);
- String sql = stringSubstitutor.replace(ANALYZE_COLUMN_SQL_TEMPLATE);
+ LOG.info("ANALYZE SQL : " + sql + " start at " + startTime);
+ try (AutoCloseConnectContext r = StatisticsUtil.buildConnectContext()) {
+ r.connectContext.getSessionVariable().disableNereidsPlannerOnce();
stmtExecutor = new StmtExecutor(r.connectContext, sql);
- executeWithExceptionOnFail(stmtExecutor);
+ r.connectContext.setExecutor(stmtExecutor);
+ stmtExecutor.execute();
+ QueryState queryState = r.connectContext.getState();
+ if (queryState.getStateType().equals(MysqlStateType.ERR)) {
+ throw new RuntimeException(String.format("Failed to analyze %s.%s.%s, error: %s sql: %s",
+ info.catalogName, info.dbName, info.colName, sql, queryState.getErrorMessage()));
+ }
} finally {
- LOG.debug("analyze task {} end. cost {}ms", info,
- System.currentTimeMillis() - startTime);
+ LOG.info("Analyze SQL: " + sql + " cost time: " + (System.currentTimeMillis() - startTime) + "ms");
}
-
}
+
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/ResultRow.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/ResultRow.java
deleted file mode 100644
index 9945175a228..00000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/ResultRow.java
+++ /dev/null
@@ -1,59 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements. See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership. The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License. You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied. See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.statistics;
-
-import com.google.gson.annotations.SerializedName;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.StringJoiner;
-
-public class ResultRow {
- @SerializedName("values")
- private final List<String> values;
-
- public ResultRow(List<String> values) {
- this.values = values;
- }
-
- public List<String> getValues() {
- return values != null ? values : Collections.emptyList();
- }
-
- @Override
- public String toString() {
- StringJoiner sj = new StringJoiner(",", "ResultRow:{", "}");
- for (String val : values) {
- sj.add(val);
- }
- return sj.toString();
- }
-
- public String get(int idx) {
- return values.get(idx);
- }
-
- /**
- * If analyze an empty table, some stats would be null, return a default value
- * to avoid npe would deserialize it.
- */
- public String getWithDefault(int idx, String defaultVal) {
- String val = values.get(idx);
- return val == null ? defaultVal : val;
- }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticConstants.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticConstants.java
index e6b8297d0c0..a2194834030 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticConstants.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticConstants.java
@@ -17,9 +17,6 @@
package org.apache.doris.statistics;
-import org.apache.doris.catalog.Column;
-import org.apache.doris.catalog.OlapTable;
-import org.apache.doris.catalog.TableIf;
import org.apache.doris.cluster.ClusterNamespace;
import org.apache.doris.common.FeConstants;
import org.apache.doris.system.SystemInfoService;
@@ -29,16 +26,22 @@ import java.util.List;
import java.util.concurrent.TimeUnit;
public class StatisticConstants {
+ public static final String ANALYSIS_TBL_NAME = "table_statistics";
public static final String STATISTIC_TBL_NAME = "column_statistics";
+
public static final String HISTOGRAM_TBL_NAME = "histogram_statistics";
public static final int MAX_NAME_LEN = 64;
public static final int ID_LEN = 4096;
+ public static final int STATISTICS_CACHE_VALID_DURATION_IN_HOURS = 24 * 2;
+
public static final int STATISTICS_CACHE_REFRESH_INTERVAL = 24 * 2;
+ public static final int ROW_COUNT_CACHE_VALID_DURATION_IN_HOURS = 12;
+
/**
* Bucket count fot column_statistics and analysis_job table.
*/
@@ -60,51 +63,26 @@ public class StatisticConstants {
public static final int HISTOGRAM_MAX_BUCKET_NUM = 128;
- public static final int ANALYZE_MANAGER_INTERVAL_IN_SECS = 60;
+ /**
+ * The health of the table indicates the health of the table statistics, rang in [0, 100].
+ * Below this threshold will automatically re-collect statistics. TODO make it in fe.conf
+ */
+ public static final int TABLE_STATS_HEALTH_THRESHOLD = 80;
- public static List<String> SYSTEM_DBS = new ArrayList<>();
+ public static final int ANALYZE_MANAGER_INTERVAL_IN_SECS = 60;
- public static int ANALYZE_TASK_RETRY_TIMES = 5;
+ public static List<String> STATISTICS_DB_BLACK_LIST = new ArrayList<>();
public static final String DB_NAME = SystemInfoService.DEFAULT_CLUSTER + ":" + FeConstants.INTERNAL_DB_NAME;
- public static final String FULL_QUALIFIED_STATS_TBL_NAME = FeConstants.INTERNAL_DB_NAME + "." + STATISTIC_TBL_NAME;
-
public static final int STATISTIC_INTERNAL_TABLE_REPLICA_NUM = 3;
- public static final int RETRY_LOAD_QUEUE_SIZE = 1000;
-
- public static final int RETRY_LOAD_THREAD_POOL_SIZE = 1;
-
- public static final int LOAD_RETRY_TIMES = 3;
-
- // union more relation than 512 may cause StackOverFlowException in the future.
- public static final int UNION_ALL_LIMIT = 512;
-
- public static final String FULL_AUTO_ANALYZE_START_TIME = "00:00:00";
- public static final String FULL_AUTO_ANALYZE_END_TIME = "23:59:59";
+ public static int ANALYZE_TASK_RETRY_TIMES = 3;
static {
- SYSTEM_DBS.add(SystemInfoService.DEFAULT_CLUSTER
+ STATISTICS_DB_BLACK_LIST.add(SystemInfoService.DEFAULT_CLUSTER
+ ClusterNamespace.CLUSTER_DELIMITER + FeConstants.INTERNAL_DB_NAME);
- SYSTEM_DBS.add(SystemInfoService.DEFAULT_CLUSTER
+ STATISTICS_DB_BLACK_LIST.add(SystemInfoService.DEFAULT_CLUSTER
+ ClusterNamespace.CLUSTER_DELIMITER + "information_schema");
}
-
- public static boolean isSystemTable(TableIf tableIf) {
- if (tableIf instanceof OlapTable) {
- OlapTable olapTable = (OlapTable) tableIf;
- if (StatisticConstants.SYSTEM_DBS.contains(olapTable.getQualifiedDbName())) {
- return true;
- }
- }
- return false;
- }
-
- public static boolean shouldIgnoreCol(TableIf tableIf, Column c) {
- if (isSystemTable(tableIf)) {
- return true;
- }
- return !c.isVisible();
- }
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticRange.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticRange.java
index 74b77c2ee7c..d3bdbdabd0b 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticRange.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticRange.java
@@ -17,8 +17,6 @@
package org.apache.doris.statistics;
-import org.apache.doris.analysis.LiteralExpr;
-import org.apache.doris.common.Pair;
import org.apache.doris.nereids.types.DataType;
import java.util.Objects;
@@ -31,47 +29,20 @@ public class StatisticRange {
* {@code NaN} represents empty range ({@code high} must be {@code NaN} too)
*/
private final double low;
-
- private final LiteralExpr lowExpr;
/**
* {@code NaN} represents empty range ({@code low} must be {@code NaN} too)
*/
private final double high;
- private final LiteralExpr highExpr;
-
private final double distinctValues;
private final DataType dataType;
- private final boolean isEmpty;
-
- public StatisticRange(double low, LiteralExpr lowExpr, double high, LiteralExpr highExpr,
- double distinctValues, DataType dataType) {
- this(low, lowExpr, high, highExpr, distinctValues, dataType, false);
- }
-
- private StatisticRange(double low, LiteralExpr lowExpr, double high, LiteralExpr highExpr,
- double distinctValues, DataType dataType, boolean isEmpty) {
+ public StatisticRange(double low, double high, double distinctValues, DataType dataType) {
this.low = low;
- this.lowExpr = lowExpr;
this.high = high;
- this.highExpr = highExpr;
this.distinctValues = distinctValues;
this.dataType = dataType;
- this.isEmpty = isEmpty;
- }
-
- public LiteralExpr getLowExpr() {
- return lowExpr;
- }
-
- public LiteralExpr getHighExpr() {
- return highExpr;
- }
-
- public DataType getDataType() {
- return dataType;
}
public double overlapPercentWith(StatisticRange other) {
@@ -108,29 +79,19 @@ public class StatisticRange {
}
public static StatisticRange empty(DataType dataType) {
- return new StatisticRange(Double.NEGATIVE_INFINITY, null, Double.POSITIVE_INFINITY,
- null, 0, dataType, true);
+ return new StatisticRange(Double.NaN, Double.NaN, 0, dataType);
}
public boolean isEmpty() {
- return isEmpty;
+ return Double.isNaN(low) && Double.isNaN(high);
}
public boolean isBothInfinite() {
return Double.isInfinite(low) && Double.isInfinite(high);
}
- public boolean isInfinite() {
- return Double.isInfinite(low) || Double.isInfinite(high);
- }
-
- public boolean isFinite() {
- return Double.isFinite(low) && Double.isFinite(high);
- }
-
- public static StatisticRange from(ColumnStatistic colStats, DataType dataType) {
- return new StatisticRange(colStats.minValue, colStats.minExpr, colStats.maxValue, colStats.maxExpr,
- colStats.ndv, dataType);
+ public static StatisticRange from(ColumnStatistic column, DataType dataType) {
+ return new StatisticRange(column.minValue, column.maxValue, column.ndv, dataType);
}
public double getLow() {
@@ -146,49 +107,22 @@ public class StatisticRange {
}
public StatisticRange intersect(StatisticRange other) {
- Pair<Double, LiteralExpr> biggerLow = maxPair(low, lowExpr, other.low, other.lowExpr);
- double newLow = biggerLow.first;
- LiteralExpr newLowExpr = biggerLow.second;
-
- Pair<Double, LiteralExpr> smallerHigh = minPair(high, highExpr, other.high, other.highExpr);
- double newHigh = smallerHigh.first;
- LiteralExpr newHighExpr = smallerHigh.second;
+ double newLow = Math.max(low, other.low);
+ double newHigh = Math.min(high, other.high);
if (newLow <= newHigh) {
- return new StatisticRange(newLow, newLowExpr, newHigh, newHighExpr,
- overlappingDistinctValues(other), dataType);
+ return new StatisticRange(newLow, newHigh, overlappingDistinctValues(other), dataType);
}
return empty(dataType);
}
- public Pair<Double, LiteralExpr> minPair(double r1, LiteralExpr e1, double r2, LiteralExpr e2) {
- if (r1 < r2) {
- return Pair.of(r1, e1);
- }
- return Pair.of(r2, e2);
- }
-
- public Pair<Double, LiteralExpr> maxPair(double r1, LiteralExpr e1, double r2, LiteralExpr e2) {
- if (r1 > r2) {
- return Pair.of(r1, e1);
- }
- return Pair.of(r2, e2);
- }
-
public StatisticRange cover(StatisticRange other) {
- // double newLow = Math.max(low, other.low);
- // double newHigh = Math.min(high, other.high);
- Pair<Double, LiteralExpr> biggerLow = maxPair(low, lowExpr, other.low, other.lowExpr);
- double newLow = biggerLow.first;
- LiteralExpr newLowExpr = biggerLow.second;
- Pair<Double, LiteralExpr> smallerHigh = minPair(high, highExpr, other.high, other.highExpr);
- double newHigh = smallerHigh.first;
- LiteralExpr newHighExpr = smallerHigh.second;
-
+ double newLow = Math.max(low, other.low);
+ double newHigh = Math.min(high, other.high);
if (newLow <= newHigh) {
double overlapPercentOfLeft = overlapPercentWith(other);
double overlapDistinctValuesLeft = overlapPercentOfLeft * distinctValues;
double coveredDistinctValues = minExcludeNaN(distinctValues, overlapDistinctValuesLeft);
- return new StatisticRange(newLow, newLowExpr, newHigh, newHighExpr, coveredDistinctValues, dataType);
+ return new StatisticRange(newLow, newHigh, coveredDistinctValues, dataType);
}
return empty(dataType);
}
@@ -201,10 +135,7 @@ public class StatisticRange {
double maxOverlapNDV = Math.max(overlapNDVThis, overlapNDVOther);
double newNDV = maxOverlapNDV + ((1 - overlapPercentThis) * distinctValues)
+ ((1 - overlapPercentOther) * other.distinctValues);
- Pair<Double, LiteralExpr> smallerMin = minPair(low, lowExpr, other.low, other.lowExpr);
- Pair<Double, LiteralExpr> biggerHigh = maxPair(high, highExpr, other.high, other.highExpr);
- return new StatisticRange(smallerMin.first, smallerMin.second,
- biggerHigh.first, biggerHigh.second, newNDV, dataType);
+ return new StatisticRange(Math.min(low, other.low), Math.max(high, other.high), newNDV, dataType);
}
private double overlappingDistinctValues(StatisticRange other) {
@@ -239,4 +170,7 @@ public class StatisticRange {
return distinctValues;
}
+ public static StatisticRange fromColumnStatistics(ColumnStatistic columnStatistic, DataType dataType) {
+ return new StatisticRange(columnStatistic.minValue, columnStatistic.maxValue, columnStatistic.ndv, dataType);
+ }
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticalType.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticalType.java
index 7fe9b03cbcf..67dd9bb0543 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticalType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticalType.java
@@ -22,7 +22,6 @@ public enum StatisticalType {
AGG_NODE,
ANALYTIC_EVAL_NODE,
ASSERT_NUM_ROWS_NODE,
- CTE_SCAN_NODE,
BROKER_SCAN_NODE,
NESTED_LOOP_JOIN_NODE,
EMPTY_SET_NODE,
@@ -55,5 +54,4 @@ public enum StatisticalType {
METADATA_SCAN_NODE,
JDBC_SCAN_NODE,
TEST_EXTERNAL_TABLE,
- GROUP_COMMIT_SCAN_NODE
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/Statistics.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/Statistics.java
index 77c221f5931..5c628aaba30 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/Statistics.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/Statistics.java
@@ -17,18 +17,17 @@
package org.apache.doris.statistics;
+import org.apache.doris.nereids.stats.ExpressionEstimation;
import org.apache.doris.nereids.stats.StatsMathUtil;
import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.expressions.Slot;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
-import java.util.Set;
public class Statistics {
- private static final int K_BYTES = 1024;
+ private static int K_BYTES = 1024;
private final double rowCount;
@@ -37,10 +36,33 @@ public class Statistics {
// the byte size of one tuple
private double tupleSize;
+ @Deprecated
+ private double width;
+
+ @Deprecated
+ private double penalty;
+
+ /**
+ * after filter, compute the new ndv of a column
+ * @param ndv original ndv of column
+ * @param newRowCount the row count of table after filter
+ * @param oldRowCount the row count of table before filter
+ * @return the new ndv after filter
+ */
+ public static double computeNdv(double ndv, double newRowCount, double oldRowCount) {
+ if (newRowCount > oldRowCount) {
+ return ndv;
+ }
+ double selectOneTuple = newRowCount / StatsMathUtil.nonZeroDivisor(oldRowCount);
+ double allTuplesOfSameDistinctValueNotSelected = Math.pow((1 - selectOneTuple), oldRowCount / ndv);
+ return Math.min(ndv * (1 - allTuplesOfSameDistinctValueNotSelected), newRowCount);
+ }
+
public Statistics(Statistics another) {
this.rowCount = another.rowCount;
this.expressionToColumnStats = new HashMap<>(another.expressionToColumnStats);
- this.tupleSize = another.tupleSize;
+ this.width = another.width;
+ this.penalty = another.penalty;
}
public Statistics(double rowCount, Map<Expression, ColumnStatistic> expressionToColumnStats) {
@@ -48,6 +70,14 @@ public class Statistics {
this.expressionToColumnStats = expressionToColumnStats;
}
+ public Statistics(double rowCount, Map<Expression, ColumnStatistic> expressionToColumnStats, double width,
+ double penalty) {
+ this.rowCount = rowCount;
+ this.expressionToColumnStats = expressionToColumnStats;
+ this.width = width;
+ this.penalty = penalty;
+ }
+
public ColumnStatistic findColumnStatistics(Expression expression) {
return expressionToColumnStats.get(expression);
}
@@ -60,46 +90,53 @@ public class Statistics {
return rowCount;
}
+ /*
+ * Return a stats with new rowCount and fix each column stats.
+ */
public Statistics withRowCount(double rowCount) {
- return new Statistics(rowCount, new HashMap<>(expressionToColumnStats));
+ if (Double.isNaN(rowCount)) {
+ return this;
+ }
+ Statistics statistics = new Statistics(rowCount, new HashMap<>(expressionToColumnStats), width, penalty);
+ statistics.fix(rowCount, StatsMathUtil.nonZeroDivisor(this.rowCount));
+ return statistics;
}
/**
* Update by count.
*/
- public Statistics withRowCountAndEnforceValid(double rowCount) {
+ public Statistics updateRowCountOnly(double rowCount) {
Statistics statistics = new Statistics(rowCount, expressionToColumnStats);
- statistics.enforceValid();
+ for (Entry<Expression, ColumnStatistic> entry : expressionToColumnStats.entrySet()) {
+ ColumnStatistic columnStatistic = entry.getValue();
+ ColumnStatisticBuilder columnStatisticBuilder = new ColumnStatisticBuilder(columnStatistic);
+ columnStatisticBuilder.setNdv(Math.min(columnStatistic.ndv, rowCount));
+ double nullFactor = (rowCount - columnStatistic.numNulls) / rowCount;
+ columnStatisticBuilder.setNumNulls(nullFactor * rowCount);
+ columnStatisticBuilder.setCount(rowCount);
+ statistics.addColumnStats(entry.getKey(), columnStatisticBuilder.build());
+ }
return statistics;
}
- public void enforceValid() {
+ /**
+ * Fix by sel.
+ */
+ public void fix(double newRowCount, double originRowCount) {
+ double sel = newRowCount / originRowCount;
for (Entry<Expression, ColumnStatistic> entry : expressionToColumnStats.entrySet()) {
ColumnStatistic columnStatistic = entry.getValue();
- if (!checkColumnStatsValid(columnStatistic)) {
- double ndv = Math.min(columnStatistic.ndv, rowCount);
- ColumnStatisticBuilder columnStatisticBuilder = new ColumnStatisticBuilder(columnStatistic);
- columnStatisticBuilder.setNdv(ndv);
- columnStatisticBuilder.setNumNulls(Math.min(columnStatistic.numNulls, rowCount - ndv));
- columnStatisticBuilder.setCount(rowCount);
- columnStatistic = columnStatisticBuilder.build();
- expressionToColumnStats.put(entry.getKey(), columnStatistic);
- }
+ ColumnStatisticBuilder columnStatisticBuilder = new ColumnStatisticBuilder(columnStatistic);
+ columnStatisticBuilder.setNdv(computeNdv(columnStatistic.ndv, newRowCount, originRowCount));
+ columnStatisticBuilder.setNumNulls(Math.min(columnStatistic.numNulls * sel, newRowCount));
+ columnStatisticBuilder.setCount(newRowCount);
+ expressionToColumnStats.put(entry.getKey(), columnStatisticBuilder.build());
}
}
- public boolean checkColumnStatsValid(ColumnStatistic columnStatistic) {
- return columnStatistic.ndv <= rowCount
- && columnStatistic.numNulls <= rowCount - columnStatistic.ndv;
- }
-
public Statistics withSel(double sel) {
sel = StatsMathUtil.minNonNaN(sel, 1);
- if (Double.isNaN(rowCount)) {
- return this;
- }
- double newCount = rowCount * sel;
- return new Statistics(newCount, new HashMap<>(expressionToColumnStats));
+ return withRowCount(rowCount * sel);
}
public Statistics addColumnStats(Expression expression, ColumnStatistic columnStatistic) {
@@ -107,10 +144,9 @@ public class Statistics {
return this;
}
- public boolean isInputSlotsUnknown(Set<Slot> inputs) {
- return inputs.stream()
- .allMatch(s -> expressionToColumnStats.containsKey(s)
- && expressionToColumnStats.get(s).isUnKnown);
+ public Statistics merge(Statistics statistics) {
+ expressionToColumnStats.putAll(statistics.expressionToColumnStats);
+ return this;
}
private double computeTupleSize() {
@@ -147,20 +183,53 @@ public class Statistics {
return format.format(rowCount);
}
+ public void setWidth(double width) {
+ this.width = width;
+ }
+
+ public void setPenalty(double penalty) {
+ this.penalty = penalty;
+ }
+
+ public double getWidth() {
+ return width;
+ }
+
+ public double getPenalty() {
+ return penalty;
+ }
+
public int getBENumber() {
return 1;
}
public static Statistics zero(Statistics statistics) {
Statistics zero = new Statistics(0, new HashMap<>());
- for (Entry<Expression, ColumnStatistic> entry : statistics.expressionToColumnStats.entrySet()) {
+ for (Map.Entry<Expression, ColumnStatistic> entry : statistics.expressionToColumnStats.entrySet()) {
zero.addColumnStats(entry.getKey(), ColumnStatistic.ZERO);
}
return zero;
}
+ public boolean almostUniqueExpression(Expression expr) {
+ ExpressionEstimation estimator = new ExpressionEstimation();
+ double ndvErrorThreshold = 0.9;
+ ColumnStatistic colStats = expr.accept(estimator, this);
+ if (colStats.ndv > colStats.count * ndvErrorThreshold) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean isStatsUnknown(Expression expr) {
+ ExpressionEstimation estimator = new ExpressionEstimation();
+ ColumnStatistic colStats = expr.accept(estimator, this);
+ return colStats.isUnKnown;
+ }
+
/**
* merge this and other colStats.ndv, choose min
+ * @param other
*/
public void updateNdv(Statistics other) {
for (Expression expr : expressionToColumnStats.keySet()) {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsAutoAnalyzer.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsAutoAnalyzer.java
new file mode 100644
index 00000000000..aae783ca8b2
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsAutoAnalyzer.java
@@ -0,0 +1,215 @@
+// 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.doris.statistics;
+
+import org.apache.doris.analysis.DdlStmt;
+import org.apache.doris.catalog.Column;
+import org.apache.doris.catalog.Env;
+import org.apache.doris.catalog.Partition;
+import org.apache.doris.catalog.TableIf;
+import org.apache.doris.common.Config;
+import org.apache.doris.common.DdlException;
+import org.apache.doris.common.util.MasterDaemon;
+import org.apache.doris.statistics.AnalysisInfo.JobType;
+import org.apache.doris.statistics.util.StatisticsUtil;
+
+import com.google.common.collect.Maps;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+public class StatisticsAutoAnalyzer extends MasterDaemon {
+
+ private static final Logger LOG = LogManager.getLogger(StatisticsAutoAnalyzer.class);
+
+ public StatisticsAutoAnalyzer() {
+ super("Automatic Analyzer", TimeUnit.MINUTES.toMillis(Config.auto_check_statistics_in_minutes));
+ }
+
+ @Override
+ protected void runAfterCatalogReady() {
+ if (!Env.getCurrentEnv().isMaster()) {
+ return;
+ }
+ if (!StatisticsUtil.statsTblAvailable()) {
+ return;
+ }
+ if (Config.enable_auto_collect_statistics) {
+ analyzePeriodically();
+ analyzeAutomatically();
+ }
+ }
+
+ public void autoAnalyzeStats(DdlStmt ddlStmt) {
+ // TODO Monitor some DDL statements, and then trigger automatic analysis tasks
+ }
+
+ private void analyzePeriodically() {
+ try {
+ AnalysisManager analysisManager = Env.getCurrentEnv().getAnalysisManager();
+ List<AnalysisInfo> jobInfos = analysisManager.findPeriodicJobs();
+ for (AnalysisInfo jobInfo : jobInfos) {
+ jobInfo = new AnalysisInfoBuilder(jobInfo).setJobType(JobType.SYSTEM).build();
+ analysisManager.createAnalysisJob(jobInfo);
+ }
+ } catch (DdlException e) {
+ LOG.warn("Failed to periodically analyze the statistics." + e);
+ }
+ }
+
+ private void analyzeAutomatically() {
+ AnalysisManager analysisManager = Env.getCurrentEnv().getAnalysisManager();
+ List<AnalysisInfo> jobInfos = analysisManager.findAutomaticAnalysisJobs();
+ for (AnalysisInfo jobInfo : jobInfos) {
+ AnalysisInfo checkedJobInfo = null;
+ try {
+ checkedJobInfo = checkAutomaticJobInfo(jobInfo);
+ if (checkedJobInfo != null) {
+ analysisManager.createAnalysisJob(checkedJobInfo);
+ }
+ } catch (Throwable t) {
+ LOG.warn("Failed to create analyze job: {}", checkedJobInfo);
+ }
+
+ }
+ }
+
+ /**
+ * Check if automatic analysis of statistics is required.
+ * <p>
+ * Step1: check the health of the table, if the health is good,
+ * there is no need to re-analyze, or check partition
+ * <p>
+ * Step2: check the partition update time, if the partition is not updated
+ * after the statistics is analyzed, there is no need to re-analyze
+ * <p>
+ * Step3: if the partition is updated after the statistics is analyzed,
+ * check the health of the partition, if the health is good, there is no need to re-analyze
+ * - Step3.1: check the analyzed partition statistics
+ * - Step3.2: Check for new partitions for which statistics were not analyzed
+ * <p>
+ * TODO new columns is not currently supported to analyze automatically
+ *
+ * @param jobInfo analysis job info
+ * @return new job info after check
+ * @throws Throwable failed to check
+ */
+ private AnalysisInfo checkAutomaticJobInfo(AnalysisInfo jobInfo) throws Throwable {
+ long lastExecTimeInMs = jobInfo.lastExecTimeInMs;
+ TableIf table = StatisticsUtil
+ .findTable(jobInfo.catalogName, jobInfo.dbName, jobInfo.tblName);
+ TableStatistic tblStats = StatisticsRepository.fetchTableLevelStats(table.getId());
+
+ if (tblStats == TableStatistic.UNKNOWN) {
+ LOG.warn("Failed to automatically analyze statistics, "
+ + "no corresponding table statistics for job: {}", jobInfo.toString());
+ throw new DdlException("No corresponding table statistics for automatic job.");
+ }
+
+ if (!needReanalyzeTable(table, tblStats)) {
+ return null;
+ }
+
+ Set<String> needRunPartitions = new HashSet<>();
+ Set<String> statsPartitions = jobInfo.colToPartitions.values()
+ .stream()
+ .flatMap(Collection::stream)
+ .collect(Collectors.toSet());
+
+ checkAnalyzedPartitions(table, statsPartitions, needRunPartitions, lastExecTimeInMs);
+ checkNewPartitions(table, needRunPartitions, lastExecTimeInMs);
+
+ if (needRunPartitions.isEmpty()) {
+ return null;
+ }
+
+ return getAnalysisJobInfo(jobInfo, table, needRunPartitions);
+ }
+
+ private boolean needReanalyzeTable(TableIf table, TableStatistic tblStats) {
+ long rowCount = table.getRowCount();
+ long updateRows = Math.abs(rowCount - tblStats.rowCount);
+ int tblHealth = StatisticsUtil.getTableHealth(rowCount, updateRows);
+ return tblHealth < StatisticConstants.TABLE_STATS_HEALTH_THRESHOLD;
+ }
+
+ private void checkAnalyzedPartitions(TableIf table, Set<String> statsPartitions,
+ Set<String> needRunPartitions, long lastExecTimeInMs) throws DdlException {
+ for (String statsPartition : statsPartitions) {
+ Partition partition = table.getPartition(statsPartition);
+ if (partition == null) {
+ // Partition that has been deleted also need to
+ // be reanalyzed (delete partition statistics later)
+ needRunPartitions.add(statsPartition);
+ continue;
+ }
+ TableStatistic partitionStats = StatisticsRepository
+ .fetchTableLevelOfPartStats(partition.getId());
+ if (partitionStats == TableStatistic.UNKNOWN) {
+ continue;
+ }
+ if (needReanalyzePartition(lastExecTimeInMs, partition, partitionStats)) {
+ needRunPartitions.add(partition.getName());
+ }
+ }
+ }
+
+ private boolean needReanalyzePartition(long lastExecTimeInMs, Partition partition, TableStatistic partStats) {
+ long partUpdateTime = partition.getVisibleVersionTime();
+ if (partUpdateTime < lastExecTimeInMs) {
+ return false;
+ }
+ long pRowCount = partition.getBaseIndex().getRowCount();
+ long pUpdateRows = Math.abs(pRowCount - partStats.rowCount);
+ int partHealth = StatisticsUtil.getTableHealth(pRowCount, pUpdateRows);
+ return partHealth < StatisticConstants.TABLE_STATS_HEALTH_THRESHOLD;
+ }
+
+ private void checkNewPartitions(TableIf table, Set<String> needRunPartitions, long lastExecTimeInMs) {
+ Set<String> partitionNames = table.getPartitionNames();
+ partitionNames.removeAll(needRunPartitions);
+ needRunPartitions.addAll(
+ partitionNames.stream()
+ .map(table::getPartition)
+ .filter(partition -> partition.getVisibleVersionTime() >= lastExecTimeInMs)
+ .map(Partition::getName)
+ .collect(Collectors.toSet())
+ );
+ }
+
+ private AnalysisInfo getAnalysisJobInfo(AnalysisInfo jobInfo, TableIf table,
+ Set<String> needRunPartitions) {
+ Map<String, Set<String>> newColToPartitions = Maps.newHashMap();
+ Map<String, Set<String>> colToPartitions = jobInfo.colToPartitions;
+ colToPartitions.keySet().forEach(colName -> {
+ Column column = table.getColumn(colName);
+ if (column != null) {
+ newColToPartitions.put(colName, needRunPartitions);
+ }
+ });
+ return new AnalysisInfoBuilder(jobInfo)
+ .setColToPartitions(newColToPartitions).build();
+ }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsAutoCollector.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsAutoCollector.java
deleted file mode 100644
index fe535b0fb4a..00000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsAutoCollector.java
+++ /dev/null
@@ -1,196 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements. See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership. The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License. You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied. See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.statistics;
-
-import org.apache.doris.analysis.TableName;
-import org.apache.doris.catalog.Column;
-import org.apache.doris.catalog.DatabaseIf;
-import org.apache.doris.catalog.Env;
-import org.apache.doris.catalog.OlapTable;
-import org.apache.doris.catalog.TableIf;
-import org.apache.doris.catalog.external.ExternalTable;
-import org.apache.doris.common.Config;
-import org.apache.doris.common.util.TimeUtils;
-import org.apache.doris.datasource.CatalogIf;
-import org.apache.doris.statistics.AnalysisInfo.AnalysisMethod;
-import org.apache.doris.statistics.AnalysisInfo.JobType;
-import org.apache.doris.statistics.AnalysisInfo.ScheduleType;
-import org.apache.doris.statistics.util.StatisticsUtil;
-
-import com.google.common.collect.Maps;
-import org.apache.hudi.common.util.VisibleForTesting;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-
-import java.time.LocalTime;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-
-public class StatisticsAutoCollector extends StatisticsCollector {
-
- private static final Logger LOG = LogManager.getLogger(StatisticsAutoCollector.class);
-
- public StatisticsAutoCollector() {
- super("Automatic Analyzer",
- TimeUnit.MINUTES.toMillis(Config.auto_check_statistics_in_minutes),
- new AnalysisTaskExecutor(Config.full_auto_analyze_simultaneously_running_task_num));
- }
-
- @Override
- protected void collect() {
- if (!StatisticsUtil.inAnalyzeTime(LocalTime.now(TimeUtils.getTimeZone().toZoneId()))) {
- analysisTaskExecutor.clear();
- return;
- }
- if (StatisticsUtil.enableAutoAnalyze()) {
- analyzeAll();
- }
- }
-
- @SuppressWarnings({"rawtypes", "unchecked"})
- private void analyzeAll() {
- Set<CatalogIf> catalogs = Env.getCurrentEnv().getCatalogMgr().getCopyOfCatalog();
- for (CatalogIf ctl : catalogs) {
- if (!ctl.enableAutoAnalyze()) {
- continue;
- }
- Collection<DatabaseIf> dbs = ctl.getAllDbs();
- for (DatabaseIf<TableIf> databaseIf : dbs) {
- if (StatisticConstants.SYSTEM_DBS.contains(databaseIf.getFullName())) {
- continue;
- }
- analyzeDb(databaseIf);
- }
- }
- }
-
- public void analyzeDb(DatabaseIf<TableIf> databaseIf) {
- List<AnalysisInfo> analysisInfos = constructAnalysisInfo(databaseIf);
- for (AnalysisInfo analysisInfo : analysisInfos) {
- analysisInfo = getReAnalyzeRequiredPart(analysisInfo);
- if (analysisInfo == null) {
- continue;
- }
- try {
- createSystemAnalysisJob(analysisInfo);
- } catch (Exception e) {
- LOG.warn("Failed to create analysis job", e);
- }
- }
- }
-
- protected List<AnalysisInfo> constructAnalysisInfo(DatabaseIf<? extends TableIf> db) {
- List<AnalysisInfo> analysisInfos = new ArrayList<>();
- for (TableIf table : db.getTables()) {
- if (skip(table)) {
- continue;
- }
- createAnalyzeJobForTbl(db, analysisInfos, table);
- }
- return analysisInfos;
- }
-
- // return true if skip auto analyze this time.
- protected boolean skip(TableIf table) {
- if (!(table instanceof OlapTable || table instanceof ExternalTable)) {
- return true;
- }
- if (table.getDataSize(true) < Config.huge_table_lower_bound_size_in_bytes) {
- return false;
- }
- TableStatsMeta tableStats = Env.getCurrentEnv().getAnalysisManager().findTableStatsStatus(table.getId());
- return System.currentTimeMillis() - tableStats.updatedTime < Config.huge_table_auto_analyze_interval_in_millis;
- }
-
- protected void createAnalyzeJobForTbl(DatabaseIf<? extends TableIf> db,
- List<AnalysisInfo> analysisInfos, TableIf table) {
- AnalysisMethod analysisMethod = table.getDataSize(true) > Config.huge_table_lower_bound_size_in_bytes
- ? AnalysisMethod.SAMPLE : AnalysisMethod.FULL;
- TableName tableName = new TableName(db.getCatalog().getName(), db.getFullName(),
- table.getName());
- AnalysisInfo jobInfo = new AnalysisInfoBuilder()
- .setJobId(Env.getCurrentEnv().getNextId())
- .setCatalogName(db.getCatalog().getName())
- .setDbName(db.getFullName())
- .setTblName(tableName.getTbl())
- .setColName(
- table.getBaseSchema().stream().filter(c -> !StatisticsUtil.isUnsupportedType(c.getType()))
- .map(
- Column::getName).collect(Collectors.joining(","))
- )
- .setAnalysisType(AnalysisInfo.AnalysisType.FUNDAMENTALS)
- .setAnalysisMode(AnalysisInfo.AnalysisMode.INCREMENTAL)
- .setAnalysisMethod(analysisMethod)
- .setSampleRows(Config.huge_table_default_sample_rows)
- .setScheduleType(ScheduleType.AUTOMATIC)
- .setState(AnalysisState.PENDING)
- .setTaskIds(new ArrayList<>())
- .setLastExecTimeInMs(System.currentTimeMillis())
- .setJobType(JobType.SYSTEM).build();
- analysisInfos.add(jobInfo);
- }
-
- @VisibleForTesting
- protected AnalysisInfo getReAnalyzeRequiredPart(AnalysisInfo jobInfo) {
- TableIf table = StatisticsUtil
- .findTable(jobInfo.catalogName, jobInfo.dbName, jobInfo.tblName);
- AnalysisManager analysisManager = Env.getServingEnv().getAnalysisManager();
- TableStatsMeta tblStats = analysisManager.findTableStatsStatus(table.getId());
-
- if (!table.needReAnalyzeTable(tblStats)) {
- return null;
- }
-
- Map<String, Set<String>> needRunPartitions = table.findReAnalyzeNeededPartitions();
-
- if (needRunPartitions.isEmpty()) {
- return null;
- }
-
- return new AnalysisInfoBuilder(jobInfo).setColToPartitions(needRunPartitions).build();
- }
-
- @VisibleForTesting
- protected AnalysisInfo getAnalysisJobInfo(AnalysisInfo jobInfo, TableIf table,
- Set<String> needRunPartitions) {
- Map<String, Set<String>> newColToPartitions = Maps.newHashMap();
- Map<String, Set<String>> colToPartitions = jobInfo.colToPartitions;
- if (colToPartitions == null) {
- for (Column c : table.getColumns()) {
- if (StatisticsUtil.isUnsupportedType(c.getType())) {
- continue;
- }
- newColToPartitions.put(c.getName(), needRunPartitions);
- }
- } else {
- colToPartitions.keySet().forEach(colName -> {
- Column column = table.getColumn(colName);
- if (column != null) {
- newColToPartitions.put(colName, needRunPartitions);
- }
- });
- }
- return new AnalysisInfoBuilder(jobInfo)
- .setColToPartitions(newColToPartitions).build();
- }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsBuilder.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsBuilder.java
index a0e75f7df38..3622006542d 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsBuilder.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsBuilder.java
@@ -26,7 +26,7 @@ public class StatisticsBuilder {
private double rowCount;
- private final Map<Expression, ColumnStatistic> expressionToColumnStats;
+ private Map<Expression, ColumnStatistic> expressionToColumnStats;
public StatisticsBuilder() {
expressionToColumnStats = new HashMap<>();
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsCache.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsCache.java
index c9b049a8cfc..cb9e4ca3228 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsCache.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsCache.java
@@ -24,6 +24,7 @@ import org.apache.doris.common.ThreadPoolManager;
import org.apache.doris.ha.FrontendNodeType;
import org.apache.doris.persist.gson.GsonUtils;
import org.apache.doris.qe.ConnectContext;
+import org.apache.doris.statistics.util.InternalQueryResult.ResultRow;
import org.apache.doris.statistics.util.StatisticsUtil;
import org.apache.doris.system.Frontend;
import org.apache.doris.thrift.FrontendService;
@@ -33,21 +34,16 @@ import org.apache.doris.thrift.TUpdateFollowerStatsCacheRequest;
import com.github.benmanes.caffeine.cache.AsyncLoadingCache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.apache.commons.collections.CollectionUtils;
-import org.apache.hudi.common.util.VisibleForTesting;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.time.Duration;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Optional;
-import java.util.Set;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
public class StatisticsCache {
@@ -63,6 +59,7 @@ public class StatisticsCache {
private final ColumnStatisticsCacheLoader columnStatisticsCacheLoader = new ColumnStatisticsCacheLoader();
private final HistogramCacheLoader histogramCacheLoader = new HistogramCacheLoader();
+ private final TableStatisticsCacheLoader tableStatisticsCacheLoader = new TableStatisticsCacheLoader();
private final AsyncLoadingCache<StatisticsCacheKey, Optional<ColumnStatistic>> columnStatisticsCache =
Caffeine.newBuilder()
@@ -78,12 +75,20 @@ public class StatisticsCache {
.executor(threadPool)
.buildAsync(histogramCacheLoader);
+ private final AsyncLoadingCache<StatisticsCacheKey, Optional<TableStatistic>> tableStatisticsCache =
+ Caffeine.newBuilder()
+ .maximumSize(Config.stats_cache_size)
+ .refreshAfterWrite(Duration.ofHours(StatisticConstants.STATISTICS_CACHE_REFRESH_INTERVAL))
+ .executor(threadPool)
+ .buildAsync(tableStatisticsCacheLoader);
+
{
threadPool.submit(() -> {
while (true) {
try {
columnStatisticsCacheLoader.removeExpiredInProgressing();
histogramCacheLoader.removeExpiredInProgressing();
+ tableStatisticsCacheLoader.removeExpiredInProgressing();
} catch (Throwable t) {
// IGNORE
}
@@ -136,6 +141,23 @@ public class StatisticsCache {
return Optional.empty();
}
+ public Optional<TableStatistic> getTableStatistics(long catalogId, long dbId, long tableId) {
+ ConnectContext ctx = ConnectContext.get();
+ if (ctx != null && ctx.getSessionVariable().internalSession) {
+ return Optional.empty();
+ }
+ StatisticsCacheKey k = new StatisticsCacheKey(catalogId, dbId, tableId);
+ try {
+ CompletableFuture<Optional<TableStatistic>> f = tableStatisticsCache.get(k);
+ if (f.isDone()) {
+ return f.get();
+ }
+ } catch (Exception e) {
+ LOG.warn("Unexpected exception while returning Histogram", e);
+ }
+ return Optional.empty();
+ }
+
public void invalidate(long tblId, long idxId, String colName) {
columnStatisticsCache.synchronous().invalidate(new StatisticsCacheKey(tblId, idxId, colName));
}
@@ -152,6 +174,14 @@ public class StatisticsCache {
columnStatisticsCache.synchronous().refresh(new StatisticsCacheKey(catalogId, dbId, tblId, idxId, colName));
}
+ public void invalidateTableStats(long catalogId, long dbId, long tblId) {
+ tableStatisticsCache.synchronous().invalidate(new StatisticsCacheKey(catalogId, dbId, tblId));
+ }
+
+ public void refreshTableStatsSync(long catalogId, long dbId, long tblId) {
+ tableStatisticsCache.synchronous().refresh(new StatisticsCacheKey(catalogId, dbId, tblId));
+ }
+
public void refreshHistogramSync(long tblId, long idxId, String colName) {
histogramCache.synchronous().refresh(new StatisticsCacheKey(tblId, idxId, colName));
}
@@ -189,113 +219,83 @@ public class StatisticsCache {
if (CollectionUtils.isEmpty(recentStatsUpdatedCols)) {
return;
}
- Map<StatisticsCacheKey, ColumnStatistic> keyToColStats = new HashMap<>();
for (ResultRow r : recentStatsUpdatedCols) {
try {
- StatsId statsId = new StatsId(r);
- long tblId = statsId.tblId;
- long idxId = statsId.idxId;
- String colId = statsId.colId;
+ long tblId = Long.parseLong(r.getColumnValue("tbl_id"));
+ long idxId = Long.parseLong(r.getColumnValue("idx_id"));
+ String colId = r.getColumnValue("col_id");
final StatisticsCacheKey k =
new StatisticsCacheKey(tblId, idxId, colId);
final ColumnStatistic c = ColumnStatistic.fromResultRow(r);
- keyToColStats.put(k, c);
+ c.loadPartitionStats(tblId, idxId, colId);
putCache(k, c);
} catch (Throwable t) {
LOG.warn("Error when preheating stats cache", t);
}
}
- try {
- loadPartStats(keyToColStats);
- } catch (Exception e) {
- LOG.warn("Fucka", e);
- }
}
- /**
- * Return false if the log of corresponding stats load is failed.
- */
- public boolean syncLoadColStats(long tableId, long idxId, String colName) {
+ public void syncLoadColStats(long tableId, long idxId, String colName) {
List<ResultRow> columnResults = StatisticsRepository.loadColStats(tableId, idxId, colName);
final StatisticsCacheKey k =
new StatisticsCacheKey(tableId, idxId, colName);
final ColumnStatistic c = ColumnStatistic.fromResultRow(columnResults);
if (c == ColumnStatistic.UNKNOWN) {
- return false;
+ return;
}
putCache(k, c);
- if (ColumnStatistic.UNKNOWN == c) {
- return false;
- }
TUpdateFollowerStatsCacheRequest updateFollowerStatsCacheRequest = new TUpdateFollowerStatsCacheRequest();
updateFollowerStatsCacheRequest.key = GsonUtils.GSON.toJson(k);
- updateFollowerStatsCacheRequest.statsRows = columnResults.stream().map(GsonUtils.GSON::toJson).collect(
- Collectors.toList());
+ updateFollowerStatsCacheRequest.colStats = GsonUtils.GSON.toJson(c);
for (Frontend frontend : Env.getCurrentEnv().getFrontends(FrontendNodeType.FOLLOWER)) {
- if (StatisticsUtil.isMaster(frontend)) {
+ if (frontend.getHost().equals(Env.getCurrentEnv().getSelfNode().getHost())) {
+ // Doesn't need to send request to current node.
continue;
}
- sendStats(frontend, updateFollowerStatsCacheRequest);
- }
- return true;
- }
-
- @VisibleForTesting
- public void sendStats(Frontend frontend, TUpdateFollowerStatsCacheRequest updateFollowerStatsCacheRequest) {
- TNetworkAddress address = new TNetworkAddress(frontend.getHost(),
- frontend.getRpcPort());
- FrontendService.Client client = null;
- try {
- client = ClientPool.frontendPool.borrowObject(address);
- client.updateStatsCache(updateFollowerStatsCacheRequest);
- } catch (Throwable t) {
- LOG.warn("Failed to sync stats to follower: {}", address, t);
- } finally {
- if (client != null) {
- ClientPool.frontendPool.returnObject(address, client);
+ TNetworkAddress address = new TNetworkAddress(frontend.getHost(),
+ frontend.getRpcPort());
+ FrontendService.Client client = null;
+ try {
+ client = ClientPool.frontendPool.borrowObject(address);
+ client.updateStatsCache(updateFollowerStatsCacheRequest);
+ } catch (Throwable t) {
+ LOG.warn("Failed to sync stats to follower: {}", address, t);
+ } finally {
+ if (client != null) {
+ ClientPool.frontendPool.returnObject(address, client);
+ }
}
}
+
}
public void putCache(StatisticsCacheKey k, ColumnStatistic c) {
- CompletableFuture<Optional<ColumnStatistic>> f = new CompletableFuture<Optional<ColumnStatistic>>();
- f.obtrudeValue(Optional.of(c));
- columnStatisticsCache.put(k, f);
- }
+ CompletableFuture<Optional<ColumnStatistic>> f = new CompletableFuture<Optional<ColumnStatistic>>() {
- private void loadPartStats(Map<StatisticsCacheKey, ColumnStatistic> keyToColStats) {
- final int batchSize = Config.expr_children_limit;
- Set<StatisticsCacheKey> keySet = new HashSet<>();
- for (StatisticsCacheKey statisticsCacheKey : keyToColStats.keySet()) {
- if (keySet.size() < batchSize - 1) {
- keySet.add(statisticsCacheKey);
- } else {
- List<ResultRow> partStats = StatisticsRepository.loadPartStats(keySet);
- addPartStatsToColStats(keyToColStats, partStats);
- keySet = new HashSet<>();
+ @Override
+ public Optional<ColumnStatistic> get() throws InterruptedException, ExecutionException {
+ return Optional.of(c);
}
- }
- if (!keySet.isEmpty()) {
- List<ResultRow> partStats = StatisticsRepository.loadPartStats(keySet);
- addPartStatsToColStats(keyToColStats, partStats);
- }
- }
- private void addPartStatsToColStats(Map<StatisticsCacheKey, ColumnStatistic> keyToColStats,
- List<ResultRow> partsStats) {
- for (ResultRow r : partsStats) {
- try {
- StatsId statsId = new StatsId(r);
- long tblId = statsId.tblId;
- long idxId = statsId.idxId;
- String partId = statsId.partId;
- String colId = statsId.colId;
- ColumnStatistic partStats = ColumnStatistic.fromResultRow(r);
- keyToColStats.get(new StatisticsCacheKey(tblId, idxId, colId)).putPartStats(partId, partStats);
- } catch (Throwable t) {
- LOG.warn("Failed to deserialized part stats", t);
+ @Override
+ public boolean isDone() {
+ return true;
}
+
+ @Override
+ public boolean complete(Optional<ColumnStatistic> value) {
+ return true;
+ }
+
+ @Override
+ public Optional<ColumnStatistic> join() {
+ return Optional.of(c);
+ }
+ };
+ if (c.isUnKnown) {
+ return;
}
+ columnStatisticsCache.put(k, f);
}
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsCleaner.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsCleaner.java
index 6521a8b4a59..9aaee6bf1d7 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsCleaner.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsCleaner.java
@@ -27,6 +27,7 @@ import org.apache.doris.common.FeConstants;
import org.apache.doris.common.util.MasterDaemon;
import org.apache.doris.datasource.CatalogIf;
import org.apache.doris.datasource.InternalCatalog;
+import org.apache.doris.statistics.util.InternalQueryResult.ResultRow;
import org.apache.doris.statistics.util.StatisticsUtil;
import org.apache.doris.system.SystemInfoService;
@@ -194,32 +195,31 @@ public class StatisticsCleaner extends MasterDaemon {
pos += StatisticConstants.FETCH_LIMIT;
for (ResultRow r : rows) {
try {
- StatsId statsId = new StatsId(r);
- String id = statsId.id;
- long catalogId = statsId.catalogId;
+ String id = r.getColumnValue("id");
+ long catalogId = Long.parseLong(r.getColumnValue("catalog_id"));
if (!idToCatalog.containsKey(catalogId)) {
expiredStats.expiredCatalog.add(catalogId);
continue;
}
- long dbId = statsId.dbId;
+ long dbId = Long.parseLong(r.getColumnValue("db_id"));
if (!idToDb.containsKey(dbId)) {
expiredStats.expiredDatabase.add(dbId);
continue;
}
- long tblId = statsId.tblId;
+ long tblId = Long.parseLong(r.getColumnValue("tbl_id"));
if (!idToTbl.containsKey(tblId)) {
expiredStats.expiredTable.add(tblId);
continue;
}
- long idxId = statsId.idxId;
+ long idxId = Long.parseLong(r.getColumnValue("idx_id"));
if (idxId != -1 && !idToMVIdx.containsKey(idxId)) {
expiredStats.expiredIdxId.add(idxId);
continue;
}
TableIf t = idToTbl.get(tblId);
- String colId = statsId.colId;
+ String colId = r.getColumnValue("col_id");
if (t.getColumn(colId) == null) {
expiredStats.ids.add(id);
continue;
@@ -228,11 +228,12 @@ public class StatisticsCleaner extends MasterDaemon {
continue;
}
OlapTable olapTable = (OlapTable) t;
- String partId = statsId.partId;
- if (partId == null) {
+ String partIdStr = r.getColumnValue("part_id");
+ if (partIdStr == null) {
continue;
}
- if (!olapTable.getPartitionIds().contains(Long.parseLong(partId))) {
+ long partId = Long.parseLong(partIdStr);
+ if (!olapTable.getPartitionIds().contains(partId)) {
expiredStats.ids.add(id);
}
} catch (Exception e) {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsCollector.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsCollector.java
deleted file mode 100644
index 2d5c4816835..00000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsCollector.java
+++ /dev/null
@@ -1,86 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements. See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership. The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License. You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied. See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.statistics;
-
-import org.apache.doris.catalog.Env;
-import org.apache.doris.common.DdlException;
-import org.apache.doris.common.util.MasterDaemon;
-import org.apache.doris.statistics.util.StatisticsUtil;
-
-import org.apache.hudi.common.util.VisibleForTesting;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public abstract class StatisticsCollector extends MasterDaemon {
-
- private static final Logger LOG = LogManager.getLogger(StatisticsCollector.class);
-
- protected final AnalysisTaskExecutor analysisTaskExecutor;
-
-
- public StatisticsCollector(String name, long intervalMs, AnalysisTaskExecutor analysisTaskExecutor) {
- super(name, intervalMs);
- this.analysisTaskExecutor = analysisTaskExecutor;
- analysisTaskExecutor.start();
- }
-
- @Override
- protected void runAfterCatalogReady() {
- if (!Env.getCurrentEnv().isMaster()) {
- return;
- }
- if (!StatisticsUtil.statsTblAvailable()) {
- LOG.info("Stats table not available, skip");
- return;
- }
- if (Env.isCheckpointThread()) {
- return;
- }
-
- if (!analysisTaskExecutor.idle()) {
- LOG.info("Analyze tasks those submitted in last time is not finished, skip");
- return;
- }
- collect();
- }
-
- protected abstract void collect();
-
- // Analysis job created by the system
- @VisibleForTesting
- protected void createSystemAnalysisJob(AnalysisInfo jobInfo)
- throws DdlException {
- if (jobInfo.colToPartitions.isEmpty()) {
- // No statistics need to be collected or updated
- return;
- }
-
- Map<Long, BaseAnalysisTask> analysisTaskInfos = new HashMap<>();
- AnalysisManager analysisManager = Env.getCurrentEnv().getAnalysisManager();
- analysisManager.createTaskForEachColumns(jobInfo, analysisTaskInfos, false);
- if (StatisticsUtil.isExternalTable(jobInfo.catalogName, jobInfo.dbName, jobInfo.tblName)) {
- analysisManager.createTableLevelTaskForExternalTable(jobInfo, analysisTaskInfos, false);
- }
- Env.getCurrentEnv().getAnalysisManager().registerSysJob(jobInfo, analysisTaskInfos);
- analysisTaskInfos.values().forEach(analysisTaskExecutor::submitTask);
- }
-
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsPeriodCollector.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsPeriodCollector.java
deleted file mode 100644
index f34ad0f1221..00000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsPeriodCollector.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements. See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership. The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License. You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied. See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.statistics;
-
-import org.apache.doris.catalog.Env;
-import org.apache.doris.common.Config;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-public class StatisticsPeriodCollector extends StatisticsCollector {
- private static final Logger LOG = LogManager.getLogger(StatisticsPeriodCollector.class);
-
- public StatisticsPeriodCollector() {
- super("Automatic Analyzer",
- TimeUnit.MINUTES.toMillis(Config.auto_check_statistics_in_minutes) / 2,
- new AnalysisTaskExecutor(Config.period_analyze_simultaneously_running_task_num));
- }
-
- @Override
- protected void collect() {
- try {
- AnalysisManager analysisManager = Env.getCurrentEnv().getAnalysisManager();
- List<AnalysisInfo> jobInfos = analysisManager.findPeriodicJobs();
- for (AnalysisInfo jobInfo : jobInfos) {
- createSystemAnalysisJob(jobInfo);
- }
- } catch (Exception e) {
- LOG.warn("Failed to periodically analyze the statistics." + e);
- }
- }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsRepository.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsRepository.java
index cd3cc67f3c9..d20bb358c18 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsRepository.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/StatisticsRepository.java
@@ -18,6 +18,7 @@
package org.apache.doris.statistics;
import org.apache.doris.analysis.AlterColumnStatsStmt;
+import org.apache.doris.analysis.AlterTableStatsStmt;
import org.apache.doris.analysis.TableName;
import org.apache.doris.catalog.Column;
import org.apache.doris.catalog.Env;
@@ -27,15 +28,16 @@ import org.apache.doris.common.Config;
import org.apache.doris.common.DdlException;
import org.apache.doris.common.FeConstants;
import org.apache.doris.statistics.util.DBObjects;
+import org.apache.doris.statistics.util.InternalQueryResult.ResultRow;
import org.apache.doris.statistics.util.StatisticsUtil;
import org.apache.doris.system.SystemInfoService;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import org.apache.commons.text.StringSubstitutor;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
-import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -93,18 +95,38 @@ public class StatisticsRepository {
+ " ORDER BY update_time "
+ "LIMIT ${limit} OFFSET ${offset}";
- private static final String FETCH_STATS_PART_ID = "SELECT * FROM "
+ private static final String FETCH_STATS_PART_ID = "SELECT col_id, part_id FROM "
+ FeConstants.INTERNAL_DB_NAME + "." + StatisticConstants.STATISTIC_TBL_NAME
+ " WHERE tbl_id = ${tblId}"
+ " AND part_id IS NOT NULL";
+ private static final String PERSIST_TABLE_STATS_TEMPLATE = "INSERT INTO "
+ + FeConstants.INTERNAL_DB_NAME + "." + StatisticConstants.ANALYSIS_TBL_NAME
+ + " VALUES('${id}', ${catalogId}, ${dbId}, ${tblId}, ${indexId}, ${partId}, ${rowCount},"
+ + " ${lastAnalyzeTimeInMs}, NOW())";
+
+ private static final String FETCH_TABLE_LEVEL_STATS_TEMPLATE = "SELECT * FROM "
+ + FeConstants.INTERNAL_DB_NAME + "." + StatisticConstants.ANALYSIS_TBL_NAME
+ + " WHERE tbl_id = ${tblId}"
+ + " AND part_id IS NULL";
+
+ private static final String FETCH_TABLE_LEVEL_PART_STATS_TEMPLATE = "SELECT * FROM "
+ + FeConstants.INTERNAL_DB_NAME + "." + StatisticConstants.ANALYSIS_TBL_NAME
+ + " WHERE part_id = ${partId}";
+
+
+ private static final String FETCH_PART_TABLE_STATS_TEMPLATE = "SELECT * FROM "
+ + FeConstants.INTERNAL_DB_NAME + "." + StatisticConstants.ANALYSIS_TBL_NAME
+ + " WHERE tbl_id = ${tblId}"
+ + " AND part_id IS NOT NULL";
+
private static final String QUERY_COLUMN_STATISTICS = "SELECT * FROM " + FeConstants.INTERNAL_DB_NAME
+ "." + StatisticConstants.STATISTIC_TBL_NAME + " WHERE "
+ "tbl_id=${tblId} AND idx_id=${idxId} AND col_id='${colId}'";
private static final String QUERY_PARTITION_STATISTICS = "SELECT * FROM " + FeConstants.INTERNAL_DB_NAME
+ "." + StatisticConstants.STATISTIC_TBL_NAME + " WHERE "
- + " ${inPredicate}"
+ + " tbl_id=${tblId} AND idx_id=${idxId} AND col_id='${colId}' "
+ " AND part_id IS NOT NULL";
public static ColumnStatistic queryColumnStatisticsByName(long tableId, String colName) {
@@ -179,7 +201,8 @@ public class StatisticsRepository {
return stringJoiner.toString();
}
- public static void dropStatistics(Set<String> partIds) throws DdlException {
+ public static void dropStatistics(Set<Long> partIds) throws DdlException {
+ dropStatisticsByPartId(partIds, StatisticConstants.ANALYSIS_TBL_NAME);
dropStatisticsByPartId(partIds, StatisticConstants.STATISTIC_TBL_NAME);
}
@@ -188,6 +211,18 @@ public class StatisticsRepository {
dropStatisticsByColName(tblId, colNames, StatisticConstants.HISTOGRAM_TBL_NAME);
}
+ public static void dropExternalTableStatistics(long tblId) throws DdlException {
+ Map<String, String> params = new HashMap<>();
+ String inPredicate = String.format("tbl_id = %s", tblId);
+ params.put("tblName", StatisticConstants.ANALYSIS_TBL_NAME);
+ params.put("condition", inPredicate);
+ try {
+ StatisticsUtil.execUpdate(new StringSubstitutor(params).replace(DROP_TABLE_STATISTICS_TEMPLATE));
+ } catch (Exception e) {
+ throw new DdlException(e.getMessage(), e);
+ }
+ }
+
public static void dropStatisticsByColName(long tblId, Set<String> colNames, String statsTblName)
throws DdlException {
Map<String, String> params = new HashMap<>();
@@ -202,7 +237,7 @@ public class StatisticsRepository {
}
}
- public static void dropStatisticsByPartId(Set<String> partIds, String statsTblName) throws DdlException {
+ public static void dropStatisticsByPartId(Set<Long> partIds, String statsTblName) throws DdlException {
Map<String, String> params = new HashMap<>();
String right = StatisticsUtil.joinElementsToString(partIds, ",");
String inPredicate = String.format(" part_id IN (%s)", right);
@@ -215,6 +250,34 @@ public class StatisticsRepository {
}
}
+ public static void persistTableStats(Map<String, String> params) throws Exception {
+ StatisticsUtil.execUpdate(PERSIST_TABLE_STATS_TEMPLATE, params);
+ }
+
+ public static void alterTableStatistics(AlterTableStatsStmt alterTableStatsStmt) throws Exception {
+ TableName tableName = alterTableStatsStmt.getTableName();
+ DBObjects objects = StatisticsUtil.convertTableNameToObjects(tableName);
+ String rowCount = alterTableStatsStmt.getValue(StatsType.ROW_COUNT);
+ TableStatisticBuilder builder = new TableStatisticBuilder();
+ builder.setRowCount(Long.parseLong(rowCount));
+ builder.setLastAnalyzeTimeInMs(0);
+ TableStatistic tableStatistic = builder.build();
+ Map<String, String> params = new HashMap<>();
+ String id = StatisticsUtil.constructId(objects.table.getId(), -1);
+ params.put("id", id);
+ params.put("catalogId", String.valueOf(objects.catalog.getId()));
+ params.put("dbId", String.valueOf(objects.db.getId()));
+ params.put("tblId", String.valueOf(objects.table.getId()));
+ params.put("indexId", "-1");
+ params.put("partId", "NULL");
+ params.put("rowCount", String.valueOf(tableStatistic.rowCount));
+ params.put("lastAnalyzeTimeInMs", "0");
+ StatisticsUtil.execUpdate(PERSIST_TABLE_STATS_TEMPLATE, params);
+ // TODO update statistics cache
+ // Env.getCurrentEnv().getStatisticsCache()
+ // .updateColStatsCache(objects.table.getId(), -1, builder.build());
+ }
+
public static void alterColumnStatistics(AlterColumnStatsStmt alterColumnStatsStmt) throws Exception {
TableName tableName = alterColumnStatsStmt.getTableName();
List<Long> partitionIds = alterColumnStatsStmt.getPartitionIds();
@@ -296,24 +359,25 @@ public class StatisticsRepository {
return StatisticsUtil.execStatisticQuery(new StringSubstitutor(params).replace(FETCH_STATS_FULL_NAME));
}
- public static Map<String, Set<String>> fetchColAndPartsForStats(long tblId) {
+ public static Map<String, Set<Long>> fetchColAndPartsForStats(long tblId) {
Map<String, String> params = Maps.newHashMap();
params.put("tblId", String.valueOf(tblId));
StringSubstitutor stringSubstitutor = new StringSubstitutor(params);
String partSql = stringSubstitutor.replace(FETCH_STATS_PART_ID);
List<ResultRow> resultRows = StatisticsUtil.execStatisticQuery(partSql);
- Map<String, Set<String>> columnToPartitions = Maps.newHashMap();
+ Map<String, Set<Long>> columnToPartitions = Maps.newHashMap();
resultRows.forEach(row -> {
try {
- StatsId statsId = new StatsId(row);
- if (statsId.partId == null) {
+ String colId = row.getColumnValue("col_id");
+ String partId = row.getColumnValue("part_id");
+ if (partId == null) {
return;
}
- columnToPartitions.computeIfAbsent(String.valueOf(statsId.colId),
- k -> new HashSet<>()).add(statsId.partId);
- } catch (NumberFormatException e) {
+ columnToPartitions.computeIfAbsent(colId,
+ k -> new HashSet<>()).add(Long.valueOf(partId));
+ } catch (NumberFormatException | DdlException e) {
LOG.warn("Failed to obtain the column and partition for statistics.",
e);
}
@@ -322,6 +386,50 @@ public class StatisticsRepository {
return columnToPartitions;
}
+ public static TableStatistic fetchTableLevelStats(long tblId) throws DdlException {
+ ImmutableMap<String, String> params = ImmutableMap
+ .of("tblId", String.valueOf(tblId));
+ String sql = StatisticsUtil.replaceParams(FETCH_TABLE_LEVEL_STATS_TEMPLATE, params);
+ List<ResultRow> resultRows = StatisticsUtil.execStatisticQuery(sql);
+ if (resultRows.size() == 1) {
+ return TableStatistic.fromResultRow(resultRows.get(0));
+ }
+ throw new DdlException("Query result is not as expected: " + sql);
+ }
+
+ public static TableStatistic fetchTableLevelOfPartStats(long partId) throws DdlException {
+ ImmutableMap<String, String> params = ImmutableMap
+ .of("partId", String.valueOf(partId));
+ String sql = StatisticsUtil.replaceParams(FETCH_TABLE_LEVEL_PART_STATS_TEMPLATE, params);
+ List<ResultRow> resultRows = StatisticsUtil.execStatisticQuery(sql);
+ if (resultRows.size() == 1) {
+ return TableStatistic.fromResultRow(resultRows.get(0));
+ }
+ throw new DdlException("Query result is not as expected: " + sql);
+ }
+
+ public static Map<Long, TableStatistic> fetchTableLevelOfIdPartStats(long tblId) throws DdlException {
+ ImmutableMap<String, String> params = ImmutableMap
+ .of("tblId", String.valueOf(tblId));
+ StringSubstitutor stringSubstitutor = new StringSubstitutor(params);
+ String sql = stringSubstitutor.replace(FETCH_PART_TABLE_STATS_TEMPLATE);
+ List<ResultRow> resultRows = StatisticsUtil.execStatisticQuery(sql);
+
+ if (resultRows.size() == 0) {
+ return Collections.emptyMap();
+ }
+
+ Map<Long, TableStatistic> idToPartitionTableStats = Maps.newHashMap();
+
+ for (ResultRow resultRow : resultRows) {
+ long partId = Long.parseLong(resultRow.getColumnValue("part_id"));
+ TableStatistic partStats = TableStatistic.fromResultRow(resultRow);
+ idToPartitionTableStats.put(partId, partStats);
+ }
+
+ return idToPartitionTableStats;
+ }
+
public static List<ResultRow> loadColStats(long tableId, long idxId, String colName) {
Map<String, String> params = new HashMap<>();
params.put("tblId", String.valueOf(tableId));
@@ -332,14 +440,12 @@ public class StatisticsRepository {
.replace(QUERY_COLUMN_STATISTICS));
}
- public static List<ResultRow> loadPartStats(Collection<StatisticsCacheKey> keys) {
- String inPredicate = "CONCAT(tbl_id, '-', idx_id, '-', col_id) in (%s)";
- StringJoiner sj = new StringJoiner(",");
- for (StatisticsCacheKey statisticsCacheKey : keys) {
- sj.add("'" + statisticsCacheKey.toString() + "'");
- }
+ public static List<ResultRow> loadPartStats(long tableId, long idxId, String colName) {
Map<String, String> params = new HashMap<>();
- params.put("inPredicate", String.format(inPredicate, sj.toString()));
+ params.put("tblId", String.valueOf(tableId));
+ params.put("idxId", String.valueOf(idxId));
+ params.put("colId", colName);
+
return StatisticsUtil.execStatisticQuery(new StringSubstitutor(params)
.replace(QUERY_PARTITION_STATISTICS));
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/StatsDeriveResult.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/StatsDeriveResult.java
index 8c301f911be..6010daa6db4 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/StatsDeriveResult.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/StatsDeriveResult.java
@@ -144,6 +144,13 @@ public class StatsDeriveResult {
return statsDeriveResult;
}
+ public StatsDeriveResult merge(StatsDeriveResult other) {
+ for (Entry<Id, ColumnStatistic> entry : other.getSlotIdToColumnStats().entrySet()) {
+ this.slotIdToColumnStats.put(entry.getKey(), entry.getValue().copy());
+ }
+ return this;
+ }
+
public StatsDeriveResult copy() {
return new StatsDeriveResult(this);
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/StatsId.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/StatsId.java
deleted file mode 100644
index 3f9b2641b75..00000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/StatsId.java
+++ /dev/null
@@ -1,58 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements. See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership. The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License. You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied. See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.statistics;
-
-import org.apache.doris.statistics.util.StatisticsUtil;
-
-import java.util.StringJoiner;
-
-public class StatsId {
-
- public final String id;
- public final long catalogId;
- public final long dbId;
- public final long tblId;
- public final long idxId;
-
- public final String colId;
-
- // nullable
- public final String partId;
-
- public StatsId(ResultRow row) {
- this.id = row.get(0);
- this.catalogId = Long.parseLong(row.get(1));
- this.dbId = Long.parseLong(row.get(2));
- this.tblId = Long.parseLong(row.get(3));
- this.idxId = Long.parseLong(row.get(4));
- this.colId = row.get(5);
- this.partId = row.get(6);
- }
-
- public String toSQL() {
- StringJoiner sj = new StringJoiner(",");
- sj.add(StatisticsUtil.quote(id));
- sj.add(String.valueOf(catalogId));
- sj.add(String.valueOf(dbId));
- sj.add(String.valueOf(tblId));
- sj.add(String.valueOf(idxId));
- sj.add(StatisticsUtil.quote(colId));
- sj.add(StatisticsUtil.quote(partId));
- return sj.toString();
- }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/TableStatistic.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/TableStatistic.java
new file mode 100644
index 00000000000..28d0c17b561
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/TableStatistic.java
@@ -0,0 +1,61 @@
+// 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.doris.statistics;
+
+import org.apache.doris.common.DdlException;
+import org.apache.doris.statistics.util.InternalQueryResult.ResultRow;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+public class TableStatistic {
+
+ private static final Logger LOG = LogManager.getLogger(TableStatistic.class);
+
+ public static TableStatistic UNKNOWN = new TableStatisticBuilder()
+ .setRowCount(0).setUpdateTime("NULL").setLastAnalyzeTimeInMs(0L)
+ .build();
+
+ public final long rowCount;
+ public final long lastAnalyzeTimeInMs;
+ public final String updateTime;
+
+ public TableStatistic(long rowCount, long lastAnalyzeTimeInMs, String updateTime) {
+ this.rowCount = rowCount;
+ this.lastAnalyzeTimeInMs = lastAnalyzeTimeInMs;
+ this.updateTime = updateTime;
+ }
+
+ // TODO: use thrift
+ public static TableStatistic fromResultRow(ResultRow resultRow) {
+ try {
+ TableStatisticBuilder tableStatisticBuilder = new TableStatisticBuilder();
+ long rowCount = Long.parseLong(resultRow.getColumnValue("count"));
+ String updateTime = resultRow.getColumnValue("update_time");
+ long lastAnalyzeTimeInMs = Long
+ .parseLong(resultRow.getColumnValue("last_analyze_time_in_ms"));
+ tableStatisticBuilder.setRowCount(rowCount);
+ tableStatisticBuilder.setLastAnalyzeTimeInMs(lastAnalyzeTimeInMs);
+ tableStatisticBuilder.setUpdateTime(updateTime);
+ return tableStatisticBuilder.build();
+ } catch (DdlException e) {
+ LOG.warn("Failed to deserialize table statistics", e);
+ return TableStatistic.UNKNOWN;
+ }
+ }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/TableStatisticBuilder.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/TableStatisticBuilder.java
new file mode 100644
index 00000000000..ddb45b824cb
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/TableStatisticBuilder.java
@@ -0,0 +1,51 @@
+// 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.doris.statistics;
+
+public class TableStatisticBuilder {
+ public long rowCount;
+ public long lastAnalyzeTimeInMs;
+ public String updateTime;
+
+ public TableStatisticBuilder() {
+ }
+
+ public TableStatisticBuilder(TableStatistic tableStatistic) {
+ this.rowCount = tableStatistic.rowCount;
+ this.updateTime = tableStatistic.updateTime;
+ }
+
+ public TableStatisticBuilder setRowCount(long rowCount) {
+ this.rowCount = rowCount;
+ return this;
+ }
+
+ public TableStatisticBuilder setLastAnalyzeTimeInMs(long lastAnalyzeTimeInMs) {
+ this.lastAnalyzeTimeInMs = lastAnalyzeTimeInMs;
+ return this;
+ }
+
+ public TableStatisticBuilder setUpdateTime(String updateTime) {
+ this.updateTime = updateTime;
+ return this;
+ }
+
+ public TableStatistic build() {
+ return new TableStatistic(rowCount, lastAnalyzeTimeInMs, updateTime);
+ }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/TableStatisticsCacheLoader.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/TableStatisticsCacheLoader.java
new file mode 100644
index 00000000000..953bc9a4274
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/TableStatisticsCacheLoader.java
@@ -0,0 +1,60 @@
+// 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.doris.statistics;
+
+import org.apache.doris.catalog.Env;
+import org.apache.doris.catalog.TableIf;
+import org.apache.doris.common.DdlException;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Optional;
+
+public class TableStatisticsCacheLoader extends StatisticsCacheLoader<Optional<TableStatistic>> {
+
+ private static final Logger LOG = LogManager.getLogger(TableStatisticsCacheLoader.class);
+
+ @Override
+ protected Optional<TableStatistic> doLoad(StatisticsCacheKey key) {
+ try {
+ TableStatistic tableStatistic = StatisticsRepository.fetchTableLevelStats(key.tableId);
+ if (tableStatistic != TableStatistic.UNKNOWN) {
+ return Optional.of(tableStatistic);
+ }
+ } catch (DdlException e) {
+ LOG.debug("Fail to get table line number from table_statistics table. "
+ + "Will try to get from data source.", e);
+ }
+ // Get row count by call TableIf interface getRowCount
+ // when statistic table doesn't contain a record for this table.
+ try {
+ TableIf table = Env.getCurrentEnv().getCatalogMgr().getCatalog(key.catalogId)
+ .getDbOrDdlException(key.dbId).getTableOrAnalysisException(key.tableId);
+ long rowCount = table.getRowCount();
+ long lastAnalyzeTimeInMs = System.currentTimeMillis();
+ String updateTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(lastAnalyzeTimeInMs));
+ return Optional.of(new TableStatistic(rowCount, lastAnalyzeTimeInMs, updateTime));
+ } catch (Exception e) {
+ LOG.warn(String.format("Fail to get row count for table %d", key.tableId), e);
+ }
+ return Optional.empty();
+ }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/TableStatsMeta.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/TableStatsMeta.java
deleted file mode 100644
index 17ca61e9da5..00000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/TableStatsMeta.java
+++ /dev/null
@@ -1,137 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements. See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership. The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License. You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied. See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.statistics;
-
-import org.apache.doris.common.io.Text;
-import org.apache.doris.common.io.Writable;
-import org.apache.doris.persist.gson.GsonUtils;
-import org.apache.doris.statistics.AnalysisInfo.JobType;
-
-import com.google.gson.annotations.SerializedName;
-
-import java.io.DataInput;
-import java.io.DataOutput;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.stream.Collectors;
-
-public class TableStatsMeta implements Writable {
-
- @SerializedName("tblId")
- public final long tblId;
-
- @SerializedName("idxId")
- public final long idxId;
- @SerializedName("updatedRows")
- public final AtomicLong updatedRows = new AtomicLong();
-
- // We would like to analyze tables which queried frequently with higher priority in the future.
- @SerializedName("queriedTimes")
- public final AtomicLong queriedTimes = new AtomicLong();
-
- // Used for external table.
- @SerializedName("rowCount")
- public final long rowCount;
-
- @SerializedName("updateTime")
- public long updatedTime;
-
- @SerializedName("colNameToColStatsMeta")
- private ConcurrentMap<String, ColStatsMeta> colNameToColStatsMeta = new ConcurrentHashMap<>();
-
- @SerializedName("trigger")
- public JobType jobType;
-
- // It's necessary to store these fields separately from AnalysisInfo, since the lifecycle between AnalysisInfo
- // and TableStats is quite different.
- public TableStatsMeta(long tblId, long rowCount, AnalysisInfo analyzedJob) {
- this.tblId = tblId;
- this.idxId = -1;
- this.rowCount = rowCount;
- updateByJob(analyzedJob);
- }
-
- @Override
- public void write(DataOutput out) throws IOException {
- String json = GsonUtils.GSON.toJson(this);
- Text.writeString(out, json);
- }
-
- public static TableStatsMeta read(DataInput dataInput) throws IOException {
- String json = Text.readString(dataInput);
- TableStatsMeta tableStats = GsonUtils.GSON.fromJson(json, TableStatsMeta.class);
- // Might be null counterintuitively, for compatible
- if (tableStats.colNameToColStatsMeta == null) {
- tableStats.colNameToColStatsMeta = new ConcurrentHashMap<>();
- }
- return tableStats;
- }
-
- public long findColumnLastUpdateTime(String colName) {
- ColStatsMeta colStatsMeta = colNameToColStatsMeta.get(colName);
- if (colStatsMeta == null) {
- return 0;
- }
- return colStatsMeta.updatedTime;
- }
-
- public ColStatsMeta findColumnStatsMeta(String colName) {
- return colNameToColStatsMeta.get(colName);
- }
-
- public void removeColumn(String colName) {
- colNameToColStatsMeta.remove(colName);
- }
-
- public Set<String> analyzeColumns() {
- return colNameToColStatsMeta.keySet();
- }
-
- public void reset() {
- updatedTime = 0;
- colNameToColStatsMeta.values().forEach(ColStatsMeta::clear);
- }
-
- public void updateByJob(AnalysisInfo analyzedJob) {
- updatedTime = System.currentTimeMillis();
- String colNameStr = analyzedJob.colName;
- // colName field AnalyzeJob's format likes: "[col1, col2]", we need to remove brackets here
- // TODO: Refactor this later
- if (analyzedJob.colName.startsWith("[") && analyzedJob.colName.endsWith("]")) {
- colNameStr = colNameStr.substring(1, colNameStr.length() - 1);
- }
- List<String> cols = Arrays.stream(colNameStr.split(",")).map(String::trim).collect(Collectors.toList());
- for (String col : cols) {
- ColStatsMeta colStatsMeta = colNameToColStatsMeta.get(col);
- if (colStatsMeta == null) {
- colNameToColStatsMeta.put(col, new ColStatsMeta(updatedTime,
- analyzedJob.analysisMethod, analyzedJob.analysisType, analyzedJob.jobType, 0));
- } else {
- colStatsMeta.updatedTime = updatedTime;
- colStatsMeta.analysisType = analyzedJob.analysisType;
- colStatsMeta.analysisMethod = analyzedJob.analysisMethod;
- }
- }
- jobType = analyzedJob.jobType;
- }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/TaskStatusWrapper.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/TaskStatusWrapper.java
deleted file mode 100644
index d74b14267d1..00000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/TaskStatusWrapper.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements. See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership. The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License. You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied. See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.statistics;
-
-public class TaskStatusWrapper {
-
- public final AnalysisInfo info;
- public final AnalysisState taskState;
- public final String message;
- public final long time;
-
- public TaskStatusWrapper(AnalysisInfo info, AnalysisState taskState, String message, long time) {
- this.info = info;
- this.taskState = taskState;
- this.message = message;
- this.time = time;
- }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/util/InternalQuery.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/util/InternalQuery.java
index 40669b6a939..09af38d830a 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/util/InternalQuery.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/util/InternalQuery.java
@@ -24,6 +24,7 @@ import org.apache.doris.analysis.SqlScanner;
import org.apache.doris.analysis.StatementBase;
import org.apache.doris.analysis.UserIdentity;
import org.apache.doris.catalog.Env;
+import org.apache.doris.catalog.PrimitiveType;
import org.apache.doris.cluster.ClusterNamespace;
import org.apache.doris.common.Config;
import org.apache.doris.common.DdlException;
@@ -37,7 +38,7 @@ import org.apache.doris.qe.Coordinator;
import org.apache.doris.qe.OriginStatement;
import org.apache.doris.qe.QeProcessorImpl;
import org.apache.doris.qe.RowBatch;
-import org.apache.doris.statistics.ResultRow;
+import org.apache.doris.statistics.util.InternalQueryResult.ResultRow;
import org.apache.doris.system.SystemInfoService;
import org.apache.doris.thrift.TQueryOptions;
import org.apache.doris.thrift.TResultBatch;
@@ -49,9 +50,9 @@ import org.apache.logging.log4j.Logger;
import java.io.StringReader;
import java.nio.ByteBuffer;
-import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
+import java.util.stream.Collectors;
/**
* Execute SQL query statements internally(in FE). Internal-query mainly used for statistics module,
@@ -86,7 +87,7 @@ public class InternalQuery {
* @return Result of the query statement
* @throws Exception Errors in parsing or execution
*/
- public List<ResultRow> query() throws Exception {
+ public InternalQueryResult query() throws Exception {
// step1: mock connectContext
buildContext();
@@ -179,9 +180,14 @@ public class InternalQuery {
}
}
- private List<ResultRow> fetchResult() {
+ private InternalQueryResult fetchResult() {
List<String> columns = stmt.getColLabels();
- List<ResultRow> resultRows = new ArrayList<>();
+ List<PrimitiveType> types = stmt.getResultExprs().stream()
+ .map(e -> e.getType().getPrimitiveType())
+ .collect(Collectors.toList());
+
+ InternalQueryResult result = new InternalQueryResult();
+ List<ResultRow> resultRows = result.getResultRows();
for (TResultBatch batch : resultBatches) {
List<ByteBuffer> rows = batch.getRows();
@@ -194,11 +200,12 @@ public class InternalQuery {
values.add(value);
}
- ResultRow resultRow = new ResultRow(values);
+ ResultRow resultRow = new ResultRow(columns, types, values);
resultRows.add(resultRow);
}
}
- return resultRows;
+
+ return result;
}
public void cancel() {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/util/InternalQueryResult.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/util/InternalQueryResult.java
new file mode 100644
index 00000000000..e7919860107
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/util/InternalQueryResult.java
@@ -0,0 +1,242 @@
+// 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.doris.statistics.util;
+
+import org.apache.doris.catalog.PrimitiveType;
+import org.apache.doris.common.DdlException;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Readable results of internal SQL execution,
+ * providing some read operations.
+ */
+public class InternalQueryResult {
+ private final List<ResultRow> resultRows = Lists.newArrayList();
+
+ public InternalQueryResult() {
+ }
+
+ public List<ResultRow> getResultRows() {
+ return resultRows;
+ }
+
+ public static class ResultRow {
+ private final List<String> columns;
+ private final List<PrimitiveType> types;
+ private final List<String> values;
+
+ private final Map<String, Integer> columnNameMap = Maps.newHashMap();
+ private final Map<Integer, String> columnIndexMap = Maps.newHashMap();
+
+ public ResultRow(List<String> columns, List<PrimitiveType> types, List<String> values) {
+ this.columns = columns;
+ this.types = types;
+ this.values = values;
+ buildColumnNameMap();
+ buildColumnIndexMap();
+ }
+
+ public List<String> getColumns() {
+ return columns != null ? columns : Collections.emptyList();
+ }
+
+ public List<PrimitiveType> getTypes() {
+ return types != null ? types : Collections.emptyList();
+ }
+
+ public List<String> getValues() {
+ return values != null ? values : Collections.emptyList();
+ }
+
+ private void buildColumnNameMap() {
+ List<String> columns = getColumns();
+ for (int i = 0; i < columns.size(); i++) {
+ columnNameMap.put(columns.get(i), i);
+ }
+ }
+
+ private void buildColumnIndexMap() {
+ List<String> columns = getColumns();
+ for (int i = 0; i < columns.size(); i++) {
+ columnIndexMap.put(i, columns.get(i));
+ }
+ }
+
+ public int getColumnIndex(String columnName) {
+ return columnNameMap.getOrDefault(columnName, -1);
+ }
+
+ public String getColumnName(int index) throws DdlException {
+ List<String> columns = getColumns();
+ if (columnIndexMap.containsKey(index)) {
+ return columnIndexMap.get(index);
+ } else {
+ throw new DdlException("Index should be between 0 and " + columns.size());
+ }
+ }
+
+ public PrimitiveType getColumnType(String columnName) throws DdlException {
+ List<PrimitiveType> types = getTypes();
+ int index = getColumnIndex(columnName);
+ if (index == -1) {
+ throw new DdlException(String.format("The column name:[%s] does not exist.", columnName));
+ }
+ return types.get(index);
+ }
+
+ public PrimitiveType getColumnType(int index) throws DdlException {
+ List<PrimitiveType> types = getTypes();
+ if (index >= 0 && index < types.size()) {
+ return types.get(index);
+ } else {
+ throw new DdlException("Index should be between 0 and " + types.size());
+ }
+ }
+
+ public String getColumnValue(String columnName) throws DdlException {
+ int index = getColumnIndex(columnName);
+ if (index == -1) {
+ throw new DdlException(String.format("The column name:[%s] does not exist.", columnName));
+ }
+ return values.get(index);
+ }
+
+ public String getColumnValueWithDefault(String columnName, String defaultVal) throws DdlException {
+ String val = getColumnValue(columnName);
+ return val == null ? defaultVal : val;
+ }
+
+ public Object getColumnValue(int index) throws DdlException {
+ List<String> columns = getColumns();
+ if (index >= 0 && index < columns.size()) {
+ return values.get(index);
+ } else {
+ throw new DdlException("Index should be between 0 and " + columns.size());
+ }
+ }
+
+ public String getString(int index) throws DdlException {
+ List<String> columns = getColumns();
+ if (index >= 0 && index < columns.size()) {
+ return values.get(index);
+ }
+ throw new DdlException("Index should be between 0 and " + columns.size());
+ }
+
+ public int getInt(int index) throws DdlException {
+ List<PrimitiveType> types = getTypes();
+ if (index >= 0 && index < types.size()) {
+ String value = values.get(index);
+ PrimitiveType type = types.get(index);
+ switch (type) {
+ case BOOLEAN:
+ case TINYINT:
+ case SMALLINT:
+ case INT:
+ case BIGINT:
+ return new Integer(value);
+ default:
+ throw new DdlException("Unable to convert field to int: " + value);
+ }
+ }
+ throw new DdlException("Index should be between 0 and " + types.size());
+ }
+
+ public long getLong(int index) throws DdlException {
+ List<PrimitiveType> types = getTypes();
+ if (index >= 0 && index < types.size()) {
+ String value = values.get(index);
+ PrimitiveType type = types.get(index);
+ switch (type) {
+ case TINYINT:
+ case SMALLINT:
+ case INT:
+ case BIGINT:
+ return Long.parseLong(value);
+ default:
+ throw new DdlException("Unable to convert field to long: " + value);
+ }
+ }
+ throw new DdlException("Index should be between 0 and " + types.size());
+ }
+
+ public float getFloat(int index) throws DdlException {
+ List<PrimitiveType> types = getTypes();
+ if (index >= 0 && index < types.size()) {
+ String value = values.get(index);
+ PrimitiveType type = types.get(index);
+ if (type == PrimitiveType.FLOAT) {
+ return Float.parseFloat(value);
+ }
+ throw new DdlException("Unable to convert field to float: " + value);
+ }
+ throw new DdlException("Index should be between 0 and " + types.size());
+ }
+
+ public double getDouble(int index) throws DdlException {
+ List<PrimitiveType> types = getTypes();
+ if (index >= 0 && index < types.size()) {
+ String value = values.get(index);
+ PrimitiveType type = types.get(index);
+ if (type == PrimitiveType.DOUBLE) {
+ return Double.parseDouble(value);
+ }
+ throw new DdlException("Unable to convert field to long: " + value);
+ }
+ throw new DdlException("Index should be between 0 and " + types.size());
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("ResultRow{ ");
+ if (values != null && values.size() > 0) {
+ List<String> columns = getColumns();
+ for (int i = 0; i < values.size(); i++) {
+ sb.append(columns.get(i));
+ sb.append(":");
+ sb.append(values.get(i));
+ sb.append(" ");
+ }
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+ }
+
+ @Override
+ public String toString() {
+ if (resultRows.size() > 0) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("InternalQueryResult:\n");
+ for (ResultRow resultRow : resultRows) {
+ sb.append(" - ");
+ sb.append(resultRow.toString());
+ sb.append("\n");
+ }
+ return sb.toString();
+ }
+ return "InternalQueryResult{" + "resultRows=" + resultRows + '}';
+ }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/util/SimpleQueue.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/util/SimpleQueue.java
deleted file mode 100644
index 5740c4e3088..00000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/util/SimpleQueue.java
+++ /dev/null
@@ -1,65 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements. See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership. The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License. You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied. See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.statistics.util;
-
-import java.util.Collection;
-import java.util.LinkedList;
-import java.util.function.Function;
-
-// Any operation on this structure should be thread-safe
-public class SimpleQueue<T> extends LinkedList<T> {
-
- private final long limit;
-
- private final Function<T, Void> offerFunc;
-
- private final Function<T, Void> evictFunc;
-
-
- public SimpleQueue(long limit, Function<T, Void> offerFunc, Function<T, Void> evictFunc) {
- this.limit = limit;
- this.offerFunc = offerFunc;
- this.evictFunc = evictFunc;
- }
-
- @Override
- public synchronized boolean offer(T analysisInfo) {
- while (size() >= limit) {
- remove();
- }
- super.offer(analysisInfo);
- offerFunc.apply(analysisInfo);
- return true;
- }
-
- @Override
- public synchronized T remove() {
- T analysisInfo = super.remove();
- evictFunc.apply(analysisInfo);
- return analysisInfo;
- }
-
- public SimpleQueue(long limit, Function<T, Void> offerFunc, Function<T, Void> evictFunc, Collection<T> collection) {
- this(limit, offerFunc, evictFunc);
- if (collection != null) {
- for (T e : collection) {
- offer(e);
- }
- }
- }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/util/StatisticsUtil.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/util/StatisticsUtil.java
index 40ae13a0e0e..89a5ae1f3e6 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/statistics/util/StatisticsUtil.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/util/StatisticsUtil.java
@@ -25,12 +25,10 @@ import org.apache.doris.analysis.FloatLiteral;
import org.apache.doris.analysis.IntLiteral;
import org.apache.doris.analysis.LargeIntLiteral;
import org.apache.doris.analysis.LiteralExpr;
-import org.apache.doris.analysis.SetType;
import org.apache.doris.analysis.StatementBase;
import org.apache.doris.analysis.StringLiteral;
import org.apache.doris.analysis.TableName;
import org.apache.doris.analysis.UserIdentity;
-import org.apache.doris.analysis.VariableExpr;
import org.apache.doris.catalog.ArrayType;
import org.apache.doris.catalog.Column;
import org.apache.doris.catalog.DatabaseIf;
@@ -46,12 +44,10 @@ import org.apache.doris.catalog.StructType;
import org.apache.doris.catalog.TableIf;
import org.apache.doris.catalog.Type;
import org.apache.doris.catalog.VariantType;
-import org.apache.doris.catalog.external.ExternalTable;
import org.apache.doris.catalog.external.HMSExternalTable;
import org.apache.doris.common.AnalysisException;
import org.apache.doris.common.Config;
import org.apache.doris.common.FeConstants;
-import org.apache.doris.common.Pair;
import org.apache.doris.common.UserException;
import org.apache.doris.datasource.CatalogIf;
import org.apache.doris.datasource.HMSExternalCatalog;
@@ -65,13 +61,12 @@ import org.apache.doris.qe.ConnectContext;
import org.apache.doris.qe.QueryState;
import org.apache.doris.qe.SessionVariable;
import org.apache.doris.qe.StmtExecutor;
-import org.apache.doris.qe.VariableMgr;
+import org.apache.doris.statistics.AnalysisInfo;
import org.apache.doris.statistics.ColumnStatistic;
import org.apache.doris.statistics.ColumnStatisticBuilder;
import org.apache.doris.statistics.Histogram;
-import org.apache.doris.statistics.ResultRow;
import org.apache.doris.statistics.StatisticConstants;
-import org.apache.doris.system.Frontend;
+import org.apache.doris.statistics.util.InternalQueryResult.ResultRow;
import org.apache.doris.system.SystemInfoService;
import com.google.common.base.Preconditions;
@@ -87,12 +82,9 @@ import org.apache.iceberg.TableScan;
import org.apache.iceberg.types.Types;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import org.apache.thrift.TException;
-import java.net.InetSocketAddress;
import java.text.SimpleDateFormat;
-import java.time.LocalTime;
-import java.time.format.DateTimeFormatter;
-import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -102,7 +94,6 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
-import java.util.Set;
import java.util.StringJoiner;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -111,6 +102,7 @@ public class StatisticsUtil {
private static final Logger LOG = LogManager.getLogger(StatisticsUtil.class);
private static final String ID_DELIMITER = "-";
+ private static final String VALUES_DELIMITER = ",";
private static final String TOTAL_SIZE = "totalSize";
private static final String NUM_ROWS = "numRows";
@@ -150,6 +142,16 @@ public class StatisticsUtil {
}
}
+ public static List<AnalysisInfo> deserializeToAnalysisJob(List<ResultRow> resultBatches)
+ throws TException {
+ if (CollectionUtils.isEmpty(resultBatches)) {
+ return Collections.emptyList();
+ }
+ return resultBatches.stream()
+ .map(AnalysisInfo::fromResultRow)
+ .collect(Collectors.toList());
+ }
+
public static ColumnStatistic deserializeToColumnStatistics(List<ResultRow> resultBatches)
throws Exception {
if (CollectionUtils.isEmpty(resultBatches)) {
@@ -164,22 +166,15 @@ public class StatisticsUtil {
}
public static AutoCloseConnectContext buildConnectContext() {
- return buildConnectContext(false);
- }
-
- public static AutoCloseConnectContext buildConnectContext(boolean limitScan) {
ConnectContext connectContext = new ConnectContext();
SessionVariable sessionVariable = connectContext.getSessionVariable();
sessionVariable.internalSession = true;
sessionVariable.setMaxExecMemByte(Config.statistics_sql_mem_limit_in_bytes);
- sessionVariable.cpuResourceLimit = Config.cpu_resource_limit_per_analyze_task;
sessionVariable.setEnableInsertStrict(true);
- sessionVariable.enablePageCache = false;
sessionVariable.parallelExecInstanceNum = Config.statistics_sql_parallel_exec_instance_num;
sessionVariable.parallelPipelineTaskNum = Config.statistics_sql_parallel_exec_instance_num;
sessionVariable.setEnableNereidsPlanner(false);
sessionVariable.enableProfile = false;
- sessionVariable.enableScanRunSerial = limitScan;
sessionVariable.queryTimeoutS = Config.analyze_task_timeout_in_hours * 60 * 60;
sessionVariable.insertTimeoutS = Config.analyze_task_timeout_in_hours * 60 * 60;
sessionVariable.enableFileCache = false;
@@ -221,7 +216,7 @@ public class StatisticsUtil {
case DOUBLE:
return new FloatLiteral(columnValue);
case DECIMALV2:
- // no need to check precision and scale, since V2 is fixed point
+ //no need to check precision and scale, since V2 is fixed point
return new DecimalLiteral(columnValue);
case DECIMAL32:
case DECIMAL64:
@@ -397,12 +392,11 @@ public class StatisticsUtil {
.findTable(InternalCatalog.INTERNAL_CATALOG_NAME,
dbName,
StatisticConstants.STATISTIC_TBL_NAME));
- // uncomment it when hist is available for user.
- // statsTbls.add(
- // (OlapTable) StatisticsUtil
- // .findTable(InternalCatalog.INTERNAL_CATALOG_NAME,
- // dbName,
- // StatisticConstants.HISTOGRAM_TBL_NAME));
+ statsTbls.add(
+ (OlapTable) StatisticsUtil
+ .findTable(InternalCatalog.INTERNAL_CATALOG_NAME,
+ dbName,
+ StatisticConstants.HISTOGRAM_TBL_NAME));
} catch (Throwable t) {
return false;
}
@@ -436,15 +430,6 @@ public class StatisticsUtil {
));
}
- public static Set<String> getPartitionIds(TableIf table) {
- if (table instanceof OlapTable) {
- return ((OlapTable) table).getPartitionIds().stream().map(String::valueOf).collect(Collectors.toSet());
- } else if (table instanceof ExternalTable) {
- return table.getPartitionNames();
- }
- throw new RuntimeException(String.format("Not supported Table %s", table.getClass().getName()));
- }
-
public static <T> String joinElementsToString(Collection<T> values, String delimiter) {
StringJoiner builder = new StringJoiner(delimiter);
values.forEach(v -> builder.add(String.valueOf(v)));
@@ -490,9 +475,9 @@ public class StatisticsUtil {
* when update_rows < row_count, the health degree is 100 (1 - update_rows row_count).
*
* @param updatedRows The number of rows updated by the table
- * @param totalRows The current number of rows in the table
- * the healthier the statistics of the table
* @return Health, the value range is [0, 100], the larger the value,
+ * @param totalRows The current number of rows in the table
+ * the healthier the statistics of the table
*/
public static int getTableHealth(long totalRows, long updatedRows) {
if (updatedRows >= totalRows) {
@@ -506,25 +491,19 @@ public class StatisticsUtil {
/**
* Estimate hive table row count.
* First get it from remote table parameters. If not found, estimate it : totalSize/estimatedRowSize
- *
* @param table Hive HMSExternalTable to estimate row count.
- * @param isInit Flag to indicate if this is called during init. To avoid recursively get schema.
* @return estimated row count
*/
- public static long getHiveRowCount(HMSExternalTable table, boolean isInit) {
+ public static long getHiveRowCount(HMSExternalTable table) {
Map<String, String> parameters = table.getRemoteTable().getParameters();
if (parameters == null) {
return -1;
}
// Table parameters contains row count, simply get and return it.
if (parameters.containsKey(NUM_ROWS)) {
- long rows = Long.parseLong(parameters.get(NUM_ROWS));
- // Sometimes, the NUM_ROWS in hms is 0 but actually is not. Need to check TOTAL_SIZE if NUM_ROWS is 0.
- if (rows != 0) {
- return rows;
- }
+ return Long.parseLong(parameters.get(NUM_ROWS));
}
- if (!parameters.containsKey(TOTAL_SIZE) || isInit) {
+ if (!parameters.containsKey(TOTAL_SIZE)) {
return -1;
}
// Table parameters doesn't contain row count but contain total size. Estimate row count : totalSize/rowSize
@@ -542,7 +521,6 @@ public class StatisticsUtil {
/**
* Estimate iceberg table row count.
* Get the row count by adding all task file recordCount.
- *
* @param table Iceberg HMSExternalTable to estimate row count.
* @return estimated row count
*/
@@ -566,7 +544,6 @@ public class StatisticsUtil {
/**
* Estimate hive table row count : totalFileSize/estimatedRowSize
- *
* @param table Hive HMSExternalTable to estimate row count.
* @return estimated row count
*/
@@ -641,7 +618,6 @@ public class StatisticsUtil {
/**
* Get Iceberg column statistics.
- *
* @param colName
* @param table Iceberg table.
* @return Optional Column statistic for the given column.
@@ -650,8 +626,8 @@ public class StatisticsUtil {
TableScan tableScan = table.newScan().includeColumnStats();
ColumnStatisticBuilder columnStatisticBuilder = new ColumnStatisticBuilder();
columnStatisticBuilder.setCount(0);
- columnStatisticBuilder.setMaxValue(Double.POSITIVE_INFINITY);
- columnStatisticBuilder.setMinValue(Double.NEGATIVE_INFINITY);
+ columnStatisticBuilder.setMaxValue(Double.MAX_VALUE);
+ columnStatisticBuilder.setMinValue(Double.MIN_VALUE);
columnStatisticBuilder.setDataSize(0);
columnStatisticBuilder.setAvgSizeByte(0);
columnStatisticBuilder.setNumNulls(0);
@@ -666,7 +642,7 @@ public class StatisticsUtil {
}
private static void processDataFile(DataFile dataFile, PartitionSpec partitionSpec,
- String colName, ColumnStatisticBuilder columnStatisticBuilder) {
+ String colName, ColumnStatisticBuilder columnStatisticBuilder) {
int colId = -1;
for (Types.NestedField column : partitionSpec.schema().columns()) {
if (column.name().equals(colName)) {
@@ -702,87 +678,4 @@ public class StatisticsUtil {
// IGNORE
}
}
-
- public static String quote(String str) {
- return "'" + str + "'";
- }
-
- public static boolean isMaster(Frontend frontend) {
- InetSocketAddress socketAddress = new InetSocketAddress(frontend.getHost(), frontend.getEditLogPort());
- return Env.getCurrentEnv().getHaProtocol().getLeader().equals(socketAddress);
- }
-
- public static String escapeSQL(String str) {
- if (str == null) {
- return null;
- }
- return org.apache.commons.lang3.StringUtils.replace(str, "'", "''");
- }
-
- public static boolean isExternalTable(String catalogName, String dbName, String tblName) {
- TableIf table;
- try {
- table = StatisticsUtil.findTable(catalogName, dbName, tblName);
- } catch (Throwable e) {
- LOG.warn(e.getMessage());
- return false;
- }
- return table instanceof ExternalTable;
- }
-
- public static boolean inAnalyzeTime(LocalTime now) {
- try {
- Pair<LocalTime, LocalTime> range = findRangeFromGlobalSessionVar();
- if (range == null) {
- return false;
- }
- LocalTime start = range.first;
- LocalTime end = range.second;
- if (start.isAfter(end) && (now.isAfter(start) || now.isBefore(end))) {
- return true;
- } else {
- return now.isAfter(start) && now.isBefore(end);
- }
- } catch (DateTimeParseException e) {
- LOG.warn("Parse analyze start/end time format fail", e);
- return true;
- }
- }
-
- private static Pair<LocalTime, LocalTime> findRangeFromGlobalSessionVar() {
- try {
- String startTime =
- findRangeFromGlobalSessionVar(SessionVariable.FULL_AUTO_ANALYZE_START_TIME)
- .fullAutoAnalyzeStartTime;
- // For compatibility
- if (StringUtils.isEmpty(startTime)) {
- startTime = StatisticConstants.FULL_AUTO_ANALYZE_START_TIME;
- }
- String endTime = findRangeFromGlobalSessionVar(SessionVariable.FULL_AUTO_ANALYZE_END_TIME)
- .fullAutoAnalyzeEndTime;
- if (StringUtils.isEmpty(startTime)) {
- endTime = StatisticConstants.FULL_AUTO_ANALYZE_END_TIME;
- }
- DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");
- return Pair.of(LocalTime.parse(startTime, timeFormatter), LocalTime.parse(endTime, timeFormatter));
- } catch (Exception e) {
- return null;
- }
- }
-
- private static SessionVariable findRangeFromGlobalSessionVar(String varName) throws Exception {
- SessionVariable sessionVariable = VariableMgr.newSessionVariable();
- VariableExpr variableExpr = new VariableExpr(varName, SetType.GLOBAL);
- VariableMgr.getValue(sessionVariable, variableExpr);
- return sessionVariable;
- }
-
- public static boolean enableAutoAnalyze() {
- try {
- return findRangeFromGlobalSessionVar(SessionVariable.ENABLE_FULL_AUTO_ANALYZE).enableFullAutoAnalyze;
- } catch (Exception e) {
- LOG.warn("Fail to get value of enable auto analyze, return false by default", e);
- }
- return false;
- }
}
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/OtherJoinTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/OtherJoinTest.java
index a4062d2edff..feeb971b15e 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/OtherJoinTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/OtherJoinTest.java
@@ -20,7 +20,6 @@ package org.apache.doris.nereids.jobs.joinorder.hypergraph;
import org.apache.doris.nereids.CascadesContext;
import org.apache.doris.nereids.datasets.tpch.TPCHTestBase;
import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
import org.apache.doris.nereids.util.HyperGraphBuilder;
import org.apache.doris.nereids.util.MemoTestUtils;
import org.apache.doris.nereids.util.PlanChecker;
@@ -33,37 +32,23 @@ import java.util.Set;
public class OtherJoinTest extends TPCHTestBase {
@Test
- public void test() {
- for (int t = 3; t < 10; t++) {
- for (int e = t - 1; e <= (t * (t - 1)) / 2; e++) {
- for (int i = 0; i < 10; i++) {
- System.out.println(String.valueOf(t) + " " + e + ": " + i);
- randomTest(t, e);
- }
- }
- }
- }
-
- private void randomTest(int tableNum, int edgeNum) {
+ public void randomTest() {
HyperGraphBuilder hyperGraphBuilder = new HyperGraphBuilder();
Plan plan = hyperGraphBuilder
- .randomBuildPlanWith(tableNum, edgeNum);
- plan = new LogicalProject(plan.getOutput(), plan);
- Set<List<String>> res1 = hyperGraphBuilder.evaluate(plan);
+ .randomBuildPlanWith(10, 20);
+ Set<List<Integer>> res1 = hyperGraphBuilder.evaluate(plan);
CascadesContext cascadesContext = MemoTestUtils.createCascadesContext(connectContext, plan);
hyperGraphBuilder.initStats(cascadesContext);
Plan optimizedPlan = PlanChecker.from(cascadesContext)
- .dpHypOptimize()
- .getBestPlanTree();
+ .dpHypOptimize()
+ .getBestPlanTree();
- Set<List<String>> res2 = hyperGraphBuilder.evaluate(optimizedPlan);
+ Set<List<Integer>> res2 = hyperGraphBuilder.evaluate(optimizedPlan);
if (!res1.equals(res2)) {
- System.out.println(plan.treeString());
- System.out.println(optimizedPlan.treeString());
- cascadesContext = MemoTestUtils.createCascadesContext(connectContext, plan);
- PlanChecker.from(cascadesContext).dpHypOptimize().getBestPlanTree();
System.out.println(res1);
System.out.println(res2);
+ System.out.println(plan.treeString());
+ System.out.println(optimizedPlan.treeString());
}
Assertions.assertTrue(res1.equals(res2));
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/stats/FilterEstimationTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/stats/FilterEstimationTest.java
index 31affe06252..1fe5e5b0a0e 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/stats/FilterEstimationTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/stats/FilterEstimationTest.java
@@ -17,7 +17,6 @@
package org.apache.doris.nereids.stats;
-import org.apache.doris.analysis.IntLiteral;
import org.apache.doris.nereids.trees.expressions.And;
import org.apache.doris.nereids.trees.expressions.Cast;
import org.apache.doris.nereids.trees.expressions.EqualTo;
@@ -76,7 +75,7 @@ class FilterEstimationTest {
Statistics expected = filterEstimation.estimate(or, stat);
Assertions.assertTrue(
Precision.equals(expected.getRowCount(), 750,
- 0.01));
+ 0.01));
}
// a > 500 and b < 100
@@ -133,12 +132,12 @@ class FilterEstimationTest {
Map<Expression, ColumnStatistic> slotToColumnStat = new HashMap<>();
ColumnStatisticBuilder builder = new ColumnStatisticBuilder()
.setNdv(500)
- .setIsUnknown(false);
+ .setIsUnknown(true);
slotToColumnStat.put(a, builder.build());
Statistics stat = new Statistics(1000, slotToColumnStat);
FilterEstimation filterEstimation = new FilterEstimation();
Statistics expected = filterEstimation.estimate(notIn, stat);
- Assertions.assertTrue(Precision.equals(1000, expected.getRowCount(), 0.01));
+ Assertions.assertTrue(Precision.equals(666.666, expected.getRowCount(), 0.01));
}
/**
@@ -166,6 +165,7 @@ class FilterEstimationTest {
ColumnStatistic aStatsEst = result.findColumnStatistics(a);
Assertions.assertEquals(100, aStatsEst.minValue);
Assertions.assertEquals(200, aStatsEst.maxValue);
+ Assertions.assertEquals(1.0, aStatsEst.selectivity);
Assertions.assertEquals(10, aStatsEst.ndv);
}
@@ -198,7 +198,7 @@ class FilterEstimationTest {
Statistics stat = new Statistics(1000, slotToColumnStat);
FilterEstimation filterEstimation = new FilterEstimation();
Statistics expected = filterEstimation.estimate(or, stat);
- Assertions.assertEquals(51.9, expected.getRowCount(), 0.1);
+ Assertions.assertEquals(51, expected.getRowCount(), 0.1);
}
// a > 500 and b < 100 or a > c
@@ -418,9 +418,7 @@ class FilterEstimationTest {
.setAvgSizeByte(4)
.setNumNulls(0)
.setMinValue(1)
- .setMinExpr(new IntLiteral(1))
- .setMaxValue(10)
- .setMaxExpr(new IntLiteral(10));
+ .setMaxValue(10);
slotToColumnStat.put(a, builder.build());
Statistics stat = new Statistics(1000, slotToColumnStat);
FilterEstimation filterEstimation = new FilterEstimation();
@@ -469,19 +467,22 @@ class FilterEstimationTest {
.setAvgSizeByte(4)
.setNumNulls(0)
.setMinValue(1000)
- .setMaxValue(10000);
+ .setMaxValue(10000)
+ .setSelectivity(1.0);
ColumnStatisticBuilder builderB = new ColumnStatisticBuilder()
.setNdv(100)
.setAvgSizeByte(4)
.setNumNulls(0)
.setMinValue(0)
- .setMaxValue(500);
+ .setMaxValue(500)
+ .setSelectivity(1.0);
ColumnStatisticBuilder builderC = new ColumnStatisticBuilder()
.setNdv(100)
.setAvgSizeByte(4)
.setNumNulls(0)
.setMinValue(0)
- .setMaxValue(200);
+ .setMaxValue(200)
+ .setSelectivity(1.0);
slotToColumnStat.put(a, builderA.build());
slotToColumnStat.put(b, builderB.build());
slotToColumnStat.put(c, builderC.build());
@@ -503,7 +504,7 @@ class FilterEstimationTest {
* filter range has intersection with (c.min, c.max)
* rows = 100
* a primary key, a.ndv reduced by 1/4
- * b normal field, b.ndv=20
+ * b normal field, b.ndv=20 =>
* c.ndv = 10/40 * c.ndv
*/
@Test
@@ -523,19 +524,22 @@ class FilterEstimationTest {
.setAvgSizeByte(4)
.setNumNulls(0)
.setMinValue(0)
- .setMaxValue(100);
+ .setMaxValue(100)
+ .setSelectivity(1.0);
ColumnStatisticBuilder builderB = new ColumnStatisticBuilder()
.setNdv(20)
.setAvgSizeByte(4)
.setNumNulls(0)
.setMinValue(0)
- .setMaxValue(500);
+ .setMaxValue(500)
+ .setSelectivity(1.0);
ColumnStatisticBuilder builderC = new ColumnStatisticBuilder()
.setNdv(40)
.setAvgSizeByte(4)
.setNumNulls(0)
.setMinValue(0)
- .setMaxValue(40);
+ .setMaxValue(40)
+ .setSelectivity(1.0);
slotToColumnStat.put(a, builderA.build());
slotToColumnStat.put(b, builderB.build());
slotToColumnStat.put(c, builderC.build());
@@ -550,21 +554,25 @@ class FilterEstimationTest {
Assertions.assertEquals(100, statsA.maxValue);
ColumnStatistic statsB = estimated.findColumnStatistics(b);
- Assertions.assertEquals(20, statsB.ndv, 0.1);
+ Assertions.assertEquals(15.6, statsB.ndv, 0.1);
Assertions.assertEquals(0, statsB.minValue);
Assertions.assertEquals(500, statsB.maxValue);
+ Assertions.assertEquals(1.0, statsB.selectivity);
ColumnStatistic statsC = estimated.findColumnStatistics(c);
Assertions.assertEquals(10, statsC.ndv);
Assertions.assertEquals(10, statsC.minValue);
Assertions.assertEquals(20, statsC.maxValue);
+ Assertions.assertEquals(1.0, statsC.selectivity);
}
/**
* test filter estimation, c > 300, where 300 is out of c's range (0,200)
* after filter
+ * c.selectivity=a.selectivity=b.selectivity = 0
* c.ndv=a.ndv=b.ndv=0
+ * a.ndv = b.ndv = 0
*/
@Test
@@ -579,23 +587,23 @@ class FilterEstimationTest {
.setNdv(1000)
.setAvgSizeByte(4)
.setNumNulls(0)
- .setMinValue(1000)
- .setMaxValue(10000)
- .setCount(1000);
+ .setMinValue(10000)
+ .setMaxValue(1000)
+ .setSelectivity(1.0);
ColumnStatisticBuilder builderB = new ColumnStatisticBuilder()
.setNdv(100)
.setAvgSizeByte(4)
.setNumNulls(0)
.setMinValue(0)
.setMaxValue(500)
- .setCount(1000);
+ .setSelectivity(1.0);
ColumnStatisticBuilder builderC = new ColumnStatisticBuilder()
.setNdv(100)
.setAvgSizeByte(4)
.setNumNulls(0)
.setMinValue(0)
.setMaxValue(200)
- .setCount(1000);
+ .setSelectivity(1.0);
slotToColumnStat.put(a, builderA.build());
slotToColumnStat.put(b, builderB.build());
slotToColumnStat.put(c, builderC.build());
@@ -608,8 +616,8 @@ class FilterEstimationTest {
Assertions.assertEquals(0, statsB.ndv);
ColumnStatistic statsC = estimated.findColumnStatistics(c);
Assertions.assertEquals(0, statsC.ndv);
- Assertions.assertTrue(Double.isInfinite(statsC.minValue));
- Assertions.assertTrue(Double.isInfinite(statsC.maxValue));
+ Assertions.assertTrue(Double.isNaN(statsC.minValue));
+ Assertions.assertTrue(Double.isNaN(statsC.maxValue));
}
/**
@@ -652,19 +660,22 @@ class FilterEstimationTest {
.setAvgSizeByte(4)
.setNumNulls(0)
.setMinValue(0)
- .setMaxValue(100);
+ .setMaxValue(100)
+ .setSelectivity(1.0);
ColumnStatisticBuilder builderB = new ColumnStatisticBuilder()
.setNdv(20)
.setAvgSizeByte(4)
.setNumNulls(0)
.setMinValue(0)
- .setMaxValue(500);
+ .setMaxValue(500)
+ .setSelectivity(1.0);
ColumnStatisticBuilder builderC = new ColumnStatisticBuilder()
.setNdv(40)
.setAvgSizeByte(4)
.setNumNulls(0)
.setMinValue(0)
- .setMaxValue(40);
+ .setMaxValue(40)
+ .setSelectivity(1.0);
slotToColumnStat.put(a, builderA.build());
slotToColumnStat.put(b, builderB.build());
slotToColumnStat.put(c, builderC.build());
@@ -679,7 +690,7 @@ class FilterEstimationTest {
Assertions.assertEquals(5, statsA.ndv, 0.1);
Assertions.assertEquals(0, statsA.minValue);
Assertions.assertEquals(100, statsA.maxValue);
- Assertions.assertEquals(5, statsB.ndv, 0.1);
+ Assertions.assertEquals(4.5, statsB.ndv, 0.1);
Assertions.assertEquals(0, statsB.minValue);
Assertions.assertEquals(500, statsB.maxValue);
Assertions.assertEquals(2, statsC.ndv);
@@ -721,6 +732,7 @@ class FilterEstimationTest {
.setNumNulls(0)
.setMinValue(0)
.setMaxValue(100)
+ .setSelectivity(1.0)
.setCount(100);
ColumnStatisticBuilder builderB = new ColumnStatisticBuilder()
.setCount(100)
@@ -728,14 +740,16 @@ class FilterEstimationTest {
.setAvgSizeByte(4)
.setNumNulls(0)
.setMinValue(0)
- .setMaxValue(500);
+ .setMaxValue(500)
+ .setSelectivity(1.0);
ColumnStatisticBuilder builderC = new ColumnStatisticBuilder()
.setCount(100)
.setNdv(40)
.setAvgSizeByte(4)
.setNumNulls(0)
.setMinValue(0)
... 1103 lines suppressed ...
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org