StarRocks学习一把梭

2,188 阅读14分钟

0.简介

StarRocks是什么

StarRocks在计算引擎的定义中是MPP引擎,在数据库中的定义是OLAP引擎,具体而言是兼顾MOLAP的ROLAP引擎。
计算引擎

  • MPP引擎——可以理解为系统架构的一种服务器分法,一般用于大规模并行计算,通过将CPU、IO、内存资源分散到各个机器,以提升并行计算能力。单个查询请求可以充分利用所有执行节点的资源,所以单个查询的性能可以随着集群的水平扩展而不断提升。同时基于计算向数据靠拢的设计理念,每个task只被分配到持有数据切片的executor上(资源隔离:executor间不能交换数据)。MPP不需要将中间数据写入磁盘,因为一个单一的executor只处理一个task,因此你可以直接将数据流式流入下一个执行阶段(everything is in stream(内存)),所以快。例子:Presto/Trino,Doris/StarRocks
  • 批处理引擎——最典型的分布式计算引擎,例子:MapReduce,Spark
  • 流处理引擎——略
    MPP和Spark在同硬件集群下,Spark通常慢3-5倍,50机器的MPP集群能提供250机器的Spark集群的处理能力,但是Spark可以扩展到250节点以上,MPP引擎一般扩展到50机器以上后收益锐减。
    批处理引擎能提供的:共享存储和支持更细粒度的调度,这两种技术的结合使批处理相比MPP更具可扩展性,可支持上千个节点。
    MPP引擎一般容易遇到单点瓶颈的问题(Scatter-Gather框架),但是Doris/StarRocks额外支持Shuffle节点以增强任务的健壮性。

OLAP引擎:区别于OLTP在先系统,用于进行多维分析

  • MOLAP——预聚合、预处理的数据,丧失多维分析灵活性,例子:Kylin, Druid
  • ROLAP——即席计算(解析query,生成执行计划,扫描数据,执行算子),拼的是资源和算力的大小
  • HTAP——混合事务分析处理,既可以用做联机数据处理,也可以用做线上数据分析

组件

  • 采用MySQL通信协议,通过MySQL Client或mysql JDBC直连SR集群
  • 不依赖hadoop集群,不需要花费机器去搭建小hadoop集群

和Doris区别

StarRocks是Doris的商用版(Doris开源),两者在某个基础版本之后就分道扬镳,分成两个版本分支去迭代。同一个功能,可能两个分支的实现各有差异(比如bitmap索引,在Doris是推荐低基数列使用,在StarRocks却是只有高基数时才触发)。我听说StarRocks在性能优化和功能支持上做得更好。

用法

高并发即席查询做多维分析

能力

  • 即席查询快速返回结果——StarRocks慢查询的定义是5秒,大部分查询在默认优化下都可以在5秒内解决
  • 单表查询和处理——和ClickHouse媲美的查询效率
  • 多表关联查询——值得深究的对JOIN性能的支持,原文:“在 SSB 的标准测试集的对比中,StarRocks 的多表关联性能相较于单表查询并无明显下降”

1.数据组织方式

字段

维度列——也等于排序列/排序键,通常为查询时过滤条件频繁使用的维度列,可以加速查询,在主键模型和更新模型还用于满足唯一性约束。

指标列——对聚合模型来说,所有没有指定聚合方式的列为维度列,其余为指标列,维度列相同的行会被聚合成一条。指标列的值可以通过聚合函数 SUM、COUNT、MIN、MAX、REPLACE、HLL_UNION 和 BITMAP_UNION 等累加起来。

