StarRocks 的学习笔记

StarRocks 的学习笔记

文章目录

1. 介绍

StarRocks 是鼎石科技基于 Apache Doris(incubating) 0.13 打造出的企业级国产数据库产品,目前已在Github开源:

https://github.com/StarRocks/starrocks

官网:https://www.starrocks.com/zh-CN/index

1.1 StarRocks 特性

StarRocks 是一款极速全场景MPP 分析型数据库,可以“一栈式”的响应企业各类低延迟场景的查询需求。

StarRocks的愿景是能够让用户的数据分析变得更加简单和敏捷。用户无需经过复杂的预处理,就可以用StarRocks 来支持多种数据分析场景的极速分析。

StarRocks的架构简洁,采用了全面向量化引擎,并配备全新设计的 CBO 优化器,查询速度(尤其是多表关联查询)远超同类产品

StarRocks 概念的几个关键词如下:“极速”、“全场景”、“MPP”和“分析型数据库”。

  • 极速

StarRocks 打造了原生向量化执行引擎,采用向量化技术,充分利用CPU的并行计算能力,在多维分析中实现亚秒级查询返回。StarRocks 的向量化技术配合列式存储、智能物化视图和CBO查询优化器等诸多加速手段,实现较以往系统5-10倍的性能提升。

  • 全场景

StarRocks兼容MySQL协议,我们可以方便的使用各种客户端和BI工具来访问它。StarRocks可以灵活的满足多累数据分析需求,例如固定历史报表,实时数据分析,交互式数据分析和探索式数据分析等,让用户摆脱冗杂的技术栈。让数据分析不再需要做Cube,也不需要打成宽表,而是通过星型模型、雪花模型等进行多表关联灵活join,真正的“一栈式”解决数据分析难题。

在这里插入图片描述

同时,StarRocks还具有强大的联邦查询能力,支持创建外部表直接访问来自Hive、MySQL和Elasticsearch的数据,而无需导入。

  • MPP架构

StarRocks使用现代化MPP(Massively Parallel Processing,大规模并行处理)架构,简单来说,StarRocks 会将任务并行的分散到集群多个服务器节点上,在每个节点上计算完成后,将各自部分的结果汇总在一起得到最终的结果。这种架构下,StarRocks集群每个节点都有独立的磁盘存储系统和内存系统,业务数据根据数据库模型和应用特点划分到各个节点上,每台数据节点通过网络互相连接,彼此协同计算,作为整体提供数据库服务。StarRocks的现代化MPP架构保证了StarRocks具有可伸缩、高可用、高性能、高性价比、高容错、易运维等优势

  • 分析型数据库

StarRocks 的定位是OLAP分析型数据库,这也就是说它适合对大规模的数据进行多维度的查询分析,而不是和像OLTP类数据库(比如MySQL)一样对少量的数据进行大并发频繁的插入和修改。经过业界头部互联网公司的长期使用验证,StarRocks完全可以应对PB级别的结构化数据分析场景,查询事件一般可达到秒级或毫秒级。

OLTP(联机事务处理):操作型处理,叫联机事务处理(On-line Transaction Processing),也可以成为面向交易的处理系统,它是针对具体业务在数据库联机的日常操作,通过对少数记录进行查询、修改。用户较为关心操作的响应时间、数据的安全性、完整性和并发支持的用户数等问题。传统的数据库作为数据管理的主要手段,主要用于操作型处理。

OLAP(联机分析处理):分析型处理,叫联机分析处理(On-line Analytical Processing)一般针对某些主题的历史数据进行分析,支持管理决策。

比较项 OLAP(联机分析处理) OLTP(联机事务处理)
特性 信息处理 操作处理
用户 面向决策人员 面向操作人员
功能 支持管理需要 支持日常操作
面向 面向数据分析 面向应用
驱动 分析驱动 事务驱动
数据量 一次处理的信息量大 一次处理的数据量小
访问 不可更新,周期性刷新 可更新
数据量 历史数据 当前值数据
汇总 综合性和提炼性数据 细节性数据
视图 导出数据 原始数据

1.2 使用场景

StarRocks 可以满足企业级用户的多种分析需求,包括 OLAP 多维分析、定制报表、实时数据分析和Adhoc 数据分析等。

1.3 OLAP 多维分析

利用 StarRocks 的 MPP 框架和向量化执行引擎,用户可以灵活的选择雪花模型,星型模型,宽表模型或者预聚合模型。适用于灵活配置的多维分析报表,业务场景包括:

  • 用户行为分析
  • 用户画像、标签分析、圈人
  • 高维业务指标报表
  • 自助式报表平台
  • 业务问题探查分析
  • 跨主题业务分析
  • 财务报表
  • 系统监控分析

1.4 实时数据仓库

StarRocks 设计和实现了 Primary-Key 模型,能够实时更新数据并极速查询,可以秒级同步 TP 数据库的变化,构建实时数仓,业务场景包括:

  • 电商大促数据分析
  • 物流行业的运单分析
  • 金融行业绩效分析、指标计算
  • 直播质量分析
  • 广告投放分析
  • 管理驾驶舱
  • 探针分析APM (Application Performance Management)

1.5 高并发查询

StarRocks 通过良好的数据分布特性,灵活的索引以及物化视图等特性,可以解决面向用户侧的分析场景,业务场景包括:

  • 广告主报表分析
  • 零售行业渠道人员分析
  • SaaS 行业面向用户分析报表
  • Dashboard 多页面分析

1.6 统一分析

  • 通过使用一套系统解决多维分析、高并发查询、预计算、实时分析查询等场景,降低系统复杂度和多技术栈开发与维护成本。
  • 使用StarRocks 来统一数据库和数据仓库,将高并发和实时要求性很高的业务放在StarRocks种分析,把数据库上的分析使用StarRocks外表查询,统一使用StarRocks 管理库仓数据。

2. 系统架构

2.1 StarRocks架构

StarRocks的架构很简洁,核心只有FE(Frontend)、BE(Backend)两类进程,不依赖外部组件,方便部署和维护。同时,FE和BE都可以横向扩容,元数据和业务数据都有副本机制,确保整个集群无单点,运维也非常省心。StarRocks 提供 MySQL 客户端方便地查询和分析 StarRocks 中地数据。

在这里插入图片描述

针对上面的架构图,由上至下简单介绍几个基本概念:

Client

StarRocks没有自带客户端,推荐使用mysql-client进行访问。StarRocks兼容MySQL协议,所以我们可以使用任意的MySQL JDBC/ODBC客户端,例如SQLyog、DBeaver、Navicat等,将StarRocks是为MySQL进行连接访问。

FE

StarRocks的前端节点,使用Java语言编写,负责管理元数据,管理客户端连接,进行查询规划,查询调度等工作。每个 FE 节点都会在内存保留一份完整地元数据,这样每个 FE 节点能够提供无差别地服务。

FE根据配置会有两种角色:Follower和Observer,其中Follower会通过类Paxos的BDBJE 协议选主出一个Leader (实现选主需要集群中有半数以上的Follower实例存活),只有Leader会对元数据进行写操作。Observer和Follower的差别就是Observer不会参与选主。Observer同样不会进行任何的数据写入,只会参与读取,用来扩展集群的查询能力。每个FE节点都有一份完整的元数据,以此来达到元数据的高可用,保证单节点宕机的情况下,元数据能够实时地在线恢复,而不影响整个服务。

  • Leader
    • 提供元数据读写服务。只有 Leader 节点会对元数据进行写操作,Follower 和 observer 只有读取权限。Follower 和 Observer 将元数据写入请求路由到 Leader 节点,Leader 更新完数据后,会通过 BDBJE 同步给 Follower 和 Observer。必须有半数以上Follower 节点同步成功才算作元数据写入成功。
    • Leader 从 Follower 中选举,实现选主需要集群中有半数以上的 Follower 节点存活。如果 Leader 节点失败,Follower 会发起新一轮选主。
  • Follower
    • 只有元数据读取权限,无写入权限。通过回放 Leader 的元数据日志来异步同步数据。
    • 参与Leader 选主,必须有半数以上的 Follower 节点存活才能进行选主。
  • Observer
    • 主要用户扩展集群的查询并发能力,可选部署。
    • 不参与选主,不会增加集群选主压力。
    • 通过回访 Leader 的元数据日志来异步同步数据。

BE

StarRocks的后端节点,使用C++语言开发,负责数据存储,SQL执行,以及compaction,副本管理等工作。StarRocks会将我们创建的表根据分区和分桶机制划分成多个Tablet多副本存储在不同BE节点上。这里的Tablet是StarRocks表的逻辑分片,也是StarRocks中副本管理的基本单位。

  • 数据存储方面,StarRocks 的 BE 节点都是完全对等的,FE 按照一定策略将数据分配到对应的BE节点。BE 负责将导入数据写成对应的格式存储下来,并生成相关索引。
  • 在执行 SQL 计算时,一条SQL语句首先会按照具体的语义桂花城逻辑执行单元,然后再按照数据的分布情况拆分成具体的物理执行单元。物理执行单元会在对应的数据存储节点上执行,这样可以实现本地计算,避免数据的传输与拷贝,从而能够得到极致的查询性能。

在进行 Stream load 导入时,FE 会选定一个 BE 节点作为 Coordinator BE,负责将数据分发到其他 BE 节点。导入的最终结果由 Coordinator BE 返回给用户。

Broker

在上面的架构图中没有体现,它是StarRocks和外部HDFS/对象存储等外部数据对接的中转服务组件,辅助提供导入导出功能,可以视需求添加使用。Broker本身无状态,可以随意启停,不会影响集群。

2.2 StarRocks业务架构

在对StarRocks的架构有了基本的了解后,我们再简单介绍一下企业使用StarRocks时,大数据架构的整体涉及。下图就是一个常见的大数据业务架构图,StarRocks在整个大数据架构中一般会处在比较靠上的位置,我们会将数据ETL后导入至StarRocks,而后提供给上层的各类业务系统进行展示和分析。

在这套架构中,Hadoop作为大数据存储和批量处理的行业标准,用于原始数据的落地和存储。Kafka 用于支持实时数据的传输。Hive,Spark,Flink作为数据加工和处理的工具,将经过清洗和处理的明细数据导入StarRocks。根据业务对数据实时性的要求,我们既可以按照“T+1”的方式批量导入,也可以进行实时的导入。在StarRocks中对明细数据进行进一步的计算、聚合、建立物化视图等处理后,便可以直接提供给上层的各类线上业务系统。

上层应用可以根据自身需要,通过标准SQL语句直接查明细数据,或者查聚合数据。查询的灵活性完全由SQL语句来提供,不需要再额外开发其他用于数据处理的程序模块。

