Starrocks 怎么计算各个算子的统计信息

背景

写这个文章的原因是为了了解一下Starrocks对各个算子的代价是怎么计算的,以便在后续对Starrocks做优化的时候,能够起到辅助作用

本文基于 Starrocks 3.3.5

结论

StatisticsCalculator 通过获取底层数据源的统计信息而进行自上而下的数据信息的统计,这些统计信息的计算大部分也是估算获得的。

分析

直接到 StatisticsCalculator 类中(该类会被DeriveStatsTask调用),这个类采用的典型的Visitor模式,对于不同的算子,会进入到不同的方法中去:

这里我们分析三种算子:

复制代码
1. Scan olap
2. Filter
3. Projection

其他的算子可以看源码实现。

  1. Scan olap算子

    复制代码
    @Override
     public Void visitLogicalOlapScan(LogicalOlapScanOperator node, ExpressionContext context) {
         return computeOlapScanNode(node, context, node.getTable(), node.getSelectedPartitionId(),
                 node.getColRefToColumnMetaMap());
     }
    
     @Override
     public Void visitPhysicalOlapScan(PhysicalOlapScanOperator node, ExpressionContext context) {
         return computeOlapScanNode(node, context, node.getTable(), node.getSelectedPartitionId(),
                 node.getColRefToColumnMetaMap());
     }
    
     private Void computeOlapScanNode(Operator node, ExpressionContext context, Table table,
                                     Collection<Long> selectedPartitionIds,
                                     Map<ColumnRefOperator, Column> colRefToColumnMetaMap) {
        Preconditions.checkState(context.arity() == 0);
        // 1. get table row count
        long tableRowCount = StatisticsCalcUtils.getTableRowCount(table, node, optimizerContext);
        // 2. get required columns statistics
        Statistics.Builder builder = StatisticsCalcUtils.estimateScanColumns(table, colRefToColumnMetaMap, optimizerContext);
        if (tableRowCount <= 1) {
            builder.setTableRowCountMayInaccurate(true);
        }
        // 3. deal with column statistics for partition prune
        OlapTable olapTable = (OlapTable) table;
        adjustPartitionColsStatistic(selectedPartitionIds, olapTable, builder, colRefToColumnMetaMap);
        builder.setOutputRowCount(tableRowCount);
        if (isRewrittenMvGE(node, table, context)) {
            adjustNestedMvStatistics(context.getGroupExpression().getGroup(), (MaterializedView) olapTable, builder);
            if (node.getProjection() != null) {
                builder.setShadowColumns(node.getProjection().getOutputColumns());
            }
        }
        // 4. estimate cardinality
        context.setStatistics(builder.build());
    
        return visitOperator(node, context);
    }