表模型

  • 明细模型(Duplicate Key Model)——默认建表模型,不对数据做处理,适合任意维度的ad-hoc查询
  • 聚合模型(Aggregate Key Model)——支持定义排序键和指标列,并为指标列指定聚合函数。key列相同的行,会聚合成一行
  • 更新模型(Unique Key Model))聚合模型的一种特殊情况,指标列指定的聚合函数为 REPLACE,返回具有相同主键的一组数据中的最新数据(同一主键的数据可能有多个版本,查询时返回版本最新(即版本号最大)的数据(读时合并Merge on Read)),注意,该模型目前不支持像HBase一样通过时间戳来路由到事件发生时的版本来做维度关联,目前还没次功能排期
  • 主键模型(Primary Key Model)——相对于更新模型,主键模型在查询时不需要执行聚合操作( Delete+Insert 的策略,保证同一个主键下仅存在一条记录,完全避免了 Merge 操作),因此支持谓词和索引下推,更适用于实时和频繁更新的场景

数据划分

partition——分区,数据导入和删除的逻辑最小管理单元,高效过滤,日期分区支持自动分级存储(冷热数据)和自动清除历史

tablet/bucket——数据分桶/分片,Hash划分,移动复制等操作的最小物理存储单元,分桶充分发挥集群性能,避免热点问题

SSTable(Sorted String Table)——底层数据存储,都会按照各自建表语句中,XX KEY中指定的列进行排序存储的。这种有序数据结构,如果以排序列作为条件进行查找,会非常高效

2.为什么快

MPP+向量化+Pipeline三位一体

MPP

多机并行,见前面所述

向量化

处理器(SMID等手段)并行。 实现全面向量化引擎,充分发挥了CPU的处理能力。全面向量化引擎按照列式的方式组织和处理数据。StarRocks的数据存储、内存中数据的组织方式,以及SQL算子的计算方式,都是列式实现的。按列的数据组织也会更加充分的利用CPU的Cache,按列计算会有更少的虚函数调用以及更少的分支判断从而获得更加充分的CPU指令流水。
另一方面,StarRocks的全面向量化引擎通过向量化算法充分的利用CPU提供的SIMD指令。这样StarRocks可以用更少的指令数目,完成更多的数据操作。经过标准测试集的验证,StarRocks的全面向量化引擎可以将执行算子的性能,整体提升3—10倍。

Pipeline

单机多核并行。
原本StarRocks的物理计划生成(分配plan fragment分布到各个机器)是一个单机多核线程模型,因此会遭受线程堵塞和DAG导致的数据等待问题。Pipeline使用协同式多任务模型,配合上资源组等理念,使得计算比原本加快至少2倍。 这部分还没看完,待补充(感觉需要先翻下大学的操作系统课件温习一下才能看懂。。),感兴趣的推荐去B站看StarRocks_Lab的介绍pipeline的视频。

CBO

StarRocks从零设计并实现了一款全新的,基于代价的优化器CBO(Cost Based Optimizer)。该优化器是Cascades Like的,在设计时,针对StarRocks的全面向量化执行引擎进行了深度定制,并进行了多项优化和创新。该优化器内部实现了公共表达式复用,相关子查询重写,Lateral Join,Join Reorder,Join 分布式执行策略选择,低基数字典优化等重要功能和优化。目前,该优化器已可以完整支持 TPC-DS 99 条SQL语句。

物化视图

将预先计算好的数据集,存储在StarRocks一个特殊的表,适合那些经常重复使用的子查询,查询性能会大幅提升。
使用方法:抽象出多个查询共有的分组或聚合方式,创建物化视图。
自动维护物化视图的数据(具有事务性),勿论导入还是删除,都能保证base和物化视图的数据一致性。
RollUp:物化视图的一个子类,仅用于聚合模型(上卷)。
RollupTree:同Kylin的Cube(分层构建),下一层级的rollup可以由上一层的rollup计算得来。
透明化使用 —— 使用时只需指定原始表,系统自动选择最优的物化视图(查询自动路由),并保证查询结果一致性。
目前还不支持物化视图加筛选条件,预期2.5版本支持。

JOIN优化

join reorder: 显式降低join生成中间结果的大小,小表驱动大表

shuffle join:同Spark的Hash-based Join