在这里插入图片描述

如果用户的数据规模不大,上图中的业务架构还可以进一步简化。比如我们就不用再搭建架构较重的Hadoop集群,也不需要再使用Flink等外部计算框架,而是直接使用Kafka作为缓冲,在外部数据进入Kafka后通过StarRocks自带的Routine Load六十拉取数据,来将所有的原始数据直接存放在StarRocks中。

同时,我们可以在StarRocks中使用 insert into select 配合物化视图构建上卷的模型,实现内部的ETL操作。这种简介的架构可以极大的降低企业的运维成本,并且让企业的数据分析体系更加简单。

2.3 StarRocks 数据生态

StarRocks 拥有完备的数据生态,可以灵活的对接企业当前的数据架构,进行数据导入和数据迁移。当前社区提供了多种导入方式,我们可以根据数据元位置、数据量大小、导入频率等需求选择最适合自己业务需求的导入方式:

在这里插入图片描述

StarRocks 目前主要提供了5种不同的导入方式来适配不同的数据导入需求。各导入方式的使用场景、数据原类型以及适合的数据规模等信息如下表:

导入方式 使用场景 数据量(单任务) 数据源类型 格式 执行方式
Stream Load 通过HTTP协议导入本地文本文件/程序导入 10GB以内 本地文件,程序 CSV,Json 同步
Broker Load 通过独立的Broker进程从外部数据源拉取数据导入 数十GB到数百GB HDFS,Amazon S3,阿里云OSS CSV,ORC,Parquet 异步
Routine Load 流式数据导入 微批导入MB-GB Kafka 文本,Json 异步
Spark Load 使用Spark集群导入(大数据量时) 数十GB到TB级别 HDFS,Hive CSV,Hive table 异步
Insert into values 仅用于导入几条测试数据,生产时不建议使用 SQL 同步
Insert into select 外表导入,集群内部ETL操作 与内存相关 StarRocks表(含外部表) StarRocks数据表 同步

ETL 是英文 Extract-Transform-Load的缩写,用来描述将数据从源端经过抽取(extract)、转换(transform)、加载(load)至目的端的过程。

除了表中的5种主要方式外,StarRocks 还开发了Flink-Connector-StarRocks和DataX-StarRocksWriter数据导入插件,让我们可以对接更多类型的数据源完成数据的高效导入和迁移。

2.4 *数据管理

StarRocks 使用列式存储,采用分区分桶机制进行数据管理。一张表可以被划分成多个分区,如将一张表按照时间来进行分区,粒度可以是一天,或者一周等。一个分区内的数据可以根据一列或者多列进行分桶,将数据切分成多个 Tablet。Tablet 是 StarRocks 中最小的数据管理单元。每个 Tablet 都会以多副本(replica)的形式存储在不同的 BE 节点中。可以自行指定 Tablet 的个数和大小。StarRocks 会管理好每个 Tablet 副本的分布信息。

如下展示了 StarRocks 的数据划分以及 Tablet 多副本机制。图中,表按照日期划分为4个分区,第一个分区进一步切分成4个 Tablet。每个 Tablet 使用 3 副本进行备份,分布在 3个不同的 BE 节点上。

在这里插入图片描述

由于一张表被切分成了多个 Tablet,StarRocks 在执行SQL 语句时,可以对所有 Tablet 实现并发处理,从而充分的利用多机、多核提供的计算能力。用户也可以利用 StarRocks 数据的切分方式,将高并发请求压力分摊到多个物理节点,从而可以通过增加物理节点的方式来扩展系统支持高并发的能力。

Tablet 的分布方式与具体的物理节点没有相关性。在 BE 节点规模发生变化时,比如在扩容、缩容时,StarRocks 可以做到无需停止服务,直接完成节点的增减。节点的变化会触发 Tablet 的自动迁移。当节点增加时,一部分 Tablet 会在后台自动被均衡到新增的节点,从而使得数据能够在集群内分布的更加均衡。在节点减少时,下线机器上的 Tablet 会被自动均衡到其他节点,从而自动保证数据的副本数不变 ,管理员能够非常容易地实现 StarRocks 的弹性伸缩,无需手工进行任何数据的重分布。

StarRocks 支持 Tablet 多副本存储,默认副本数为三个。多副本能够保证数据存储的高可靠以及服务的高可用。在使用三副本的情况下,一个节点的异常不会影响服务的可用性,集群的读、写服务仍然能够正常进行。另外,增加副本数还有助于提供系统的高并发查询能力。

3. 特性

3.1 MPP分布式执行框架

StarRocks 采用MPP(Massively Parallel Processing)分布式执行框架。在MPP执行框架中,一条查询请求会被拆分成多个物理计算单元,在多机并行执行。每个执行节点拥有独享的资源(CPU、内存)。MPP执行框架能够使用单个查询请求可以充分利用搜友执行节点的资源,所以单个查询的性能可以随着集群的水平扩展而不断提升。

在这里插入图片描述

如图,StarRocks 会将一个查询在逻辑上切分为多个逻辑执行单元(Query Fragment)按照每个逻辑执行单元需要处理的计算量,每个逻辑执行单元会由一个或者多个物理执行单元来具体实现。物理执行单元是最小的调度单位。一个物理执行单元会被调度到集群某个BE上执行。一个逻辑执行单元可以包括一个或者多个执行算子,如图中的 Fragment 包括了 Scan,Project,Aggregate。每个物理执行单元只处理部分数据。由于每个逻辑执行单元处理的复杂度不一样,所以每个逻辑执行单元的并行度是不一样的,即,不同逻辑执行单元可以由不同数目的物理执行单元来具体执行,以提高资源使用率,提升查询速度

在这里插入图片描述

与很多数据分析系统采用的 Scatter-Gather 分布式执行框架不同,MPP 分布式执行框架可以利用更多的资源处理查询请求。在Scatter-Gather 框架中,只有Gather 节点能处理最后一级的汇总计算。而在MPP框架中,数据会被Shuffle到多个节点,并且由多个节点来完成最后的汇总计算。在复杂计算时(比如高基数 Group By,大表Join等操作),StarRocks的MPP框架相对于 Scatter-Gather 模式的产品有明显的性能优势。

3.2 全面向量化执行引擎

StarRocks 通过实现全面向量化引擎,充分发挥了CPU的处理能力。全面向量化引擎按照列式的方式组织和处理数据。StarRocks 的数据存储、内存中数据的组织方式,以及SQL算子的计算方式,都是列式实现的。按列的数据组织也会更加充分的利用CPU的Cashe,按列计算会有更少的虚函数调用以及更少的分支判断从而获得更加充分CPU指令流水。

另一方面,StarRocks的全面向量化引擎通过向量化算法充分的利用CPU提供的SIMD指令。这样StarRocks 可以用更少的指令数目,完成更多的数据操作。经过标准测试集的验证,StarRocks 的全面向量化引擎可以将执行算子的性能,整体提升3-10倍。

除了使用向量化技术实现所有算子外,StarRocks 还在执行引擎中实现了其他的优化。比如StarRocks 实现了 Operation on Encoded Data 的技术。对于字符串字段的操作,StarRocks 在无需解码情况下就可以直接基于编码字段完成算子执行,比如实现关联算子、聚合算子、表达式算子计算等。这可以极大的降低SQL在执行过程中的计算复杂度。通过这个优化手段,相关查询速度可以提升2倍以上。

3.3 CBO优化器

在这里插入图片描述

在多表关联查询场景下,仅靠优秀的执行引擎没有办法获得最极致的执行性能。因为这类场景下,不同执行计划的复杂度可能会相差几个数量级。查询中关联表的数目越大,可能的执行计划就越多,在众多的可能中选择一个最优的计划,这是一个NP-Hard的问题。只有优秀的查询优化器,才能选择出相对最优的查询计划,从而实现机制的多表分析性能。

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

由于全新CBO的支持,StarRocks 能比同类产品更好地 支持多表关联查询,特别是复杂地多表关联查询,让全面向量化引擎能够发挥极致的性能。

3.4 可实时更新的列式存储引擎

StarRocks 实现了列式存储引擎,数据以按列的方式进行存储。通过这样的方式,相同类型的数据连续存放。一方面,数据可以使用更加高效的编码方式,获得更高的压缩比,降低存储成本。另一方面,也降低了系统读取数据的IO总量,提升了查询性能。此外,在大部分OLAP场景中,查询只会设计部分列。相对于行存,列存只需要读取不分裂的数据,能够极大地降低磁盘IO吞吐。

StarRocks 能够支持秒级的导入延迟,提供准实时的服务能力。StarRocks 的存储引擎在数据导入时能够保证每一次操作的ACID。一个批次的导入数据生效是原子性的,要么全部导入成功,要么全部失败。并发进行的哥哥十五相互之间互不影响,对外提供Snapshot lsolation 的事务隔离级别。

在这里插入图片描述

StarRocks 存储引擎不仅能够提供高效的Append 操作,也能高效的处理 Upsert 类操作。使用 Delete-and-insert 的实现方式,通过逐渐索引快速过滤,消除了读取时 Sort merge 操作,同时还可以充分利用其他二级索引。可以在大量更新的场景下,仍然可以保证查询的极速性能。

3.5 智能的物化视图

在这里插入图片描述

StarRocks 支持用户使用物化视图进行查询加速。不同于一些同类产品的物化视图需要异步和原表做数据同步,StarRocks 的物化视图可以自动根据原始表更新数据。只要原始表数据发生变更,物化视图的更新也同步完成,不需要额外的维护操作就可以保证物化视图能够维持与原表一致。不仅如此,物化视图的选择也是自动进行的。StarRocks在进行查询规划时,如果有合适的物化视图能够加速查询,那么StarRocks能够自动的将查询改写,使用合适的物化视图来加速用户的查询请求。

StarRocks的物化视图可以按需灵活创建和删除。用户不需要在原始表创建时就构建物化视图,而可以在使用过程中视实际使用情况来判断是否需要创建或删除物化视图。StarRocks 会在后台自动完成物化视图的相关调整。

3.6 数据库分析

在这里插入图片描述

StarRocks 不仅能高效的分析本地存储的数据,也可以作为计算引擎直接分析数据库中的数据,支持包括Apache Hive、Apache lceberg、Apache Hudi等数据组织结构,支持 Parquet、ORC、CSV 等文件格式,也支持 HDFS、S3、OSS 等存储方式。

如图所示,在数据库分析的场景中,StarRocks 主要负责数据的计算分析,而数据库则主要负责数据的存储、组织和维护。使用数据库的优势在于可以使用开放的存储格式和灵活多变的schema定义方式,可以让 BI/AI/Adhoc/报表等业务在统一的single source of truth的,而StarRocks 作为数据库的计算引擎,可以充分发挥向量化引擎和CBO的优势,大大提升了数据库分析的性能。