这个也是统计信息的来源,其他算子的统计信息都是基于此类算子计算出来的。

  • StatisticsCalcUtils.getTableRowCount 首先计算行数
    该方法会首先获取到扫描的分区数,之后再从CachedStatisticStorage获取到对应分区的行数信息,从而累加,最小为1行
  • StatisticsCalcUtils.estimateScanColumns 获取到对应列的统计信息
    该方法也是从CachedStatisticStorage 获取ColumnStatisticsHistogramStatistics信息
  • 如果行数只有一行,那就标记为统计信息不准确
  • 调增分区列的统计信息
  • visitOperator 这里会对scan涉及到的谓词以及 limit projection 做进一步的统计分析
    对于不同的谓词,利用 BaseCalculatingVisitor / LargeOrCalculatingVisitor 进行统计,比如说 in / or / and等
    对于projection 利用 ExpressionStatisticVisitor类进行统计
  1. Filter算子

    复制代码
    @Override
    public Void visitLogicalFilter(LogicalFilterOperator node, ExpressionContext context) {
        return computeFilterNode(node, context);
    }
     @Override
     public Void visitPhysicalFilter(PhysicalFilterOperator node, ExpressionContext context) {
         return computeFilterNode(node, context);
     }
    private Void computeFilterNode(Operator node, ExpressionContext context) {
         Statistics inputStatistics = context.getChildStatistics(0);
    
         Statistics.Builder builder = Statistics.builder();
         builder.addColumnStatistics(inputStatistics.getColumnStatistics());
         builder.setOutputRowCount(inputStatistics.getOutputRowCount());
    
         context.setStatistics(builder.build());
         return visitOperator(node, context);
     }
  • 对于filter操作来说 ,也是沿用了子节点的统计信息,所以说filter这一层级统计信息和子节点是一致的
  1. Projection算子

    @Override
    public Void visitLogicalProject(LogicalProjectOperator node, ExpressionContext context) {
    return computeProjectNode(context, node.getColumnRefMap());
    }

    复制代码
     @Override
     public Void visitPhysicalProject(PhysicalProjectOperator node, ExpressionContext context) {
         return computeProjectNode(context, node.getColumnRefMap());
     }
    
     private Void computeProjectNode(ExpressionContext context, Map<ColumnRefOperator, ScalarOperator> columnRefMap) {
         Preconditions.checkState(context.arity() == 1);
    
         Statistics.Builder builder = Statistics.builder();
         Statistics inputStatistics = context.getChildStatistics(0);
         builder.setOutputRowCount(inputStatistics.getOutputRowCount());
    
         Statistics.Builder allBuilder = Statistics.builder();
         allBuilder.setOutputRowCount(inputStatistics.getOutputRowCount());
         allBuilder.addColumnStatistics(inputStatistics.getColumnStatistics());
    
         for (ColumnRefOperator requiredColumnRefOperator : columnRefMap.keySet()) {
             ScalarOperator mapOperator = columnRefMap.get(requiredColumnRefOperator);
             if (mapOperator instanceof SubfieldOperator && context.getOptExpression() != null) {
                 Operator child = context.getOptExpression().inputAt(0).getOp();
                 if (child instanceof LogicalScanOperator || child instanceof PhysicalScanOperator) {
                     addSubFiledStatistics(child, ImmutableMap.of(requiredColumnRefOperator,
                                     (SubfieldOperator) mapOperator), builder);
                     continue;
                 }
             }
             ColumnStatistic outputStatistic =
                     ExpressionStatisticCalculator.calculate(mapOperator, allBuilder.build());
             builder.addColumnStatistic(requiredColumnRefOperator, outputStatistic);
             allBuilder.addColumnStatistic(requiredColumnRefOperator, outputStatistic);
         }
    
         context.setStatistics(builder.build());
         return visitOperator(context.getOp(), context);
     }

对于Projection来说,也是根据不同的 Projection 算子来做区别对待的(也是用ExpressionStatisticVisitor类),如case when操作,函数操作等,这些对应的列的最大值和最小值都是不一样的

相关推荐
Georgeviewer1 小时前
商业落地评测|实体门店GEO优化性价比与服务体系深度复盘
大数据·人工智能
腻害兔3 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
GIR1233 小时前
官方出品 | 多通道土壤呼吸测量系统市场现状与十五五规划深度报告:行业分析+趋势预测全收录
大数据·人工智能·机器学习
南讯股份Nascent5 小时前
洽洽全域会员项目启动会圆满召开
大数据·人工智能
大郭鹏宇5 小时前
基于 LangGraph 构建智能分诊系统(一):项目概述与环境搭建
大数据·人工智能·microsoft·langchain
破碎的南瓜8 小时前
sqli--sql注入过关笔记(1,2,3,4,5,9)
数据库·笔记·sql
天天爱吃肉82188 小时前
【电源拆解:跟着问答逐一认识开关适配器PCB元器件】
大数据·人工智能·python·功能测试·嵌入式硬件·汽车
Elastic 中国社区官方博客9 小时前
将你的 Grafana Kubernetes 仪表板迁移到 Elastic Observability:相同的 PromQL,30 倍更快的查询
大数据·人工智能·elasticsearch·搜索引擎·容器·kubernetes·grafana
abcy0712139 小时前
storm核心实时机制
大数据·storm
灵机一物9 小时前
合伙制律所分红和提成律师个税怎么合规优化?
大数据·人工智能