broadcast join:同Spark的Broadcast Join

bucket (shuffle) join: 按高频join key将数据hash分发到多个节点,则join时取数据出来已经按照join key划分好了,降低shuffle成本

colocation join:bucket join基础上,将同join key的数据分发到同一天机器上本地join,连网络IO都节省了

索引

前缀索引:SR对数据进行有序存储(Sorted String Table),在数据有序基础上为其维度列建立的稀疏索引,索引粒度为block(1024行),索引项的值为该block第一行固定长度的前缀(36字节),区分度越大/越高频查询字段/字段长度越短 -> 越往前放

  • 前缀索引如何加速查询?
    取查询中where和on的条件下推到ScanNode,从前缀索引的第一列开始匹配,检查条件中是否包含这些列,有则累计匹配的长度,直到匹配不上或36字节结束,选取匹配最长的一个basae/rollup表。然后比较rollup的行数,选择最小的rollup表,再使用索引定位(维度列的行号索引所在数据块),在数据中做二分查找
  • 注意,同MySQL的最左前缀匹配一样,即从左到右匹配时只能匹配in或=,遇到范围查询就停止匹配

bloomfilter索引:反向测试:快速过滤没有符合条件数据的文件,对区分度大(基数高)的字段效果尤为明显
bitmap索引:key为列值,value为行数,区分度低不会触发(枚举值数量/总条数 > 1/1000)-> 描述存疑,doris和SR文档都滞后于版本迭代,doris是建议低基数列使用,SR是高基数列才触发,见仁见智
min-max索引:page级,segment级都有,快速过滤

RBO

  • 常量折叠 -> 适合裁剪和索引过滤
  • 子查询改写 IN (xx) -> .. JOIN ..
  • 提取公共表达式 -> 提取公共条件并预过滤
  • ...

3.数据导入

导入过程分几个步骤:

  1. 数据按原始文件中列的顺序读入到SR
  2. 通过前置过滤条件(PRECEDING FILTER)对原始数据进行一次过滤
  3. 通过列映射和转换,将原始数据映射到目标列顺序
  4. 通过后置过滤条件(WHERE)对转换后的数据再进行一次过滤
  5. 写入最终数据

load.png 此处只讲我接触过的几个导入方式:

Stream Load

通过 HTTP Push 从本地文件系统或流式数据源导入数据,同步导入,提交导入作业以后,StarRocks 会同步地执行导入作业,并返回导入作业的结果信息。
在作业帮,我们配合调度平台使用。使用方法是,调度任务在每次数据完成后SELECT选中想要导入的字段,通过SQL后处理选项,配置上相应的Stream Load脚本,每次表生成后SELECT需要的数据完成导入。

Routine Load

适合不间断地消费Kafka Topic,导入至 StarRocks 的场景。StarRocks会常驻地运行导入作业,持续生成一系列导入任务,消费 Kafka 集群中该 Topic 中的全部或部分分区的消息并导入到 StarRocks 中。此处生成的一系列导入任务为Stream Load任务。
不由调度平台,而是由 StarRocks 集群负责调度,因此监控只能在 StarRocks 配置相应告警发送到钉钉或邮件,可运维性较差,可视性在升级提供更多可视化看板前也较差。

Flink-CDC-StarRocks

等同于flink-connector-starrocks ,内部实现是通过缓存并批量由 stream load 导入。
该方法的好处是,如果你们公司已经实现直接写Flink SQL的实时开发平台,则可以直接在上面写类SQL脚本去执行此类导入任务,同时还能复用实时任务的监控/保障功能,去监控该导入任务的消费延迟,或者失败自动重新挂起,监控的可视性也更好(比如Prometheus+Grafana)
部分朋友在初次尝试该导入方法时,可能会发愁bitmap这种StarRocks特有的列怎么用Flink代码指定和导入,经过实践,写Flink导入Job时不需要在意bitmap列从Flink到StarRocks的转换(Flink没有bitmap类型,会编译报错),只需要指定构建bitmap的原始指标列的类型即可,比如是字符串去构建bitmap则指定string类型去导入,例子参考:

bitmap导入.png

导入效率

  • 模型中key列越多,导入过程中需要排序的列就越多
  • 维度信息更新会反映到整张表中,而更新的频率直接影响查询的效率(同样占据BE的资源去执行compaction),所以不建议大批量实时导入的方式导入数据
  • SR是以高吞吐见长的OLAP引擎,可以在业务允许的情况下,加大调度的时间间隔和最大行数,如下:
PROPERTIES
(
  "max_batch_rows" = "2000000",
  "max_batch_interval" = "300", --每5分钟触发一次导入调度
  "max_error_number" = "100"
)
  • 导入效率除了可配置的参数外,还受制于硬件设施,比如Routine Load任务的实际任务并行度,受制于这几个参数:
  min(aliveBeNum, partitionNum, desired_concurrent_number, max_routine_load_task_concurrent_num)

同时还受制于槽位数(routine_load_thread_pool_size),即每个BE能同时处理多少个task,如果对及时性要求较高,务必确认这几个参数都配置到机器允许下的最高了,这样才不会出现导入瓶颈。
槽位数——集群任务的并发数,整个集群共享槽位,FE下发任务前需要确保BE上有剩余的槽位才下发,确保集群负载在一个安全的范围。

4.案例

作业帮的部分近实时业务,使用StarRocks做准实时数仓,把StarRocks当Hive用,不需要Flink join,不需要担心时序问题。
感兴趣可以网上搜索具体官方层面的分享资料。

5.常见问题

已解决

  1. count(*)对查询很不友好,推荐添加指标列时设置列值恒为1,聚合方式为SUM
  2. bitmap列必须使用count(distinct xx)算得去重值,遇到窗口函数这种场景:select count(dsitinct uid) over (partition by xx) from tbl 可能无法满足要求,目前可能就2.3版本开始支持select bitmap_union_count(uv) over (partition by xx) from tbl
  3. 哈希碰撞问题
    1.使用精确度更高的bitmap_hash64函数(仍可能有误差)
    2.ETL时将准备入bitmap的数据类型映射为Int/BigInt
    3.构建全局字典
    作业帮构建实时全局字段的实践:www.infoq.cn/article/moe…
  4. 维度异步更新问题:
    1.通过Lambda架构,使用离线天级批处理去定时刷新。该方法开发成本高,但也可以顺便解决数据漂移,业务刷数等问题
    2.维表拆分成两张,一张带可变维度,一张既有可变维度的外键也有不可变维度,需要时现join。开发成本会较高,需留意join的效率
    3.将变化的维度当成指标去replace。因为不被当做维度,针对该维度的查询效率会下降
  5. bitmap列支持跨表合并去查询吗(两表的字典可能不一致)?
    支持的,用bitmap_count(bitmap_union(xx))

未解决

  1. 分区如何支持既按日期,又按小时?

  2. 一个高频查询需要跨多个大表做同维度组合的指标聚合,但是一个查询只能利用一个物化视图/rollup,如何优化该高频查询能支持快速返回结果?

  3. 当把整个数仓都放在StarRocks时,如何解决上层表从下层的StarRocks模型生成时,只能用INSERT INTO?是否有更好的方法去通过下层表调度就绪,去触发上层表更新?

  4. 消费Kafka时,即使指定consumer group,消费中断后重启仍不能接续消费,而是会按默认的OFFSET_END或OFFSET_BEGINNING即最早最晚去消费

  5. 导入任务里,衍生列支持多层映射

6.参考资料

官方文档:docs.starrocks.com/zh-cn/main/…
阿里云各OLAP方案比较:developer.aliyun.com/adc/series/…
作业帮在画像侧的SR实践:www.infoq.cn/article/moe…
StarRocks的某commiter的博客:blog.bcmeng.com/post/starro…