4. 使用

4.1 部署

参考官网部署步骤:https://docs.starrocks.io/zh-cn/2.4/quick_start/Deploy

4.2 创建表

1、连接StarRocks

在成功 部署StarRocks 集群 后,可以通过 MySQL 客户端连接任意一个 FE 节点的 query_port (默认为9030)以连接StarRocks。StarRocks 内置 root 用户,密码默认为空。

mysql -h <fe_host> -p9030 -u root

2、创建数据库

使用root 用户创建 example_db 数据库。

CREATE DATABASE example_db;

可以通过 SHOW DATABASES; 命令查看当前StarRocks 集群中所有数据库。

MySQL [(none)]> SHOW DATABASES;

+--------------------+
| Database           |
+--------------------+
| _statistics_       |
| example_db         |
| information_schema |
+--------------------+
3 rows in set (0.00 sec)

说明:与 MySQL 的表结构类似,

Information_schema 包含当前 StarRocks 集群的元数据信息,但是部分统计信息还不完善。推荐通过 DESC table_name 等命令来获取数据库元数据信息。

3、创建表

在新建的数据库中创建表。

StarRocks 支持 多种数据类型,以适用不同的应用场景。以下示例基于 明细表模型 编写建表语句。

use example_db;
CREATE TABLE IF NOT EXISTS detailDemo (
    recruit_date  DATE           NOT NULL COMMENT "YYYY-MM-DD",
    region_num    TINYINT        COMMENT "range [-128, 127]",
    num_plate     SMALLINT       COMMENT "range [-32768, 32767] ",
    tel           INT            COMMENT "range [-2147483648, 2147483647]",
    id            BIGINT         COMMENT "range [-2^63 + 1 ~ 2^63 - 1]",
    password      LARGEINT       COMMENT "range [-2^127 + 1 ~ 2^127 - 1]",
    name          CHAR(20)       NOT NULL COMMENT "range char(m),m in (1-255) ",
    profile       VARCHAR(500)   NOT NULL COMMENT "upper limit value 65533 bytes",
    hobby         STRING         NOT NULL COMMENT "upper limit value 65533 bytes",
    leave_time    DATETIME       COMMENT "YYYY-MM-DD HH:MM:SS",
    channel       FLOAT          COMMENT "4 bytes",
    income        DOUBLE         COMMENT "8 bytes",
    account       DECIMAL(12,4)  COMMENT "",
    ispass        BOOLEAN        COMMENT "true/false"
) ENGINE=OLAP
DUPLICATE KEY(recruit_date, region_num)
DISTRIBUTED BY HASH(recruit_date, region_num) BUCKETS 8;

注意

  • 在 StarRocks 中,字段名不区分大小写,表名区分大小写。
  • 建表时,DISTRIBUTED BY 为必填字段。

4、建表语句说明

排序键

StarRocks 表内部组织存储数据时会按照指定列排序,这些列为排序列(Sort Key)。明细模型中由DUPLICATE KEY 指定排序列。以上示例中的 recruit_date 以及 region_num两列为排序列。

注意:排序列在建表时应定义在其他列之前。

字段类型

StarRocks 表中支持多种字段类型,除上述示例中已经列举的字段类型,还支持 BITMAP类型,HLL类型,ARRAY类型。

注意:在建表时,应尽量使用精确的类型。例如,整形数据不应使用字符串类型,INT 类型即可满足的数据不应使用 BIGINT 类型。精确的数据类型能够更好的发挥数据库的性能。

分区分桶

PARTITION 关键字用于给表 创建分区。上述示例中使用 recruit_date 进行范围分区,从11日到15日每天创建一个分区。StarRocks 支持动态生成分区。

DISTRIBUTED 关键字用于给表 创建分桶,上述示例中使用 recruit_date 以及 region_num 两个字段通过 Hash 算法创建8个桶。

创建表时合理的分区和分桶设计可以优化表的查询性能。

数据模型

DUPLICATE关键字表示当前表为明细模型,KEY 中的列表示当前表的排序列。StarRocks 支持多种数据模型,分别为 明细模型,聚合模型,更新模型,主键模型。不同模型的适用于多种业务场景,合理选择可优化查询效率。

索引

StarRocks 默认会给Key 列创建系数索引加速查询、支持的索引类型有 Bitmap 索引,Bloomfilter 索引等。

注意:索引创建对表模型和列有要求。

ENGINE 类型

默认 ENGINE 类型为 OLAP ,对应 StarRocks 集群内部表。其他可选项包括 mysqlelasticsearchhive,以及ICEBERG,分别代表所创建的表为响应类型的外部表。

查看表信息

可以通过SQL命令查看表的相关信息。

  • 查看当前数据库中所有的表
SHOW TABLES;
  • 查看表的结构
DESC table_name;

示例:

DESC detailDemo;
  • 查看建表语句
SHOW CREATE TABLE table_name;

示例:

SHOW CREATE TABLE detailDemo;

5、修改表结构

StarRocks 支持多种 DDL 操作。

可以通过 ALTER TABLE 命令可以修改表的Schema,包括增加列,删除列,修改列类型(暂不支持修改列名称),改变列顺序。

增加列

例如,在以上创建的表中,与ispass 列后新增一列uv,类型为 BIGINT,默认值为0

ALTER TABLE detailDemo ADD COLUMN uv BIGINT DEFAULT '0' after ispass;
删除列

删除以上步骤新增的列。

ALTER TABLE detailDemo DROP COLUMN uv;

6、查看修改表结构作业状态

修改表结构为异步操作。操作成功后,可以通过以下命令查看作业状态。

SHOW ALTER TABLE COLUMN\G;

当作业状态为 FINISHED,则表示作业完成,新的表结构修改已生效。

修改 Schema 完成之后,可以通过以下命令查看最新的表结构。

DESC table_name;

示例如下:

MySQL [example_db]> desc detailDemo;

+--------------+-----------------+------+-------+---------+-------+
| Field        | Type            | Null | Key   | Default | Extra |
+--------------+-----------------+------+-------+---------+-------+
| recruit_date | DATE            | No   | true  | NULL    |       |
| region_num   | TINYINT         | Yes  | true  | NULL    |       |
| num_plate    | SMALLINT        | Yes  | false | NULL    |       |
| tel          | INT             | Yes  | false | NULL    |       |
| id           | BIGINT          | Yes  | false | NULL    |       |
| password     | LARGEINT        | Yes  | false | NULL    |       |
| name         | CHAR(20)        | No   | false | NULL    |       |
| profile      | VARCHAR(500)    | No   | false | NULL    |       |
| hobby        | VARCHAR(65533)  | No   | false | NULL    |       |
| leave_time   | DATETIME        | Yes  | false | NULL    |       |
| channel      | FLOAT           | Yes  | false | NULL    |       |
| income       | DOUBLE          | Yes  | false | NULL    |       |
| account      | DECIMAL64(12,4) | Yes  | false | NULL    |       |
| ispass       | BOOLEAN         | Yes  | false | NULL    |       |
| uv           | BIGINT          | Yes  | false | 0       |       |
+--------------+-----------------+------+-------+---------+-------+
15 rows in set (0.00 sec)
取消修改表结构

可以通过以下命令取消当前正在执行的作业。

CANCEL ALTER TABLE COLUMN FROM table_name\G;

7、创建用户并授权

在 StarRocks 中,只有拥有 CREATE_PRIV 权限的用户才可建立数据库。

example_db 数据库创建完成之后,可以使用root账户创建test账户,并授予其example_db的读写权限。

CERATE USER 'test' IDENTIFIED by '123456';
GRANT ALL on example_db to test;

通过登录被授权的test账户,就可以操作example_db数据库。

mysql -h 127.0.0.1 -P9030 -utest -p123456

4.3 导入和查询数据

1、导入数据

为适配不同的数据导入需求,StarRocks 系统提供了五种不同的导入方式,以支持不同的数据源(如HDFS、Kafka、本地文件等)或者方式(异步或同步)导入数据。

StarRocks 数据导入整体生态图如下:

在这里插入图片描述

Broker Load

Broker Load 模式是一种异步数据导入模式,通过Broker 进程访问并读取外部数据源,然后采用MySQL协议向 StarRocks 创建导入作业。

Broker Load 模式适用于源数据在 Broker 进程可访问的存储系统(如 HDFS,S3)中的情景,可以支撑数据量达数百 GB 的导入作业。该导入方式支持的数据源有 Apache Hive 等。

Spark Load

Spark Load 是一种异步数据导入模式,通过外部的Apache Spark 资源实现对导入数据的预处理,提高StarRocks 大数据量的导入性能并且节省 StarRocks 集群的计算资源。

Spark Load 模式适用于初次向 StarRocks 迁移大数据量(TB 级别)的场景。该导入方式支持的数据源应位于 Apache Spark 可访问的存储系统(如HDFS)中。

通过 Spark Load 可以基于 Apache Hive 表实现全局字典的数据结构,对输入数据进行类型转换,保存原始值到编码值得映射,例如将字符串类型映射成整型。

Stream Load

Stream Load 是一种同步数据导入模式。用户通过HTTP 协议发送请求将本地文件或数据流导入到StarRocks中,并等待系统返回导入得结果状态,从而判断导入是否成功。

Stream Load 模式适用于导入本地文件,或通过程序导入数据流中的数据。该导入方式支持的数据源有 Apache Flink、CSV 文件等。

Routine Load

Routine Load (例行导入)提供从指定数据源进行自动数据导入的功能。可以通过MySQL协议提交例行导入作业,生成一个常驻线程,不间断地从数据源(如 Apache kafka)中读取数据并导入到 StarRocks 中。

Insert Into

Insert Into 导入模式是一种同步数据导入模式,类似MySQL中的 Insert 语句,StarRocks 支持通过INSERT INTO tbl SELECT ...;的方式从StarRocks 的表中读取数据并导入到另一张表。也可以通过INSERT INTO tbl VALUES(...);插入单挑数据。该导入方式支持的数据源有 DataX/DTS、Kettle/Informatic、以及StarRocks本身。

通过Stream Load 导入数据

以下示例以 Stream load 导入方式为例,将文件中的数据导入到 detailDemo表中。

在本地创建数据文件 detailDemo_data,以逗号作为数据之间的分隔符,插入两条数据。具体内容如下:

2022-03-13,1,1212,1231231231,123412341234,123452342342343324,hello,welcome,starrocks,2022-03-15 12:21:32,123.04,21.12345,123456.123456,true
2022-03-14,2,1212,1231231231,123412341234,123452342342343324,hello,welcome,starrocks,2022-03-15 12:21:32,123.04,21.12345,123456.123456,false

接着,以“streamDemo” 为 Label,通过 curl 命令封装 HTTP 请求,将本地文件 detailDemo_data 导入 detailDemo 表。

curl --location-trusted -u root: -T detailDemo_data -H "label: streamDemo" \
-H "column_separator:," \
http://127.0.0.1:8030/api/example_db/detailDemo/_stream_load

注意:以上示例中,root为连接 FE 节点的用户名,默认密码为空,若使用的用户有密码,需在磨耗后面补充密码;HTTP 地址中 IP 为 FE 节点IP,端口为 fe.conf 中配置的http port

2、查询

StarRocks 兼容 MySQL 协议,其查询语句基本符合 SQL92标准。

简单查询

通过 MySQL 客户端登录 StarRocks ,查询表中全部数据。

use example_db;
select * from detailDemo;
通过标准 SQL 查询

将查询结果以 region_num 字段降序排列。

select * from detailDemo order by region_num desc;

StarRocks 支持多种 select 用法,包括:Join,子查询,With 子句等。

3、扩展支持

StarRocks 拓展支持多种函数、视图、以及外部表。

函数

StarRocks 中支持多种函数,包括:日期函数,地理位置函数,字符串函数,聚合函数,Bitmap函数,数组函数,cast函数,hash 函数,加密函数,窗口函数等。

视图

StarRocks 支持创建 逻辑视图 和 物化视图。

外部表

StarRocks 支持多种外部表:MySQL 外部表,Elasticsearch 外部表,Apache Hive 外表,

StarRocks 外部表,Apache lceberg 外表,Apache Hudi 外表。成功创建外部表后,可通过查询外部表的方式接入其他数据源。

慢查询分析

StarRocks 支持通过多种方式分析查询瓶颈以及优化查询效率。

通过调整并行度优化查询效率

通过设置Pipeline 执行引擎变量。也可以通过调整一个 Fragment 示例的并行数量 set parallel_fragment_exec_instance_num = 8;来设置查询并行度,从而提高 CPU 资源利用率和查询效率。

查看 Profile 并分析查询瓶颈
  • 查看查询计划
explain costs select * from detailDemo;

StarRocks 1.19 以前版本需使用 explain sql 查看查询计划。

  • 开启 Profile 上报。

注意:通过此方式设置 Profile 上报仅在当前 session 生效。

set is_report_success = true;

5. 表设计

5.1 理解 StarRocks 表设计

1、列式存储

在这里插入图片描述

StarRocks 中的表由行和列构成。每行数据对应用户一条记录,每列数据具有相同的数据类型。所有数据行的列数相同,可以动态增删列。在 StarRocks 中,一张表的列可以分为维度列(也称为Key 列)和指标列(也称为 Value 列)。维度列用于分组和排序,指标列的值可以通过聚合函数 sum、count、min、max、hll_union_agg 和 bitmap_union 等累加起来。因此,StarRocks 中的表也可以认为是多为的 Key 到 多维指标的映射。

在 StarRocks 中,表数据按列存储。物理上,一列数据会经过分块编码、压缩等操作,然后持久化存储到非易失设备上。但在逻辑上,一列数据可以看成是由相同类型的元素构成的一个数组。一行数据的所有列值在各自的数组中按照列顺序排列,即拥有相同的数组下表。数组下标是隐式的,不需要存储。表中所有的行按照维度列,做多重排序,排序后的位置就是该行的行号。

查询时,如果指定了维度列上的等职条件或者范围条件、并且这些条件中的维度列可以构成表的维度列前缀,则可以利用数据的有序性,使用二分查找法快速锁定目标行。例如,表table1 包含 event_daysiteidcitycodeusername 四列,其中 event_daysiteid 时维度列。如果查询条件为event_day = 2020-09-18siteid = 2,因为 event_daysiteid 可以构成维度列前缀,因此可以使用二分查找法,值需要处理指定范围i内的数据;如果查询条件为 citycode = 4username = Andy,因为citycodeusername 不能构成维度列前缀,因此无法使用二分查找法,必须处理整表的数据。

2、索引

StarRocks 通过前缀索引(Prefix Index)和列级索引,能够快速找到目标行所在数据块的起始行号。

StarRocks 表设计原理如下图所示:

在这里插入图片描述

一张表中的数据组织主要有三部分构成:

  • 前缀索引

    表中每 1024 行数据构成一个逻辑数据块(Data Block)。每个逻辑数据块在前缀索引表中存储一个索引项,索引项的内容为数据块中第一行数据的维度列所构成的前缀,长度不超过36字节。前缀索引是一种稀疏索引。使用表中某行数据的维度列所构成的前缀查找前缀索引表,可以确定该行数据所在逻辑数据块的起始行号。

  • 列级数据块

    表中每列数据都按 64 KB 分块存储。数据块作为一个单位单独编码、压缩,也作为I/O单位,整体协会设备或者读出。

  • 列级索引

    表中每列数据都有一个独立的行号索引。行号索引表中,该列的数据块和行号一一对应。每个行号索引项由对应数据块的起始行号、位置和长度信息构成。用某行数据的行号查找行号索引表,可以获取包含该行号对应的数据块所在的位置,读取目标数据块后,可以进一步查找数据。

由此可见,通过某行数据的维度列所构成的前缀查找该行数据的过程包含以下五个步骤:

  1. 先查找前缀索引表,获得逻辑数据块的起始行号。
  2. 查找维度列的行号索引,定位到维度列的数据块。
  3. 读取数据块。
  4. 解压、解码数据块。
  5. 从数据块中找到维度列前缀对应的数据项。

3、加速处理

StarRocks 通过如下机制实现数据的加速处理:

预先聚合

StarRocks 支持聚合模型,维度列取值相同的数据行可合并一行。合并后,数据行的维度列取值不变,指标列的取值为这些数据行的聚合结果。需要给指标列指定聚合函数。通过预先聚合,可以加速聚合操作。

分区分桶

StarRocks 中,表被划分成多个 Tablet,每个 Tablet 多副本冗余存储在 BE 上。BE 和 Tablet 的数量可以根据计算资源和数据规模的变化而弹性伸缩。查询时,多台 BE 可以并行地查找 Tablet,从而快速获取数据。此外,Tablet 地副本可以复制和迁移,从而增强数据可靠性,并避免数据倾斜。总之,分区分桶有效保证了数据访问地高效性和稳定性。

物化视图

前缀索引可以加速数据查找,但是前缀索引依赖维度列地排列次序。如果使用非前缀地维度列构造查找为此,则无法使用前缀索引。可以为数据表创建物化视图。物化视图的数据组织和存储与数据表相同,但物化视图拥有自己的前缀索引。在为物化视图创建索引时,可指定聚合的粒度、列的数量和维度列的次序,使频繁使用的查询条件能够命中相应的物化视图索引。

列级索引

StarRocks 支持布隆过滤器(Bloom Filter)、ZoneMap索引和 位图(Bitmap)索引等列级别的索引技术:

  • 布隆过滤器有助于快速判断数据块中不含所查找的值。
  • ZoneMap 索引有助于通过数据范围快速过滤出待查找的值。
  • 位图索引有助于快速计算出枚举类型的列满足一定条件的行。

5.2 数据模型

建表时,需要指定数据模型(Data Model),这样数据导入至数据模型时,StarRocks 会按照排序键对数据进行排序、处理和存储。

1、基本概念

数据模型

StarRocks 支持四种数据模型,分别是明细模型(DUplicate Key Model)、聚合模型(Aggregate Key Model)、更新模型(Unique Key Model)和主键模型(Primary Key Model)。这四种数据模型能够支持多种数据分析场景,例如日志分析、数据汇总分析、实时分析等。

排序键

数据导入至数据模型,按照建表时指定的一列或多列排序和存储,这部分用于排序的列就称为排序键。排序键通常为查询时过滤条件频繁使用的维度列,可以加速查询。明细模型中,排序键就是用于排序的列,即 DUPLICATE KEY 指定的列。聚合模型中,排序键就是用于聚合的列,即 AGGREGATE KEY 指定的列。主键模型和更新模型中,排序键就是满足唯一性约束的列,分别由 PRIMARY KEYUNIQUE KEY 指定。

StarRocks 中的排序键,相对于传统的主键,具有如下特点:

  • 排序键通常为查询时过滤条件中频繁使用的维度列。
  • 在明细模型中,排序键可重复,不必满足唯一性约束。在聚合模型、主键模型和更新模型中,排序键必须满足唯一性约束。
  • 数据表采用聚簇存储,即表中每列的值,按照排序键进行排序并存储。
  • 根据排序键生成前缀索引(Prefix Index)。

2、注意事项

  • 在建表语句中,排序键必须定义在其他列之前。
  • 在创建表时,可以将一个或多个列定义为排序键。排序键在建表语句中的出现次序,为数据存储时多重排序的次序。
  • 不支持排序键的数据类型为 BITMAP 、HLL。
  • 前缀索引的长度限制为36字节。如果排序键中全部列的值的长度加起来超过 36字节,则前缀索引仅会保存限制范围内排序键的若干前缀列。
  • 如果导入的数据存在重复的主键,则数据导入至数据模型,存储在 StarRocks 时,则会按照如下方式进行处理:
    • 明细模型:表中会存在主键重复的数据行,并且与导入的数据是完全对应的。可以召回所导入的全部历史数据
    • 聚合模型:表中不沉溺在主键重复的数据行,主键满足唯一性约束。导入的数据中主键重复的数据行聚合为一行,即具有相同主键的指标列,会通过聚合函数进行聚合。只能召回导入的全部历史数据的聚合结果,但是无法召回历史明细数据。
    • 主键模型和更新模型:表中不存在主键重复的数据行,主键满足唯一性约束。最新导入的数据行,替换掉其他主键重复的数据行。这两种模型可以视为聚合模型的特殊情况,相当于在聚合模型中,为表的指标列指定聚合函数为 REPLACE,REPLACE 函数返回主键相同的一组数据中的最新数据。

3、明细模型

明细模型是默认的建表模型。

创建表时,支持定义排序键。如果查询的过滤条件包含排序键,则 StarRocks 能够快速地过滤数据,提高查询效率。明细模型适用于分析日志数据等,支持追加新数据,不支持修改历史数据。

适用场景
  • 分析原始数据,例如原始日志、原始操作记录等。
  • 查询方式灵活,不需要局限于预聚合的分析方式。
  • 导入日志数据或者时序数据,主要特点时旧数据不会更新,只会追加新的数据。
创建表

例如,需要分析某时间范围的某一类事件的数据,则可以将事件时间(event_time)和事件类型(event_type)作为排序键。

CREATE TABLE IF NOT EXISTS detail (
    event_time DATETIME NOT NULL COMMENT "datetime of event",
    event_type INT NOT NULL COMMENT "type of event",
    user_id INT COMMENT "id of user",
    device_code INT COMMENT "device code",
    channel INT COMMENT ""
)
DUPLICATE KEY(event_time, event_type)
DISTRIBUTED BY HASH(user_id) BUCKETS 8;

建表时必须使用 DISTRIBUTED BY HASH 子句指定分桶键。

使用说明
  • 排序键的相关说明:

    • 在建表语句中,排序键必须定义在其他列之前。

    • 排序键可以通过 DUPLICATE KEY 显式定义。本示例中排序键为 event_timeevent_type

      如果未指定,则默认选择表的前三列作为排序键。

    • 明细模型中的排序键可以为部分或全部维度列。

  • 建表时,支持位指标列创建 BIATMAP、Bloom Filter 等索引。

4、聚合模型

建表时,支持定义排序键和指标列,并未指标列指定聚合函数。当多条数据具有相同的排序键时,指标列会进行聚合。在分析统计和汇总数据时,聚合模型能够减少查询时所需要处理的数据,提升查询效率。

适用场景

适用于分析统计和汇总数据。比如:

  • 通过分析网站或APP的访问流量,统计用户的访问总时长、访问总次数。
  • 广告厂商为广告主提供的广告点击总量、展示总量、消费统计等。
  • 通过分析电商的全年交易数据,获得指定季度或者月份中,各类消费人群的爆款商品。

在这些场景中,数据查询和导入,具有以下特点:

  • 多为汇总类查询,比如 SUM、COUNT、MAX 等类型的查询。
  • 不需要查询原始的明细数据。
  • 旧数据更新不频繁,只会追加新的数据。
原理

从数据导入至数据查询节点,聚合模型内部统一排序键的数据会多次聚合,聚合的具体时机和机制如下:

  1. 数据导入阶段:数据按批次导入至聚合模型时,每一个批次的数据形成一个版本,在一个版本中,同意排序见的数据会进行一次聚合。
  2. 后台文件合并节点(Compaction):数据分批次多次导入至聚合模型中,会生成多个版本的文件,多个版本的文件定期合并成一个大版本文件时,同一排序键的数据会进行一次聚合。
  3. 查询阶段:所有版本中同一排序键的数据进行聚合,然后返回查询结果。

因此,聚合模型中数据多次聚合,能够减少查询时所需要的处理的数据量,进而提升查询的效率。

例如,导入如下数据至聚合模型中:

Date Country PV
2020.05.01 CHN 1
2020.05.01 CHN 2
2020.05.01 USA 3
2020.05.01 USA 4

在聚合模型中,以上四条数据会聚合为两条数据。这样在后续查询处理的时候,处理的数据量就会显著降低。

Date Country PV
2020.05.01 CHN 3
2020.05.01 USA 7
创建表

例如需要分析某一段时间内,来自不同城市的用户,访问不同网页的总次数。则可以将网页地址site_id、日期date 和城市代码 city_code 作为排序键,将访问次数 pv 作为指标列,并为指标列 pv 指定聚合函数为 SUM。

在该业务场景下,建表语句如下:

CREATE TABLE IF NOT EXISTS example_db.aggregate_tbl (
    site_id LARGEINT NOT NULL COMMENT "id of site",
    date DATE NOT NULL COMMENT "time of event",
    city_code VARCHAR(20) COMMENT "city_code of user",
    pv BIGINT SUM DEFAULT "0" COMMENT "total page views"
)
DISTRIBUTED BY HASH(site_id) BUCKETS 8;

建表时必须使用 DISTRIBUTED BY HASH 子句指定分桶键。

使用说明
  • 排序键的相关说明:

    • 在建表语句中,排序键必须定义在其他列之前。

    • 排序键可以通过 AGGREGATE KEY 显式定义。

      • 如果AGGREGATE KEY 未包含全部维度列(除指标列之外的列),则建表会失败。
      • 如果不通过 AGGREGATE KEY 显示定义排序键,则默认除指标列之外的列均为排序键。
    • 排序键必须满足唯一性约束,必须包含全部维度列且列的值不会修改。

  • 指标列:通过在列名后指定聚合函数,定义该列为指标列。一般为需要汇总统计的数据。

  • 聚合函数:指标列使用的聚合函数。

  • 查询时,排序键在多版聚合之前就能进行过滤,而指标列的过滤在多版本聚合之后。因此建议将频繁使用的过滤字段作为排序键,在聚合前就能过滤数据,从而提升查询性能。

  • 建表时,不支持为指标列创建 BITMAP、Bloom Filter 等索引。

5、更新模型

建表时,支持定义主键和指标列,查询时返回主键相同的一组数据中的最新数据。相对于明细模型,更新模型简化了数据导入流程,能够更好地支持实时和频繁更新的场景。

适用场景

实时和频繁更新的业务场景,例如分析电商订单。在电商场景中,订单的状态经常会发生变化,每天的订单更新量可突破上亿。

原理

更新模型可以视为聚合模型的特殊情况,指标列指定的聚合函数为 REPLACE,返回具有相同主键的一组数据中的最新数据。

数据分批次多次导入至更新模型,每一批次数据分配一个版本号,因此同一主键的数据可能有多个版本,查询时返回版本最新(即版本号最大)的数据。相对于明细模型,更新模型通过简化导入流程,能够更好地支持实时和频繁更新。

例如下表中。ID 时主键,value 时指标列, _version 是 StarRocks 内部的版本号。其中 ID 为1 的数据有两个导入批次,版本号分别为 1 和 2;ID 为 2 的数据有三个导入批次,版本号分别为 3、4、5。

ID value _version
1 100 1
1 101 2
2 100 3
2 101 4
2 102 5

查询 ID 为 1 的数据时,仅会返回最新版本 2 的数据,而查询 ID 为 2 的数据时,仅会返回最新版本 5 的数据,最终查询结果如下:

ID value
1 101
2 101
创建表

在电商订单分析场景中,经常按照日期对订单状态进行统计分析,则可以将经常使用的过滤字段订单创建时间create_time、订单编号 order_id作为主键,其余列订单状态order_state 和订单总价 tatal_pricae作为指标列。这样即能够满足实时更新订单状态的需求,又能够在查询中进行快速过滤。

在该业务场景下,建表语句如下:

CREATE TABLE IF NOT EXISTS orders (
    create_time DATE NOT NULL COMMENT "create time of an order",
    order_id BIGINT NOT NULL COMMENT "id of an order",
    order_state INT COMMENT "state of an order",
    total_price BIGINT COMMENT "price of an order"
)
UNIQUE KEY(create_time, order_id)
DISTRIBUTED BY HASH(order_id) BUCKETS 8;

建表时必须使用 DisTRIBUTED BY HASH 子句指定分桶键。

使用说明
  • 主键的相关说明:
    • 在建表语句中,主键必须定义在其他列之前。
    • 主键通过UNIQUE KEY定义。
    • 主键必须满足唯一性约束,且列的值不会修改。
    • 设置合理的主键。
      • 查询时,主键在聚合之前救恩那个进行过滤,而指标列的过滤通常在多版本聚合之后,因此建议将频繁使用的过滤字段作为主键,在聚合前就能过滤数据,从而提升查询性能。
      • 聚合过程中会比较所有主键,因此需要避免设置过多的主键,一面降低查询性能。如果某个列只是偶尔会作为查询中的过滤条件,则不建议放在主键中,
  • 建表时,不支持为指标列创建 BITMAP、Bloom Filter 等索引。

6、主键模型

StarRocks 1.19 版本推出了主键模型(Primary Key Model)。建表时,支持定义主键和指标列,查询时返回主键相同的一组数据中的最新数据,相对于更新模型,主键模型在查询时不需要执行聚合操作,并且支持谓词和索引下推,能够在支持实时和频繁更新等场景的同时,提供高效查询。

使用场景
  • 主键模型适用于实时和频繁更新的场景,例如:

    • 实时对接事务型数据至 StarRocks。事务型数据库中,除了插入数据外,一般还会涉及较多更新和删除数据的操作,因此事务型数据库的数据同步至 StarRocks 时,建议使用主键模型。通过 Flink-CDC 等工具直接对接TP的 Binlog,实时同步增删改的数据至主键模型,可以简化数据同步流程,并且相对于读时合并(Merge-On-Read)策略的更新模型,查询性能能够提升 3~10 倍。
    • 利用部分列更新轻松实现多留 JOIN。在用户画像等分析场景中,一般会采用大宽表方式来提升多维分析的性能,同时简化数据分析师的使用模型。而这种场景中的上游数据,往往可能来自于多个不同业务(比如来自购物消费业务、快递业务、银行业务等)或系统(比如计算用户不同标签属性的机器学习系统),主键模型的部分列更新功能就很好地满足这种需求,不同业务直接各自按需更新与业务相关地列即可,并且继续享受主键模型的实时同步增删改数据及高效的查询性能。
  • 主键模型适用于主键占用空间相对可控的场景。这是由于 StarRocks 存储引擎会为主键模型的主键创建索引,并导入时将主键索引加载至内存中,索引相对于其他模型,主键模型对内存的要求比较高。目前主键模型中,主键编码后,占用内存空间上线为 127 字节。

  • 如下两个场景中,主键占用空间相对可控:

    • 数据有冷热特征,即最近几天的热数据经常倍修改,老的冷数据很少被修改。例如,MySQL 订单表实时同步到 StarRocks 中提供分析查询。其中,数据按天分区,对订单的修改集中在最近几天新创建的订单,老的订单完成后就不再更新,因此导入时其主键索引就不会加载,也就不会占用内存,内存中仅会加载最近几天的索引。

      在这里插入图片描述

      如图,数据按天分区,最新两个分区的数据更新比较频繁。

    • 大宽表(数百到数千列)。主键只占整个数据的很小一部分,其内存开销比较低。比如用户状态和画像表,虽然列非常多,但总的用户数不大(千万至亿级别),主键索引内存占用相对可控。

      在这里插入图片描述

      如图,大宽表中排序键只占一小部分,且数据行数不多。

原理

主键模型是由 StarRocks 全新设计开发的存储引擎支撑。相比于更新模型,主键模型的元数据组织、读取、写入方式完全不同,不需要执行聚合操作,并且支持谓词和索引下推,极大地提高了查询性能。

更新模型整体上采用了读时合并的策略。虽然写入时处理简单高效,但是查询时需要在线聚合多版本。并且由于 Merge 算子的存在,谓词和索引无法下推,严重影响了查询性能。

而逐渐模型采用了 Delete + Insert 的策略,保证同一个主键下仅存在一条记录,这样就完全避免了 Merge 操作。具体实现方式如下:

  • StarRocks 收到对某记录的更新操作时,会通过主键索引找到该条记录的位置,并对其标记为删除,再插入一条新的记录。相当于把 Update 改写成 Delete + Insert。
  • StarRocks 收到对某记录的删除操作时,会通过主键索引找到该条记录的位置,对其标记为删除。

这样,查询时不需要执行聚合操作,不影响谓词和索引的下推,保证了查询的高效执行。

创建表
  • 例如,需要按天实时分析订单,则可以将时间dt、订单编号order_id 作为主键,其余列为指标列。建表语句如下:

    create table orders (
        dt date NOT NULL,
        order_id bigint NOT NULL,
        user_id int NOT NULL,
        merchant_id int NOT NULL,
        good_id int NOT NULL,
        good_name string NOT NULL,
        price int NOT NULL,
        cnt int NOT NULL,
        revenue int NOT NULL,
        state tinyint NOT NULL
    ) PRIMARY KEY (dt, order_id)
    PARTITION BY RANGE(`dt`) (
        PARTITION p20210820 VALUES [('2021-08-20'), ('2021-08-21')),
        PARTITION p20210821 VALUES [('2021-08-21'), ('2021-08-22')),
        ...
        PARTITION p20210929 VALUES [('2021-09-29'), ('2021-09-30')),
        PARTITION p20210930 VALUES [('2021-09-30'), ('2021-10-01'))
    ) DISTRIBUTED BY HASH(order_id) BUCKETS 4
    PROPERTIES("replication_num" = "3",
    "enable_persistent_index" = "true");
    

    建表时必须使用DISTRIBUTED BY HASH子句指定分桶键。

  • 例如,需要实时分析用户情况,则可以将用户 ID user_id 作为主键,其余为指标列。建表语句如下:

    create table users (
        user_id bigint NOT NULL,
        name string NOT NULL,
        email string NULL,
        address string NULL,
        age tinyint NULL,
        sex tinyint NULL,
        last_active datetime,
        property0 tinyint NOT NULL,
        property1 tinyint NOT NULL,
        property2 tinyint NOT NULL,
        property3 tinyint NOT NULL,
        ....
    ) PRIMARY KEY (user_id)
    DISTRIBUTED BY HASH(user_id) BUCKETS 4
    PROPERTIES("replication_num" = "3",
    "enable_persistent_index" = "true");
    

    建表时必须使用DISTRIBUTED BY HASH子句指定分桶键。

使用说明
  • 主键相关的说明:

    • 在建表语句中,主键必须定义在其他列之前。
    • 主键通过 PRIMARY KEY 定义。
    • 主键必须满足唯一性约束,且列的值不会修改。本示例中主键为 dtorder_id
    • 支持主键的数据类型为 BOOLEAN、TINYINT、SMALLINT、INT、BIGINT、LARGEINT、STRING、VARCHAR、DATE、DATETIME,且不允许为 NULL。
    • 分区列和分桶列必须在主键中。
    • 合理设置主键的列数和长度,以节约内存。建议主键为占用内存空间较少的数据类型,例如 INT、BIGINT等,暂时不建议为 VARCHAR。
    • 在建表前,建议根据主键的数据类型和表的行数来预估主键索引占用内存空间,以避免出现内存溢出。以下示例说明主键索引占用内存空间的计算方式:
      • 假设存在主键模型,排序键为 dtid,数据类型为 DATE(4个字节)、BIGINT(8个字节)。则排序键占 12 个字节。
      • 假设该表的热数据有 1000 万行,存储为三个副本。
      • 则内存占用的计算方式:(12 + 9(每行固定开销) ) * 1000W * 3 * 1.5(哈希表平均额外开销) = 945 M
  • enable_persistent_index:是否持久化主键索引,同时使用磁盘和内存存储主键索引,避免主键索引占用过大内存空间。通常情况下,持久化主键索引后,主键索引所占内存为之前的 1/10。可以在建表时,在PROPERTITES 中配置该参数,取值范围为 true 或者false (默认值)。

    • 建表后,如果需要修改该参数,可以参考 ALTER TABLE 修改表的属性。
    • 如果磁盘为固态硬盘 SSD,则建议设置为true
    • 自 2.3.0 版本起,StarRocks 支持配置该参数。
  • 自2.3.0 版本起,指标列新增支持 BITMAP、HLL数据类型。

  • 创建表时,支持为指标列创建 BITMAP、Bloom Filter 等索引。

  • 自2.4.0 版本起,主键模型支持单表和多表物化视图。

  • 暂不支持使用 ALTER TABLE 修改列类型。

5.3 数据分布

建表时,需要通过设置分区和分桶,指定数据分布方式,并且建议合理设置分区和分桶,实现数据均匀的分布。数据分布是指数据划分为子集,并按一定规则均衡地分布在不同节点上,能够有效裁剪数据扫描量,最大限度地利用集群的并发性能,从而提升查询性能。

1、数据分布概览

常见的数据分布方式

现代分布式数据库中,常见的数据分布方式有如下四种:Round-Robin、Range、List 和 Hash。如下图所示:

在这里插入图片描述

  • Round-Robin:以轮询的方式把数据逐个防止在相邻节点上。
  • Range:按区间进行数据分布。如下图所示,区间[1-3]、[4-6] 分别对应不同的范围(Range)。
  • List:直接基于离散的各个取值做数据分布,性别、省份等数据就满足这种离散的特性。每个离散值会映射到一个节点上,多个不同的取值可能也会映射到相同节点上。
  • Hash:通过哈希函数把数据映射到不同节点上。

为了更灵活地划分数据,除了单独采用上述四种数据分布方式之一以外,也可以根据具体的业务场景需求组合使用这些数据分布方式。常见的组合方式有 Hash+Hash、Range+Hash、Hash+List。

StarRocks 的数据分布方式

StarRocks 支持如下两种数据分布方式:

  • Hash 数据分布方式:一张表为一个分区,分区按照分桶键和分桶数量进一步进行数据划分。
  • Range+Hash 数据分布方式:一张表拆分成多个分区,每个分区按照分桶键和分桶数量进一步进行数据划分。

采用 Hash 分布的建表语句如下,其中分桶键为site_id

CREATE TABLE site_access(
site_id INT DEFAULT '10',
city_code SMALLINT,
user_name VARCHAR(32) DEFAULT '',
pv BIGINT SUM DEFAULT '0'
)
AGGREGATE KEY(site_id, city_code, user_name)
DISTRIBUTED BY HASH(site_id) BUCKETS 10;

采用 Range+Hash 组合分布的建表语句如下,其中分区键为 event_day,分桶键为 site_id

CREATE TABLE site_access(
event_day DATE,
site_id INT DEFAULT '10',
city_code VARCHAR(100),
user_name VARCHAR(32) DEFAULT '',
pv BIGINT SUM DEFAULT '0'
)
AGGREGATE KEY(event_day, site_id, city_code, user_name)
PARTITION BY RANGE(event_day)
(
PARTITION p1 VALUES LESS THAN ("2020-01-31"),
PARTITION p2 VALUES LESS THAN ("2020-02-29"),
PARTITION p3 VALUES LESS THAN ("2020-03-31")
)
DISTRIBUTED BY HASH(site_id) BUCKETS 10;
分区

分区用于将数据划分成不同的区间。分区的主要作用是将一张表按照分区键拆分成不同的管理单元,针对每一个管理单元选择相应的存储策略,比如副本数、冷热策略和存储介质等等。StarRocks 支持在一个集群内使用多种存储介质,可以将新数据所在分区放在 SSD 盘上,利用 SSD 的优秀的随机读写性能来提高查询性能,将旧数据存放在 SATA盘上,以节省数据存储的成本。

业务系统中一般会选择根据时间进行分区,以优化大量删除过期数据带来的性能问题,同时也方便冷热数据分级存储。

StarRocks 支持动态分区。可以按需为新数据动态创建分区,同时 StarRocks 会自动删除过期分区,从而确保数据的实效性,实现对分区的生命周期管理(TIme to Life,简称“TTL”),大幅减少运维管理的成本。

StarRocks 还支持批量创建分区。

分桶

分区的下一级是分桶,StarRocks 采用 Hash 算法作为分桶算法。在同一分区内,分桶键哈希值相同的数据形成 Tablet,Tablet 以多副本冗余的形式存储,是数据均衡和恢复的最小单位。Tablet 的副本由一个单独的本地存储引擎管理,数据导入和查询最终都下沉到所涉及的Tablet 副本上。建表时,必须指定分桶键。

2、设置分区和分桶

选择分区键

选择合理的分区键可以有效的裁剪扫描的数据量。**目前仅支持分区键的数据类型为日期和整数类型。**在实际业务场景中,一般从数据管理的角度选择分区键,常见的分区键为时间或者区域。按照分区键划分数据后,单个分区原始数据量建议不要超过 100 GB。

选择分桶键

选择高基数的列(例如唯一 ID)来作为分桶键,可以保证数据在各个分桶中尽可能均衡。如果数据倾斜情况严重,可以使用多个列作为数据的分桶键,但是不建议超过 3 个列。

还是以上述 Range+Hash 组合分布的建表语句为例:

CREATE TABLE site_access(
event_day DATE,
site_id INT DEFAULT '10',
city_code VARCHAR(100),
user_name VARCHAR(32) DEFAULT '',
pv BIGINT SUM DEFAULT '0'
)
AGGREGATE KEY(event_day, site_id, city_code, user_name)
PARTITION BY RANGE(event_day)
(
PARTITION p1 VALUES LESS THAN ("2020-01-31"),
PARTITION p2 VALUES LESS THAN ("2020-02-29"),
PARTITION p3 VALUES LESS THAN ("2020-03-31")
)
DISTRIBUTED BY HASH(site_id) BUCKETS 10;

如上示例中,site_access 表采用 site_id 作为分桶键,其原因在于,针对site_access 表的查询请求,基本上都以站点(高基数列)作为查询过滤条件。采用site_id 作为分桶键,可以在查询时裁剪掉大量无关分桶。

如下查询中,10个分桶中的 9 个分桶被裁剪,因而系统只需要扫描site_access 表中 1/10 的数据:

select sum(pv)
from site_access
where site_id = 54321;

但是如果 site_id 分布十分不均匀,大量的访问数据是关于少数网站的(幂律分布,二八规则),那么采用上述分桶方式会造成数据分布出现严重的倾斜,进而导致系统局部的性能瓶颈。此时,需要适当调整分桶的字段,以将数据打散,避免性能问题。例如,可以采用site_idcity_code 组合作为分桶键,将数据划分的更加均匀。相关建表语句如下:

CREATE TABLE site_access
(
site_id INT DEFAULT '10',
city_code SMALLINT,
user_name VARCHAR(32) DEFAULT '',
pv BIGINT SUM DEFAULT '0'
)
AGGREGATE KEY(site_id, city_code, user_name)
DISTRIBUTED BY HASH(site_id,city_code) BUCKETS 10;

在实际使用中,可以依据自身的业务特点选择以上两种分桶方式。采用site_id 的分桶方式对于段查询十分有利,能够减少节点之间的数据交换,提高集群整体性能;采用site_idcity_code的组合分桶方式对于长查询有利,能够利用分布式集群的整体并发性能。

说明:

  • 短查询是指扫描数据量不大,单机就能完成扫描的查询。
  • 长查询是指扫描数据量大,多机并行扫描能显著提升性能的查询。
确定分桶数量

在 StarRocks中,分桶是实际物理文件组织的单元。自 2.4 版本起,StarRocks 提供了自适应的 Tablet 并行扫描能力,即一个查询中涉及到的任意一个 Tablet 可能是由多个线程并行地分段扫描,减少了 Tablet 数量对查询能力的限制,从而可以简化对分桶数量的设定。简化后的分桶方式可以是:首先预估每个分区的数据量,然后按照每 10 GB 原始数据一个 Tablet 计算,从而确定分桶数量。

注意:

  • 需要执行SET GLOBAL enable_tablet_internal_parallel;,开启并行扫描 Tablet。
  • 不支持修改已创建的分区的分桶数量,支持在增加分区时为新增分区设置新的分桶数量。
管理分区
增加分区

增加新的分区,用于存储新的数据。新增分区的默认分桶数量和原分区相同。也可以根据新分区的数据规模调整分桶数量。

如下示例中,在 site_access 表添加新的分区,用于存储新月份的数据,并且调整分桶数量为20:

ALTER TABLE site_access
ADD PARTITION p4 VALUES LESS THAN ("2020-04-30")
DISTRIBUTED BY HASH(site_id) BUCKETS 20;
删除分区

执行如下语句,删除 site_access 表中分区 p1 及数据:

说明:分区中的数据不会立即删除,会在 Trash 中保留一段时间(默认为一天)。如果误删分区,可以通过 RECOVER 命令恢复分区及数据。

ALTER TABLE site_access
DROP PARTITION p1;
恢复分区

执行如下语句,恢复 site_access 表中分区 p1 及数据:

RECOVER PARTITION p1 FROM site_access;
查看分区

执行如下语句,查看 site_access 表中分区情况:

SHOW PARTITIONS FROM site_access;
最佳实践

对于 StarRocks 而言,分区和分桶的选择时非常关键的。在建表时选择合理的分区键和分桶键,可以有效提高集群整体性能。因此建议在选择分区键和分桶键时,根据业务情况进行调整。

  • 数据倾斜
    • 如果业务场景中单独采用倾斜度大的列做分桶,很大程度会导致访问倾斜,那么建议采用多列组合的方式进行数据分桶。
  • 高并发
    • 分区和分桶应该尽量覆盖查询语句所带的条件,这样可以有效减少扫描数据,提高并发。
  • 高并发
    • 尽量把数据打散,让集群以更高的并发扫描数据,完成相应计算。

3、管理动态分区

创建支持动态分区的表

如下示例,创建一张支持动态分区的表,表名为 site_access,动态分区通过PEROPERTIES 进行配置。分区的区间为当前时间的前后3天,总共6天。

CREATE TABLE site_access(
event_day DATE,
site_id INT DEFAULT '10',
city_code VARCHAR(100),
user_name VARCHAR(32) DEFAULT '',
pv BIGINT DEFAULT '0'
)
DUPLICATE KEY(event_day, site_id, city_code, user_name)
PARTITION BY RANGE(event_day)(
PARTITION p20200321 VALUES LESS THAN ("2020-03-22"),
PARTITION p20200322 VALUES LESS THAN ("2020-03-23"),
PARTITION p20200323 VALUES LESS THAN ("2020-03-24"),
PARTITION p20200324 VALUES LESS THAN ("2020-03-25")
)
DISTRIBUTED BY HASH(event_day, site_id) BUCKETS 32
PROPERTIES(
    "dynamic_partition.enable" = "true",
    "dynamic_partition.time_unit" = "DAY",
    "dynamic_partition.start" = "-3",
    "dynamic_partition.end" = "3",
    "dynamic_partition.prefix" = "p",
    "dynamic_partition.buckets" = "32"
);

PROPERTIES 配置向如下:

  • dynamic_partition.enable :是否开启动态分区特性,取值为 TRUEFALSE。默认值为 TRUE

  • dynamic_partition.time_unit :必填,调度动态分区特性的时间粒度,取值为 DAYWEEKMONTH,标识按天、周或月调度动态分区特性。并且,时间粒度必须对应分区名后缀格式。具体对应规则如下:

    • 取值为 DAY时,分区名后缀的格式应该为 yyyyMMdd,例如 20200321

      PARTITION BY RANGE(event_day)(
      PARTITION p20200321 VALUES LESS THAN ("2020-03-22"),
      PARTITION p20200322 VALUES LESS THAN ("2020-03-23"),
      PARTITION p20200323 VALUES LESS THAN ("2020-03-24"),
      PARTITION p20200324 VALUES LESS THAN ("2020-03-25")
      )
      
    • 取值为 WEEK时,分区名后缀的格式应该为 yyyy_ww,例如 2020_13 代表 2020年第13周。

    • 取值为 MONTH时,分区名后缀的格式应该为 yyyyMM,例如 202003

  • dynamic_partition.start:必填,动态分区的开始时间。以当天为基准,超过该时间范围的分区将会被删除。取值范围为小于 0 的负整数,最大值为 -1。默认值为 Integer.MIN_VALUE,即-2147483648。

  • dynamic_partition.end:必填,动态分区的结束时间。以当天为基准,提前创建指定数量的分区。取值范围为大于 0 的正整数,最小值为 1。

  • dynamic_partition.prefix :动态分区的前缀名,默认值为 p 。

  • dynamic_partition.buckets :动态分区的分桶数量,默认与 BUCKETS 关键词指定的分桶数量保持一致。

查看表当前的分区情况

开启动态分区特性后,会不断地自动增减分区。可以执行如下语句,查看表当前的分区情况:

SHOW PARTITIONS FROM site_access;

假设当前时间为 2020-03-25,在调度动态分区时,会删除分区上界小于 2020-03-22 的分区。则如上语句的返回结果中,Range 列显示当前分区的信息如下:

[types: [DATE]; keys: [2020-03-22];types: [DATE]; keys: [2020-03-23]; )
[types: [DATE]; keys: [2020-03-23];types: [DATE]; keys: [2020-03-24]; )
[types: [DATE]; keys: [2020-03-24];types: [DATE]; keys: [2020-03-25]; )
[types: [DATE]; keys: [2020-03-25];types: [DATE]; keys: [2020-03-26]; )
[types: [DATE]; keys: [2020-03-26];types: [DATE]; keys: [2020-03-27]; )
[types: [DATE]; keys: [2020-03-27];types: [DATE]; keys: [2020-03-28]; )
[types: [DATE]; keys: [2020-03-28];types: [DATE]; keys: [2020-03-29]; )
修改表的动态分区属性

执行 ALTER TABLE ,修改动态分区的属性,例如暂停或者开启动态分区特性。

ALTER TABLE site_access SET("dynamic_partition.enable"="false");
ALTER TABLE site_access SET("dynamic_partition.enable"="true");

说明:

  • 可以执行 SHOW CREATE TABLE 命令,查看表的动态分区属性。
  • ALTER TABLE 也适用于修改 PEROPERTIES 中的其他配置项。
使用说明

开启动态分区特性,相当于将创建分区的判断逻辑交由 StarRocks 完成。因此创建表时,必须保证动态分区配置项 dynamic_partition.time_unit 指定的时间粒度与分区名后缀格式对应,否则创建表会失败。具体对应规则如下:

  • dynamic_partition.time_unit 指定为 DAY 时,分区名后缀的格式应该为 yyyyMMdd,例如20200325

    PARTITION BY RANGE(event_day)(
    PARTITION p20200321 VALUES LESS THAN ("2020-03-22"),
    PARTITION p20200322 VALUES LESS THAN ("2020-03-23"),
    PARTITION p20200323 VALUES LESS THAN ("2020-03-24"),
    PARTITION p20200324 VALUES LESS THAN ("2020-03-25")
    )
    
  • dynamic_partition.time_unit 指定为 WEEK 时,分区名后缀的格式应该为 yyyy_ww,例如2020_13,代表 2020 年第 13 周。

  • dynamic_partition.time_unit 指定为 MONTH 时,分区名后缀的格式应该为 yyyyMM,例如 202003

4、批量创建分区

StarRocks 1.16 及以后支持该功能。

建表时和建表后,支持批量创建分区,通过 START、END 指定批量分区的开始和结束,EVERY 子句指定分区增量值。其中,批量分区包含 START 的值,但是不包含 END 的值。分区的命名规则同动态分区一样。

建表时批量创建日期分区

当分区键位日期类型时,建表时通过 START、END 指定批量分区的开始日期和结束日期,EVERY 子句指定分区增量值。并且 EVERY 子句中用 INTERVAL 关键字标识日期间隔,目前仅支持日期间隔的单位为 DAY、WEEK、MONTH、YEAR。

如下示例中,批量分区的开始日期为 2021-01-01 和结束日期为 2021-01-04,增量值为一天:

CREATE TABLE site_access (
    datekey DATE,
    site_id INT,
    city_code SMALLINT,
    user_name VARCHAR(32),
    pv BIGINT DEFAULT '0'
)
ENGINE=olap
DUPLICATE KEY(datekey, site_id, city_code, user_name)
PARTITION BY RANGE (datekey) (
    START ("2021-01-01") END ("2021-01-04") EVERY (INTERVAL 1 DAY)
)
DISTRIBUTED BY HASH(site_id) BUCKETS 10
PROPERTIES (
    "replication_num" = "1" 
);

则相当于在建表语句中使用如下 PARTITION BY 子句:

PARTITION BY RANGE (datekey) (
PARTITION p20210101 VALUES [('2021-01-01'), ('2021-01-02')),
PARTITION p20210102 VALUES [('2021-01-02'), ('2021-01-03')),
PARTITION p20210103 VALUES [('2021-01-03'), ('2021-01-04'))
)
建表时批量创建不同日期间隔的日期分区

建表时批量创建日期分区时,支持针对不同的日期分区区间(日期分区区间不能相重合),使用不同的 EVERY 子句指定日期间隔。一个日期分区区间,按照对应 EVERY 子句定义的日期间隔,批量创建分区,例如:

CREATE TABLE site_access (
    datekey DATE,
    site_id INT,
    city_code SMALLINT,
    user_name VARCHAR(32),
    pv BIGINT DEFAULT '0'
)
ENGINE=olap
DUPLICATE KEY(datekey, site_id, city_code, user_name)
PARTITION BY RANGE (datekey) (
    START ("2019-01-01") END ("2021-01-01") EVERY (INTERVAL 1 YEAR),
    START ("2021-01-01") END ("2021-05-01") EVERY (INTERVAL 1 MONTH),
    START ("2021-05-01") END ("2021-05-04") EVERY (INTERVAL 1 DAY)
)
DISTRIBUTED BY HASH(site_id) BUCKETS 10
PROPERTIES (
    "replication_num" = "1"
);

则相当于在建表语句中使用如下 PARTITION BY 子句:

PARTITION BY RANGE (datekey) (
PARTITION p2019 VALUES [('2019-01-01'), ('2020-01-01')),
PARTITION p2020 VALUES [('2020-01-01'), ('2021-01-01')),
PARTITION p202101 VALUES [('2021-01-01'), ('2021-02-01')),
PARTITION p202102 VALUES [('2021-02-01'), ('2021-03-01')),
PARTITION p202103 VALUES [('2021-03-01'), ('2021-04-01')),
PARTITION p202104 VALUES [('2021-04-01'), ('2021-05-01')),
PARTITION p20210501 VALUES [('2021-05-01'), ('2021-05-02')),
PARTITION p20210502 VALUES [('2021-05-02'), ('2021-05-03')),
PARTITION p20210503 VALUES [('2021-05-03'), ('2021-05-04'))
)
建表时批量创建数字分区

当分区键为整数类型时,建表时通过 START、END 指定批量分区的开始值和结束值,EVERY 子句指定分区增量值。

说明:分区键的值需要使用英文引号包裹,而 EVERY 子句中的分区增量值不用英文引号包裹。

如下实例中,批量分区的开始值为1 和结束值为 5,分区增量值为 1

CREATE TABLE site_access (
    datekey INT,
    site_id INT,
    city_code SMALLINT,
    user_name VARCHAR(32),
    pv BIGINT DEFAULT '0'
)
ENGINE=olap
DUPLICATE KEY(datekey, site_id, city_code, user_name)
PARTITION BY RANGE (datekey) (
    START ("1") END ("5") EVERY (1)
)
DISTRIBUTED BY HASH(site_id) BUCKETS 10
PROPERTIES (
    "replication_num" = "1"
);

则相当于在角标语句中使用如下PARTITION BY 子句:

PARTITION BY RANGE (datekey) (
PARTITION p1 VALUES [("1"), ("2")),
PARTITION p2 VALUES [("2"), ("3")),
PARTITION p3 VALUES [("3"), ("4")),
PARTITION p4 VALUES [("4"), ("5"))
)
建表后批量创建分区

建表后,支持通过ALTER TABLE 语句批量创建分区。相关语法与建表时批量创建分区类似,通过指定 ADD PARTITIONS 关键字,以及START、END 以及 EVERY 子句来批量创建分区。示例如下:

ALTER TABLE site_access 
ADD PARTITIONS START ("2021-01-04") END ("2021-01-06") EVERY (INTERVAL 1 DAY);

5.4 排序键和前缀索引

在建表时,可以指定一个或多个列构成“排序键(Sort Key)”。表中的行会根据排序键进行排序以后再落入磁盘进行存储。这样,查询数据时,可以使用排序列指定过滤条件,StarRocks 不需要扫描全表即可快速找到需要处理的数据,降低搜索的复杂度,从而加速查询。

同时,为减少内存开销,StarRocks 在排序键的基础上又引入了前缀索引(Prefix Index)。前缀索引时一种系数索引。表中每 1024 行数据构成一个逻辑数据块(Data Block)。每个逻辑数据块在前缀索引表中存储一个索引项,索引项的长度不超过36 字节,其内容为数据块中第一行数据的排序列组成的前缀,在查找前缀索引表时可以帮助确定该行数据所在逻辑数据块的起始行号。前缀索引的大小会比数据量少 1024 倍,因此会全量缓存在内存中,在实际查找的过程中可以有效加速查询。

排序原理

在明细模型中,排序列就是通过 DUPLICATE KEY 关键字指定的列。

在聚合模型中,排序列就是通过 AGGREGATE KEY 关键字指定的列。

在更新模型中,排序列就是通过 UNIQUE KEY 关键字指定的列。

在主键模型中,排序列就是主键列,通过 PRIMARY KEY 关键字指定。

在定义排序列时,需要注意以下几点:

  • 排序列必须从定义的第一列开始、并且是连续的。
  • 在定义各列时,计划作为排序列的列必须定义在其他普通列之前。
  • 排序列的顺序必须与表定义的列顺序一致。

例如,建表语句中声明要创建 site_idcity_codeuser_namepv 四列。这种情况下,正确的排序列组合和错误的排序列组合举例如下:

  • 正确的排序列
    • site_idcity_code
    • site_idcity_codeuser_name
  • 错误的排序列
    • city_codesite_id
    • city_codeuser_name
    • site_idcity_codepv

如下通过实例来说明如何创建使用各个数据模型的表。

明细模型

创建一个名为 site_access_duplicate 的明细模型表,包含 site_idcity_codeuser_namepv 四列,其中site_idcity_code 为排序列。

建表语句如下:

CREATE TABLE site_access_duplicate
(
    site_id INT DEFAULT '10',
    city_code SMALLINT,
    user_name VARCHAR(32) DEFAULT '',
    pv BIGINT DEFAULT '0'
)
DUPLICATE KEY(site_id, city_code)
DISTRIBUTED BY HASH(site_id) BUCKETS 10;
聚合模型

创建一个名为 site_access_aggregate 的明细模型表,包含 site_idcity_codeuser_namepv 四列,其中site_idcity_code 为排序列。

建表语句如下:

CREATE TABLE site_access_aggregate
(
    site_id INT DEFAULT '10',
    city_code SMALLINT,
    user_name VARCHAR(32) DEFAULT '',
    pv BIGINT SUM DEFAULT '0'
)
AGGREGATE KEY(site_id, city_code)
DISTRIBUTED BY HASH(site_id) BUCKETS 10;
更新模型

创建一个名为 site_access_aggregate 的明细模型表,包含 site_idcity_codeuser_namepv 四列,其中site_idcity_code 为排序列。

建表语句如下:

CREATE TABLE site_access_unique
(
    site_id INT DEFAULT '10',
    city_code SMALLINT,
    user_name VARCHAR(32) DEFAULT '',
    pv BIGINT DEFAULT '0'
)
UNIQUE KEY(site_id, city_code)
DISTRIBUTED BY HASH(site_id) BUCKETS 10;
主键模型

创建一个名为 site_access_primary 的明细模型表,包含 site_idcity_codeuser_namepv 四列,其中site_idcity_code 为排序列。

建表语句如下:

CREATE TABLE site_access_primary
(
    site_id INT DEFAULT '10',
    city_code SMALLINT,
    user_name VARCHAR(32) DEFAULT '',
    pv BIGINT DEFAULT '0'
)
PRIMARY KEY(site_id, city_code)
DISTRIBUTED BY HASH(site_id) BUCKETS 10;
排序效果

以上述建表语句为例,排序效果可以分为三种情况:

  • 如果查询条件只包含 site_idcity_code 两列,如下所示,则可以大幅减少查询过程中需要扫描的数据行:

    select sum(pv) from site_access_duplicate where site_id = 123 and city_code = 2;
    
  • 如果查询条件只包含 site_id 一列,如下所示,可以定位到只包含 site_id 的数据行:

    select sum(pv) from site_access_duplicate where site_id = 123;
    
  • 如果查询条件只包含 city_code 一列,如下所示,则需要扫描所有数据行,排序效果大打折扣:

    select sum(pv) from site_access_duplicate where city_code = 2;
    

    说明:这种情况下,排序列无法实现应有的排序效果。

在第一种情况下,为了定位到数据行的位置,需进行二分查找,已找到指定区间。如果数据行非常多,直接对 site_idcity_code 两列进行二分查找,需要把两列数据都加载到内存中,这样会消耗大量的内存空间。这时候可以使用前缀索引来减少缓存的数据量、并有效加速查询。

另外,在实际业务场景中,如果指定的排序列非常多,也会占用大量内存。为了避免这种情况,StarRocks 对前缀索引做了如下限制:

  • 前缀索引项的内容只能由数据块中第一行的排序列的前缀组成。
  • 前缀索引列的数量不能超过 3 。
  • 前缀索引项的长度不能超过 36 字节。
  • 前缀索引中不能包含 FLOAT 或 DOUBLE 类型的列。
  • 前缀索引中 VARCHAE 类型的列只能出现一次,并且处在末尾位置。
  • 当前缀索引的末尾列是 CHAR 或 VARCHAR 类型时,前缀索引项的长度不会超过 36 字节。
选择排序列

这里以 site_access_duplicate 表为例,介绍如何选择排序列。

  • 经常作为查询条件的列,建议选为排序列。

  • 当排序键涉及多个列的时候,建议把区分度高、且经常查询的列放在前面。

    区分度高的列是指取值个数较多、且持续增加的列。例如,在上述site_access_duplicate 表中,因为城市的数目是固定的,所以 city_code 列中取值的个数是固定的,而 site_id 列中取值的个数要比 city_code 列中取值的个数大得多、并且还会不断地增加,所以 site_id 列中取值的个数要比 city_code 列中取值的个数大得多、并且还会不断地增加,所以 site_id 列的区分度就比 city_code 列要高不少。

  • 排序键不应该包含过多的列。选择很多排序列并不有助于提升查询性能,而且会增大排序的开销,进而增加数据导入的开销。

综上所述,在为 site_access_duplicate 表选择排序列时,需要注意以下三点:

  • 如果需要经常按 site_id 列加 city_code 列的组合进行查询,建议选择site_id 列作为排序键的第一列。
  • 如果需要经常按 city_code 列进行查询、偶尔按 site_id 列加 city_code 列的组合进行查询,建议选择 city_code 列作为排序键的第一列。
  • 极端情况下,如果按 site_id 列加 city_code 列组合查询所占的比例比按 city_code 列单独查询所占的比例不相上下,可以创建一个以 city_code 列为第一列的 Rollup 表。Rollup 表会为 city_code 列再建一个排序索引(Sort Index)。

猜你喜欢

转载自blog.csdn.net/weixin_45866849/article/details/127572538